blob: b1f26b53fa27873124bca3079b43ba89d61f7da7 [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"
Arend van Spriel5b435de2011-10-05 13:19:03 +020048
49MODULE_AUTHOR("Broadcom Corporation");
50MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver.");
51MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards");
52MODULE_LICENSE("Dual BSD/GPL");
53
54
Arend van Spriel5b435de2011-10-05 13:19:03 +020055/* Error bits */
Franky Lin4f96bf12011-11-22 17:21:48 -080056int brcmf_msg_level = BRCMF_ERROR_VAL;
Arend van Spriel5b435de2011-10-05 13:19:03 +020057module_param(brcmf_msg_level, int, 0);
58
Franky Lind08b6a32011-12-16 18:36:51 -080059int brcmf_ifname2idx(struct brcmf_pub *drvr, char *name)
Arend van Spriel5b435de2011-10-05 13:19:03 +020060{
61 int i = BRCMF_MAX_IFS;
62 struct brcmf_if *ifp;
63
64 if (name == NULL || *name == '\0')
65 return 0;
66
67 while (--i > 0) {
Franky Lind08b6a32011-12-16 18:36:51 -080068 ifp = drvr->iflist[i];
Arend van Spriel5b435de2011-10-05 13:19:03 +020069 if (ifp && !strncmp(ifp->ndev->name, name, IFNAMSIZ))
70 break;
71 }
72
73 brcmf_dbg(TRACE, "return idx %d for \"%s\"\n", i, name);
74
75 return i; /* default - the primary interface */
76}
77
78char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
79{
Arend van Spriel5b435de2011-10-05 13:19:03 +020080 if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
81 brcmf_dbg(ERROR, "ifidx %d out of range\n", ifidx);
82 return "<if_bad>";
83 }
84
Franky Lind08b6a32011-12-16 18:36:51 -080085 if (drvr->iflist[ifidx] == NULL) {
Arend van Spriel5b435de2011-10-05 13:19:03 +020086 brcmf_dbg(ERROR, "null i/f %d\n", ifidx);
87 return "<if_null>";
88 }
89
Franky Lind08b6a32011-12-16 18:36:51 -080090 if (drvr->iflist[ifidx]->ndev)
91 return drvr->iflist[ifidx]->ndev->name;
Arend van Spriel5b435de2011-10-05 13:19:03 +020092
93 return "<if_none>";
94}
95
96static void _brcmf_set_multicast_list(struct work_struct *work)
97{
98 struct net_device *ndev;
99 struct netdev_hw_addr *ha;
Arend van Sprielb5036242011-10-12 20:51:17 +0200100 u32 dcmd_value, cnt;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200101 __le32 cnt_le;
Arend van Sprielb5036242011-10-12 20:51:17 +0200102 __le32 dcmd_le_value;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200103
104 struct brcmf_dcmd dcmd;
105 char *buf, *bufp;
106 uint buflen;
107 int ret;
108
Franky Lind08b6a32011-12-16 18:36:51 -0800109 struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200110 multicast_work);
111
Franky Lind08b6a32011-12-16 18:36:51 -0800112 ndev = drvr->iflist[0]->ndev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200113 cnt = netdev_mc_count(ndev);
114
115 /* Determine initial value of allmulti flag */
Arend van Sprielb5036242011-10-12 20:51:17 +0200116 dcmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200117
118 /* Send down the multicast list first. */
119
120 buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETH_ALEN);
121 bufp = buf = kmalloc(buflen, GFP_ATOMIC);
122 if (!bufp)
123 return;
124
125 strcpy(bufp, "mcast_list");
126 bufp += strlen("mcast_list") + 1;
127
128 cnt_le = cpu_to_le32(cnt);
129 memcpy(bufp, &cnt_le, sizeof(cnt));
130 bufp += sizeof(cnt_le);
131
132 netdev_for_each_mc_addr(ha, ndev) {
133 if (!cnt)
134 break;
135 memcpy(bufp, ha->addr, ETH_ALEN);
136 bufp += ETH_ALEN;
137 cnt--;
138 }
139
140 memset(&dcmd, 0, sizeof(dcmd));
141 dcmd.cmd = BRCMF_C_SET_VAR;
142 dcmd.buf = buf;
143 dcmd.len = buflen;
144 dcmd.set = true;
145
Franky Lind08b6a32011-12-16 18:36:51 -0800146 ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200147 if (ret < 0) {
148 brcmf_dbg(ERROR, "%s: set mcast_list failed, cnt %d\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800149 brcmf_ifname(drvr, 0), cnt);
Arend van Sprielb5036242011-10-12 20:51:17 +0200150 dcmd_value = cnt ? true : dcmd_value;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200151 }
152
153 kfree(buf);
154
155 /* Now send the allmulti setting. This is based on the setting in the
156 * net_device flags, but might be modified above to be turned on if we
157 * were trying to set some addresses and dongle rejected it...
158 */
159
Arend van Sprielb5036242011-10-12 20:51:17 +0200160 buflen = sizeof("allmulti") + sizeof(dcmd_value);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200161 buf = kmalloc(buflen, GFP_ATOMIC);
162 if (!buf)
163 return;
164
Arend van Sprielb5036242011-10-12 20:51:17 +0200165 dcmd_le_value = cpu_to_le32(dcmd_value);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200166
Alwin Beukers53a22772011-10-12 20:51:30 +0200167 if (!brcmf_c_mkiovar
Arend van Sprielb5036242011-10-12 20:51:17 +0200168 ("allmulti", (void *)&dcmd_le_value,
169 sizeof(dcmd_le_value), buf, buflen)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200170 brcmf_dbg(ERROR, "%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800171 brcmf_ifname(drvr, 0),
Arend van Sprielb5036242011-10-12 20:51:17 +0200172 (int)sizeof(dcmd_value), buflen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200173 kfree(buf);
174 return;
175 }
176
177 memset(&dcmd, 0, sizeof(dcmd));
178 dcmd.cmd = BRCMF_C_SET_VAR;
179 dcmd.buf = buf;
180 dcmd.len = buflen;
181 dcmd.set = true;
182
Franky Lind08b6a32011-12-16 18:36:51 -0800183 ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200184 if (ret < 0) {
185 brcmf_dbg(ERROR, "%s: set allmulti %d failed\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800186 brcmf_ifname(drvr, 0),
Arend van Sprielb5036242011-10-12 20:51:17 +0200187 le32_to_cpu(dcmd_le_value));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200188 }
189
190 kfree(buf);
191
192 /* Finally, pick up the PROMISC flag as well, like the NIC
193 driver does */
194
Arend van Sprielb5036242011-10-12 20:51:17 +0200195 dcmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
196 dcmd_le_value = cpu_to_le32(dcmd_value);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200197
198 memset(&dcmd, 0, sizeof(dcmd));
199 dcmd.cmd = BRCMF_C_SET_PROMISC;
Arend van Sprielb5036242011-10-12 20:51:17 +0200200 dcmd.buf = &dcmd_le_value;
201 dcmd.len = sizeof(dcmd_le_value);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200202 dcmd.set = true;
203
Franky Lind08b6a32011-12-16 18:36:51 -0800204 ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200205 if (ret < 0) {
206 brcmf_dbg(ERROR, "%s: set promisc %d failed\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800207 brcmf_ifname(drvr, 0),
Arend van Sprielb5036242011-10-12 20:51:17 +0200208 le32_to_cpu(dcmd_le_value));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200209 }
210}
211
212static void
213_brcmf_set_mac_address(struct work_struct *work)
214{
215 char buf[32];
216 struct brcmf_dcmd dcmd;
217 int ret;
218
Franky Lind08b6a32011-12-16 18:36:51 -0800219 struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200220 setmacaddr_work);
221
222 brcmf_dbg(TRACE, "enter\n");
Franky Lind08b6a32011-12-16 18:36:51 -0800223 if (!brcmf_c_mkiovar("cur_etheraddr", (char *)drvr->macvalue,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200224 ETH_ALEN, buf, 32)) {
225 brcmf_dbg(ERROR, "%s: mkiovar failed for cur_etheraddr\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800226 brcmf_ifname(drvr, 0));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200227 return;
228 }
229 memset(&dcmd, 0, sizeof(dcmd));
230 dcmd.cmd = BRCMF_C_SET_VAR;
231 dcmd.buf = buf;
232 dcmd.len = 32;
233 dcmd.set = true;
234
Franky Lind08b6a32011-12-16 18:36:51 -0800235 ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200236 if (ret < 0)
237 brcmf_dbg(ERROR, "%s: set cur_etheraddr failed\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800238 brcmf_ifname(drvr, 0));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200239 else
Franky Lind08b6a32011-12-16 18:36:51 -0800240 memcpy(drvr->iflist[0]->ndev->dev_addr,
241 drvr->macvalue, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200242
243 return;
244}
245
246static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
247{
Franky Line1b83582011-10-21 16:16:33 +0200248 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800249 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200250 struct sockaddr *sa = (struct sockaddr *)addr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200251
Franky Lind08b6a32011-12-16 18:36:51 -0800252 memcpy(&drvr->macvalue, sa->sa_data, ETH_ALEN);
253 schedule_work(&drvr->setmacaddr_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200254 return 0;
255}
256
257static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
258{
Franky Line1b83582011-10-21 16:16:33 +0200259 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800260 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200261
Franky Lind08b6a32011-12-16 18:36:51 -0800262 schedule_work(&drvr->multicast_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200263}
264
Arend van Spriel5b435de2011-10-05 13:19:03 +0200265static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
266{
267 int ret;
Franky Line1b83582011-10-21 16:16:33 +0200268 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800269 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200270
271 brcmf_dbg(TRACE, "Enter\n");
272
273 /* Reject if down */
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800274 if (!drvr->bus_if->drvr_up ||
Franky Lind08b6a32011-12-16 18:36:51 -0800275 (drvr->bus_if->state == BRCMF_BUS_DOWN)) {
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800276 brcmf_dbg(ERROR, "xmit rejected drvup=%d state=%d\n",
277 drvr->bus_if->drvr_up,
Franky Lind08b6a32011-12-16 18:36:51 -0800278 drvr->bus_if->state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200279 netif_stop_queue(ndev);
280 return -ENODEV;
281 }
282
Franky Lind08b6a32011-12-16 18:36:51 -0800283 if (!drvr->iflist[ifp->idx]) {
Franky Line1b83582011-10-21 16:16:33 +0200284 brcmf_dbg(ERROR, "bad ifidx %d\n", ifp->idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200285 netif_stop_queue(ndev);
286 return -ENODEV;
287 }
288
289 /* Make sure there's enough room for any header */
Franky Lind08b6a32011-12-16 18:36:51 -0800290 if (skb_headroom(skb) < drvr->hdrlen) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200291 struct sk_buff *skb2;
292
293 brcmf_dbg(INFO, "%s: insufficient headroom\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800294 brcmf_ifname(drvr, ifp->idx));
Franky Lin9c1a0432011-12-16 18:37:07 -0800295 drvr->bus_if->tx_realloc++;
Franky Lind08b6a32011-12-16 18:36:51 -0800296 skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200297 dev_kfree_skb(skb);
298 skb = skb2;
299 if (skb == NULL) {
300 brcmf_dbg(ERROR, "%s: skb_realloc_headroom failed\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800301 brcmf_ifname(drvr, ifp->idx));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200302 ret = -ENOMEM;
303 goto done;
304 }
305 }
306
Franky Lin8514fd02012-09-13 21:11:57 +0200307 /* Update multicast statistic */
308 if (skb->len >= ETH_ALEN) {
309 u8 *pktdata = (u8 *)(skb->data);
310 struct ethhdr *eh = (struct ethhdr *)pktdata;
311
312 if (is_multicast_ether_addr(eh->h_dest))
313 drvr->tx_multicast++;
314 if (ntohs(eh->h_proto) == ETH_P_PAE)
315 atomic_inc(&drvr->pend_8021x_cnt);
316 }
317
318 /* If the protocol uses a data header, apply it */
319 brcmf_proto_hdrpush(drvr, ifp->idx, skb);
320
321 /* Use bus module to send data frame */
322 ret = drvr->bus_if->brcmf_bus_txdata(drvr->dev, skb);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200323
324done:
325 if (ret)
Franky Lin719f2732011-12-16 18:37:06 -0800326 drvr->bus_if->dstats.tx_dropped++;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200327 else
Franky Lin719f2732011-12-16 18:37:06 -0800328 drvr->bus_if->dstats.tx_packets++;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200329
330 /* Return ok: we always eat the packet */
331 return 0;
332}
333
Hante Meuleman90d03ff2012-09-11 21:18:46 +0200334void brcmf_txflowblock(struct device *dev, bool state)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200335{
336 struct net_device *ndev;
Franky Lin2b459052011-12-16 18:36:57 -0800337 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
338 struct brcmf_pub *drvr = bus_if->drvr;
Hante Meuleman90d03ff2012-09-11 21:18:46 +0200339 int i;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200340
341 brcmf_dbg(TRACE, "Enter\n");
342
Hante Meuleman90d03ff2012-09-11 21:18:46 +0200343 for (i = 0; i < BRCMF_MAX_IFS; i++)
344 if (drvr->iflist[i]) {
345 ndev = drvr->iflist[i]->ndev;
346 if (state)
347 netif_stop_queue(ndev);
348 else
349 netif_wake_queue(ndev);
350 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200351}
352
Franky Lind08b6a32011-12-16 18:36:51 -0800353static int brcmf_host_event(struct brcmf_pub *drvr, int *ifidx,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200354 void *pktdata, struct brcmf_event_msg *event,
355 void **data)
356{
357 int bcmerror = 0;
358
Franky Lind08b6a32011-12-16 18:36:51 -0800359 bcmerror = brcmf_c_host_event(drvr, ifidx, pktdata, event, data);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200360 if (bcmerror != 0)
361 return bcmerror;
362
Franky Lind08b6a32011-12-16 18:36:51 -0800363 if (drvr->iflist[*ifidx]->ndev)
364 brcmf_cfg80211_event(drvr->iflist[*ifidx]->ndev,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200365 event, *data);
366
367 return bcmerror;
368}
369
Franky Lin228bb432011-12-16 18:37:00 -0800370void brcmf_rx_frame(struct device *dev, int ifidx,
Arend van Spriel0b45bf72011-11-22 17:21:36 -0800371 struct sk_buff_head *skb_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200372{
Arend van Spriel5b435de2011-10-05 13:19:03 +0200373 unsigned char *eth;
374 uint len;
375 void *data;
Arend van Spriel0b45bf72011-11-22 17:21:36 -0800376 struct sk_buff *skb, *pnext;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200377 struct brcmf_if *ifp;
378 struct brcmf_event_msg event;
Franky Lin228bb432011-12-16 18:37:00 -0800379 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
380 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200381
382 brcmf_dbg(TRACE, "Enter\n");
383
Arend van Spriel0b45bf72011-11-22 17:21:36 -0800384 skb_queue_walk_safe(skb_list, skb, pnext) {
385 skb_unlink(skb, skb_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200386
387 /* Get the protocol, maintain skb around eth_type_trans()
388 * The main reason for this hack is for the limitation of
389 * Linux 2.4 where 'eth_type_trans' uses the
390 * 'net->hard_header_len'
391 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
392 * coping of the packet coming from the network stack to add
393 * BDC, Hardware header etc, during network interface
394 * registration
395 * we set the 'net->hard_header_len' to ETH_HLEN + extra space
396 * required
397 * for BDC, Hardware header etc. and not just the ETH_HLEN
398 */
399 eth = skb->data;
400 len = skb->len;
401
Franky Lind08b6a32011-12-16 18:36:51 -0800402 ifp = drvr->iflist[ifidx];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200403 if (ifp == NULL)
Franky Lind08b6a32011-12-16 18:36:51 -0800404 ifp = drvr->iflist[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200405
Franky Lin0c094c72011-11-22 17:21:46 -0800406 if (!ifp || !ifp->ndev ||
407 ifp->ndev->reg_state != NETREG_REGISTERED) {
408 brcmu_pkt_buf_free_skb(skb);
409 continue;
410 }
411
Arend van Spriel5b435de2011-10-05 13:19:03 +0200412 skb->dev = ifp->ndev;
413 skb->protocol = eth_type_trans(skb, skb->dev);
414
415 if (skb->pkt_type == PACKET_MULTICAST)
Franky Lin719f2732011-12-16 18:37:06 -0800416 bus_if->dstats.multicast++;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200417
418 skb->data = eth;
419 skb->len = len;
420
421 /* Strip header, count, deliver upward */
422 skb_pull(skb, ETH_HLEN);
423
424 /* Process special event packets and then discard them */
425 if (ntohs(skb->protocol) == ETH_P_LINK_CTL)
Franky Lind08b6a32011-12-16 18:36:51 -0800426 brcmf_host_event(drvr, &ifidx,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200427 skb_mac_header(skb),
428 &event, &data);
429
Franky Lind08b6a32011-12-16 18:36:51 -0800430 if (drvr->iflist[ifidx]) {
431 ifp = drvr->iflist[ifidx];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200432 ifp->ndev->last_rx = jiffies;
Franky Lind1a5b6f2011-10-21 16:16:34 +0200433 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200434
Franky Lin719f2732011-12-16 18:37:06 -0800435 bus_if->dstats.rx_bytes += skb->len;
436 bus_if->dstats.rx_packets++; /* Local count */
Arend van Spriel5b435de2011-10-05 13:19:03 +0200437
438 if (in_interrupt())
439 netif_rx(skb);
440 else
441 /* If the receive is not processed inside an ISR,
442 * the softirqd must be woken explicitly to service
443 * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
444 * by netif_rx_ni(), but in earlier kernels, we need
445 * to do it manually.
446 */
447 netif_rx_ni(skb);
448 }
449}
450
Franky Linc9957882011-12-16 18:36:58 -0800451void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200452{
453 uint ifidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200454 struct ethhdr *eh;
455 u16 type;
Franky Linc9957882011-12-16 18:36:58 -0800456 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
457 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200458
Franky Lind5625ee2011-12-16 18:37:01 -0800459 brcmf_proto_hdrpull(dev, &ifidx, txp);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200460
461 eh = (struct ethhdr *)(txp->data);
462 type = ntohs(eh->h_proto);
463
464 if (type == ETH_P_PAE)
Franky Lind08b6a32011-12-16 18:36:51 -0800465 atomic_dec(&drvr->pend_8021x_cnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200466
467}
468
469static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
470{
Franky Line1b83582011-10-21 16:16:33 +0200471 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lin719f2732011-12-16 18:37:06 -0800472 struct brcmf_bus *bus_if = ifp->drvr->bus_if;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200473
474 brcmf_dbg(TRACE, "Enter\n");
475
Arend van Spriel5b435de2011-10-05 13:19:03 +0200476 /* Copy dongle stats to net device stats */
Franky Lin719f2732011-12-16 18:37:06 -0800477 ifp->stats.rx_packets = bus_if->dstats.rx_packets;
478 ifp->stats.tx_packets = bus_if->dstats.tx_packets;
479 ifp->stats.rx_bytes = bus_if->dstats.rx_bytes;
480 ifp->stats.tx_bytes = bus_if->dstats.tx_bytes;
481 ifp->stats.rx_errors = bus_if->dstats.rx_errors;
482 ifp->stats.tx_errors = bus_if->dstats.tx_errors;
483 ifp->stats.rx_dropped = bus_if->dstats.rx_dropped;
484 ifp->stats.tx_dropped = bus_if->dstats.tx_dropped;
485 ifp->stats.multicast = bus_if->dstats.multicast;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200486
487 return &ifp->stats;
488}
489
490/* Retrieve current toe component enables, which are kept
491 as a bitmap in toe_ol iovar */
Franky Lind08b6a32011-12-16 18:36:51 -0800492static int brcmf_toe_get(struct brcmf_pub *drvr, int ifidx, u32 *toe_ol)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200493{
494 struct brcmf_dcmd dcmd;
Arend van Spriel10629042011-10-12 20:51:18 +0200495 __le32 toe_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200496 char buf[32];
497 int ret;
498
499 memset(&dcmd, 0, sizeof(dcmd));
500
501 dcmd.cmd = BRCMF_C_GET_VAR;
502 dcmd.buf = buf;
503 dcmd.len = (uint) sizeof(buf);
504 dcmd.set = false;
505
506 strcpy(buf, "toe_ol");
Franky Lind08b6a32011-12-16 18:36:51 -0800507 ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200508 if (ret < 0) {
509 /* Check for older dongle image that doesn't support toe_ol */
510 if (ret == -EIO) {
511 brcmf_dbg(ERROR, "%s: toe not supported by device\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800512 brcmf_ifname(drvr, ifidx));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200513 return -EOPNOTSUPP;
514 }
515
516 brcmf_dbg(INFO, "%s: could not get toe_ol: ret=%d\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800517 brcmf_ifname(drvr, ifidx), ret);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200518 return ret;
519 }
520
Arend van Spriel10629042011-10-12 20:51:18 +0200521 memcpy(&toe_le, buf, sizeof(u32));
522 *toe_ol = le32_to_cpu(toe_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200523 return 0;
524}
525
526/* Set current toe component enables in toe_ol iovar,
527 and set toe global enable iovar */
Franky Lind08b6a32011-12-16 18:36:51 -0800528static int brcmf_toe_set(struct brcmf_pub *drvr, int ifidx, u32 toe_ol)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200529{
530 struct brcmf_dcmd dcmd;
531 char buf[32];
Arend van Spriel10629042011-10-12 20:51:18 +0200532 int ret;
533 __le32 toe_le = cpu_to_le32(toe_ol);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200534
535 memset(&dcmd, 0, sizeof(dcmd));
536
537 dcmd.cmd = BRCMF_C_SET_VAR;
538 dcmd.buf = buf;
539 dcmd.len = (uint) sizeof(buf);
540 dcmd.set = true;
541
542 /* Set toe_ol as requested */
Arend van Spriel5b435de2011-10-05 13:19:03 +0200543 strcpy(buf, "toe_ol");
Arend van Spriel10629042011-10-12 20:51:18 +0200544 memcpy(&buf[sizeof("toe_ol")], &toe_le, sizeof(u32));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200545
Franky Lind08b6a32011-12-16 18:36:51 -0800546 ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200547 if (ret < 0) {
548 brcmf_dbg(ERROR, "%s: could not set toe_ol: ret=%d\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800549 brcmf_ifname(drvr, ifidx), ret);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200550 return ret;
551 }
552
553 /* Enable toe globally only if any components are enabled. */
Arend van Spriel10629042011-10-12 20:51:18 +0200554 toe_le = cpu_to_le32(toe_ol != 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200555
556 strcpy(buf, "toe");
Arend van Spriel10629042011-10-12 20:51:18 +0200557 memcpy(&buf[sizeof("toe")], &toe_le, sizeof(u32));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200558
Franky Lind08b6a32011-12-16 18:36:51 -0800559 ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200560 if (ret < 0) {
561 brcmf_dbg(ERROR, "%s: could not set toe: ret=%d\n",
Franky Lind08b6a32011-12-16 18:36:51 -0800562 brcmf_ifname(drvr, ifidx), ret);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200563 return ret;
564 }
565
566 return 0;
567}
568
569static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
570 struct ethtool_drvinfo *info)
571{
Franky Line1b83582011-10-21 16:16:33 +0200572 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800573 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200574
575 sprintf(info->driver, KBUILD_MODNAME);
Franky Lind08b6a32011-12-16 18:36:51 -0800576 sprintf(info->version, "%lu", drvr->drv_version);
577 sprintf(info->bus_info, "%s", dev_name(drvr->dev));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200578}
579
Stephen Hemminger3eb1fa72012-01-04 14:52:51 -0800580static const struct ethtool_ops brcmf_ethtool_ops = {
581 .get_drvinfo = brcmf_ethtool_get_drvinfo,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200582};
583
Franky Lind08b6a32011-12-16 18:36:51 -0800584static int brcmf_ethtool(struct brcmf_pub *drvr, void __user *uaddr)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200585{
586 struct ethtool_drvinfo info;
587 char drvname[sizeof(info.driver)];
588 u32 cmd;
589 struct ethtool_value edata;
590 u32 toe_cmpnt, csum_dir;
591 int ret;
592
593 brcmf_dbg(TRACE, "Enter\n");
594
595 /* all ethtool calls start with a cmd word */
596 if (copy_from_user(&cmd, uaddr, sizeof(u32)))
597 return -EFAULT;
598
599 switch (cmd) {
600 case ETHTOOL_GDRVINFO:
601 /* Copy out any request driver name */
602 if (copy_from_user(&info, uaddr, sizeof(info)))
603 return -EFAULT;
604 strncpy(drvname, info.driver, sizeof(info.driver));
605 drvname[sizeof(info.driver) - 1] = '\0';
606
607 /* clear struct for return */
608 memset(&info, 0, sizeof(info));
609 info.cmd = cmd;
610
611 /* if requested, identify ourselves */
612 if (strcmp(drvname, "?dhd") == 0) {
613 sprintf(info.driver, "dhd");
614 strcpy(info.version, BRCMF_VERSION_STR);
615 }
616
617 /* otherwise, require dongle to be up */
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800618 else if (!drvr->bus_if->drvr_up) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200619 brcmf_dbg(ERROR, "dongle is not up\n");
620 return -ENODEV;
621 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200622 /* finally, report dongle driver type */
Arend van Spriel5b435de2011-10-05 13:19:03 +0200623 else
Hante Meuleman91917c772012-10-22 10:36:15 -0700624 sprintf(info.driver, "wl");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200625
Franky Lind08b6a32011-12-16 18:36:51 -0800626 sprintf(info.version, "%lu", drvr->drv_version);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200627 if (copy_to_user(uaddr, &info, sizeof(info)))
628 return -EFAULT;
629 brcmf_dbg(CTL, "given %*s, returning %s\n",
630 (int)sizeof(drvname), drvname, info.driver);
631 break;
632
633 /* Get toe offload components from dongle */
634 case ETHTOOL_GRXCSUM:
635 case ETHTOOL_GTXCSUM:
Franky Lind08b6a32011-12-16 18:36:51 -0800636 ret = brcmf_toe_get(drvr, 0, &toe_cmpnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200637 if (ret < 0)
638 return ret;
639
640 csum_dir =
641 (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
642
643 edata.cmd = cmd;
644 edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
645
646 if (copy_to_user(uaddr, &edata, sizeof(edata)))
647 return -EFAULT;
648 break;
649
650 /* Set toe offload components in dongle */
651 case ETHTOOL_SRXCSUM:
652 case ETHTOOL_STXCSUM:
653 if (copy_from_user(&edata, uaddr, sizeof(edata)))
654 return -EFAULT;
655
656 /* Read the current settings, update and write back */
Franky Lind08b6a32011-12-16 18:36:51 -0800657 ret = brcmf_toe_get(drvr, 0, &toe_cmpnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200658 if (ret < 0)
659 return ret;
660
661 csum_dir =
662 (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
663
664 if (edata.data != 0)
665 toe_cmpnt |= csum_dir;
666 else
667 toe_cmpnt &= ~csum_dir;
668
Franky Lind08b6a32011-12-16 18:36:51 -0800669 ret = brcmf_toe_set(drvr, 0, toe_cmpnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200670 if (ret < 0)
671 return ret;
672
673 /* If setting TX checksum mode, tell Linux the new mode */
674 if (cmd == ETHTOOL_STXCSUM) {
675 if (edata.data)
Franky Lind08b6a32011-12-16 18:36:51 -0800676 drvr->iflist[0]->ndev->features |=
Arend van Spriel5b435de2011-10-05 13:19:03 +0200677 NETIF_F_IP_CSUM;
678 else
Franky Lind08b6a32011-12-16 18:36:51 -0800679 drvr->iflist[0]->ndev->features &=
Arend van Spriel5b435de2011-10-05 13:19:03 +0200680 ~NETIF_F_IP_CSUM;
681 }
682
683 break;
684
685 default:
686 return -EOPNOTSUPP;
687 }
688
689 return 0;
690}
691
692static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr,
693 int cmd)
694{
Franky Line1b83582011-10-21 16:16:33 +0200695 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800696 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200697
Franky Line1b83582011-10-21 16:16:33 +0200698 brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifp->idx, cmd);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200699
Franky Lind08b6a32011-12-16 18:36:51 -0800700 if (!drvr->iflist[ifp->idx])
Arend van Spriel5b435de2011-10-05 13:19:03 +0200701 return -1;
702
703 if (cmd == SIOCETHTOOL)
Franky Lind08b6a32011-12-16 18:36:51 -0800704 return brcmf_ethtool(drvr, ifr->ifr_data);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200705
706 return -EOPNOTSUPP;
707}
708
Arend van Spriel5b435de2011-10-05 13:19:03 +0200709static int brcmf_netdev_stop(struct net_device *ndev)
710{
Franky Line1b83582011-10-21 16:16:33 +0200711 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800712 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200713
714 brcmf_dbg(TRACE, "Enter\n");
715 brcmf_cfg80211_down(drvr->config);
Franky Lin3fb1d8d2011-12-16 18:37:04 -0800716 if (drvr->bus_if->drvr_up == 0)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200717 return 0;
718
719 /* Set state and stop OS transmissions */
John W. Linville57adc1f2012-01-03 15:16:34 -0500720 drvr->bus_if->drvr_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200721 netif_stop_queue(ndev);
722
723 return 0;
724}
725
726static int brcmf_netdev_open(struct net_device *ndev)
727{
Franky Line1b83582011-10-21 16:16:33 +0200728 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800729 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel1bb1f382012-02-09 21:09:05 +0100730 struct brcmf_bus *bus_if = drvr->bus_if;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200731 u32 toe_ol;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200732 s32 ret = 0;
Franky Lin33380842012-04-11 11:52:47 +0200733 uint up = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200734
Franky Line1b83582011-10-21 16:16:33 +0200735 brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200736
Franky Line1b83582011-10-21 16:16:33 +0200737 if (ifp->idx == 0) { /* do it only for primary eth0 */
Arend van Spriel1bb1f382012-02-09 21:09:05 +0100738 /* If bus is not ready, can't continue */
739 if (bus_if->state != BRCMF_BUS_DATA) {
740 brcmf_dbg(ERROR, "failed bus is not ready\n");
741 return -EAGAIN;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200742 }
Arend van Spriel1bb1f382012-02-09 21:09:05 +0100743
Franky Lind08b6a32011-12-16 18:36:51 -0800744 atomic_set(&drvr->pend_8021x_cnt, 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200745
Franky Lind08b6a32011-12-16 18:36:51 -0800746 memcpy(ndev->dev_addr, drvr->mac, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200747
748 /* Get current TOE mode from dongle */
Franky Lind08b6a32011-12-16 18:36:51 -0800749 if (brcmf_toe_get(drvr, ifp->idx, &toe_ol) >= 0
Arend van Spriel5b435de2011-10-05 13:19:03 +0200750 && (toe_ol & TOE_TX_CSUM_OL) != 0)
Franky Lind08b6a32011-12-16 18:36:51 -0800751 drvr->iflist[ifp->idx]->ndev->features |=
Arend van Spriel5b435de2011-10-05 13:19:03 +0200752 NETIF_F_IP_CSUM;
753 else
Franky Lind08b6a32011-12-16 18:36:51 -0800754 drvr->iflist[ifp->idx]->ndev->features &=
Arend van Spriel5b435de2011-10-05 13:19:03 +0200755 ~NETIF_F_IP_CSUM;
756 }
Franky Lin33380842012-04-11 11:52:47 +0200757
758 /* make sure RF is ready for work */
759 brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_UP, (char *)&up, sizeof(up));
760
Arend van Spriel5b435de2011-10-05 13:19:03 +0200761 /* Allow transmit calls */
762 netif_start_queue(ndev);
John W. Linville57adc1f2012-01-03 15:16:34 -0500763 drvr->bus_if->drvr_up = true;
Franky Lind08b6a32011-12-16 18:36:51 -0800764 if (brcmf_cfg80211_up(drvr->config)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200765 brcmf_dbg(ERROR, "failed to bring up cfg80211\n");
766 return -1;
767 }
768
769 return ret;
770}
771
Franky Lindfded552011-10-21 16:16:20 +0200772static const struct net_device_ops brcmf_netdev_ops_pri = {
773 .ndo_open = brcmf_netdev_open,
774 .ndo_stop = brcmf_netdev_stop,
775 .ndo_get_stats = brcmf_netdev_get_stats,
776 .ndo_do_ioctl = brcmf_netdev_ioctl_entry,
777 .ndo_start_xmit = brcmf_netdev_start_xmit,
778 .ndo_set_mac_address = brcmf_netdev_set_mac_address,
779 .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
780};
781
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700782int brcmf_net_attach(struct brcmf_if *ifp)
Arend van Spriel75c49902012-04-11 11:52:43 +0200783{
Arend van Spriel3625c142012-04-11 11:52:44 +0200784 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel75c49902012-04-11 11:52:43 +0200785 struct net_device *ndev;
Arend van Spriel3625c142012-04-11 11:52:44 +0200786 u8 temp_addr[ETH_ALEN];
Arend van Spriel75c49902012-04-11 11:52:43 +0200787
Arend van Spriel3625c142012-04-11 11:52:44 +0200788 brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx);
Arend van Spriel75c49902012-04-11 11:52:43 +0200789
Arend van Spriel3625c142012-04-11 11:52:44 +0200790 ndev = drvr->iflist[ifp->idx]->ndev;
Arend van Spriel75c49902012-04-11 11:52:43 +0200791 ndev->netdev_ops = &brcmf_netdev_ops_pri;
792
793 /*
Arend van Spriel3625c142012-04-11 11:52:44 +0200794 * determine mac address to use
Arend van Spriel75c49902012-04-11 11:52:43 +0200795 */
Arend van Spriel3625c142012-04-11 11:52:44 +0200796 if (is_valid_ether_addr(ifp->mac_addr))
797 memcpy(temp_addr, ifp->mac_addr, ETH_ALEN);
798 else
Arend van Spriel75c49902012-04-11 11:52:43 +0200799 memcpy(temp_addr, drvr->mac, ETH_ALEN);
800
Arend van Spriel3625c142012-04-11 11:52:44 +0200801 if (ifp->idx == 1) {
Arend van Spriel75c49902012-04-11 11:52:43 +0200802 brcmf_dbg(TRACE, "ACCESS POINT MAC:\n");
803 /* ACCESSPOINT INTERFACE CASE */
804 temp_addr[0] |= 0X02; /* set bit 2 ,
805 - Locally Administered address */
806
807 }
808 ndev->hard_header_len = ETH_HLEN + drvr->hdrlen;
809 ndev->ethtool_ops = &brcmf_ethtool_ops;
810
811 drvr->rxsz = ndev->mtu + ndev->hard_header_len +
812 drvr->hdrlen;
813
814 memcpy(ndev->dev_addr, temp_addr, ETH_ALEN);
815
Arend van Spriel75c49902012-04-11 11:52:43 +0200816 if (register_netdev(ndev) != 0) {
817 brcmf_dbg(ERROR, "couldn't register the net device\n");
818 goto fail;
819 }
820
821 brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
822
823 return 0;
824
825fail:
826 ndev->netdev_ops = NULL;
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700827 free_netdev(ndev);
Arend van Spriel75c49902012-04-11 11:52:43 +0200828 return -EBADE;
829}
830
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700831struct brcmf_if *brcmf_add_if(struct device *dev, int ifidx, s32 bssidx,
832 char *name, u8 *mac_addr)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200833{
834 struct brcmf_if *ifp;
Franky Line1b83582011-10-21 16:16:33 +0200835 struct net_device *ndev;
Franky Lin55a63bc2011-12-16 18:36:54 -0800836 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
837 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200838
Franky Lin15d45b62011-10-21 16:16:32 +0200839 brcmf_dbg(TRACE, "idx %d\n", ifidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200840
Franky Lind08b6a32011-12-16 18:36:51 -0800841 ifp = drvr->iflist[ifidx];
Franky Line1b83582011-10-21 16:16:33 +0200842 /*
843 * Delete the existing interface before overwriting it
844 * in case we missed the BRCMF_E_IF_DEL event.
845 */
846 if (ifp) {
847 brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n",
848 ifp->ndev->name);
849 netif_stop_queue(ifp->ndev);
850 unregister_netdev(ifp->ndev);
851 free_netdev(ifp->ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800852 drvr->iflist[ifidx] = NULL;
Franky Lin15d45b62011-10-21 16:16:32 +0200853 }
Franky Line1b83582011-10-21 16:16:33 +0200854
855 /* Allocate netdev, including space for private structure */
856 ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup);
857 if (!ndev) {
858 brcmf_dbg(ERROR, "OOM - alloc_netdev\n");
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700859 return ERR_PTR(-ENOMEM);
Franky Line1b83582011-10-21 16:16:33 +0200860 }
861
862 ifp = netdev_priv(ndev);
863 ifp->ndev = ndev;
Franky Lind08b6a32011-12-16 18:36:51 -0800864 ifp->drvr = drvr;
865 drvr->iflist[ifidx] = ifp;
Franky Lin15d45b62011-10-21 16:16:32 +0200866 ifp->idx = ifidx;
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -0700867 ifp->bssidx = bssidx;
Franky Lin15d45b62011-10-21 16:16:32 +0200868 if (mac_addr != NULL)
869 memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200870
Franky Lin15d45b62011-10-21 16:16:32 +0200871 brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n",
872 current->pid, ifp->ndev->name);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200873
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700874 return ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200875}
876
Franky Lind08b6a32011-12-16 18:36:51 -0800877void brcmf_del_if(struct brcmf_pub *drvr, int ifidx)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200878{
879 struct brcmf_if *ifp;
880
881 brcmf_dbg(TRACE, "idx %d\n", ifidx);
882
Franky Lind08b6a32011-12-16 18:36:51 -0800883 ifp = drvr->iflist[ifidx];
Arend van Spriel5b435de2011-10-05 13:19:03 +0200884 if (!ifp) {
885 brcmf_dbg(ERROR, "Null interface\n");
886 return;
887 }
Franky Lindfded552011-10-21 16:16:20 +0200888 if (ifp->ndev) {
889 if (ifidx == 0) {
890 if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
891 rtnl_lock();
892 brcmf_netdev_stop(ifp->ndev);
893 rtnl_unlock();
894 }
895 } else {
896 netif_stop_queue(ifp->ndev);
897 }
898
Arend van Spriel5b435de2011-10-05 13:19:03 +0200899 unregister_netdev(ifp->ndev);
Franky Lind08b6a32011-12-16 18:36:51 -0800900 drvr->iflist[ifidx] = NULL;
Franky Lindfded552011-10-21 16:16:20 +0200901 if (ifidx == 0)
Franky Lind08b6a32011-12-16 18:36:51 -0800902 brcmf_cfg80211_detach(drvr->config);
Franky Lindfded552011-10-21 16:16:20 +0200903 free_netdev(ifp->ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200904 }
905}
906
Franky Lin2447ffb2011-12-16 18:37:10 -0800907int brcmf_attach(uint bus_hdrlen, struct device *dev)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200908{
Franky Lind08b6a32011-12-16 18:36:51 -0800909 struct brcmf_pub *drvr = NULL;
Franky Lin712ac5b2011-12-16 18:37:09 -0800910 int ret = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200911
912 brcmf_dbg(TRACE, "Enter\n");
913
Arend van Spriel5b435de2011-10-05 13:19:03 +0200914 /* Allocate primary brcmf_info */
Franky Lind08b6a32011-12-16 18:36:51 -0800915 drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
916 if (!drvr)
Franky Lin712ac5b2011-12-16 18:37:09 -0800917 return -ENOMEM;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200918
Franky Lind08b6a32011-12-16 18:36:51 -0800919 mutex_init(&drvr->proto_block);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200920
921 /* Link to bus module */
Franky Lind08b6a32011-12-16 18:36:51 -0800922 drvr->hdrlen = bus_hdrlen;
923 drvr->bus_if = dev_get_drvdata(dev);
Franky Lin55a63bc2011-12-16 18:36:54 -0800924 drvr->bus_if->drvr = drvr;
Franky Lind08b6a32011-12-16 18:36:51 -0800925 drvr->dev = dev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200926
Arend van Sprield319a7c2012-06-09 22:51:43 +0200927 /* create device debugfs folder */
928 brcmf_debugfs_attach(drvr);
929
Arend van Spriel5b435de2011-10-05 13:19:03 +0200930 /* Attach and link in the protocol */
Franky Lin712ac5b2011-12-16 18:37:09 -0800931 ret = brcmf_proto_attach(drvr);
932 if (ret != 0) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200933 brcmf_dbg(ERROR, "brcmf_prot_attach failed\n");
934 goto fail;
935 }
936
Franky Lind08b6a32011-12-16 18:36:51 -0800937 INIT_WORK(&drvr->setmacaddr_work, _brcmf_set_mac_address);
938 INIT_WORK(&drvr->multicast_work, _brcmf_set_multicast_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200939
Franky Lin135e4c62012-06-26 21:26:34 +0200940 INIT_LIST_HEAD(&drvr->bus_if->dcmd_list);
941
Franky Lin712ac5b2011-12-16 18:37:09 -0800942 return ret;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200943
944fail:
Franky Lin712ac5b2011-12-16 18:37:09 -0800945 brcmf_detach(dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200946
Franky Lin712ac5b2011-12-16 18:37:09 -0800947 return ret;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200948}
949
Franky Lined683c92011-12-16 18:36:55 -0800950int brcmf_bus_start(struct device *dev)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200951{
952 int ret = -1;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200953 /* Room for "event_msgs" + '\0' + bitvec */
954 char iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
Franky Lined683c92011-12-16 18:36:55 -0800955 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
956 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700957 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200958
959 brcmf_dbg(TRACE, "\n");
960
961 /* Bring up the bus */
Franky Lin99a0b8f2011-12-16 18:37:14 -0800962 ret = bus_if->brcmf_bus_init(dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200963 if (ret != 0) {
964 brcmf_dbg(ERROR, "brcmf_sdbrcm_bus_init failed %d\n", ret);
965 return ret;
966 }
967
Alwin Beukers53a22772011-10-12 20:51:30 +0200968 brcmf_c_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200969 iovbuf, sizeof(iovbuf));
970 brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR, iovbuf,
971 sizeof(iovbuf));
972 memcpy(drvr->eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
973
974 setbit(drvr->eventmask, BRCMF_E_SET_SSID);
975 setbit(drvr->eventmask, BRCMF_E_PRUNE);
976 setbit(drvr->eventmask, BRCMF_E_AUTH);
977 setbit(drvr->eventmask, BRCMF_E_REASSOC);
978 setbit(drvr->eventmask, BRCMF_E_REASSOC_IND);
979 setbit(drvr->eventmask, BRCMF_E_DEAUTH_IND);
980 setbit(drvr->eventmask, BRCMF_E_DISASSOC_IND);
981 setbit(drvr->eventmask, BRCMF_E_DISASSOC);
982 setbit(drvr->eventmask, BRCMF_E_JOIN);
983 setbit(drvr->eventmask, BRCMF_E_ASSOC_IND);
984 setbit(drvr->eventmask, BRCMF_E_PSK_SUP);
985 setbit(drvr->eventmask, BRCMF_E_LINK);
986 setbit(drvr->eventmask, BRCMF_E_NDIS_LINK);
987 setbit(drvr->eventmask, BRCMF_E_MIC_ERROR);
988 setbit(drvr->eventmask, BRCMF_E_PMKID_CACHE);
989 setbit(drvr->eventmask, BRCMF_E_TXFAIL);
990 setbit(drvr->eventmask, BRCMF_E_JOIN_START);
991 setbit(drvr->eventmask, BRCMF_E_SCAN_COMPLETE);
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -0700992 setbit(drvr->eventmask, BRCMF_E_IF);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200993
Arend van Spriel5b435de2011-10-05 13:19:03 +0200994 /* Setup filter to allow only unicast */
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700995 drvr->pktfilter_count = 1;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200996 drvr->pktfilter[0] = "100 0 0 0 0x01 0x00";
997
Arend van Spriel1ed9baf2012-10-22 10:36:20 -0700998 /* add primary networking interface */
999 ifp = brcmf_add_if(dev, 0, 0, "wlan%d", drvr->mac);
1000 if (IS_ERR(ifp))
1001 return PTR_ERR(ifp);
1002
Arend van Spriel5b435de2011-10-05 13:19:03 +02001003 /* Bus is ready, do any protocol initialization */
Franky Lind08b6a32011-12-16 18:36:51 -08001004 ret = brcmf_proto_init(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001005 if (ret < 0)
1006 return ret;
1007
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07001008 drvr->config = brcmf_cfg80211_attach(drvr);
1009 if (drvr->config == NULL)
1010 return -ENOMEM;
1011
1012 ret = brcmf_net_attach(ifp);
1013 if (ret < 0) {
1014 brcmf_dbg(ERROR, "brcmf_net_attach failed");
1015 drvr->iflist[0] = NULL;
Arend van Spriel474a64c2012-04-11 11:52:45 +02001016 return ret;
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07001017 }
1018
Arend van Spriel474a64c2012-04-11 11:52:45 +02001019
Arend van Spriel1bb1f382012-02-09 21:09:05 +01001020 /* signal bus ready */
1021 bus_if->state = BRCMF_BUS_DATA;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001022 return 0;
1023}
1024
Arend van Spriel5b435de2011-10-05 13:19:03 +02001025static void brcmf_bus_detach(struct brcmf_pub *drvr)
1026{
Arend van Spriel5b435de2011-10-05 13:19:03 +02001027 brcmf_dbg(TRACE, "Enter\n");
1028
1029 if (drvr) {
Franky Lind08b6a32011-12-16 18:36:51 -08001030 /* Stop the protocol module */
1031 brcmf_proto_stop(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001032
Franky Lind08b6a32011-12-16 18:36:51 -08001033 /* Stop the bus module */
Franky Lina9ffda82011-12-16 18:37:12 -08001034 drvr->bus_if->brcmf_bus_stop(drvr->dev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001035 }
1036}
1037
Franky Lin5f947ad2011-12-16 18:36:56 -08001038void brcmf_detach(struct device *dev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001039{
Franky Lin5f947ad2011-12-16 18:36:56 -08001040 int i;
1041 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
1042 struct brcmf_pub *drvr = bus_if->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001043
1044 brcmf_dbg(TRACE, "Enter\n");
1045
Arend van Spriel5b435de2011-10-05 13:19:03 +02001046
Franky Lin5f947ad2011-12-16 18:36:56 -08001047 /* make sure primary interface removed last */
1048 for (i = BRCMF_MAX_IFS-1; i > -1; i--)
1049 if (drvr->iflist[i])
1050 brcmf_del_if(drvr, i);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001051
Franky Lin5f947ad2011-12-16 18:36:56 -08001052 brcmf_bus_detach(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001053
Franky Lin89fdb462012-02-09 21:09:01 +01001054 if (drvr->prot) {
1055 cancel_work_sync(&drvr->setmacaddr_work);
1056 cancel_work_sync(&drvr->multicast_work);
Franky Lin5f947ad2011-12-16 18:36:56 -08001057 brcmf_proto_detach(drvr);
Franky Lin89fdb462012-02-09 21:09:01 +01001058 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001059
Arend van Sprield319a7c2012-06-09 22:51:43 +02001060 brcmf_debugfs_detach(drvr);
Franky Lin5f947ad2011-12-16 18:36:56 -08001061 bus_if->drvr = NULL;
1062 kfree(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001063}
1064
Franky Lind08b6a32011-12-16 18:36:51 -08001065static int brcmf_get_pend_8021x_cnt(struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001066{
Franky Lind08b6a32011-12-16 18:36:51 -08001067 return atomic_read(&drvr->pend_8021x_cnt);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001068}
1069
1070#define MAX_WAIT_FOR_8021X_TX 10
1071
1072int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
1073{
Franky Line1b83582011-10-21 16:16:33 +02001074 struct brcmf_if *ifp = netdev_priv(ndev);
Franky Lind08b6a32011-12-16 18:36:51 -08001075 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001076 int timeout = 10 * HZ / 1000;
1077 int ntimes = MAX_WAIT_FOR_8021X_TX;
Franky Lind08b6a32011-12-16 18:36:51 -08001078 int pend = brcmf_get_pend_8021x_cnt(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001079
1080 while (ntimes && pend) {
1081 if (pend) {
1082 set_current_state(TASK_INTERRUPTIBLE);
1083 schedule_timeout(timeout);
1084 set_current_state(TASK_RUNNING);
1085 ntimes--;
1086 }
Franky Lind08b6a32011-12-16 18:36:51 -08001087 pend = brcmf_get_pend_8021x_cnt(drvr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001088 }
1089 return pend;
1090}
1091
Arend van Spriele64a4b72012-03-02 22:55:49 +01001092static void brcmf_driver_init(struct work_struct *work)
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001093{
Arend van Sprield319a7c2012-06-09 22:51:43 +02001094 brcmf_debugfs_init();
1095
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001096#ifdef CONFIG_BRCMFMAC_SDIO
Arend van Spriel549040a2012-03-02 22:55:48 +01001097 brcmf_sdio_init();
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001098#endif
Arend van Spriel71bb2442012-02-09 21:09:08 +01001099#ifdef CONFIG_BRCMFMAC_USB
Arend van Spriel549040a2012-03-02 22:55:48 +01001100 brcmf_usb_init();
Arend van Spriel71bb2442012-02-09 21:09:08 +01001101#endif
Arend van Spriele64a4b72012-03-02 22:55:49 +01001102}
1103static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init);
1104
1105static int __init brcmfmac_module_init(void)
1106{
1107 if (!schedule_work(&brcmf_driver_work))
1108 return -EBUSY;
1109
Arend van Spriel549040a2012-03-02 22:55:48 +01001110 return 0;
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001111}
1112
Arend van Spriele64a4b72012-03-02 22:55:49 +01001113static void __exit brcmfmac_module_exit(void)
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001114{
Arend van Spriele64a4b72012-03-02 22:55:49 +01001115 cancel_work_sync(&brcmf_driver_work);
1116
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001117#ifdef CONFIG_BRCMFMAC_SDIO
1118 brcmf_sdio_exit();
1119#endif
Arend van Spriel71bb2442012-02-09 21:09:08 +01001120#ifdef CONFIG_BRCMFMAC_USB
1121 brcmf_usb_exit();
1122#endif
Arend van Sprield319a7c2012-06-09 22:51:43 +02001123 brcmf_debugfs_exit();
Arend van Sprielf3d7cdc2012-02-09 21:09:03 +01001124}
1125
Arend van Spriele64a4b72012-03-02 22:55:49 +01001126module_init(brcmfmac_module_init);
1127module_exit(brcmfmac_module_exit);