diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 90 |
1 files changed, 55 insertions, 35 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 8c28a1518409..14b8fdde6954 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | |||
@@ -14,8 +14,6 @@ | |||
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
18 | |||
19 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
20 | #include <linux/etherdevice.h> | 18 | #include <linux/etherdevice.h> |
21 | #include <linux/module.h> | 19 | #include <linux/module.h> |
@@ -162,28 +160,31 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev) | |||
162 | schedule_work(&ifp->multicast_work); | 160 | schedule_work(&ifp->multicast_work); |
163 | } | 161 | } |
164 | 162 | ||
165 | static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) | 163 | static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, |
164 | struct net_device *ndev) | ||
166 | { | 165 | { |
167 | int ret; | 166 | int ret; |
168 | struct brcmf_if *ifp = netdev_priv(ndev); | 167 | struct brcmf_if *ifp = netdev_priv(ndev); |
169 | struct brcmf_pub *drvr = ifp->drvr; | 168 | struct brcmf_pub *drvr = ifp->drvr; |
169 | struct ethhdr *eh; | ||
170 | 170 | ||
171 | brcmf_dbg(TRACE, "Enter\n"); | 171 | brcmf_dbg(TRACE, "Enter\n"); |
172 | 172 | ||
173 | /* Reject if down */ | 173 | /* Can the device send data? */ |
174 | if (!drvr->bus_if->drvr_up || | 174 | if (drvr->bus_if->state != BRCMF_BUS_DATA) { |
175 | (drvr->bus_if->state != BRCMF_BUS_DATA)) { | 175 | brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); |
176 | brcmf_err("xmit rejected drvup=%d state=%d\n", | ||
177 | drvr->bus_if->drvr_up, | ||
178 | drvr->bus_if->state); | ||
179 | netif_stop_queue(ndev); | 176 | netif_stop_queue(ndev); |
180 | return -ENODEV; | 177 | dev_kfree_skb(skb); |
178 | ret = -ENODEV; | ||
179 | goto done; | ||
181 | } | 180 | } |
182 | 181 | ||
183 | if (!drvr->iflist[ifp->idx]) { | 182 | if (!drvr->iflist[ifp->idx]) { |
184 | brcmf_err("bad ifidx %d\n", ifp->idx); | 183 | brcmf_err("bad ifidx %d\n", ifp->idx); |
185 | netif_stop_queue(ndev); | 184 | netif_stop_queue(ndev); |
186 | return -ENODEV; | 185 | dev_kfree_skb(skb); |
186 | ret = -ENODEV; | ||
187 | goto done; | ||
187 | } | 188 | } |
188 | 189 | ||
189 | /* Make sure there's enough room for any header */ | 190 | /* Make sure there's enough room for any header */ |
@@ -204,17 +205,20 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
204 | } | 205 | } |
205 | } | 206 | } |
206 | 207 | ||
207 | /* Update multicast statistic */ | 208 | /* validate length for ether packet */ |
208 | if (skb->len >= ETH_ALEN) { | 209 | if (skb->len < sizeof(*eh)) { |
209 | u8 *pktdata = (u8 *)(skb->data); | 210 | ret = -EINVAL; |
210 | struct ethhdr *eh = (struct ethhdr *)pktdata; | 211 | dev_kfree_skb(skb); |
211 | 212 | goto done; | |
212 | if (is_multicast_ether_addr(eh->h_dest)) | ||
213 | drvr->tx_multicast++; | ||
214 | if (ntohs(eh->h_proto) == ETH_P_PAE) | ||
215 | atomic_inc(&drvr->pend_8021x_cnt); | ||
216 | } | 213 | } |
217 | 214 | ||
215 | /* handle ethernet header */ | ||
216 | eh = (struct ethhdr *)(skb->data); | ||
217 | if (is_multicast_ether_addr(eh->h_dest)) | ||
218 | drvr->tx_multicast++; | ||
219 | if (ntohs(eh->h_proto) == ETH_P_PAE) | ||
220 | atomic_inc(&drvr->pend_8021x_cnt); | ||
221 | |||
218 | /* If the protocol uses a data header, apply it */ | 222 | /* If the protocol uses a data header, apply it */ |
219 | brcmf_proto_hdrpush(drvr, ifp->idx, skb); | 223 | brcmf_proto_hdrpush(drvr, ifp->idx, skb); |
220 | 224 | ||
@@ -228,7 +232,7 @@ done: | |||
228 | drvr->bus_if->dstats.tx_packets++; | 232 | drvr->bus_if->dstats.tx_packets++; |
229 | 233 | ||
230 | /* Return ok: we always eat the packet */ | 234 | /* Return ok: we always eat the packet */ |
231 | return 0; | 235 | return NETDEV_TX_OK; |
232 | } | 236 | } |
233 | 237 | ||
234 | void brcmf_txflowblock(struct device *dev, bool state) | 238 | void brcmf_txflowblock(struct device *dev, bool state) |
@@ -250,8 +254,7 @@ void brcmf_txflowblock(struct device *dev, bool state) | |||
250 | } | 254 | } |
251 | } | 255 | } |
252 | 256 | ||
253 | void brcmf_rx_frame(struct device *dev, u8 ifidx, | 257 | void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) |
254 | struct sk_buff_head *skb_list) | ||
255 | { | 258 | { |
256 | unsigned char *eth; | 259 | unsigned char *eth; |
257 | uint len; | 260 | uint len; |
@@ -259,12 +262,24 @@ void brcmf_rx_frame(struct device *dev, u8 ifidx, | |||
259 | struct brcmf_if *ifp; | 262 | struct brcmf_if *ifp; |
260 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 263 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
261 | struct brcmf_pub *drvr = bus_if->drvr; | 264 | struct brcmf_pub *drvr = bus_if->drvr; |
265 | u8 ifidx; | ||
266 | int ret; | ||
262 | 267 | ||
263 | brcmf_dbg(TRACE, "Enter\n"); | 268 | brcmf_dbg(TRACE, "Enter\n"); |
264 | 269 | ||
265 | skb_queue_walk_safe(skb_list, skb, pnext) { | 270 | skb_queue_walk_safe(skb_list, skb, pnext) { |
266 | skb_unlink(skb, skb_list); | 271 | skb_unlink(skb, skb_list); |
267 | 272 | ||
273 | /* process and remove protocol-specific header | ||
274 | */ | ||
275 | ret = brcmf_proto_hdrpull(drvr, &ifidx, skb); | ||
276 | if (ret < 0) { | ||
277 | if (ret != -ENODATA) | ||
278 | bus_if->dstats.rx_errors++; | ||
279 | brcmu_pkt_buf_free_skb(skb); | ||
280 | continue; | ||
281 | } | ||
282 | |||
268 | /* Get the protocol, maintain skb around eth_type_trans() | 283 | /* Get the protocol, maintain skb around eth_type_trans() |
269 | * The main reason for this hack is for the limitation of | 284 | * The main reason for this hack is for the limitation of |
270 | * Linux 2.4 where 'eth_type_trans' uses the | 285 | * Linux 2.4 where 'eth_type_trans' uses the |
@@ -328,13 +343,13 @@ void brcmf_rx_frame(struct device *dev, u8 ifidx, | |||
328 | 343 | ||
329 | void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) | 344 | void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) |
330 | { | 345 | { |
331 | uint ifidx; | 346 | u8 ifidx; |
332 | struct ethhdr *eh; | 347 | struct ethhdr *eh; |
333 | u16 type; | 348 | u16 type; |
334 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 349 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
335 | struct brcmf_pub *drvr = bus_if->drvr; | 350 | struct brcmf_pub *drvr = bus_if->drvr; |
336 | 351 | ||
337 | brcmf_proto_hdrpull(dev, &ifidx, txp); | 352 | brcmf_proto_hdrpull(drvr, &ifidx, txp); |
338 | 353 | ||
339 | eh = (struct ethhdr *)(txp->data); | 354 | eh = (struct ethhdr *)(txp->data); |
340 | type = ntohs(eh->h_proto); | 355 | type = ntohs(eh->h_proto); |
@@ -452,7 +467,7 @@ static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr) | |||
452 | sprintf(info.version, "%lu", drvr->drv_version); | 467 | sprintf(info.version, "%lu", drvr->drv_version); |
453 | if (copy_to_user(uaddr, &info, sizeof(info))) | 468 | if (copy_to_user(uaddr, &info, sizeof(info))) |
454 | return -EFAULT; | 469 | return -EFAULT; |
455 | brcmf_dbg(CTL, "given %*s, returning %s\n", | 470 | brcmf_dbg(TRACE, "given %*s, returning %s\n", |
456 | (int)sizeof(drvname), drvname, info.driver); | 471 | (int)sizeof(drvname), drvname, info.driver); |
457 | break; | 472 | break; |
458 | 473 | ||
@@ -572,14 +587,9 @@ static int brcmf_netdev_open(struct net_device *ndev) | |||
572 | /* Get current TOE mode from dongle */ | 587 | /* Get current TOE mode from dongle */ |
573 | if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 | 588 | if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 |
574 | && (toe_ol & TOE_TX_CSUM_OL) != 0) | 589 | && (toe_ol & TOE_TX_CSUM_OL) != 0) |
575 | drvr->iflist[ifp->idx]->ndev->features |= | 590 | ndev->features |= NETIF_F_IP_CSUM; |
576 | NETIF_F_IP_CSUM; | ||
577 | else | 591 | else |
578 | drvr->iflist[ifp->idx]->ndev->features &= | 592 | ndev->features &= ~NETIF_F_IP_CSUM; |
579 | ~NETIF_F_IP_CSUM; | ||
580 | |||
581 | /* make sure RF is ready for work */ | ||
582 | brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); | ||
583 | 593 | ||
584 | /* Allow transmit calls */ | 594 | /* Allow transmit calls */ |
585 | netif_start_queue(ndev); | 595 | netif_start_queue(ndev); |
@@ -847,6 +857,17 @@ static void brcmf_bus_detach(struct brcmf_pub *drvr) | |||
847 | } | 857 | } |
848 | } | 858 | } |
849 | 859 | ||
860 | void brcmf_dev_reset(struct device *dev) | ||
861 | { | ||
862 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); | ||
863 | struct brcmf_pub *drvr = bus_if->drvr; | ||
864 | |||
865 | if (drvr == NULL) | ||
866 | return; | ||
867 | |||
868 | brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1); | ||
869 | } | ||
870 | |||
850 | void brcmf_detach(struct device *dev) | 871 | void brcmf_detach(struct device *dev) |
851 | { | 872 | { |
852 | int i; | 873 | int i; |
@@ -868,9 +889,8 @@ void brcmf_detach(struct device *dev) | |||
868 | 889 | ||
869 | brcmf_bus_detach(drvr); | 890 | brcmf_bus_detach(drvr); |
870 | 891 | ||
871 | if (drvr->prot) { | 892 | if (drvr->prot) |
872 | brcmf_proto_detach(drvr); | 893 | brcmf_proto_detach(drvr); |
873 | } | ||
874 | 894 | ||
875 | brcmf_debugfs_detach(drvr); | 895 | brcmf_debugfs_detach(drvr); |
876 | bus_if->drvr = NULL; | 896 | bus_if->drvr = NULL; |