blob: 32b73550e5ad2ce1f75d1cd31f516354ccfbc509 [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Joe Perches02f77192012-01-15 00:38:44 -080017#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
Arend van Spriel5b435de2011-10-05 13:19:03 +020019#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/kthread.h>
22#include <linux/slab.h>
23#include <linux/skbuff.h>
24#include <linux/netdevice.h>
25#include <linux/etherdevice.h>
26#include <linux/mmc/sdio_func.h>
27#include <linux/random.h>
28#include <linux/spinlock.h>
29#include <linux/ethtool.h>
30#include <linux/fcntl.h>
31#include <linux/fs.h>
32#include <linux/uaccess.h>
33#include <linux/hardirq.h>
34#include <linux/mutex.h>
35#include <linux/wait.h>
Stephen Rothwellb7a57e72011-10-12 21:35:07 +020036#include <linux/module.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020037#include <net/cfg80211.h>
38#include <net/rtnetlink.h>
39#include <defs.h>
40#include <brcmu_utils.h>
41#include <brcmu_wifi.h>
42
43#include "dhd.h"
44#include "dhd_bus.h"
45#include "dhd_proto.h"
46#include "dhd_dbg.h"
47#include "wl_cfg80211.h"
Hante Meulemandb22ae82012-11-05 16:22:14 -080048#include "fwil.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020049
50MODULE_AUTHOR("Broadcom Corporation");
51MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver.");
52MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards");
53MODULE_LICENSE("Dual BSD/GPL");
54
Hante Meuleman21fff752012-11-05 16:22:16 -080055#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */
Arend van Spriel5b435de2011-10-05 13:19:03 +020056
Arend van Spriel5b435de2011-10-05 13:19:03 +020057/* Error bits */
Franky Lin4f96bf12011-11-22 17:21:48 -080058int brcmf_msg_level = BRCMF_ERROR_VAL;
Arend van Spriel5b435de2011-10-05 13:19:03 +020059module_param(brcmf_msg_level, int, 0);
60
Franky Lind08b6a32011-12-16 18:36:51 -080061int brcmf_ifname2idx(struct brcmf_pub *drvr, char *name)
Arend van Spriel5b435de2011-10-05 13:19:03 +020062{
63 int i = BRCMF_MAX_IFS;
64 struct brcmf_if *ifp;
65
66 if (name == NULL || *name == '\0')
67 return 0;
68
69 while (--i > 0) {
Franky Lind08b6a32011-12-16 18:36:51 -080070 ifp = drvr->iflist[i];
Arend van Spriel5b435de2011-10-05 13:19:03 +020071 if (ifp && !strncmp(ifp->ndev->name, name, IFNAMSIZ))
72 break;
73 }
74
75 brcmf_dbg(TRACE, "return idx %d for \"%s\"\n", i, name);
76
77 return i; /* default - the primary interface */
78}
79
80char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
81{
Arend van Spriel5b435de2011-10-05 13:19:03 +020082 if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
83 brcmf_dbg(ERROR, "ifidx %d out of range\n", ifidx);
84 return "<if_bad>";
85 }
86
Franky Lind08b6a32011-12-16 18:36:51 -080087 if (drvr->iflist[ifidx] == NULL) {
Arend van Spriel5b435de2011-10-05 13:19:03 +020088 brcmf_dbg(ERROR, "null i/f %d\n", ifidx);
89 return "<if_null>";
90 }
91
Franky Lind08b6a32011-12-16 18:36:51 -080092 if (drvr->iflist[ifidx]->ndev)
93 return drvr->iflist[ifidx]->ndev->name;
Arend van Spriel5b435de2011-10-05 13:19:03 +020094
95 return "<if_none>";
96}
97
98static void _brcmf_set_multicast_list(struct work_struct *work)
99{
Hante Meulemandb22ae82012-11-05 16:22:14 -0800100 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200101 struct net_device *ndev;
102 struct netdev_hw_addr *ha;
Hante Meulemandb22ae82012-11-05 16:22:14 -0800103 u32 cmd_value, cnt;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200104 __le32 cnt_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200105 char *buf, *bufp;
Hante Meulemandb22ae82012-11-05 16:22:14 -0800106 u32 buflen;
107 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200108
Hante Meulemandb22ae82012-11-05 16:22:14 -0800109 brcmf_dbg(TRACE, "enter\n");
110
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800111 ifp = container_of(work, struct brcmf_if, multicast_work);
Hante Meulemandb22ae82012-11-05 16:22:14 -0800112 ndev = ifp->ndev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200113
114 /* Determine initial value of allmulti flag */
Hante Meulemandb22ae82012-11-05 16:22:14 -0800115 cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200116
117 /* Send down the multicast list first. */
Hante Meulemandb22ae82012-11-05 16:22:14 -0800118 cnt = netdev_mc_count(ndev);
119 buflen = sizeof(cnt) + (cnt * ETH_ALEN);
120 buf = kmalloc(buflen, GFP_ATOMIC);
121 if (!buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200122 return;
Hante Meulemandb22ae82012-11-05 16:22:14 -0800123 bufp = buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200124
125 cnt_le = cpu_to_le32(cnt);
Hante Meulemandb22ae82012-11-05 16:22:14 -0800126 memcpy(bufp, &cnt_le, sizeof(cnt_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200127 bufp += sizeof(cnt_le);
128
129 netdev_for_each_mc_addr(ha, ndev) {
130 if (!cnt)
131 break;
132 memcpy(bufp, ha->addr, ETH_ALEN);
133 bufp += ETH_ALEN;
134 cnt--;
135 }
136
Hante Meulemandb22ae82012-11-05 16:22:14 -0800137 err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
138 if (err < 0) {
139 brcmf_dbg(ERROR, "Setting mcast_list failed, %d\n", err);
140 cmd_value = cnt ? true : cmd_value;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200141 }
142
143 kfree(buf);
144
Hante Meulemandb22ae82012-11-05 16:22:14 -0800145 /*
146 * Now send the allmulti setting. This is based on the setting in the
Arend van Spriel5b435de2011-10-05 13:19:03 +0200147 * net_device flags, but might be modified above to be turned on if we
148 * were trying to set some addresses and dongle rejected it...
149 */
Hante Meulemandb22ae82012-11-05 16:22:14 -0800150 err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
151 if (err < 0)
152 brcmf_dbg(ERROR, "Setting allmulti failed, %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200153
Hante Meulemandb22ae82012-11-05 16:22:14 -0800154 /*Finally, pick up the PROMISC flag */
155 cmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
156 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value);
157 if (err < 0)
158 brcmf_dbg(ERROR, "Setting BRCMF_C_SET_PROMISC failed, %d\n",
159 err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200160}
161
162static void
163_brcmf_set_mac_address(struct work_struct *work)
164{
Hante Meulemandb22ae82012-11-05 16:22:14 -0800165 struct brcmf_if *ifp;
Hante Meulemandb22ae82012-11-05 16:22:14 -0800166 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200167
Arend van Spriel5b435de2011-10-05 13:19:03 +0200168 brcmf_dbg(TRACE, "enter\n");
Hante Meulemandb22ae82012-11-05 16:22:14 -0800169
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800170 ifp = container_of(work, struct brcmf_if, setmacaddr_work);
171 err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
Hante Meulemandb22ae82012-11-05 16:22:14 -0800172 ETH_ALEN);
173 if (err < 0) {
174 brcmf_dbg(ERROR, "Setting cur_etheraddr failed, %d\n", err);
175 } else {
176 brcmf_dbg(TRACE, "MAC address updated to %pM\n",
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800177 ifp->mac_addr);
178 memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200179 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200180}
181
182static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
183{
Franky Line1b83582011-10-21 16:16:33 +0200184 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200185 struct sockaddr *sa = (struct sockaddr *)addr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200186
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800187 memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN);
188 schedule_work(&ifp->setmacaddr_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200189 return 0;
190}
191
192static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
193{
Franky Line1b83582011-10-21 16:16:33 +0200194 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200195
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800196 schedule_work(&ifp->multicast_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200197}
198
Arend van Spriel5b435de2011-10-05 13:19:03 +0200199static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
200{
201 int ret;
Franky Line1b83582011-10-21 16:16:33 +0200202 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800203 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200204
205 brcmf_dbg(TRACE, "Enter\n");
206
207 /* Reject if down */
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800208 if (!drvr->bus_if->drvr_up ||
Hante Meuleman6034d0a2012-11-05 16:22:29 -0800209 (drvr->bus_if->state != BRCMF_BUS_DATA)) {
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800210 brcmf_dbg(ERROR, "xmit rejected drvup=%d state=%d\n",
211 drvr->bus_if->drvr_up,
Franky Lind08b6a32011-12-16 18:36:51 -0800212 drvr->bus_if->state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200213 netif_stop_queue(ndev);
214 return -ENODEV;
215 }
216
Franky Lind08b6a32011-12-16 18:36:51 -0800217 if (!drvr->iflist[ifp->idx]) {
Franky Line1b83582011-10-21 16:16:33 +0200218 brcmf_dbg(ERROR, "bad ifidx %d\n", ifp->idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200219 netif_stop_queue(ndev);
220 return -ENODEV;
221 }
222
223 /* Make sure there's enough room for any header */
Franky Lind08b6a32011-12-16 18:36:51 -0800224 if (skb_headroom(skb) < drvr->hdrlen) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200225 struct sk_buff *skb2;
226
227 brcmf_dbg(INFO, "%s: insufficient headroom\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800228 brcmf_ifname(drvr, ifp->idx));
Franky Lin9c1a0432011-12-16 18:37:07 -0800229 drvr->bus_if->tx_realloc++;
Franky Lind08b6a32011-12-16 18:36:51 -0800230 skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200231 dev_kfree_skb(skb);
232 skb = skb2;
233 if (skb == NULL) {
234 brcmf_dbg(ERROR, "%s: skb_realloc_headroom failed\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800235 brcmf_ifname(drvr, ifp->idx));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200236 ret = -ENOMEM;
237 goto done;
238 }
239 }
240
Franky Lin8514fd02012-09-13 21:11:57 +0200241 /* Update multicast statistic */
242 if (skb->len >= ETH_ALEN) {
243 u8 *pktdata = (u8 *)(skb->data);
244 struct ethhdr *eh = (struct ethhdr *)pktdata;
245
246 if (is_multicast_ether_addr(eh->h_dest))
247 drvr->tx_multicast++;
248 if (ntohs(eh->h_proto) == ETH_P_PAE)
249 atomic_inc(&drvr->pend_8021x_cnt);
250 }
251
252 /* If the protocol uses a data header, apply it */
253 brcmf_proto_hdrpush(drvr, ifp->idx, skb);
254
255 /* Use bus module to send data frame */
256 ret = drvr->bus_if->brcmf_bus_txdata(drvr->dev, skb);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200257
258done:
259 if (ret)
Franky Lin719f2732011-12-16 18:37:06 -0800260 drvr->bus_if->dstats.tx_dropped++;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200261 else
Franky Lin719f2732011-12-16 18:37:06 -0800262 drvr->bus_if->dstats.tx_packets++;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200263
264 /* Return ok: we always eat the packet */
265 return 0;
266}
267
Hante Meuleman90d03ff2012-09-11 21:18:46 +0200268void brcmf_txflowblock(struct device *dev, bool state)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200269{
270 struct net_device *ndev;
Franky Lin2b459052011-12-16 18:36:57 -0800271 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
272 struct brcmf_pub *drvr = bus_if->drvr;
Hante Meuleman90d03ff2012-09-11 21:18:46 +0200273 int i;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200274
275 brcmf_dbg(TRACE, "Enter\n");
276
Hante Meuleman90d03ff2012-09-11 21:18:46 +0200277 for (i = 0; i < BRCMF_MAX_IFS; i++)
278 if (drvr->iflist[i]) {
279 ndev = drvr->iflist[i]->ndev;
280 if (state)
281 netif_stop_queue(ndev);
282 else
283 netif_wake_queue(ndev);
284 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200285}
286
Arend van Spriel5c36b992012-11-14 18:46:05 -0800287void brcmf_rx_frame(struct device *dev, u8 ifidx,
Arend van Spriel0b45bf72011-11-22 17:21:36 -0800288 struct sk_buff_head *skb_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200289{
Arend van Spriel5b435de2011-10-05 13:19:03 +0200290 unsigned char *eth;
291 uint len;
Arend van Spriel0b45bf72011-11-22 17:21:36 -0800292 struct sk_buff *skb, *pnext;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200293 struct brcmf_if *ifp;
Franky Lin228bb432011-12-16 18:37:00 -0800294 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
295 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200296
297 brcmf_dbg(TRACE, "Enter\n");
298
Arend van Spriel0b45bf72011-11-22 17:21:36 -0800299 skb_queue_walk_safe(skb_list, skb, pnext) {
300 skb_unlink(skb, skb_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200301
302 /* Get the protocol, maintain skb around eth_type_trans()
303 * The main reason for this hack is for the limitation of
304 * Linux 2.4 where 'eth_type_trans' uses the
305 * 'net->hard_header_len'
306 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
307 * coping of the packet coming from the network stack to add
308 * BDC, Hardware header etc, during network interface
309 * registration
310 * we set the 'net->hard_header_len' to ETH_HLEN + extra space
311 * required
312 * for BDC, Hardware header etc. and not just the ETH_HLEN
313 */
314 eth = skb->data;
315 len = skb->len;
316
Franky Lind08b6a32011-12-16 18:36:51 -0800317 ifp = drvr->iflist[ifidx];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200318 if (ifp == NULL)
Franky Lind08b6a32011-12-16 18:36:51 -0800319 ifp = drvr->iflist[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200320
Franky Lin0c094c72011-11-22 17:21:46 -0800321 if (!ifp || !ifp->ndev ||
322 ifp->ndev->reg_state != NETREG_REGISTERED) {
323 brcmu_pkt_buf_free_skb(skb);
324 continue;
325 }
326
Arend van Spriel5b435de2011-10-05 13:19:03 +0200327 skb->dev = ifp->ndev;
328 skb->protocol = eth_type_trans(skb, skb->dev);
329
330 if (skb->pkt_type == PACKET_MULTICAST)
Franky Lin719f2732011-12-16 18:37:06 -0800331 bus_if->dstats.multicast++;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200332
333 skb->data = eth;
334 skb->len = len;
335
336 /* Strip header, count, deliver upward */
337 skb_pull(skb, ETH_HLEN);
338
339 /* Process special event packets and then discard them */
Arend van Spriel5c36b992012-11-14 18:46:05 -0800340 brcmf_fweh_process_skb(drvr, skb, &ifidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200341
Franky Lind08b6a32011-12-16 18:36:51 -0800342 if (drvr->iflist[ifidx]) {
343 ifp = drvr->iflist[ifidx];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200344 ifp->ndev->last_rx = jiffies;
Franky Lind1a5b6f2011-10-21 16:16:34 +0200345 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200346
Franky Lin719f2732011-12-16 18:37:06 -0800347 bus_if->dstats.rx_bytes += skb->len;
348 bus_if->dstats.rx_packets++; /* Local count */
Arend van Spriel5b435de2011-10-05 13:19:03 +0200349
350 if (in_interrupt())
351 netif_rx(skb);
352 else
353 /* If the receive is not processed inside an ISR,
354 * the softirqd must be woken explicitly to service
355 * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
356 * by netif_rx_ni(), but in earlier kernels, we need
357 * to do it manually.
358 */
359 netif_rx_ni(skb);
360 }
361}
362
Franky Linc9957882011-12-16 18:36:58 -0800363void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200364{
365 uint ifidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200366 struct ethhdr *eh;
367 u16 type;
Franky Linc9957882011-12-16 18:36:58 -0800368 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
369 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200370
Franky Lind5625ee2011-12-16 18:37:01 -0800371 brcmf_proto_hdrpull(dev, &ifidx, txp);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200372
373 eh = (struct ethhdr *)(txp->data);
374 type = ntohs(eh->h_proto);
375
Hante Meuleman21fff752012-11-05 16:22:16 -0800376 if (type == ETH_P_PAE) {
Franky Lind08b6a32011-12-16 18:36:51 -0800377 atomic_dec(&drvr->pend_8021x_cnt);
Hante Meuleman21fff752012-11-05 16:22:16 -0800378 if (waitqueue_active(&drvr->pend_8021x_wait))
379 wake_up(&drvr->pend_8021x_wait);
380 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200381}
382
383static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
384{
Franky Line1b83582011-10-21 16:16:33 +0200385 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lin719f2732011-12-16 18:37:06 -0800386 struct brcmf_bus *bus_if = ifp->drvr->bus_if;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200387
388 brcmf_dbg(TRACE, "Enter\n");
389
Arend van Spriel5b435de2011-10-05 13:19:03 +0200390 /* Copy dongle stats to net device stats */
Franky Lin719f2732011-12-16 18:37:06 -0800391 ifp->stats.rx_packets = bus_if->dstats.rx_packets;
392 ifp->stats.tx_packets = bus_if->dstats.tx_packets;
393 ifp->stats.rx_bytes = bus_if->dstats.rx_bytes;
394 ifp->stats.tx_bytes = bus_if->dstats.tx_bytes;
395 ifp->stats.rx_errors = bus_if->dstats.rx_errors;
396 ifp->stats.tx_errors = bus_if->dstats.tx_errors;
397 ifp->stats.rx_dropped = bus_if->dstats.rx_dropped;
398 ifp->stats.tx_dropped = bus_if->dstats.tx_dropped;
399 ifp->stats.multicast = bus_if->dstats.multicast;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200400
401 return &ifp->stats;
402}
403
Hante Meulemandb22ae82012-11-05 16:22:14 -0800404/*
405 * Set current toe component enables in toe_ol iovar,
406 * and set toe global enable iovar
407 */
408static int brcmf_toe_set(struct brcmf_if *ifp, u32 toe_ol)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200409{
Hante Meulemandb22ae82012-11-05 16:22:14 -0800410 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200411
Hante Meulemandb22ae82012-11-05 16:22:14 -0800412 err = brcmf_fil_iovar_int_set(ifp, "toe_ol", toe_ol);
413 if (err < 0) {
414 brcmf_dbg(ERROR, "Setting toe_ol failed, %d\n", err);
415 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200416 }
417
Hante Meulemandb22ae82012-11-05 16:22:14 -0800418 err = brcmf_fil_iovar_int_set(ifp, "toe", (toe_ol != 0));
419 if (err < 0)
420 brcmf_dbg(ERROR, "Setting toe failed, %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200421
Hante Meulemandb22ae82012-11-05 16:22:14 -0800422 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200423
Arend van Spriel5b435de2011-10-05 13:19:03 +0200424}
425
426static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
427 struct ethtool_drvinfo *info)
428{
Franky Line1b83582011-10-21 16:16:33 +0200429 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800430 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200431
432 sprintf(info->driver, KBUILD_MODNAME);
Franky Lind08b6a32011-12-16 18:36:51 -0800433 sprintf(info->version, "%lu", drvr->drv_version);
434 sprintf(info->bus_info, "%s", dev_name(drvr->dev));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200435}
436
Stephen Hemminger3eb1fa72012-01-04 14:52:51 -0800437static const struct ethtool_ops brcmf_ethtool_ops = {
438 .get_drvinfo = brcmf_ethtool_get_drvinfo,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200439};
440
Hante Meulemandb22ae82012-11-05 16:22:14 -0800441static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200442{
Hante Meulemandb22ae82012-11-05 16:22:14 -0800443 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200444 struct ethtool_drvinfo info;
445 char drvname[sizeof(info.driver)];
446 u32 cmd;
447 struct ethtool_value edata;
448 u32 toe_cmpnt, csum_dir;
449 int ret;
450
451 brcmf_dbg(TRACE, "Enter\n");
452
453 /* all ethtool calls start with a cmd word */
454 if (copy_from_user(&cmd, uaddr, sizeof(u32)))
455 return -EFAULT;
456
457 switch (cmd) {
458 case ETHTOOL_GDRVINFO:
459 /* Copy out any request driver name */
460 if (copy_from_user(&info, uaddr, sizeof(info)))
461 return -EFAULT;
462 strncpy(drvname, info.driver, sizeof(info.driver));
463 drvname[sizeof(info.driver) - 1] = '\0';
464
465 /* clear struct for return */
466 memset(&info, 0, sizeof(info));
467 info.cmd = cmd;
468
469 /* if requested, identify ourselves */
470 if (strcmp(drvname, "?dhd") == 0) {
471 sprintf(info.driver, "dhd");
472 strcpy(info.version, BRCMF_VERSION_STR);
473 }
474
475 /* otherwise, require dongle to be up */
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800476 else if (!drvr->bus_if->drvr_up) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200477 brcmf_dbg(ERROR, "dongle is not up\n");
478 return -ENODEV;
479 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200480 /* finally, report dongle driver type */
Arend van Spriel5b435de2011-10-05 13:19:03 +0200481 else
Hante Meuleman91917c772012-10-22 10:36:15 -0700482 sprintf(info.driver, "wl");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200483
Franky Lind08b6a32011-12-16 18:36:51 -0800484 sprintf(info.version, "%lu", drvr->drv_version);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200485 if (copy_to_user(uaddr, &info, sizeof(info)))
486 return -EFAULT;
487 brcmf_dbg(CTL, "given %*s, returning %s\n",
488 (int)sizeof(drvname), drvname, info.driver);
489 break;
490
491 /* Get toe offload components from dongle */
492 case ETHTOOL_GRXCSUM:
493 case ETHTOOL_GTXCSUM:
Hante Meulemandb22ae82012-11-05 16:22:14 -0800494 ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200495 if (ret < 0)
496 return ret;
497
498 csum_dir =
499 (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
500
501 edata.cmd = cmd;
502 edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
503
504 if (copy_to_user(uaddr, &edata, sizeof(edata)))
505 return -EFAULT;
506 break;
507
508 /* Set toe offload components in dongle */
509 case ETHTOOL_SRXCSUM:
510 case ETHTOOL_STXCSUM:
511 if (copy_from_user(&edata, uaddr, sizeof(edata)))
512 return -EFAULT;
513
514 /* Read the current settings, update and write back */
Hante Meulemandb22ae82012-11-05 16:22:14 -0800515 ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200516 if (ret < 0)
517 return ret;
518
519 csum_dir =
520 (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
521
522 if (edata.data != 0)
523 toe_cmpnt |= csum_dir;
524 else
525 toe_cmpnt &= ~csum_dir;
526
Hante Meulemandb22ae82012-11-05 16:22:14 -0800527 ret = brcmf_toe_set(ifp, toe_cmpnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200528 if (ret < 0)
529 return ret;
530
531 /* If setting TX checksum mode, tell Linux the new mode */
532 if (cmd == ETHTOOL_STXCSUM) {
533 if (edata.data)
Hante Meulemandb22ae82012-11-05 16:22:14 -0800534 ifp->ndev->features |= NETIF_F_IP_CSUM;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200535 else
Hante Meulemandb22ae82012-11-05 16:22:14 -0800536 ifp->ndev->features &= ~NETIF_F_IP_CSUM;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200537 }
538
539 break;
540
541 default:
542 return -EOPNOTSUPP;
543 }
544
545 return 0;
546}
547
548static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr,
549 int cmd)
550{
Franky Line1b83582011-10-21 16:16:33 +0200551 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800552 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200553
Franky Line1b83582011-10-21 16:16:33 +0200554 brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifp->idx, cmd);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200555
Franky Lind08b6a32011-12-16 18:36:51 -0800556 if (!drvr->iflist[ifp->idx])
Arend van Spriel5b435de2011-10-05 13:19:03 +0200557 return -1;
558
559 if (cmd == SIOCETHTOOL)
Hante Meulemandb22ae82012-11-05 16:22:14 -0800560 return brcmf_ethtool(ifp, ifr->ifr_data);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200561
562 return -EOPNOTSUPP;
563}
564
Arend van Spriel5b435de2011-10-05 13:19:03 +0200565static int brcmf_netdev_stop(struct net_device *ndev)
566{
Franky Line1b83582011-10-21 16:16:33 +0200567 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800568 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200569
570 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman817b1782012-11-05 16:22:30 -0800571
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800572 if (drvr->bus_if->drvr_up == 0)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200573 return 0;
574
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800575 brcmf_cfg80211_down(ndev);
Hante Meuleman817b1782012-11-05 16:22:30 -0800576
Arend van Spriel5b435de2011-10-05 13:19:03 +0200577 /* Set state and stop OS transmissions */
John W. Linville57adc1f2012-01-03 15:16:34 -0500578 drvr->bus_if->drvr_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200579 netif_stop_queue(ndev);
580
581 return 0;
582}
583
584static int brcmf_netdev_open(struct net_device *ndev)
585{
Franky Line1b83582011-10-21 16:16:33 +0200586 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800587 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel1bb1f382012-02-09 21:09:05 +0100588 struct brcmf_bus *bus_if = drvr->bus_if;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200589 u32 toe_ol;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200590 s32 ret = 0;
591
Franky Line1b83582011-10-21 16:16:33 +0200592 brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200593
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800594 /* If bus is not ready, can't continue */
595 if (bus_if->state != BRCMF_BUS_DATA) {
596 brcmf_dbg(ERROR, "failed bus is not ready\n");
597 return -EAGAIN;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200598 }
Franky Lin33380842012-04-11 11:52:47 +0200599
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800600 atomic_set(&drvr->pend_8021x_cnt, 0);
601
602 memcpy(ndev->dev_addr, drvr->mac, ETH_ALEN);
603
604 /* Get current TOE mode from dongle */
605 if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
606 && (toe_ol & TOE_TX_CSUM_OL) != 0)
607 drvr->iflist[ifp->idx]->ndev->features |=
608 NETIF_F_IP_CSUM;
609 else
610 drvr->iflist[ifp->idx]->ndev->features &=
611 ~NETIF_F_IP_CSUM;
612
Franky Lin33380842012-04-11 11:52:47 +0200613 /* make sure RF is ready for work */
Hante Meulemandb22ae82012-11-05 16:22:14 -0800614 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
Franky Lin33380842012-04-11 11:52:47 +0200615
Arend van Spriel5b435de2011-10-05 13:19:03 +0200616 /* Allow transmit calls */
617 netif_start_queue(ndev);
John W. Linville57adc1f2012-01-03 15:16:34 -0500618 drvr->bus_if->drvr_up = true;
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800619 if (brcmf_cfg80211_up(ndev)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200620 brcmf_dbg(ERROR, "failed to bring up cfg80211\n");
621 return -1;
622 }
623
624 return ret;
625}
626
Franky Lindfded552011-10-21 16:16:20 +0200627static const struct net_device_ops brcmf_netdev_ops_pri = {
628 .ndo_open = brcmf_netdev_open,
629 .ndo_stop = brcmf_netdev_stop,
630 .ndo_get_stats = brcmf_netdev_get_stats,
631 .ndo_do_ioctl = brcmf_netdev_ioctl_entry,
632 .ndo_start_xmit = brcmf_netdev_start_xmit,
633 .ndo_set_mac_address = brcmf_netdev_set_mac_address,
634 .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
635};
636
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800637static const struct net_device_ops brcmf_netdev_ops_virt = {
638 .ndo_open = brcmf_cfg80211_up,
639 .ndo_stop = brcmf_cfg80211_down,
640 .ndo_get_stats = brcmf_netdev_get_stats,
641 .ndo_do_ioctl = brcmf_netdev_ioctl_entry,
642 .ndo_start_xmit = brcmf_netdev_start_xmit,
643 .ndo_set_mac_address = brcmf_netdev_set_mac_address,
644 .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
645};
646
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700647int brcmf_net_attach(struct brcmf_if *ifp)
Arend van Spriel75c49902012-04-11 11:52:43 +0200648{
Arend van Spriel3625c142012-04-11 11:52:44 +0200649 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel75c49902012-04-11 11:52:43 +0200650 struct net_device *ndev;
Arend van Spriel3625c142012-04-11 11:52:44 +0200651 u8 temp_addr[ETH_ALEN];
Arend van Spriel75c49902012-04-11 11:52:43 +0200652
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800653 brcmf_dbg(TRACE, "ifidx %d mac %pM\n", ifp->idx, ifp->mac_addr);
654 ndev = ifp->ndev;
Arend van Spriel75c49902012-04-11 11:52:43 +0200655
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800656 /* set appropriate operations */
657 if (!ifp->idx)
658 ndev->netdev_ops = &brcmf_netdev_ops_pri;
659 else
660 ndev->netdev_ops = &brcmf_netdev_ops_virt;
Arend van Spriel75c49902012-04-11 11:52:43 +0200661
662 /*
Arend van Spriel3625c142012-04-11 11:52:44 +0200663 * determine mac address to use
Arend van Spriel75c49902012-04-11 11:52:43 +0200664 */
Arend van Spriel3625c142012-04-11 11:52:44 +0200665 if (is_valid_ether_addr(ifp->mac_addr))
666 memcpy(temp_addr, ifp->mac_addr, ETH_ALEN);
667 else
Arend van Spriel75c49902012-04-11 11:52:43 +0200668 memcpy(temp_addr, drvr->mac, ETH_ALEN);
669
Arend van Spriel75c49902012-04-11 11:52:43 +0200670 ndev->hard_header_len = ETH_HLEN + drvr->hdrlen;
671 ndev->ethtool_ops = &brcmf_ethtool_ops;
672
673 drvr->rxsz = ndev->mtu + ndev->hard_header_len +
674 drvr->hdrlen;
675
676 memcpy(ndev->dev_addr, temp_addr, ETH_ALEN);
677
Arend van Spriel75c49902012-04-11 11:52:43 +0200678 if (register_netdev(ndev) != 0) {
679 brcmf_dbg(ERROR, "couldn't register the net device\n");
680 goto fail;
681 }
682
683 brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
684
685 return 0;
686
687fail:
688 ndev->netdev_ops = NULL;
689 return -EBADE;
690}
691
Arend van Spriel699b5e52012-11-14 18:46:08 -0800692struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, int ifidx, s32 bssidx,
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700693 char *name, u8 *mac_addr)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200694{
695 struct brcmf_if *ifp;
Franky Line1b83582011-10-21 16:16:33 +0200696 struct net_device *ndev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200697
Franky Lin15d45b62011-10-21 16:16:32 +0200698 brcmf_dbg(TRACE, "idx %d\n", ifidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200699
Franky Lind08b6a32011-12-16 18:36:51 -0800700 ifp = drvr->iflist[ifidx];
Franky Line1b83582011-10-21 16:16:33 +0200701 /*
702 * Delete the existing interface before overwriting it
703 * in case we missed the BRCMF_E_IF_DEL event.
704 */
705 if (ifp) {
706 brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n",
707 ifp->ndev->name);
708 netif_stop_queue(ifp->ndev);
709 unregister_netdev(ifp->ndev);
710 free_netdev(ifp->ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800711 drvr->iflist[ifidx] = NULL;
Franky Lin15d45b62011-10-21 16:16:32 +0200712 }
Franky Line1b83582011-10-21 16:16:33 +0200713
714 /* Allocate netdev, including space for private structure */
715 ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup);
716 if (!ndev) {
717 brcmf_dbg(ERROR, "OOM - alloc_netdev\n");
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700718 return ERR_PTR(-ENOMEM);
Franky Line1b83582011-10-21 16:16:33 +0200719 }
720
721 ifp = netdev_priv(ndev);
722 ifp->ndev = ndev;
Franky Lind08b6a32011-12-16 18:36:51 -0800723 ifp->drvr = drvr;
724 drvr->iflist[ifidx] = ifp;
Franky Lin15d45b62011-10-21 16:16:32 +0200725 ifp->idx = ifidx;
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -0700726 ifp->bssidx = bssidx;
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800727
728 INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
729 INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
730
Franky Lin15d45b62011-10-21 16:16:32 +0200731 if (mac_addr != NULL)
732 memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200733
Franky Lin15d45b62011-10-21 16:16:32 +0200734 brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n",
735 current->pid, ifp->ndev->name);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200736
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700737 return ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200738}
739
Franky Lind08b6a32011-12-16 18:36:51 -0800740void brcmf_del_if(struct brcmf_pub *drvr, int ifidx)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200741{
742 struct brcmf_if *ifp;
743
744 brcmf_dbg(TRACE, "idx %d\n", ifidx);
745
Franky Lind08b6a32011-12-16 18:36:51 -0800746 ifp = drvr->iflist[ifidx];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200747 if (!ifp) {
748 brcmf_dbg(ERROR, "Null interface\n");
749 return;
750 }
Franky Lindfded552011-10-21 16:16:20 +0200751 if (ifp->ndev) {
752 if (ifidx == 0) {
753 if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
754 rtnl_lock();
755 brcmf_netdev_stop(ifp->ndev);
756 rtnl_unlock();
757 }
758 } else {
759 netif_stop_queue(ifp->ndev);
760 }
761
Arend van Sprielbdf5ff52012-11-14 18:46:09 -0800762 cancel_work_sync(&ifp->setmacaddr_work);
763 cancel_work_sync(&ifp->multicast_work);
764
Arend van Spriel5b435de2011-10-05 13:19:03 +0200765 unregister_netdev(ifp->ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800766 drvr->iflist[ifidx] = NULL;
Franky Lindfded552011-10-21 16:16:20 +0200767 if (ifidx == 0)
Franky Lind08b6a32011-12-16 18:36:51 -0800768 brcmf_cfg80211_detach(drvr->config);
Franky Lindfded552011-10-21 16:16:20 +0200769 free_netdev(ifp->ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200770 }
771}
772
Franky Lin2447ffb2011-12-16 18:37:10 -0800773int brcmf_attach(uint bus_hdrlen, struct device *dev)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200774{
Franky Lind08b6a32011-12-16 18:36:51 -0800775 struct brcmf_pub *drvr = NULL;
Franky Lin712ac5b2011-12-16 18:37:09 -0800776 int ret = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200777
778 brcmf_dbg(TRACE, "Enter\n");
779
Arend van Spriel5b435de2011-10-05 13:19:03 +0200780 /* Allocate primary brcmf_info */
Franky Lind08b6a32011-12-16 18:36:51 -0800781 drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
782 if (!drvr)
Franky Lin712ac5b2011-12-16 18:37:09 -0800783 return -ENOMEM;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200784
Franky Lind08b6a32011-12-16 18:36:51 -0800785 mutex_init(&drvr->proto_block);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200786
787 /* Link to bus module */
Franky Lind08b6a32011-12-16 18:36:51 -0800788 drvr->hdrlen = bus_hdrlen;
789 drvr->bus_if = dev_get_drvdata(dev);
Franky Lin55a63bc2011-12-16 18:36:54 -0800790 drvr->bus_if->drvr = drvr;
Franky Lind08b6a32011-12-16 18:36:51 -0800791 drvr->dev = dev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200792
Arend van Sprield319a7c2012-06-09 22:51:43 +0200793 /* create device debugfs folder */
794 brcmf_debugfs_attach(drvr);
795
Arend van Spriel5b435de2011-10-05 13:19:03 +0200796 /* Attach and link in the protocol */
Franky Lin712ac5b2011-12-16 18:37:09 -0800797 ret = brcmf_proto_attach(drvr);
798 if (ret != 0) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200799 brcmf_dbg(ERROR, "brcmf_prot_attach failed\n");
800 goto fail;
801 }
802
Arend van Spriel5c36b992012-11-14 18:46:05 -0800803 /* attach firmware event handler */
804 brcmf_fweh_attach(drvr);
805
Franky Lin135e4c62012-06-26 21:26:34 +0200806 INIT_LIST_HEAD(&drvr->bus_if->dcmd_list);
807
Hante Meuleman21fff752012-11-05 16:22:16 -0800808 init_waitqueue_head(&drvr->pend_8021x_wait);
809
Franky Lin712ac5b2011-12-16 18:37:09 -0800810 return ret;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200811
812fail:
Franky Lin712ac5b2011-12-16 18:37:09 -0800813 brcmf_detach(dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200814
Franky Lin712ac5b2011-12-16 18:37:09 -0800815 return ret;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200816}
817
Franky Lined683c92011-12-16 18:36:55 -0800818int brcmf_bus_start(struct device *dev)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200819{
820 int ret = -1;
Franky Lined683c92011-12-16 18:36:55 -0800821 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
822 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700823 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200824
825 brcmf_dbg(TRACE, "\n");
826
827 /* Bring up the bus */
Franky Lin99a0b8f2011-12-16 18:37:14 -0800828 ret = bus_if->brcmf_bus_init(dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200829 if (ret != 0) {
830 brcmf_dbg(ERROR, "brcmf_sdbrcm_bus_init failed %d\n", ret);
831 return ret;
832 }
833
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700834 /* add primary networking interface */
Arend van Spriel699b5e52012-11-14 18:46:08 -0800835 ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700836 if (IS_ERR(ifp))
837 return PTR_ERR(ifp);
838
Hante Meuleman0af29bf2012-10-22 10:36:25 -0700839 /* signal bus ready */
840 bus_if->state = BRCMF_BUS_DATA;
841
842 /* Bus is ready, do any initialization */
843 ret = brcmf_c_preinit_dcmds(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200844 if (ret < 0)
Hante Meuleman6b028c52012-11-05 16:22:15 -0800845 goto fail;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200846
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700847 drvr->config = brcmf_cfg80211_attach(drvr);
Hante Meuleman6b028c52012-11-05 16:22:15 -0800848 if (drvr->config == NULL) {
849 ret = -ENOMEM;
850 goto fail;
851 }
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700852
Arend van Spriel5c36b992012-11-14 18:46:05 -0800853 ret = brcmf_fweh_activate_events(ifp);
854 if (ret < 0)
855 goto fail;
856
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700857 ret = brcmf_net_attach(ifp);
Hante Meuleman6b028c52012-11-05 16:22:15 -0800858fail:
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700859 if (ret < 0) {
Arend van Spriel5c36b992012-11-14 18:46:05 -0800860 brcmf_dbg(ERROR, "failed: %d\n", ret);
Hante Meuleman6b028c52012-11-05 16:22:15 -0800861 if (drvr->config)
862 brcmf_cfg80211_detach(drvr->config);
863 free_netdev(drvr->iflist[0]->ndev);
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700864 drvr->iflist[0] = NULL;
Arend van Spriel474a64c2012-04-11 11:52:45 +0200865 return ret;
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700866 }
867
Arend van Spriel5b435de2011-10-05 13:19:03 +0200868 return 0;
869}
870
Arend van Spriel5b435de2011-10-05 13:19:03 +0200871static void brcmf_bus_detach(struct brcmf_pub *drvr)
872{
Arend van Spriel5b435de2011-10-05 13:19:03 +0200873 brcmf_dbg(TRACE, "Enter\n");
874
875 if (drvr) {
Franky Lind08b6a32011-12-16 18:36:51 -0800876 /* Stop the protocol module */
877 brcmf_proto_stop(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200878
Franky Lind08b6a32011-12-16 18:36:51 -0800879 /* Stop the bus module */
Franky Lina9ffda82011-12-16 18:37:12 -0800880 drvr->bus_if->brcmf_bus_stop(drvr->dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200881 }
882}
883
Franky Lin5f947ad2011-12-16 18:36:56 -0800884void brcmf_detach(struct device *dev)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200885{
Franky Lin5f947ad2011-12-16 18:36:56 -0800886 int i;
887 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
888 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200889
890 brcmf_dbg(TRACE, "Enter\n");
891
Hante Meuleman817b1782012-11-05 16:22:30 -0800892 if (drvr == NULL)
893 return;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200894
Arend van Spriel5c36b992012-11-14 18:46:05 -0800895 /* stop firmware event handling */
896 brcmf_fweh_detach(drvr);
897
Franky Lin5f947ad2011-12-16 18:36:56 -0800898 /* make sure primary interface removed last */
899 for (i = BRCMF_MAX_IFS-1; i > -1; i--)
900 if (drvr->iflist[i])
901 brcmf_del_if(drvr, i);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200902
Franky Lin5f947ad2011-12-16 18:36:56 -0800903 brcmf_bus_detach(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200904
Franky Lin89fdb462012-02-09 21:09:01 +0100905 if (drvr->prot) {
Franky Lin5f947ad2011-12-16 18:36:56 -0800906 brcmf_proto_detach(drvr);
Franky Lin89fdb462012-02-09 21:09:01 +0100907 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200908
Arend van Sprield319a7c2012-06-09 22:51:43 +0200909 brcmf_debugfs_detach(drvr);
Franky Lin5f947ad2011-12-16 18:36:56 -0800910 bus_if->drvr = NULL;
911 kfree(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200912}
913
Franky Lind08b6a32011-12-16 18:36:51 -0800914static int brcmf_get_pend_8021x_cnt(struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200915{
Franky Lind08b6a32011-12-16 18:36:51 -0800916 return atomic_read(&drvr->pend_8021x_cnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200917}
918
Arend van Spriel5b435de2011-10-05 13:19:03 +0200919int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
920{
Franky Line1b83582011-10-21 16:16:33 +0200921 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800922 struct brcmf_pub *drvr = ifp->drvr;
Hante Meuleman21fff752012-11-05 16:22:16 -0800923 int err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200924
Hante Meuleman21fff752012-11-05 16:22:16 -0800925 err = wait_event_timeout(drvr->pend_8021x_wait,
926 !brcmf_get_pend_8021x_cnt(drvr),
927 msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX));
928
929 WARN_ON(!err);
930
931 return !err;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200932}
933
Arend van Spriele64a4b72012-03-02 22:55:49 +0100934static void brcmf_driver_init(struct work_struct *work)
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100935{
Arend van Sprield319a7c2012-06-09 22:51:43 +0200936 brcmf_debugfs_init();
937
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100938#ifdef CONFIG_BRCMFMAC_SDIO
Arend van Spriel549040a2012-03-02 22:55:48 +0100939 brcmf_sdio_init();
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100940#endif
Arend van Spriel71bb2442012-02-09 21:09:08 +0100941#ifdef CONFIG_BRCMFMAC_USB
Arend van Spriel549040a2012-03-02 22:55:48 +0100942 brcmf_usb_init();
Arend van Spriel71bb2442012-02-09 21:09:08 +0100943#endif
Arend van Spriele64a4b72012-03-02 22:55:49 +0100944}
945static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init);
946
947static int __init brcmfmac_module_init(void)
948{
949 if (!schedule_work(&brcmf_driver_work))
950 return -EBUSY;
951
Arend van Spriel549040a2012-03-02 22:55:48 +0100952 return 0;
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100953}
954
Arend van Spriele64a4b72012-03-02 22:55:49 +0100955static void __exit brcmfmac_module_exit(void)
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100956{
Arend van Spriele64a4b72012-03-02 22:55:49 +0100957 cancel_work_sync(&brcmf_driver_work);
958
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100959#ifdef CONFIG_BRCMFMAC_SDIO
960 brcmf_sdio_exit();
961#endif
Arend van Spriel71bb2442012-02-09 21:09:08 +0100962#ifdef CONFIG_BRCMFMAC_USB
963 brcmf_usb_exit();
964#endif
Arend van Sprield319a7c2012-06-09 22:51:43 +0200965 brcmf_debugfs_exit();
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +0100966}
967
Arend van Spriele64a4b72012-03-02 22:55:49 +0100968module_init(brcmfmac_module_init);
969module_exit(brcmfmac_module_exit);