aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rndis_wlan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rndis_wlan.c')
-rw-r--r--drivers/net/wireless/rndis_wlan.c67
1 files changed, 42 insertions, 25 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 9f6d6bf06b8e..1de5b22d3efe 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -41,6 +41,7 @@
41#include <linux/if_arp.h> 41#include <linux/if_arp.h>
42#include <linux/ctype.h> 42#include <linux/ctype.h>
43#include <linux/spinlock.h> 43#include <linux/spinlock.h>
44#include <linux/slab.h>
44#include <net/iw_handler.h> 45#include <net/iw_handler.h>
45#include <net/cfg80211.h> 46#include <net/cfg80211.h>
46#include <linux/usb/usbnet.h> 47#include <linux/usb/usbnet.h>
@@ -1496,51 +1497,67 @@ static void set_multicast_list(struct usbnet *usbdev)
1496{ 1497{
1497 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 1498 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
1498 struct dev_mc_list *mclist; 1499 struct dev_mc_list *mclist;
1499 __le32 filter; 1500 __le32 filter, basefilter;
1500 int ret, i, size; 1501 int ret;
1501 char *buf; 1502 char *mc_addrs = NULL;
1503 int mc_count;
1502 1504
1503 filter = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST; 1505 basefilter = filter = RNDIS_PACKET_TYPE_DIRECTED |
1506 RNDIS_PACKET_TYPE_BROADCAST;
1504 1507
1505 netif_addr_lock_bh(usbdev->net);
1506 if (usbdev->net->flags & IFF_PROMISC) { 1508 if (usbdev->net->flags & IFF_PROMISC) {
1507 filter |= RNDIS_PACKET_TYPE_PROMISCUOUS | 1509 filter |= RNDIS_PACKET_TYPE_PROMISCUOUS |
1508 RNDIS_PACKET_TYPE_ALL_LOCAL; 1510 RNDIS_PACKET_TYPE_ALL_LOCAL;
1509 } else if (usbdev->net->flags & IFF_ALLMULTI || 1511 } else if (usbdev->net->flags & IFF_ALLMULTI) {
1510 netdev_mc_count(usbdev->net) > priv->multicast_size) { 1512 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1513 }
1514
1515 if (filter != basefilter)
1516 goto set_filter;
1517
1518 /*
1519 * mc_list should be accessed holding the lock, so copy addresses to
1520 * local buffer first.
1521 */
1522 netif_addr_lock_bh(usbdev->net);
1523 mc_count = netdev_mc_count(usbdev->net);
1524 if (mc_count > priv->multicast_size) {
1511 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST; 1525 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1512 } else if (!netdev_mc_empty(usbdev->net)) { 1526 } else if (mc_count) {
1513 size = min(priv->multicast_size, netdev_mc_count(usbdev->net)); 1527 int i = 0;
1514 buf = kmalloc(size * ETH_ALEN, GFP_KERNEL); 1528
1515 if (!buf) { 1529 mc_addrs = kmalloc(mc_count * ETH_ALEN, GFP_ATOMIC);
1530 if (!mc_addrs) {
1516 netdev_warn(usbdev->net, 1531 netdev_warn(usbdev->net,
1517 "couldn't alloc %d bytes of memory\n", 1532 "couldn't alloc %d bytes of memory\n",
1518 size * ETH_ALEN); 1533 mc_count * ETH_ALEN);
1519 netif_addr_unlock_bh(usbdev->net); 1534 netif_addr_unlock_bh(usbdev->net);
1520 return; 1535 return;
1521 } 1536 }
1522 1537
1523 i = 0; 1538 netdev_for_each_mc_addr(mclist, usbdev->net)
1524 netdev_for_each_mc_addr(mclist, usbdev->net) { 1539 memcpy(mc_addrs + i++ * ETH_ALEN,
1525 if (i == size) 1540 mclist->dmi_addr, ETH_ALEN);
1526 break; 1541 }
1527 memcpy(buf + i++ * ETH_ALEN, mclist->dmi_addr, ETH_ALEN); 1542 netif_addr_unlock_bh(usbdev->net);
1528 }
1529 1543
1530 ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, buf, 1544 if (filter != basefilter)
1531 i * ETH_ALEN); 1545 goto set_filter;
1532 if (ret == 0 && i > 0) 1546
1547 if (mc_count) {
1548 ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, mc_addrs,
1549 mc_count * ETH_ALEN);
1550 kfree(mc_addrs);
1551 if (ret == 0)
1533 filter |= RNDIS_PACKET_TYPE_MULTICAST; 1552 filter |= RNDIS_PACKET_TYPE_MULTICAST;
1534 else 1553 else
1535 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST; 1554 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1536 1555
1537 netdev_dbg(usbdev->net, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n", 1556 netdev_dbg(usbdev->net, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n",
1538 i, priv->multicast_size, ret); 1557 mc_count, priv->multicast_size, ret);
1539
1540 kfree(buf);
1541 } 1558 }
1542 netif_addr_unlock_bh(usbdev->net);
1543 1559
1560set_filter:
1544 ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter, 1561 ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
1545 sizeof(filter)); 1562 sizeof(filter));
1546 if (ret < 0) { 1563 if (ret < 0) {