From 61b6ff47d6d79057761a3c1516678df69a3342b8 Mon Sep 17 00:00:00 2001 From: Vinayak Pane Date: Fri, 29 Jun 2012 16:31:15 -0700 Subject: net: usb: raw-ip: support more rmnet interfaces New requirement to support upto 5 rmnet interfaces with raw-ip. Driver will be able to support dynamically multiple number of interfaces, maximum to 5. Bug 1006183 Signed-off-by: Vinayak Pane Reviewed-on: http://git-master/r/112790 (cherry picked from commit 0dde53830d9e21004b2e90c1b997a54c89767fa1) Change-Id: I8166c448dbfef0391491ffdef9dff2b0e2693d75 Reviewed-on: http://git-master/r/115611 Tested-by: Vinayak Pane Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Steve Lin --- drivers/net/usb/raw_ip_net.c | 155 +++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 93 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c index 1b3b89b3c274..c80746dcb8c8 100644 --- a/drivers/net/usb/raw_ip_net.c +++ b/drivers/net/usb/raw_ip_net.c @@ -3,7 +3,7 @@ * * USB network driver for RAW-IP modems. * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,25 +42,24 @@ #endif /* USB_NET_BUFSIZ */ /* maximum interface number supported */ -#define MAX_INTFS 3 +#define MAX_INTFS 5 MODULE_LICENSE("GPL"); -int g_i; +static int g_i; -int max_intfs = MAX_INTFS; -unsigned long usb_net_raw_ip_vid = 0x1519; -unsigned long usb_net_raw_ip_pid = 0x0020; -unsigned long usb_net_raw_ip_intf[MAX_INTFS] = { 0x03, 0x05, 0x07 }; +/* To support more rmnet interfaces, increase the default max_intfs or + * pass kernel module parameter. + * e.g. insmod raw_ip_net.ko max_intfs=5 + */ +static int max_intfs = 2; /* default number of interfaces */ + +static unsigned long usb_net_raw_ip_intf[MAX_INTFS] = { 3, 5, 9, 11, 13}; unsigned long usb_net_raw_ip_rx_debug; unsigned long usb_net_raw_ip_tx_debug; module_param(max_intfs, int, 0644); MODULE_PARM_DESC(max_intfs, "usb net (raw-ip) - max. interfaces supported"); -module_param(usb_net_raw_ip_vid, ulong, 0644); -MODULE_PARM_DESC(usb_net_raw_ip_vid, "usb net (raw-ip) - USB VID"); -module_param(usb_net_raw_ip_pid, ulong, 0644); -MODULE_PARM_DESC(usb_net_raw_ip_pid, "usb net (raw-ip) - USB PID"); module_param(usb_net_raw_ip_rx_debug, ulong, 0644); MODULE_PARM_DESC(usb_net_raw_ip_rx_debug, "usb net (raw-ip) - rx debug"); module_param(usb_net_raw_ip_tx_debug, ulong, 0644); @@ -99,11 +98,10 @@ struct baseband_usb { int susp_count; }; -static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0}; +static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0, 0, 0}; -static struct net_device *usb_net_raw_ip_dev[MAX_INTFS] = { 0, 0, 0}; +static struct net_device *usb_net_raw_ip_dev[MAX_INTFS] = { 0, 0, 0, 0, 0}; -static unsigned int g_usb_interface_index[MAX_INTFS]; static struct usb_interface *g_usb_interface[MAX_INTFS]; static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb); @@ -117,7 +115,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb); static int baseband_usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id) { - int i = g_i; + int i = g_i, j; pr_debug("%s(%d) { intf %p id %p\n", __func__, __LINE__, intf, id); @@ -138,16 +136,18 @@ static int baseband_usb_driver_probe(struct usb_interface *intf, pr_debug("intf->cur_altsetting->desc.iInterface %02x\n", intf->cur_altsetting->desc.iInterface); - if (g_usb_interface_index[i] != - intf->cur_altsetting->desc.bInterfaceNumber) { - pr_debug("%s(%d) } -ENODEV\n", __func__, __LINE__); - return -ENODEV; - } else { - g_usb_interface[i] = intf; + /* register interfaces that are assigned to raw-ip */ + for (j = 0; j < max_intfs; j++) { + if (usb_net_raw_ip_intf[j] == + intf->cur_altsetting->desc.bInterfaceNumber) { + pr_info("%s: raw_ip using interface %d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + g_usb_interface[j] = intf; + return 0; + } } - pr_debug("%s(%d) }\n", __func__, __LINE__); - return 0; + return -ENODEV; } static void baseband_usb_driver_disconnect(struct usb_interface *intf) @@ -350,49 +350,27 @@ static int baseband_usb_driver_reset_resume(struct usb_interface *intf) } #endif /* CONFIG_PM */ -static struct usb_device_id baseband_usb_driver_id_table[MAX_INTFS][2]; - -static char baseband_usb_driver_name[MAX_INTFS][32]; +static struct usb_device_id baseband_usb_driver_id_table[] = { + /* xmm modem vid, pid */ + { USB_DEVICE(0x1519, 0x0020), }, + { }, +}; -static struct usb_driver baseband_usb_driver[MAX_INTFS] = { - { - .name = baseband_usb_driver_name[0], - .probe = baseband_usb_driver_probe, - .disconnect = baseband_usb_driver_disconnect, - .id_table = baseband_usb_driver_id_table[0], -#ifdef CONFIG_PM - .suspend = baseband_usb_driver_suspend, - .resume = baseband_usb_driver_resume, - .reset_resume = baseband_usb_driver_reset_resume, - .supports_autosuspend = 1, -#endif - }, - { - .name = baseband_usb_driver_name[1], - .probe = baseband_usb_driver_probe, - .disconnect = baseband_usb_driver_disconnect, - .id_table = baseband_usb_driver_id_table[1], -#ifdef CONFIG_PM - .suspend = baseband_usb_driver_suspend, - .resume = baseband_usb_driver_resume, - .reset_resume = baseband_usb_driver_reset_resume, - .supports_autosuspend = 1, -#endif - }, - { - .name = baseband_usb_driver_name[2], +static struct usb_driver baseband_usb_driver = { + .name = "bb_raw_ip_net", .probe = baseband_usb_driver_probe, .disconnect = baseband_usb_driver_disconnect, - .id_table = baseband_usb_driver_id_table[2], + .id_table = baseband_usb_driver_id_table, #ifdef CONFIG_PM .suspend = baseband_usb_driver_suspend, .resume = baseband_usb_driver_resume, .reset_resume = baseband_usb_driver_reset_resume, .supports_autosuspend = 1, #endif - }, }; +MODULE_DEVICE_TABLE(usb, baseband_usb_driver_id_table); + static void find_usb_pipe(struct baseband_usb *usb) { struct usb_device *usbdev = usb->usb.device; @@ -434,13 +412,10 @@ static void find_usb_pipe(struct baseband_usb *usb) void baseband_usb_close(struct baseband_usb *usb); -struct baseband_usb *baseband_usb_open(int index, - unsigned int vid, - unsigned int pid, - unsigned int intf) +struct baseband_usb *baseband_usb_open(int index, unsigned int intf) { struct baseband_usb *usb; - int err; + int i; pr_debug("baseband_usb_open {\n"); @@ -453,37 +428,25 @@ struct baseband_usb *baseband_usb_open(int index, /* create semaphores */ sema_init(&usb->sem, 1); - /* open usb driver */ - sprintf(baseband_usb_driver_name[index], - "baseband_usb_%x_%x_%x", - vid, pid, intf); - baseband_usb_driver_id_table[index][0].match_flags = - USB_DEVICE_ID_MATCH_DEVICE; - baseband_usb_driver_id_table[index][0].idVendor = vid; - baseband_usb_driver_id_table[index][0].idProduct = pid; - g_usb_interface_index[index] = intf; - g_usb_interface[index] = (struct usb_interface *) 0; - err = usb_register(&baseband_usb_driver[index]); - if (err < 0) { - pr_err("cannot open usb driver - err %d\n", err); - kfree(usb); - return (struct baseband_usb *) 0; - } + /* open usb interface */ usb->baseband_index = index; - usb->usb.driver = &baseband_usb_driver[index]; + usb->usb.driver = &baseband_usb_driver; if (!g_usb_interface[index]) { - pr_err("cannot open usb driver - !g_usb_interface[%d]\n", - index); - usb_deregister(usb->usb.driver); - kfree(usb); - return (struct baseband_usb *) 0; + /* wait for usb probe */ + for (i = 0; i < 50; i++) + if (!g_usb_interface[index]) + msleep(20); + if (!g_usb_interface[index]) { + pr_err("can't open usb: !g_usb_interface[%d]\n", index); + kfree(usb); + return NULL; + } } usb->usb.device = interface_to_usbdev(g_usb_interface[index]); usb->usb.interface = g_usb_interface[index]; find_usb_pipe(usb); usb->usb.rx_urb = (struct urb *) 0; usb->usb.tx_urb = (struct urb *) 0; - g_usb_interface_index[index] = ~0U; g_usb_interface[index] = (struct usb_interface *) 0; pr_debug("usb->usb.driver->name %s\n", usb->usb.driver->name); pr_debug("usb->usb.device %p\n", usb->usb.device); @@ -509,12 +472,7 @@ void baseband_usb_close(struct baseband_usb *usb) return; /* close usb driver */ - if (usb->usb.driver) { - pr_debug("close usb driver {\n"); - usb_deregister(usb->usb.driver); - usb->usb.driver = (struct usb_driver *) 0; - pr_debug("close usb driver }\n"); - } + usb->usb.driver = (struct usb_driver *) 0; /* destroy semaphores */ memset(&usb->sem, 0, sizeof(usb->sem)); @@ -923,8 +881,8 @@ static void usb_net_raw_ip_tx_urb_work(struct work_struct *work) /* check if usb interface disconnected */ if (!usb->usb.interface) { - pr_err("%s: not submitting tx urb %p -interface disconnected\n", - __func__, urb); + pr_err("%s: not submitting tx urb -interface disconnected\n", + __func__); return; } @@ -1044,12 +1002,18 @@ static int usb_net_raw_ip_init(void) pr_debug("usb_net_raw_ip_init {\n"); + err = usb_register(&baseband_usb_driver); + if (err < 0) { + pr_err("cannot open usb driver - err %d\n", err); + return err; + } + /* create multiple raw-ip network devices */ for (i = 0; i < max_intfs; i++) { /* open baseband usb */ g_i = i; - baseband_usb_net[i] = baseband_usb_open(i, usb_net_raw_ip_vid, - usb_net_raw_ip_pid, usb_net_raw_ip_intf[i]); + baseband_usb_net[i] = baseband_usb_open(i, + usb_net_raw_ip_intf[i]); if (!baseband_usb_net[i]) { pr_err("cannot open baseband usb net\n"); err = -1; @@ -1138,6 +1102,7 @@ error_exit: baseband_usb_net[i] = (struct baseband_usb *) 0; } } + usb_deregister(&baseband_usb_driver); return err; } @@ -1183,6 +1148,10 @@ static void usb_net_raw_ip_exit(void) } } + pr_debug("close usb driver {\n"); + usb_deregister(&baseband_usb_driver); + pr_debug("close usb driver }\n"); + pr_debug("usb_net_raw_ip_exit }\n"); } -- cgit v1.2.3 From 7358a92910a207e264e818ceac178d1fd049bdc7 Mon Sep 17 00:00:00 2001 From: Narayan Reddy Date: Fri, 13 Jul 2012 14:39:54 +0530 Subject: net: wireless: bcmdhd: disable WLC_E_TXFAIL event. In suspend mode WLC_E_TXFAIL event is causing autowake when the device is connected to an AP, so discarding the WLC_E_TXFAIL event initialization as per BRCM team suggestion. Bug 1014610 Change-Id: I297dab7ee589f458bba3e26be090665a36c78502 Signed-off-by: Narayan Reddy Reviewed-on: http://git-master/r/115633 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Rakesh Goyal GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani --- drivers/net/wireless/bcmdhd/bcmevent.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c index 6a25d9a5a57f..84ab7564ad7d 100644 --- a/drivers/net/wireless/bcmdhd/bcmevent.c +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -54,7 +54,6 @@ const bcmevent_name_t bcmevent_names[] = { { WLC_E_MIC_ERROR, "MIC_ERROR" }, { WLC_E_NDIS_LINK, "NDIS_LINK" }, { WLC_E_ROAM, "ROAM" }, - { WLC_E_TXFAIL, "TXFAIL" }, { WLC_E_PMKID_CACHE, "PMKID_CACHE" }, { WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF" }, { WLC_E_PRUNE, "PRUNE" }, -- cgit v1.2.3 From c92c50000f737569f7f319f2b9166fcaf08e6758 Mon Sep 17 00:00:00 2001 From: Michael Hsu Date: Wed, 28 Mar 2012 14:57:53 -0700 Subject: drivers: net: raw-ip: Add IPv6 support. Upon receiving IPv6 packet, set ethernet header's ether type to 0x86dd. For transmission of IPv6, nothing extra required, as the 14 byte ethernet header (containing the 0x86dd ether type) is already stripped off as part of the raw-ip protocol. Bug 1010735 Change-Id: Id574a7feeefbde0504ad0ea449dff28340e9356a Signed-off-by: Michael Hsu Reviewed-on: http://git-master/r/113761 (cherry picked from commit 8bdfd06cae7eede4856ef825ea26b69c9ea065ef) Reviewed-on: http://git-master/r/117148 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Vinayak Pane Tested-by: Vinayak Pane GVS: Gerrit_Virtual_Submit Reviewed-by: Steve Lin --- drivers/net/usb/raw_ip_net.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c index c80746dcb8c8..dc9b5e61fd9e 100644 --- a/drivers/net/usb/raw_ip_net.c +++ b/drivers/net/usb/raw_ip_net.c @@ -3,7 +3,7 @@ * * USB network driver for RAW-IP modems. * - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2012, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -704,10 +704,28 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb) skb_reserve(skb, NET_IP_ALIGN); dst = skb_put(skb, 14); memcpy(dst, ethernet_header, 14); + if ((((unsigned char *) urb->transfer_buffer)[0] + & 0xf0) == 0x60) { + /* ipv6 ether type */ + dst[12] = 0x86; + dst[13] = 0xdd; + } dst = skb_put(skb, urb->actual_length); memcpy(dst, urb->transfer_buffer, urb->actual_length); skb->protocol = eth_type_trans(skb, usb_net_raw_ip_dev[i]); + pr_debug("%s: ntohs(skb->protocol) %04x (%s)\n", + __func__, ntohs(skb->protocol), + (ntohs(skb->protocol) == 0x0800) + ? "IPv4" + : (ntohs(skb->protocol) == 0x86dd) + ? "IPv6" + : "unknown"); + pr_debug("%s: %02x %02x %02x %02x\n", __func__, + ((unsigned char *)urb->transfer_buffer)[0], + ((unsigned char *)urb->transfer_buffer)[1], + ((unsigned char *)urb->transfer_buffer)[2], + ((unsigned char *)urb->transfer_buffer)[3]); /* pass skb to network stack */ if (netif_rx(skb) < 0) { pr_err("usb_net_raw_ip_rx_urb_comp_work - " @@ -850,6 +868,18 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb, usb_net_raw_ip_tx_urb_comp, usb); urb->transfer_flags = URB_ZERO_PACKET; + pr_debug("%s: ntohs(skb->protocol) %04x (%s)\n", + __func__, ntohs(skb->protocol), + (ntohs(skb->protocol) == 0x0800) + ? "IPv4" + : (ntohs(skb->protocol) == 0x86dd) + ? "IPv6" + : "unknown"); + pr_debug("%s: %02x %02x %02x %02x\n", __func__, + ((unsigned char *)urb->transfer_buffer)[0], + ((unsigned char *)urb->transfer_buffer)[1], + ((unsigned char *)urb->transfer_buffer)[2], + ((unsigned char *)urb->transfer_buffer)[3]); /* queue tx urb work */ usb_anchor_urb(urb, &usb->usb.tx_urb_deferred); -- cgit v1.2.3 From da93e26a4e7aca61db257f8aae75df5740e57a3b Mon Sep 17 00:00:00 2001 From: Om Prakash Singh Date: Tue, 24 Jul 2012 21:57:06 +0530 Subject: net: wireless: bcmdhd: power off card when not in use Power off the card when wifi is off and power up only when wifi is turned on Bug 1011349 Change-Id: I26a8188d932516c0490dec858acd9e8ea2c5adf8 Signed-off-by: Om Prakash Singh Reviewed-on: http://git-master/r/118097 Reviewed-by: Pavan Kunapuli Reviewed-by: Bharat Nihalani Tested-by: Bharat Nihalani Reviewed-by: Laxman Dewangan --- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 5 ++++- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index e67eeca1f99c..ffcb49bda290 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -1326,7 +1327,7 @@ sdioh_start(sdioh_info_t *si, int stage) 2.6.27. The implementation prior to that is buggy, and needs broadcom's patch for it */ - if ((ret = sdio_reset_comm(gInstance->func[0]->card))) { + if ((ret = mmc_power_restore_host((gInstance->func[0])->card->host))) { sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); return ret; } @@ -1405,6 +1406,8 @@ sdioh_stop(sdioh_info_t *si) #endif bcmsdh_oob_intr_set(FALSE); #endif /* !defined(OOB_INTR_ONLY) */ + if (mmc_power_save_host((gInstance->func[0])->card->host)) + sd_err(("%s card power save fail\n", __FUNCTION__)); } else sd_err(("%s Failed\n", __FUNCTION__)); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index 656953939b71..1a315b4dcf90 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -131,6 +132,8 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, #endif sd_trace(("F2 found, calling bcmsdh_probe_bcmdhd...\n")); ret = bcmsdh_probe_bcmdhd(&func->dev); + if (mmc_power_save_host(func->card->host)) + sd_err(("%s: card power save fail", __FUNCTION__)); } return ret; -- cgit v1.2.3 From f92af19b741e3566e64efae2fc4e5d56f189b4f1 Mon Sep 17 00:00:00 2001 From: BH Hsieh Date: Mon, 30 Jul 2012 15:16:04 +0800 Subject: usb: cdc_ether: Add rmnet device info to ZM5250 Modified device info element for ZM5250 to sync with framework. Change-Id: I855678edccef50c549960ee209e500ec6d692e36 Signed-off-by: BH Hsieh --- drivers/net/usb/cdc_ether.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index c31b1185f492..14bda2ecabde 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -604,6 +604,14 @@ static const struct usb_device_id products [] = { USB_DEVICE(0x0489,0xE03A), .driver_info = (unsigned long)&rmnet_info, }, + +/* ZM5250 */ +{ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + USB_DEVICE(0x19D2,0x1554), + .driver_info = (unsigned long)&rmnet_info, +}, /* * WHITELIST!!! * -- cgit v1.2.3 From 2c998d4b4ed946ce06ed9c70f908f4e5c3fd67d7 Mon Sep 17 00:00:00 2001 From: Deepak Nibade Date: Tue, 24 Jul 2012 18:48:31 +0530 Subject: net: caif: tegra: enable warnings as errors flag -enable warnings as errors compilation flag bug 949219 Change-Id: Ifeaba174d59c437e0068324b73dffd287f90c957 Signed-off-by: Deepak Nibade Reviewed-on: http://git-master/r/118041 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Sanjay Singh Rawat Reviewed-by: Sheshagiri Shenoy Reviewed-by: Varun Wadekar Reviewed-by: Sri Krishna Chowdary --- drivers/net/caif/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net') diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile index f30752565b33..5f85fe79c595 100644 --- a/drivers/net/caif/Makefile +++ b/drivers/net/caif/Makefile @@ -15,5 +15,6 @@ obj-$(CONFIG_CAIF_SHM) += caif_shm.o obj-$(CONFIG_CAIF_HSI) += caif_hsi.o # Tegra specific SPI slave physical interfaces module +CFLAGS_tegra_caif_sspi.o = -Werror tegra_cfspi_slave-objs := tegra_caif_sspi.o obj-$(CONFIG_TEGRA_SPI_CAIF) += tegra_cfspi_slave.o -- cgit v1.2.3 From 57716f1d1f8381ff50874cb81c326e203909b4f3 Mon Sep 17 00:00:00 2001 From: Narayan Reddy Date: Fri, 27 Jul 2012 15:27:53 +0530 Subject: net: wireless: bcmdhd: Update to 5.90.195.75 Bug 1023045 Change-Id: Id6e43c852a90cbd5445daf38279ed690712c3332 Signed-off-by: Narayan Reddy Reviewed-on: http://git-master/r/117637 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Rakesh Kumar Reviewed-by: Bharat Nihalani --- drivers/net/wireless/bcmdhd/Makefile | 8 +- drivers/net/wireless/bcmdhd/dhd.h | 6 +- drivers/net/wireless/bcmdhd/dhd_common.c | 127 ++++- drivers/net/wireless/bcmdhd/dhd_linux.c | 57 +- drivers/net/wireless/bcmdhd/include/bcmdevs.h | 3 +- drivers/net/wireless/bcmdhd/include/epivers.h | 9 +- drivers/net/wireless/bcmdhd/include/hndpmu.h | 5 +- drivers/net/wireless/bcmdhd/include/sbchipc.h | 18 +- drivers/net/wireless/bcmdhd/include/siutils.h | 4 +- drivers/net/wireless/bcmdhd/include/wlioctl.h | 10 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 780 +++++++++++++++++++++----- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 7 + drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 48 +- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 11 +- drivers/net/wireless/bcmdhd/wl_iw.c | 1 + 15 files changed, 902 insertions(+), 192 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 2851388cf194..50fac43948e2 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -8,9 +8,15 @@ DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ -DNEW_COMPAT_WIRELESS -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ -DKEEP_ALIVE -DPKT_FILTER_SUPPORT \ -DEMBEDDED_PLATFORM \ - -DSET_RANDOM_MAC_SOFTAP -DWL_CFG80211_STA_EVENT \ -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include +# Only for JB (disable on ICS) +DHDCFLAGS += -DSET_RANDOM_MAC_SOFTAP -DWL_CFG80211_STA_EVENT + +# for WFD IE support +DHDCFLAGS += -DWLWFDIE + + ifeq ($(CONFIG_BCMDHD_WIFI_CONTROL_FUNC),y) DHDCFLAGS += -DCONFIG_WIFI_CONTROL_FUNC else diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 5ff5c218ddcf..871c2bfb51d2 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd.h 328934 2012-04-23 05:15:42Z $ + * $Id: dhd.h 333052 2012-05-12 02:09:28Z $ */ /**************** @@ -437,10 +437,14 @@ extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); extern int dhd_pno_clean(dhd_pub_t *dhd); extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_set_ex(dhd_pub_t *dhd, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time); extern int dhd_pno_get_status(dhd_pub_t *dhd); extern int dhd_dev_pno_reset(struct net_device *dev); extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_set_ex(struct net_device *dev, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time); extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); extern int dhd_dev_get_pno_status(struct net_device *dev); #endif /* PNO_SUPPORT */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index d9810ace1cb4..3ccc55d87d4b 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c 329682 2012-04-26 09:20:38Z $ + * $Id: dhd_common.c 331276 2012-05-04 08:05:57Z $ */ #include #include @@ -1890,12 +1890,13 @@ dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) return ret; } - if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) - return (ret); memset(iovbuf, 0, sizeof(iovbuf)); #ifndef WL_SCHED_SCAN + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (ret); + if ((pfn_enabled) && (dhd_is_associated(dhd, NULL, NULL) == TRUE)) { DHD_ERROR(("%s pno is NOT enable : called in assoc mode , ignore\n", __FUNCTION__)); return ret; @@ -1939,9 +1940,10 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, err = -1; return err; } - +#ifndef WL_SCHED_SCAN if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) return (err); +#endif /* !WL_SCHED_SCAN */ /* Check for broadcast ssid */ for (k = 0; k < nssid; k++) { @@ -2034,6 +2036,123 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, return err; } +int +dhd_pno_set_ex(dhd_pub_t *dhd, wl_pfn_t* ssidnet, int nssid, ushort pno_interval, + int pno_repeat, int pno_expo_max, int pno_lost_time) +{ + int err = -1; + char iovbuf[128]; + int k, i; + wl_pfn_param_t pfn_param; + wl_pfn_t pfn_element; + uint len = 0; + + DHD_TRACE(("%s nssid=%d pno_interval=%d\n", __FUNCTION__, nssid, pno_interval)); + + if ((!dhd) && (!ssidnet)) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + return err; + } + + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (err); + + /* Check for broadcast ssid */ + for (k = 0; k < nssid; k++) { + if (!ssidnet[k].ssid.SSID_len) { + DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); + return err; + } + } +/* #define PNO_DUMP 1 */ +#ifdef PNO_DUMP + { + int j; + for (j = 0; j < nssid; j++) { + DHD_ERROR(("%d: scan for %s size =%d\n", j, + ssidnet[j].ssid.SSID, ssidnet[j].ssid.SSID_len)); + } + } +#endif /* PNO_DUMP */ + + /* clean up everything */ + if ((err = dhd_pno_clean(dhd)) < 0) { + DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); + return err; + } + memset(iovbuf, 0, sizeof(iovbuf)); + memset(&pfn_param, 0, sizeof(pfn_param)); + memset(&pfn_element, 0, sizeof(pfn_element)); + + /* set pfn parameters */ + pfn_param.version = htod32(PFN_VERSION); + pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + + /* check and set extra pno params */ + if ((pno_repeat != 0) || (pno_expo_max != 0)) { + pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = (uchar) (pno_repeat); + pfn_param.exp = (uchar) (pno_expo_max); + } + + /* set up pno scan fr */ + if (pno_interval != 0) + pfn_param.scan_freq = htod32(pno_interval); + + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); + return err; + } + if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { + DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); + return err; + } + + /* network lost time */ + pfn_param.lost_network_timeout = htod32(pno_lost_time); + + len = bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); + if ((err = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_set failed for error=%d\n", + __FUNCTION__, err)); + return err; + } else { + DHD_TRACE(("%s pfn_set OK with PNO time=%d repeat=%d max_adjust=%d\n", + __FUNCTION__, pfn_param.scan_freq, + pfn_param.repeat, pfn_param.exp)); + } + + /* set all pfn ssid */ + for (i = 0; i < nssid; i++) { + pfn_element.flags = htod32(ssidnet[i].flags); + pfn_element.infra = htod32(ssidnet[i].infra); + pfn_element.auth = htod32(ssidnet[i].auth); + pfn_element.wpa_auth = htod32(ssidnet[i].wpa_auth); + pfn_element.wsec = htod32(ssidnet[i].wsec); + + memcpy((char *)pfn_element.ssid.SSID, ssidnet[i].ssid.SSID, ssidnet[i].ssid.SSID_len); + pfn_element.ssid.SSID_len = htod32(ssidnet[i].ssid.SSID_len); + + if ((len = + bcm_mkiovar("pfn_add", (char *)&pfn_element, + sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { + if ((err = + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_add failed with ssidnet[%d] error=%d\n", + __FUNCTION__, i, err)); + return err; + } else { + DHD_TRACE(("%s pfn_add OK with ssidnet[%d]\n", __FUNCTION__, i)); + } + } else { + DHD_ERROR(("%s bcm_mkiovar failed with ssidnet[%d]\n", __FUNCTION__, i)); + } + } + + return err; +} + int dhd_pno_get_status(dhd_pub_t *dhd) { diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 6ba7df1cac32..44879e4a1170 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c 329682 2012-04-26 09:20:38Z $ + * $Id: dhd_linux.c 333885 2012-05-18 00:39:03Z $ */ #include @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -1526,9 +1525,12 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) tout_ctrl = DHD_PACKET_TIMEOUT_MS; if (event.event_type == WLC_E_BTA_HCI_EVENT) { dhd_bta_doevt(dhdp, data, event.datalen); - } else if (event.event_type == WLC_E_PFN_NET_FOUND) { + } +#ifdef PNO_SUPPORT + if (event.event_type == WLC_E_PFN_NET_FOUND) { tout_ctrl *= 2; } +#endif /* PNO_SUPPORT */ } else { tout_rx = DHD_PACKET_TIMEOUT_MS; } @@ -2603,7 +2605,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) } dhd_state |= DHD_ATTACH_STATE_NET_ALLOC; - SET_NETDEV_DEV(net, (struct device *)dev); /* Allocate primary dhd_info */ if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); @@ -2941,16 +2942,20 @@ dhd_concurrent_fw(dhd_pub_t *dhd) bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) { - DHD_TRACE(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); + DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); } else if (buf[0] == 1) { DHD_TRACE(("%s: P2P is supported\n", __FUNCTION__)); return 1; } } - return 0; + return ret; } #endif +/* + * dhd_preinit_ioctls makes special pre-setting in the firmware before radio turns on + * returns : 0 if all settings passed or negative value if anything failed +*/ int dhd_preinit_ioctls(dhd_pub_t *dhd) { @@ -2959,7 +2964,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ #if !defined(WL_CFG80211) uint up = 0; -#endif +#endif /* defined(WL_CFG80211) */ uint power_mode = PM_FAST; uint32 dongle_align = DHD_SDALIGN; uint32 glom = 0; @@ -3043,19 +3048,31 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* SET_RANDOM_MAC_SOFTAP */ DHD_TRACE(("Firmware = %s\n", fw_path)); + #if !defined(AP) && defined(WLP2P) /* Check if firmware with WFD support used */ +#if defined(WL_ENABLE_P2P_IF) + if ((ret = dhd_concurrent_fw(dhd)) < 0) { + DHD_ERROR(("%s error : firmware can't support p2p mode\n", __FUNCTION__)); + goto done; + } +#endif /* (WL_ENABLE_P2P_IF) */ + if ((!op_mode && strstr(fw_path, "_p2p") != NULL) #if defined(WL_ENABLE_P2P_IF) - || (op_mode == 0x04) ||(dhd_concurrent_fw(dhd)) + || (op_mode == WFD_MASK) || (dhd_concurrent_fw(dhd) == 1) #endif ) { bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { - DHD_ERROR(("%s APSTA for WFD failed ret= %d\n", __FUNCTION__, ret)); + DHD_ERROR(("%s APSTA setting failed ret= %d\n", __FUNCTION__, ret)); } else { dhd->op_mode |= WFD_MASK; +#if !defined(WL_ENABLE_P2P_IF) + /* ICS back capability : disable any packet filtering for p2p only mode */ + dhd_pkt_filter_enable = FALSE; +#endif /*!defined(WL_ENABLE_P2P_IF) */ } } #endif @@ -3100,16 +3117,20 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(ARP_OFFLOAD_SUPPORT) arpoe = 0; #endif /* (ARP_OFFLOAD_SUPPORT) */ + /* disable any filtering for SoftAP mode */ dhd_pkt_filter_enable = FALSE; } } #endif +#if !defined(WL_ENABLE_P2P_IF) + /* ICS mode setting for sta */ if ((dhd->op_mode != WFD_MASK) && (dhd->op_mode != HOSTAPD_MASK)) { /* STA only operation mode */ dhd->op_mode |= STA_MASK; dhd_pkt_filter_enable = TRUE; } +#endif /* !defined(WL_ENABLE_P2P_IF) */ DHD_ERROR(("Firmware up: fw_path=%s op_mode=%d, " "Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", @@ -3262,6 +3283,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd->pktfilter[1] = NULL; dhd->pktfilter[2] = NULL; dhd->pktfilter[3] = NULL; + /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; #if defined(SOFTAP) if (ap_fw_loaded) { @@ -4448,6 +4470,17 @@ dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); } +/* Linux wrapper to call common dhd_pno_set_ex */ +int +dhd_dev_pno_set_ex(struct net_device *dev, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_set_ex(&dhd->pub, ssidnet, nssid, + pno_interval, pno_repeat, pno_expo_max, pno_lost_time)); +} + /* Linux wrapper to get pno status */ int dhd_dev_get_pno_status(struct net_device *dev) @@ -4466,9 +4499,9 @@ static void dhd_hang_process(struct work_struct *work) struct net_device *dev; dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); - dev = dhd->iflist[0]->net; + dev = dhd->iflist[0]->net; - if (dev) { + if (dev) { rtnl_lock(); dev_close(dev); rtnl_unlock(); @@ -4478,7 +4511,7 @@ static void dhd_hang_process(struct work_struct *work) #if defined(WL_CFG80211) wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); #endif - } + } } int net_os_send_hang_message(struct net_device *dev) diff --git a/drivers/net/wireless/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/bcmdhd/include/bcmdevs.h index 287f1c65fc9a..4d2f5763a3d9 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmdevs.h +++ b/drivers/net/wireless/bcmdhd/include/bcmdevs.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmdevs.h 295140 2011-11-09 17:22:01Z $ + * $Id: bcmdevs.h 332966 2012-05-11 22:40:21Z $ */ @@ -373,6 +373,7 @@ #define BFL2_EXTLNA_TX 0x08000000 #define BFL2_4313_RADIOREG 0x10000000 +#define BFL2_SECI_LOPWR_DIS 0x20000000 diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index 3bff73e2a6bc..5df25c16b7d9 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -23,6 +23,7 @@ * */ + #ifndef _epivers_h_ #define _epivers_h_ @@ -32,17 +33,17 @@ #define EPI_RC_NUMBER 195 -#define EPI_INCREMENTAL_NUMBER 61 +#define EPI_INCREMENTAL_NUMBER 75 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 195, 61 +#define EPI_VERSION 5, 90, 195, 75 -#define EPI_VERSION_NUM 0x055ac33d +#define EPI_VERSION_NUM 0x055ac34b #define EPI_VERSION_DEV 5.90.195 -#define EPI_VERSION_STR "5.90.195.61" +#define EPI_VERSION_STR "5.90.195.75" #endif diff --git a/drivers/net/wireless/bcmdhd/include/hndpmu.h b/drivers/net/wireless/bcmdhd/include/hndpmu.h index 69a834c6b7eb..9bfc8c9275a9 100644 --- a/drivers/net/wireless/bcmdhd/include/hndpmu.h +++ b/drivers/net/wireless/bcmdhd/include/hndpmu.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndpmu.h 277737 2011-08-16 17:54:59Z $ + * $Id: hndpmu.h 335486 2012-05-28 09:47:55Z $ */ #ifndef _hndpmu_h_ @@ -31,4 +31,7 @@ extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on); extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength); +extern void si_pmu_set_otp_wr_volts(si_t *sih); +extern void si_pmu_set_otp_rd_volts(si_t *sih); + #endif /* _hndpmu_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h index 8f757509b95d..53bd2a1d5580 100644 --- a/drivers/net/wireless/bcmdhd/include/sbchipc.h +++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h @@ -5,7 +5,7 @@ * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, * GPIO interface, extbus, and support for serial and parallel flashes. * - * $Id: sbchipc.h 311371 2012-01-28 05:47:25Z $ + * $Id: sbchipc.h 333924 2012-05-18 04:48:52Z $ * * Copyright (C) 1999-2011, Broadcom Corporation * @@ -1441,18 +1441,18 @@ typedef volatile struct { #define CCTRL4331_EXT_LNA_G (1<<2) #define CCTRL4331_SPROM_GPIO13_15 (1<<3) #define CCTRL4331_EXTPA_EN (1<<4) -#define CCTRL4331_GPIOCLK_ON_SPROMCS <1<<5) +#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) #define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) #define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) #define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) #define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) -#define CCTRL4331_PCIE_AUXCLKEN <1<<10) -#define CCTRL4331_PCIE_PIPE_PLLDOWN <1<<11) -#define CCTRL4331_EXTPA_EN2 (1<<12) -#define CCTRL4331_EXT_LNA_A (1<<13) -#define CCTRL4331_BT_SHD0_ON_GPIO4 <1<<16) -#define CCTRL4331_BT_SHD1_ON_GPIO5 <1<<17) -#define CCTRL4331_EXTPA_ANA_EN (1<<24) +#define CCTRL4331_PCIE_AUXCLKEN (1<<10) +#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) +#define CCTRL4331_EXTPA_EN2 (1<<12) +#define CCTRL4331_EXT_LNA_A (1<<13) +#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) +#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) +#define CCTRL4331_EXTPA_ANA_EN (1<<24) #define CST4331_XTAL_FREQ 0x00000001 diff --git a/drivers/net/wireless/bcmdhd/include/siutils.h b/drivers/net/wireless/bcmdhd/include/siutils.h index 6a7b93c7b977..4e7aeb71cb02 100644 --- a/drivers/net/wireless/bcmdhd/include/siutils.h +++ b/drivers/net/wireless/bcmdhd/include/siutils.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: siutils.h 285387 2011-09-21 18:38:37Z $ + * $Id: siutils.h 335486 2012-05-28 09:47:55Z $ */ @@ -222,6 +222,8 @@ static INLINE void * si_seci_init(si_t *sih, uint8 use_seci) {return NULL;} extern bool si_is_otp_disabled(si_t *sih); extern bool si_is_otp_powered(si_t *sih); extern void si_otp_power(si_t *sih, bool on); +extern void si_set_otp_wr_volts(si_t *sih); +extern void si_set_otp_rd_volts(si_t *sih); extern bool si_is_sprom_available(si_t *sih); diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index 91274a0c680b..5ec0c9ade197 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wlioctl.h 312596 2012-02-03 02:53:30Z $ + * $Id: wlioctl.h 331292 2012-05-04 09:04:23Z $ */ @@ -1460,6 +1460,14 @@ typedef struct wl_sampledata { } wl_sampledata_t; +#define WL_CHAN_VALID_HW (1 << 0) +#define WL_CHAN_VALID_SW (1 << 1) +#define WL_CHAN_BAND_5G (1 << 2) +#define WL_CHAN_RADAR (1 << 3) +#define WL_CHAN_INACTIVE (1 << 4) +#define WL_CHAN_PASSIVE (1 << 5) +#define WL_CHAN_RESTRICTED (1 << 6) + #define WL_ERROR_VAL 0x00000001 #define WL_TRACE_VAL 0x00000002 diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index f728c14622a1..7f3e332dbfce 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -84,17 +84,15 @@ u32 wl_dbg_level = WL_DBG_ERR; * All the chnages in world regulatory domain are to be done here. */ static const struct ieee80211_regdomain brcm_regdom = { - .n_reg_rules = 5, + .n_reg_rules = 4, .alpha2 = "99", .reg_rules = { /* IEEE 802.11b/g, channels 1..11 */ - REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), /* IEEE 802.11b/g, channels 12..13. No HT40 * channel fits here. */ - REG_RULE(2467-10, 2472+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), + /* If any */ /* * IEEE 802.11 channel 14 - is for JP only, * we need cfg80211 to allow it (reg_flags = 0); so that @@ -231,6 +229,8 @@ static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data, bool completed); +static s32 wl_ibss_join_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed); static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, @@ -296,6 +296,7 @@ static void wl_free_wdev(struct wl_priv *wl); static s32 wl_inform_bss(struct wl_priv *wl); static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); +static s32 wl_inform_ibss(struct wl_priv *wl, const u8 *bssid); static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev); static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); @@ -335,7 +336,6 @@ static void wl_link_up(struct wl_priv *wl); static void wl_link_down(struct wl_priv *wl); static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); static void wl_init_conf(struct wl_conf *conf); -static s32 wl_update_wiphybands(struct wl_priv *wl); /* * iscan handler @@ -704,7 +704,7 @@ static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); if ((err = wldev_ioctl(dev, WLC_GET_BSS_INFO, wl->extra_buf, - sizeof(WL_EXTRA_BUF_MAX), false))) { + WL_EXTRA_BUF_MAX, false))) { WL_ERR(("Failed to get associated bss info, use temp channel \n")); chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); } @@ -925,6 +925,11 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) if (wl->p2p_supported) { memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); + + /* Clear GO_NEG_PHASE bit to take care of GO-NEG-FAIL cases + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared ")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); if (wl->p2p->vif_created) { if (wl_get_drv_status(wl, SCANNING, dev)) { wl_notify_escan_complete(wl, dev, true, true); @@ -1004,6 +1009,7 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, default: return -EINVAL; } + WL_DBG(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); if (ap) { wl_set_mode_by_netdev(wl, ndev, mode); @@ -1019,7 +1025,7 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, */ chspec = wl_cfg80211_get_shared_freq(wiphy); - wlif_type = ap ? WL_P2P_IF_GO : WL_P2P_IF_CLIENT; + wlif_type = WL_P2P_IF_GO; WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); wl_set_p2p_status(wl, IF_CHANGING); @@ -1043,6 +1049,14 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); return -EINVAL; } + } else { + infra = htod32(infra); + err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err) { + WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); + return -EAGAIN; + } + wl_set_mode_by_netdev(wl, ndev, mode); } ndev->ieee80211_ptr->iftype = type; @@ -1587,6 +1601,8 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); } + wl_clr_p2p_status(wl, GO_NEG_PHASE); + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); p2p_scan(wl) = true; } } else { @@ -1625,10 +1641,13 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } else { wpsie_len = 0; } - err = wl_cfgp2p_set_management_ie(wl, ndev, -1, - VNDR_IE_PRBREQ_FLAG, wpsie, wpsie_len); - if (unlikely(err)) { - goto scan_out; + if (wpsie_len > 0) { + err = wl_cfgp2p_set_management_ie(wl, + ndev, -1, VNDR_IE_PRBREQ_FLAG, + wpsie, wpsie_len); + if (unlikely(err)) { + goto scan_out; + } } } } @@ -1808,86 +1827,192 @@ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct wl_priv *wl = wiphy_priv(wiphy); - struct cfg80211_bss *bss; - struct ieee80211_channel *chan; struct wl_join_params join_params; - struct cfg80211_ssid ssid; - s32 scan_retry = 0; + struct wlc_ssid ssid; + struct ether_addr bssid; + size_t join_params_size = 0; + s32 wsec = 0; + s32 bcnprd; s32 err = 0; - bool rollback_lock = false; WL_TRACE(("In\n")); CHECK_SYS_UP(wl); - if (params->bssid) { - WL_ERR(("Invalid bssid\n")); - return -EOPNOTSUPP; - } - bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len); - if (!bss) { - memcpy(ssid.ssid, params->ssid, params->ssid_len); - ssid.ssid_len = params->ssid_len; - do { - if (unlikely - (__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) == - -EBUSY)) { - wl_delay(150); - } else { - break; - } - } while (++scan_retry < WL_SCAN_RETRY_MAX); - /* to allow scan_inform to propagate to cfg80211 plane */ - if (rtnl_is_locked()) { - rtnl_unlock(); - rollback_lock = true; - } - - /* wait 4 secons till scan done.... */ - schedule_timeout_interruptible(4 * HZ); - if (rollback_lock) - rtnl_lock(); - bss = cfg80211_get_ibss(wiphy, NULL, - params->ssid, params->ssid_len); - } - if (bss) { - wl->ibss_starter = false; - WL_DBG(("Found IBSS\n")); - } else { - wl->ibss_starter = true; - } - chan = params->channel; - if (chan) - wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + /* - * Join with specific BSSID and cached SSID - * If SSID is zero join based on BSSID only + * Cancel ongoing scan to sync up with sme state machine of cfg80211. */ - memset(&join_params, 0, sizeof(join_params)); - memcpy((void *)join_params.ssid.SSID, (void *)params->ssid, - params->ssid_len); - join_params.ssid.SSID_len = htod32(params->ssid_len); + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + /* Clean BSSID */ + bzero(&bssid, sizeof(bssid)); + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); + + if (params->ssid) + WL_INFO(("SSID: %s\n", params->ssid)); + else { + WL_ERR(("SSID: NULL, Not supported\n")); + err = -EOPNOTSUPP; + goto CleanUp; + } + if (params->bssid) - memcpy(&join_params.params.bssid, params->bssid, - ETHER_ADDR_LEN); + WL_INFO(("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", + params->bssid[0], params->bssid[1], params->bssid[2], + params->bssid[3], params->bssid[4], params->bssid[5])); + + if (params->channel) + WL_INFO(("channel: %d\n", params->channel->center_freq)); + + if (params->channel_fixed) + WL_INFO(("fixed channel required\n")); + + if (params->ie && params->ie_len) + WL_INFO(("ie len: %d\n", params->ie_len)); + + if (params->beacon_interval) + WL_INFO(("beacon interval: %d\n", params->beacon_interval)); + + if (params->basic_rates) + WL_INFO(("basic rates: %08X\n", params->basic_rates)); + + if (params->privacy) + WL_INFO(("privacy required\n")); + + wl_set_drv_status(wl, CONNECTING, dev); + + /* Configure Privacy for starter */ + if (params->privacy) + wsec |= WEP_ENABLED; + + err = wldev_iovar_setint(dev, "wsec", wsec); + if (err) { + WL_ERR(("wsec failed (%d)\n", err)); + goto CleanUp; + } + + err = wldev_iovar_setint(dev, "auth", WL_AUTH_OPEN_SYSTEM); + if (err) { + WL_ERR(("auth failed (%d)\n", err)); + goto CleanUp; + } + + err = wldev_iovar_setint(dev, "wpa_auth", 0); + if (err) { + WL_ERR(("wpa_auth failed (%d)\n", err)); + goto CleanUp; + } + + /* Configure Beacon Interval for starter */ + if (params->beacon_interval) + bcnprd = params->beacon_interval; else - memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN); + bcnprd = 100; - err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, - sizeof(join_params), false); - if (unlikely(err)) { - WL_ERR(("Error (%d)\n", err)); - return err; + bcnprd = htod32(bcnprd); + err = wldev_ioctl(dev, WLC_SET_BCNPRD, &bcnprd, sizeof(bcnprd), true); + if (err) { + WL_ERR(("WLC_SET_BCNPRD failed (%d)\n", err)); + goto CleanUp; + } + + /* Configure required join parameter */ + memset(&join_params, 0, sizeof(struct wl_join_params)); + + /* SSID */ + memset(&ssid, 0, sizeof(struct wlc_ssid)); + ssid.SSID_len = MIN(params->ssid_len, 32); + join_params.ssid.SSID_len = htod32(ssid.SSID_len); + memcpy(ssid.SSID, params->ssid, ssid.SSID_len); + memcpy(join_params.ssid.SSID, params->ssid, ssid.SSID_len); + join_params_size = sizeof(join_params.ssid); + + wl_update_prof(wl, dev, NULL, &ssid, WL_PROF_SSID); + + /* BSSID */ + if (params->bssid) { + memcpy(&join_params.params.bssid, params->bssid, ETHER_ADDR_LEN); + join_params_size = sizeof(join_params.ssid) + + WL_ASSOC_PARAMS_FIXED_SIZE; + + wl_update_prof(wl, dev, NULL, params->bssid, WL_PROF_BSSID); + } else { + memcpy(&join_params.params.bssid, ðer_bcast, ETHER_ADDR_LEN); + } + + /* Channel */ + if (params->channel) { + u32 target_channel; + + target_channel = ieee80211_frequency_to_channel( + params->channel->center_freq); + if (params->channel_fixed) { + /* adding chanspec */ + wl_ch_to_chanspec(target_channel, + &join_params, &join_params_size); + } + + /* set channel for starter */ + target_channel = htod32(target_channel); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &target_channel, sizeof(target_channel), true); + if (err) { + WL_ERR(("WLC_SET_CHANNEL failed (%d)\n", err)); + goto CleanUp; + } } + + wl->ibss_starter = false; + + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); + if (err) { + WL_ERR(("WLC_SET_SSID failed (%d)\n", err)); + goto CleanUp; + } + +CleanUp: + + if (err) + wl_clr_drv_status(wl, CONNECTING, dev); + + WL_TRACE(("Exit\n")); return err; } static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scbval; + bool act = false; s32 err = 0; + u8 *curbssid; + + WL_TRACE(("Enter\n")); CHECK_SYS_UP(wl); - wl_link_down(wl); + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + wl_set_drv_status(wl, DISCONNECTING, dev); + scbval.val = DOT11_RC_DISASSOC_LEAVING; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (unlikely(err)) { + wl_clr_drv_status(wl, DISCONNECTING, dev); + WL_ERR(("error (%d)\n", err)); + return err; + } + } + WL_TRACE(("Exit\n")); return err; } @@ -2216,8 +2341,9 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, */ WL_DBG(("ASSOC2 p2p index : %d sme->ie_len %d\n", wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); - wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), - VNDR_IE_PRBREQ_FLAG, sme->ie, sme->ie_len); + wl_cfgp2p_set_management_ie(wl, dev, + wl_cfgp2p_find_idx(wl, dev), VNDR_IE_PRBREQ_FLAG, + sme->ie, sme->ie_len); wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); } @@ -3328,8 +3454,8 @@ wl_cfg80211_send_at_common_channel(struct wl_priv *wl, if (wl->afx_hdl->peer_chan != WL_INVALID) wl_cfg80211_send_pending_tx_act_frm(wl); else { - WL_ERR(("Couldn't find the peer after %d retries\n", - wl->afx_hdl->retry)); + WL_ERR(("Couldn't find the peer " MACSTR " after %d retries\n", + MAC2STR(wl->afx_hdl->pending_tx_dst_addr.octet), wl->afx_hdl->retry)); } wl->afx_hdl->dev = NULL; wl->afx_hdl->bssidx = WL_INVALID; @@ -3355,7 +3481,9 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, wl_af_params_t *af_params; wifi_p2p_ie_t *p2p_ie; wpa_ie_fixed_t *wps_ie; +#ifdef WLWFDIE wifi_wfd_ie_t *wfd_ie; +#endif /* WLWFDIE */ scb_val_t scb_val; const struct ieee80211_mgmt *mgmt; struct wl_priv *wl = wiphy_priv(wiphy); @@ -3364,7 +3492,9 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, s32 bssidx = 0; u32 p2pie_len = 0; u32 wpsie_len = 0; +#ifdef WLWFDIE u32 wfdie_len = 0; +#endif /* WLWFDIE */ u32 id; u32 retry = 0; bool ack = false; @@ -3414,11 +3544,13 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, /* Total length of P2P Information Element */ p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); } +#ifdef WLWFDIE if ((wfd_ie = wl_cfgp2p_find_wfdie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Total length of WFD Information Element */ wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); } +#endif /* WLWFDIE */ if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Order of Vendor IE is 1) WPS IE + @@ -3430,7 +3562,11 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, sizeof(wps_ie->tag); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_PRBRSP_FLAG, +#ifdef WLWFDIE (u8 *)wps_ie, wpsie_len + p2pie_len + wfdie_len); +#else + (u8 *)wps_ie, wpsie_len + p2pie_len); +#endif /* WLWFDIE */ } cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); goto exit; @@ -3443,8 +3579,9 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, WL_DBG(("Disconnect STA : %s scb_val.val %d\n", bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), scb_val.val)); - /* Wait for the deauth event to come, supplicant will do the delete iface immediately - * and we will have problem in sending deauth frame if we delete the bss in firmware + /* Wait for the deauth event to come, supplicant will do the + * delete iface immediately and we will have problem in sending + * deauth frame if we delete the bss in firmware */ wl_delay(400); cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); @@ -3513,6 +3650,36 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, WL_DBG(("P2P PUB action_frame->len: %d chan %d category %d subtype %d\n", action_frame->len, af_params->channel, act_frm->category, act_frm->subtype)); + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_REQ) || + (act_frm->subtype == P2P_PAF_GON_RSP) || + (act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_REQ))) { + wldev_iovar_setint(dev, "mpc", 0); + } + + if (act_frm->subtype == P2P_PAF_GON_REQ) { + WL_DBG(("P2P: GO_NEG_PHASE status set \n")); + wl_set_p2p_status(wl, GO_NEG_PHASE); + } else if (act_frm->subtype == P2P_PAF_GON_CONF) { + /* If we reached till GO Neg confirmation + * reset the filter + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + } + + if (act_frm->subtype == P2P_PAF_GON_RSP) + retry_cnt = 1; + else retry_cnt = WL_ACT_FRAME_RETRY; + + if (act_frm && act_frm->subtype == P2P_PAF_DEVDIS_REQ) { + af_params->dwell_time = WL_LONG_DWELL_TIME; + } else if (act_frm && + (act_frm->subtype == P2P_PAF_PROVDIS_REQ || + act_frm->subtype == P2P_PAF_PROVDIS_RSP || + act_frm->subtype == P2P_PAF_GON_RSP)) { + af_params->dwell_time = WL_MED_DWELL_TIME; + } } else if (wl_cfgp2p_is_p2p_action(action_frame->data, action_frame->len)) { p2p_act_frm = (wifi_p2p_action_frame_t *) (action_frame->data); WL_DBG(("P2P action_frame->len: %d chan %d category %d subtype %d\n", @@ -3523,32 +3690,13 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, WL_DBG(("Service Discovery action_frame->len: %d chan %d category %d action %d\n", action_frame->len, af_params->channel, sd_act_frm->category, sd_act_frm->action)); - + af_params->dwell_time = WL_MED_DWELL_TIME; + retry_cnt = WL_ACT_FRAME_RETRY; } wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); /* * To make sure to send successfully action frame, we have to turn off mpc */ - - if (act_frm && ((act_frm->subtype == P2P_PAF_GON_REQ) || - (act_frm->subtype == P2P_PAF_GON_RSP) || - (act_frm->subtype == P2P_PAF_GON_CONF) || - (act_frm->subtype == P2P_PAF_PROVDIS_REQ))) { - wldev_iovar_setint(dev, "mpc", 0); - } - if (act_frm->subtype == P2P_PAF_GON_RSP) - retry_cnt = 1; - else retry_cnt = WL_ACT_FRAME_RETRY; - - if (act_frm && act_frm->subtype == P2P_PAF_DEVDIS_REQ) { - af_params->dwell_time = WL_LONG_DWELL_TIME; - } else if (act_frm && - (act_frm->subtype == P2P_PAF_PROVDIS_REQ || - act_frm->subtype == P2P_PAF_PROVDIS_RSP || - act_frm->subtype == P2P_PAF_GON_RSP)) { - af_params->dwell_time = WL_MED_DWELL_TIME; - } - if (IS_P2P_SOCIAL(af_params->channel) && (IS_P2P_PUB_ACT_REQ(act_frm, &act_frm->elts[0], action_frame->len) || IS_GAS_REQ(sd_act_frm, action_frame->len)) && @@ -3560,6 +3708,18 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, /* channel offload for action request frame */ ack = wl_cfg80211_send_at_common_channel(wl, dev, af_params); + /* We need to retry Service discovery frames as they don't get retried immediately by supplicant*/ + if ((!ack) && (IS_GAS_REQ(sd_act_frm, action_frame->len))) { + for (retry = 1; retry < retry_cnt; retry++) { + WL_DBG(("Service Discovery action_frame retry %d len: %d chan %d category %d action %d\n", + retry, action_frame->len, af_params->channel, + sd_act_frm->category, sd_act_frm->action)); + ack = (wl_cfgp2p_tx_action_frame(wl, dev, + af_params, bssidx)) ? false : true; + if (ack) + break; + } + } } else { ack = (wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx)) ? false : true; if (!ack) { @@ -3915,13 +4075,17 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, wpa_ie_fixed_t *wpa_ie; bcm_tlv_t *wpa2_ie; wifi_p2p_ie_t *p2p_ie; +#ifdef WLWFDIE wifi_wfd_ie_t *wfd_ie; +#endif /* WLWFDIE */ bool is_bssup = false; bool update_bss = false; bool pbc = false; u16 wpsie_len = 0; u16 p2pie_len = 0; +#ifdef WLWFDIE u32 wfdie_len = 0; +#endif /* WLWFDIE */ u8 beacon_ie[IE_MAX_LEN]; s32 ie_offset = 0; s32 bssidx = 0; @@ -3979,24 +4143,30 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, } else { WL_ERR(("No P2PIE in beacon \n")); } +#ifdef WLWFDIE /* find the WFD IEs */ if ((wfd_ie = wl_cfgp2p_find_wfdie((u8 *)info->tail, info->tail_len)) != NULL) { - /* Total length of P2P Information Element */ - wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); - if ((wpsie_len + p2pie_len + wfdie_len) < IE_MAX_LEN) { - memcpy(&beacon_ie[wpsie_len + p2pie_len], wfd_ie, wfdie_len); - } else { + /* Total length of P2P Information Element */ + wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); + if ((wpsie_len + p2pie_len + wfdie_len) < IE_MAX_LEN) { + memcpy(&beacon_ie[wpsie_len + p2pie_len], wfd_ie, wfdie_len); + } else { WL_ERR(("Found WFD IE but there is no space, (%d)(%d)(%d)\n", - wpsie_len, p2pie_len, wfdie_len)); + wpsie_len, p2pie_len, wfdie_len)); wfdie_len = 0; } } else { WL_ERR(("No WFDIE in beacon \n")); } +#endif /* WLWFDIE */ /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, +#ifdef WLWFDIE beacon_ie, wpsie_len + p2pie_len + wfdie_len); +#else + beacon_ie, wpsie_len + p2pie_len); +#endif /* WLWFDIE */ /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -4268,7 +4438,7 @@ exit: #ifdef WL_SCHED_SCAN #define PNO_TIME 30 #define PNO_REPEAT 4 -#define PNO_FREQ_EXPO_MAX 3 +#define PNO_FREQ_EXPO_MAX 2 int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request) @@ -4615,8 +4785,13 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) kfree(notif_bss_info); return err; } + else if( wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO) == NULL) + { + WL_DBG(("Couldn't find P2P_SEID_DEV_INFO in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } } - if (!mgmt->u.probe_resp.timestamp) { struct timeval tv; @@ -4639,6 +4814,114 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) return err; } +static s32 wl_inform_ibss(struct wl_priv *wl, const u8 *bssid) +{ + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wiphy *wiphy = wl_to_wiphy(wl); + struct wl_bss_info *bi = NULL; + struct ieee80211_channel *notify_channel; + struct ieee80211_supported_band *band; + struct cfg80211_bss *bss; + s32 err = 0; + u16 channel; + u32 freq; + u32 wsec = 0; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + WL_TRACE(("Enter\n")); + + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + + mutex_lock(&wl->usr_sync); + + *(u32 *)wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX, false); + if (err) { + WL_ERR(("Failed to get bss info for IBSS\n")); + err = -EIO; + goto CleanUp; + } + bi = (struct wl_bss_info *)(wl->extra_buf + 4); + + if (memcmp(bssid, &bi->BSSID, ETHER_ADDR_LEN)) { + WL_ERR(("BSSID mismatch: Inform %02x:%02x:%02x:%02x:%02x:%02x," + "%02x:%02x:%02x:%02x:%02x:%02x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], + bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], + bi->BSSID.octet[5])); + err = -EINVAL; + goto CleanUp; + } + + err = wldev_iovar_getint(ndev, "wsec", &wsec); + if (err) { + WL_ERR(("wsec failed: %d\n", err)); + err = -EIO; + goto CleanUp; + } + + channel = bi->ctl_ch ? bi->ctl_ch : + CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); + (void)band->band; +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = dtoh16(bi->capability); + notify_interval = dtoh16(bi->beacon_period); + notify_ie = (u8 *)bi + dtoh16(bi->ie_offset); + notify_ielen = dtoh32(bi->ie_length); + notify_signal = (int16)dtoh16(bi->RSSI) * 100; + + if (wl->p2p_supported) { + notify_capability |= DOT11_CAP_IBSS; + if (wsec) + notify_capability |= DOT11_CAP_PRIVACY; + } + + WL_DBG(("BSSID %02x:%02x:%02x:%02x:%02x:%02x", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5])); + WL_INFO(("channel: %d(%d)\n", channel, freq)); + WL_INFO(("capability: %X\n", notify_capability)); + WL_INFO(("beacon interval: %d ms\n", notify_interval)); + WL_INFO(("signal: %d dBm\n", notify_signal)); + WL_INFO(("ie_len: %d\n", notify_ielen)); + bss = cfg80211_inform_bss(wiphy, notify_channel, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, GFP_KERNEL); + if (!bss) { + WL_ERR(("cfg80211_inform_bss() Failed\n")); + err = -ENOMEM; + goto CleanUp; + } + + cfg80211_put_bss(bss); + err = 0; + +CleanUp: + + mutex_unlock(&wl->usr_sync); + + WL_TRACE(("Exit\n")); + return err; +} + static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev) { u32 event = ntoh32(e->event_type); @@ -4648,12 +4931,12 @@ static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net WL_DBG(("event %d, status %d flags %x\n", event, status, flags)); if (event == WLC_E_SET_SSID) { if (status == WLC_E_STATUS_SUCCESS) { - if (!wl_is_ibssmode(wl, ndev)) - return true; + return true; } } else if (event == WLC_E_LINK) { if (flags & WLC_EVENT_MSG_LINK) - return true; + if (!wl_is_ibssmode(wl, ndev)) + return true; } WL_DBG(("wl_is_linkup false\n")); @@ -4834,7 +5117,9 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, if((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DISASSOC_IND)) { reason = ntoh32(e->reason); wl->deauth_reason = reason; - WL_ERR(("Received %s event with reason code: %d\n", (event == WLC_E_DEAUTH_IND)? "WLC_E_DEAUTH_IND":"WLC_E_DISASSOC_IND", reason)); + WL_ERR(("Received %s event with reason code: %d\n", + (event == WLC_E_DEAUTH_IND)? + "WLC_E_DEAUTH_IND":"WLC_E_DISASSOC_IND", reason)); } if (wl_is_linkup(wl, e, ndev)) { wl_link_up(wl); @@ -4843,10 +5128,8 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); wl->deauth_reason = 0; if (wl_is_ibssmode(wl, ndev)) { - printk("cfg80211_ibss_joined\n"); - cfg80211_ibss_joined(ndev, (s8 *)&e->addr, - GFP_KERNEL); - WL_DBG(("joined in IBSS network\n")); + wl_ibss_join_done(wl, ndev, e, data, true); + WL_DBG(("wl_ibss_join_done succeeded\n")); } else { if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { printk("wl_bss_connect_done succeeded\n"); @@ -4856,7 +5139,6 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, wl_read_prof(wl, ndev, WL_PROF_SSID))->SSID)); } } - } else if (wl_is_linkdown(wl, e)) { if (wl->scan_request) { if (wl->escan_on) { @@ -4880,15 +5162,21 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, scbval.val = htod32(scbval.val); wldev_ioctl(ndev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); - WL_ERR(("link down, calling cfg80211_disconnected with deauth_reason:%d\n", wl->deauth_reason)); - cfg80211_disconnected(ndev, wl->deauth_reason , NULL, 0, GFP_KERNEL); + WL_ERR(("link down, calling cfg80211_disconnected" + " with deauth_reason:%d\n", wl->deauth_reason)); + if (!wl_is_ibssmode(wl, ndev)) + cfg80211_disconnected(ndev, wl->deauth_reason, + NULL, 0, GFP_KERNEL); wl_link_down(wl); wl_init_prof(wl, ndev); } } else if (wl_get_drv_status(wl, CONNECTING, ndev)) { printk("link down, during connecting\n"); - wl_bss_connect_done(wl, ndev, e, data, false); + if (wl_is_ibssmode(wl, ndev)) + wl_ibss_join_done(wl, ndev, e, data, false); + else + wl_bss_connect_done(wl, ndev, e, data, false); } wl_clr_drv_status(wl, DISCONNECTING, ndev); @@ -5183,6 +5471,35 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, return err; } +static s32 +wl_ibss_join_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed) +{ + s32 err = 0; + + WL_TRACE(("Enter\n")); + + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); + if (completed) { + err = wl_inform_ibss(wl, (u8 *)&e->addr); + if (err) { + WL_ERR(("wl_inform_ibss() failed: %d\n", err)); + } + wl_set_drv_status(wl, CONNECTED, ndev); + + cfg80211_ibss_joined(ndev, (u8 *)&e->addr, GFP_KERNEL); + WL_DBG(("cfg80211_ibss_joined() called with valid BSSID\n")); + } + } + + WL_TRACE(("Exit\n")); + return err; +} + static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) @@ -5365,6 +5682,13 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, WL_ERR(("No valid band")); return -EINVAL; } + + if ((event == WLC_E_P2P_PROBREQ_MSG) && + wl->p2p && wl_get_p2p_status(wl, GO_NEG_PHASE)) { + WL_DBG(("Filtering P2P probe_req while being in GO-Neg state\n")); + goto exit; + } + #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) freq = ieee80211_channel_to_frequency(channel); #else @@ -5409,6 +5733,11 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, (act_frm->subtype == P2P_PAF_PROVDIS_RSP))) { wldev_iovar_setint(dev, "mpc", 1); } + + if (act_frm && (act_frm->subtype == P2P_PAF_GON_CONF)) { + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + } } else { mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); } @@ -6044,7 +6373,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl_scan_results_t *list; u32 bi_length; u32 i; - + u8 *p2p_dev_addr = NULL; WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); /* P2P SCAN is coming from primary interface */ @@ -6086,16 +6415,18 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (!(wl_to_wiphy(wl)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { if (dtoh16(bi->capability) & DOT11_CAP_IBSS) { - WL_ERR(("Ignoring IBSS result\n")); + WL_DBG(("Ignoring IBSS result\n")); goto exit; } } if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { - if (!memcmp(bi->BSSID.octet, + p2p_dev_addr = wl_cfgp2p_retreive_p2p_dev_addr(bi, bi_length); + if (p2p_dev_addr && !memcmp(p2p_dev_addr, wl->afx_hdl->pending_tx_dst_addr.octet, ETHER_ADDR_LEN)) { s32 channel = CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); - WL_DBG(("ACTION FRAME SCAN : Peer found, channel : %d\n", channel)); + WL_DBG(("ACTION FRAME SCAN : Peer " MACSTR " found, channel : %d\n", + MAC2STR(wl->afx_hdl->pending_tx_dst_addr.octet), channel)); wl_clr_p2p_status(wl, SCANNING); wl->afx_hdl->peer_chan = channel; complete(&wl->act_frm_scan); @@ -6721,20 +7052,148 @@ eventmsg_out: } +static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) +{ + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ieee80211_channel *band_chan_arr = NULL; + wl_uint32_list_t *list; + u32 i, j, index, n_2g, n_5g, band, channel, array_size; + u32 *n_cnt = NULL; + chanspec_t c = 0; + s32 err = BCME_OK; + bool update; + bool ht40_allowed; + u8 *pbuf = NULL; + +#define LOCAL_BUF_LEN 1024 + pbuf = kzalloc(LOCAL_BUF_LEN, GFP_KERNEL); + if (pbuf == NULL) { + WL_ERR(("failed to allocate local buf\n")); + return -ENOMEM; + } + + list = (wl_uint32_list_t *)(void *)pbuf; + list->count = htod32(WL_NUMCHANSPECS); + + err = wldev_iovar_getbuf_bsscfg(dev, "chanspecs", NULL, + 0, pbuf, LOCAL_BUF_LEN, 0, &wl->ioctl_buf_sync); + if (err != 0) { + WL_ERR(("get chanspecs failed with %d\n", err)); + kfree(pbuf); + return err; + } +#undef LOCAL_BUF_LEN + + band = array_size = n_2g = n_5g = 0; + for (i = 0; i < dtoh32(list->count); i++) { + index = 0; + update = FALSE; + ht40_allowed = FALSE; + c = (chanspec_t)dtoh32(list->element[i]); + channel = CHSPEC_CHANNEL(c); + if (CHSPEC_IS40(c)) { + if (CHSPEC_SB_UPPER(c)) + channel += CH_10MHZ_APART; + else + channel -= CH_10MHZ_APART; + } + + if (CHSPEC_IS2G(c) && channel <= CH_MAX_2G_CHANNEL) { + band_chan_arr = __wl_2ghz_channels; + array_size = ARRAYSIZE(__wl_2ghz_channels); + n_cnt = &n_2g; + band = IEEE80211_BAND_2GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_40ALL) ? TRUE : FALSE; + } else if (CHSPEC_IS5G(c) && channel > CH_MAX_2G_CHANNEL) { + band_chan_arr = __wl_5ghz_a_channels; + array_size = ARRAYSIZE(__wl_5ghz_a_channels); + n_cnt = &n_5g; + band = IEEE80211_BAND_5GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_20ALL) ? FALSE : TRUE; + } + + for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { + if (band_chan_arr[j].hw_value == channel) { + update = TRUE; + break; + } + } + + if (update) + index = j; + else + index = *n_cnt; + + if (index < array_size) { +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel); +#else + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel, band); +#endif + band_chan_arr[index].hw_value = channel; + + if (CHSPEC_IS40(c) && ht40_allowed) { + u32 ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; + if (CHSPEC_SB_UPPER(c)) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40MINUS; + } + } else { + band_chan_arr[index].flags = IEEE80211_CHAN_NO_HT40; + if (band == IEEE80211_BAND_2GHZ) + channel |= WL_CHANSPEC_BAND_2G; + else + channel |= WL_CHANSPEC_BAND_5G; + err = wldev_iovar_getint(dev, "per_chan_info", &channel); + if (!err) { + if (channel & WL_CHAN_RADAR) { + band_chan_arr[index].flags |= IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS; + } + if (channel & WL_CHAN_PASSIVE) { + band_chan_arr[index].flags |= IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS; + } + } + } + + if (!update) + (*n_cnt)++; + } + } + + __wl_band_2ghz.n_channels = n_2g; + __wl_band_5ghz_a.n_channels = n_5g; + + kfree(pbuf); + return err; +} + s32 wl_update_wiphybands(struct wl_priv *wl) { struct wiphy *wiphy; + struct net_device *dev; u32 bandlist[3]; u32 nband = 0; u32 i = 0; s32 err = 0; int nmode = 0; - int bw_40 = 0; + int bw_cap = 0; int index = 0; WL_DBG(("Entry")); + + if (wl == NULL) + wl = wlcfg_drv_priv; + dev = wl_to_prmry_ndev(wl); + memset(bandlist, 0, sizeof(bandlist)); - err = wldev_ioctl(wl_to_prmry_ndev(wl), WLC_GET_BANDLIST, bandlist, + err = wldev_ioctl(dev, WLC_GET_BANDLIST, bandlist, sizeof(bandlist), false); if (unlikely(err)) { WL_ERR(("error read bandlist (%d)\n", err)); @@ -6745,41 +7204,44 @@ s32 wl_update_wiphybands(struct wl_priv *wl) wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - err = wldev_iovar_getint(wl_to_prmry_ndev(wl), "nmode", &nmode); + err = wldev_iovar_getint(dev, "nmode", &nmode); if (unlikely(err)) { WL_ERR(("error reading nmode (%d)\n", err)); - } - else { + } else { /* For nmodeonly check bw cap */ - err = wldev_iovar_getint(wl_to_prmry_ndev(wl), "mimo_bw_cap", &bw_40); + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); if (unlikely(err)) { WL_ERR(("error get mimo_bw_cap (%d)\n", err)); } } + err = wl_construct_reginfo(wl, bw_cap); + if (err) { + WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); + return err; + } for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; - if (bandlist[i] == WLC_BAND_5G) { + if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; - index = IEEE80211_BAND_5GHZ; - } else if (bandlist[i] == WLC_BAND_2G) { + index = IEEE80211_BAND_5GHZ; + if (bw_cap == WLC_N_BW_40ALL || bw_cap == WLC_N_BW_20IN2G_40IN5G) + wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + } else if (bandlist[i] == WLC_BAND_2G && __wl_band_2ghz.n_channels > 0) { wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; - index = IEEE80211_BAND_2GHZ; + index = IEEE80211_BAND_2GHZ; + if (bandlist[i] == WLC_BAND_2G && bw_cap == WLC_N_BW_40ALL) + wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } if ((index >= 0) && nmode) { - wiphy->bands[index]->ht_cap.cap = - IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40 | - IEEE80211_HT_CAP_MAX_AMSDU; + wiphy->bands[index]->ht_cap.cap |= + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40; wiphy->bands[index]->ht_cap.ht_supported = TRUE; wiphy->bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; wiphy->bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; } - - if ((index >= 0) && bw_40) { - wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - } } wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); @@ -7106,8 +7568,7 @@ static void wl_init_eq_lock(struct wl_priv *wl) static void wl_delay(u32 ms) { - if (ms < 1000 / HZ) { - cond_resched(); + if (in_atomic() || ms < 1000 / HZ) { mdelay(ms); } else { msleep(ms); @@ -7162,17 +7623,44 @@ s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, { struct wl_priv *wl; struct net_device *ndev = NULL; + struct ether_addr primary_mac; s32 ret = 0; s32 bssidx = 0; s32 pktflag = 0; wl = wlcfg_drv_priv; - if (wl->p2p && wl->p2p->vif_created) { - ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); - bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); - } else if (wl_get_drv_status(wl, AP_CREATING, net) || + + if (wl_get_drv_status(wl, AP_CREATING, net) || wl_get_drv_status(wl, AP_CREATED, net)) { ndev = net; bssidx = 0; + } else if (wl->p2p) { + if (net == wl->p2p_net) { + net = wl_to_prmry_ndev(wl); + } + + if (!wl->p2p->on) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, + &wl->p2p->int_addr); + /* In case of p2p_listen command, supplicant send remain_on_channel + * without turning on P2P + */ + p2p_on(wl) = true; + ret = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); + + if (unlikely(ret)) { + goto exit; + } + } + if (net != wl_to_prmry_ndev(wl)) { + if (wl_get_mode_by_netdev(wl, net) == WL_MODE_AP) { + ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); + } + } else { + ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + } } if (ndev != NULL) { switch (type) { @@ -7189,7 +7677,7 @@ s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, if (pktflag) ret = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, pktflag, buf, len); } - +exit: return ret; } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index fba853149c35..37c8e5850c44 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -136,6 +136,12 @@ do { \ #define WL_INVALID -1 + +/* Bring down SCB Timeout to 20secs from 60secs default */ +#ifndef WL_SCB_TIMEOUT +#define WL_SCB_TIMEOUT 20 +#endif + /* driver status */ enum wl_status { WL_STATUS_READY = 0, @@ -662,5 +668,6 @@ extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); extern s32 wl_mode_to_nl80211_iftype(s32 mode); int wl_cfg80211_do_driver_init(struct net_device *net); void wl_cfg80211_enable_trace(int level); +extern s32 wl_update_wiphybands(struct wl_priv *wl); extern s32 wl_cfg80211_if_is_group_owner(void); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index a34c4ad17c25..318e122004e7 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -59,6 +59,8 @@ static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); static int wl_cfgp2p_if_open(struct net_device *net); static int wl_cfgp2p_if_stop(struct net_device *net); +static s32 wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify); static const struct net_device_ops wl_cfgp2p_if_ops = { .ndo_open = wl_cfgp2p_if_open, @@ -338,6 +340,7 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, wl_p2p_if_t ifreq; s32 err; struct net_device *ndev = wl_to_prmry_ndev(wl); + u32 scb_timeout = WL_SCB_TIMEOUT; ifreq.type = if_type; ifreq.chspec = chspec; @@ -351,6 +354,15 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (unlikely(err < 0)) { + printk("'wl p2p_ifadd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(ndev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); + } + return err; } @@ -387,6 +399,7 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, { wl_p2p_if_t ifreq; s32 err; + u32 scb_timeout = WL_SCB_TIMEOUT; struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); ifreq.type = if_type; @@ -404,6 +417,10 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, if (unlikely(err < 0)) { printk("'wl p2p_ifupd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(netdev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); } return err; } @@ -829,10 +846,12 @@ exit: /* Check whether the given IE looks like WFA P2P IE. */ #define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) -/* Check whether the given IE looks like WFA WFDisplay IE. */ -#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */ -#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ - (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_WFD) +#ifdef WLWFDIE + /* Check whether the given IE looks like WFA WFDisplay IE. */ +#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */ +#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_WFD) +#endif /* WLWFDIE */ /* Delete and Set a management vndr ie to firmware * Parameters: * @wl : wl_private data @@ -957,8 +976,12 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss ie_len = ie_buf[pos++]; if ((ie_id == DOT11_MNG_VS_ID) && (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || +#ifdef WLWFDIE wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { +#else + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { +#endif /* WLWFDIE */ CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); @@ -984,8 +1007,12 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss ie_len = ie_buf[pos++]; if ((ie_id == DOT11_MNG_VS_ID) && (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || +#ifdef WLWFDIE wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { +#else + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { +#endif /* WLWFDIE */ CFGP2P_INFO(("ADDED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); @@ -1095,18 +1122,21 @@ wl_cfgp2p_find_p2pie(u8 *parse, u32 len) } return NULL; } + +#ifdef WLWFDIE wifi_wfd_ie_t * wl_cfgp2p_find_wfdie(u8 *parse, u32 len) { bcm_tlv_t *ie; while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { - if (wl_cfgp2p_is_wfd_ie((uint8*)ie, &parse, &len)) { + if (wl_cfgp2p_is_wfd_ie((uint8*)ie, &parse, &len)) { return (wifi_wfd_ie_t *)ie; } } return NULL; } +#endif /* WLWFDIE */ static s32 wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, @@ -1230,6 +1260,7 @@ wl_cfgp2p_listen_expired(unsigned long data) struct wl_priv *wl = (struct wl_priv *) data; CFGP2P_DBG((" Enter\n")); + memset(&msg, 0, sizeof(wl_event_msg_t)); msg.event_type = hton32(WLC_E_P2P_DISC_LISTEN_COMPLETE); wl_cfg80211_event(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), &msg, NULL); } @@ -1237,7 +1268,7 @@ wl_cfgp2p_listen_expired(unsigned long data) /* * Routine for cancelling the P2P LISTEN */ -s32 +static s32 wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, bool notify) { @@ -1402,13 +1433,12 @@ wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (ret < 0) { - CFGP2P_ERR((" sending action frame is failed\n")); goto exit; } timeout = wait_event_interruptible_timeout(wl->netif_change_event, - (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || wl_get_p2p_status(wl, ACTION_TX_NOACK)), - msecs_to_jiffies(MAX_WAIT_TIME)); + (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || wl_get_p2p_status(wl, ACTION_TX_NOACK)), + msecs_to_jiffies(MAX_WAIT_TIME)); if (timeout > 0 && wl_get_p2p_status(wl, ACTION_TX_COMPLETED)) { CFGP2P_INFO(("tx action frame operation is completed\n")); diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index 668198d31a2e..40d9e5463fe0 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -30,7 +30,11 @@ struct wl_priv; extern u32 wl_dbg_level; + +#ifdef WLWFDIE typedef struct wifi_p2p_ie wifi_wfd_ie_t; +#endif /* WLWFDIE */ + /* Enumeration of the usages of the BSSCFGs used by the P2P Library. Do not * confuse this with a bsscfg index. This value is an index into the * saved_ie[] array of structures which in turn contains a bsscfg index field. @@ -42,7 +46,7 @@ typedef enum { P2PAPI_BSSCFG_MAX } p2p_bsscfg_type_t; -#define IE_MAX_LEN 300 +#define IE_MAX_LEN 512 /* Structure to hold all saved P2P and WPS IEs for a BSSCFG */ struct p2p_saved_ie { u8 p2p_probe_req_ie[IE_MAX_LEN]; @@ -91,7 +95,8 @@ enum wl_cfgp2p_status { WLP2P_STATUS_LISTEN_EXPIRED, WLP2P_STATUS_ACTION_TX_COMPLETED, WLP2P_STATUS_ACTION_TX_NOACK, - WLP2P_STATUS_SCANNING + WLP2P_STATUS_SCANNING, + WLP2P_STATUS_GO_NEG_PHASE }; @@ -191,8 +196,10 @@ wl_cfgp2p_find_wpsie(u8 *parse, u32 len); extern wifi_p2p_ie_t * wl_cfgp2p_find_p2pie(u8 *parse, u32 len); +#ifdef WLWFDIE extern wifi_wfd_ie_t * wl_cfgp2p_find_wfdie(u8 *parse, u32 len); +#endif /* WLWFDIE */ extern s32 wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 059929340354..6bb07650b27e 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -6341,6 +6341,7 @@ wl_iw_set_cscan( else { WL_ERROR(("%s Ignoring CSCAN : First Scan is not done yet %d\n", __FUNCTION__, g_first_counter_scans)); + res = -EBUSY; goto exit_proc; } } -- cgit v1.2.3 From 81c24e718939bc0a2d774ba6790febf57b4da4a5 Mon Sep 17 00:00:00 2001 From: Steve Lin Date: Wed, 22 Aug 2012 14:55:34 -0700 Subject: net/usbnet: avoid recursive locking in usbnet_stop() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit |kernel BUG at kernel/rtmutex.c:724! |[] (rt_spin_lock_slowlock+0x108/0x2bc) from [] (defer_bh+0x1c/0xb4) |[] (defer_bh+0x1c/0xb4) from [] (rx_complete+0x14c/0x194) |[] (rx_complete+0x14c/0x194) from [] (usb_hcd_giveback_urb+0xa0/0xf0) |[] (usb_hcd_giveback_urb+0xa0/0xf0) from [] (musb_giveback+0x34/0x40) |[] (musb_giveback+0x34/0x40) from [] (musb_advance_schedule+0xb4/0x1c0) |[] (musb_advance_schedule+0xb4/0x1c0) from [] (musb_cleanup_urb.isra.9+0x80/0x8c) |[] (musb_cleanup_urb.isra.9+0x80/0x8c) from [] (musb_urb_dequeue+0xec/0x108) |[] (musb_urb_dequeue+0xec/0x108) from [] (unlink1+0xbc/0xcc) |[] (unlink1+0xbc/0xcc) from [] (usb_hcd_unlink_urb+0x54/0xa8) |[] (usb_hcd_unlink_urb+0x54/0xa8) from [] (unlink_urbs.isra.17+0x2c/0x58) |[] (unlink_urbs.isra.17+0x2c/0x58) from [] (usbnet_terminate_urbs+0x94/0x10c) |[] (usbnet_terminate_urbs+0x94/0x10c) from [] (usbnet_stop+0x100/0x15c) |[] (usbnet_stop+0x100/0x15c) from [] (__dev_close_many+0x94/0xc8) defer_bh() takes the lock which is hold during unlink_urbs(). The safe walk suggest that the skb will be removed from the list and this is done by defer_bh() so it seems to be okay to drop the lock here. Reported-by: Aníbal Almeida Pinto Signed-off-by: Sebastian Andrzej Siewior Acked-by: Oliver Neukum Signed-off-by: David S. Miller (cherry picked from commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d) --------------------------------------------------------------------- net/usbnet: reserve headroom on rx skbs network drivers should reserve some headroom on incoming skbs so that we dont need expensive reallocations, eg forwarding packets in tunnels. This NET_SKB_PAD padding is done in various helpers, like __netdev_alloc_skb_ip_align() in this patch, combining NET_SKB_PAD and NET_IP_ALIGN magic. Signed-off-by: Eric Dumazet Cc: Oliver Neukum Cc: Greg Kroah-Hartman Acked-by: Oliver Neukum Signed-off-by: David S. Miller (cherry picked from commit 7bdd402706cf26bfef9050dfee3f229b7f33ee4f) --------------------------------------------------------------------- usbnet: use netif_tx_wake_queue instead of netif_start_queue If host is going to autosuspend function with two interfaces and if IP packet has arrived in-between of two usbnet_suspend() callbacks, i.e usbnet_resume() is called in-between, tx data flow is stopped. When autosuspend timer expires and device is put to autosuspend again, tx queue is waked up and data can be sent again. This behavior might be repeated several times in a row. Tested on Intel/ARM. Reviewed-by: Sjur Brændeland Tested-by: Dmitry Tarnyagin Signed-off-by: Alexey Orishko Acked-by: Oliver Neukum Signed-off-by: David S. Miller (cherry picked from commit 1aa9bc5b2f4cf8c48944fb9a607bf1dd674e2c10) --------------------------------------------------------------------- usbnet: increase URB reference count before usb_unlink_urb Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid recursive locking in usbnet_stop()) fixes the recursive locking problem by releasing the skb queue lock, but it makes usb_unlink_urb racing with defer_bh, and the URB to being unlinked may be freed before or during calling usb_unlink_urb, so use-after-free problem may be triggerd inside usb_unlink_urb. The patch fixes the use-after-free problem by increasing URB reference count with skb queue lock held before calling usb_unlink_urb, so the URB won't be freed until return from usb_unlink_urb. Reported-by: Dave Jones Signed-off-by: Ming Lei Acked-by: Greg Kroah-Hartman Signed-off-by: David S. Miller (cherry picked from commit 0956a8c20b23d429e79ff86d4325583fc06f9eb4) --------------------------------------------------------------------- usbnet: don't clear urb->dev in tx_complete URB unlinking is always racing with its completion and tx_complete may be called before or during running usb_unlink_urb, so tx_complete must not clear urb->dev since it will be used in unlink path, otherwise invalid memory accesses or usb device leak may be caused inside usb_unlink_urb. Signed-off-by: Ming Lei Acked-by: Greg Kroah-Hartman Signed-off-by: David S. Miller (cherry picked from commit 5d5440a835710d09f0ef18da5000541ec98b537a) --------------------------------------------------------------------- usbnet: consider device busy at each recieved packet usbnet should centrally handle busy reporting in the rx path so subdrivers need not worry. This hurts use cases which do rx only or predominantly. Signed-off-by: Oliver Neukum Signed-off-by: David S. Miller (cherry picked from commit 8a78335442cea429afb2b964318b6e257448ea00) --------------------------------------------------------------------- usbnet: fix leak of transfer buffer of dev->interrupt The transfer buffer of dev->interrupt is allocated in .probe path, but not freed in .disconnet path, so mark the interrupt URB as URB_FREE_BUFFER to free the buffer when the URB is destroyed. Signed-off-by: Ming Lei Acked-by: Oliver Neukum Signed-off-by: David S. Miller (cherry picked from commit 267a83e8e00dc5a878b24c39883643c20a8b1482) Bug 1036768 Change-Id: I5d6620c8ff4e6cef52c3f467fb2196658c4d47b1 Signed-off-by: Steve Lin Reviewed-on: http://git-master/r/125338 GVS: Gerrit_Virtual_Submit --- drivers/net/usb/usbnet.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 82965e20e076..a379613d0b58 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -210,6 +210,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf) } else { usb_fill_int_urb(dev->interrupt, dev->udev, pipe, buf, maxp, intr_complete, dev, period); + dev->interrupt->transfer_flags |= URB_FREE_BUFFER; dev_dbg(&intf->dev, "status ep%din, %d bytes period %d\n", usb_pipeendpoint(pipe), maxp, period); @@ -324,13 +325,13 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) unsigned long lockflags; size_t size = dev->rx_urb_size; - if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) { + skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); + if (!skb) { netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); usbnet_defer_kevent (dev, EVENT_RX_MEMORY); usb_free_urb (urb); return -ENOMEM; } - skb_reserve (skb, NET_IP_ALIGN); entry = (struct skb_data *) skb->cb; entry->urb = urb; @@ -489,6 +490,7 @@ block: if (netif_running (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags)) { rx_submit (dev, urb, GFP_ATOMIC); + usb_mark_last_busy(dev->udev); return; } usb_free_urb (urb); @@ -585,6 +587,15 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) entry = (struct skb_data *) skb->cb; urb = entry->urb; + /* + * Get reference count of the URB to avoid it to be + * freed during usb_unlink_urb, which may trigger + * use-after-free problem inside usb_unlink_urb since + * usb_unlink_urb is always racing with .complete + * handler(include defer_bh). + */ + usb_get_urb(urb); + spin_unlock_irqrestore(&q->lock, flags); // during some PM-driven resume scenarios, // these (async) unlinks complete immediately retval = usb_unlink_urb (urb); @@ -592,6 +603,8 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) netdev_dbg(dev->net, "unlink urb err, %d\n", retval); else count++; + usb_put_urb(urb); + spin_lock_irqsave(&q->lock, flags); } spin_unlock_irqrestore (&q->lock, flags); return count; @@ -1022,7 +1035,6 @@ static void tx_complete (struct urb *urb) } usb_autopm_put_interface_async(dev->intf); - urb->dev = NULL; entry->state = tx_done; defer_bh(dev, skb, &dev->txq); } @@ -1531,7 +1543,7 @@ int usbnet_resume (struct usb_interface *intf) if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { if (!(dev->txq.qlen >= TX_QLEN(dev))) - netif_start_queue(dev->net); + netif_tx_wake_all_queues(dev->net); tasklet_schedule (&dev->bh); } } -- cgit v1.2.3 From 6f61e5baa83ef8c75746eabe90be0588f6d86b37 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 26 Apr 2012 11:33:46 +0800 Subject: usbnet: fix skb traversing races during unlink(v2) Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid recursive locking in usbnet_stop()) fixes the recursive locking problem by releasing the skb queue lock before unlink, but may cause skb traversing races: - after URB is unlinked and the queue lock is released, the refered skb and skb->next may be moved to done queue, even be released - in skb_queue_walk_safe, the next skb is still obtained by next pointer of the last skb - so maybe trigger oops or other problems This patch extends the usage of entry->state to describe 'start_unlink' state, so always holding the queue(rx/tx) lock to change the state if the referd skb is in rx or tx queue because we need to know if the refered urb has been started unlinking in unlink_urbs. The other part of this patch is based on Huajun's patch: always traverse from head of the tx/rx queue to get skb which is to be unlinked but not been started unlinking. Signed-off-by: Huajun Li Signed-off-by: Ming Lei Cc: Oliver Neukum Cc: stable@kernel.org Signed-off-by: David S. Miller (cherry picked from commit 5b6e9bcdeb65634b4ad604eb4536404bbfc62cfa) Bug 1040642 Change-Id: I1a8c248016529bebf71d540738ad4726cf3f59b7 Signed-off-by: Steve Lin Reviewed-on: http://git-master/r/128693 GVS: Gerrit_Virtual_Submit --- drivers/net/usb/usbnet.c | 54 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 16 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index a379613d0b58..930a4c1959d2 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -278,17 +278,32 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu) } EXPORT_SYMBOL_GPL(usbnet_change_mtu); +/* The caller must hold list->lock */ +static void __usbnet_queue_skb(struct sk_buff_head *list, + struct sk_buff *newsk, enum skb_state state) +{ + struct skb_data *entry = (struct skb_data *) newsk->cb; + + __skb_queue_tail(list, newsk); + entry->state = state; +} + /*-------------------------------------------------------------------------*/ /* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from * completion callbacks. 2.5 should have fixed those bugs... */ -static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list) +static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb, + struct sk_buff_head *list, enum skb_state state) { unsigned long flags; + enum skb_state old_state; + struct skb_data *entry = (struct skb_data *) skb->cb; spin_lock_irqsave(&list->lock, flags); + old_state = entry->state; + entry->state = state; __skb_unlink(skb, list); spin_unlock(&list->lock); spin_lock(&dev->done.lock); @@ -296,6 +311,7 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea if (dev->done.qlen == 1) tasklet_schedule(&dev->bh); spin_unlock_irqrestore(&dev->done.lock, flags); + return old_state; } /* some work can't be done in tasklets, so we use keventd @@ -336,7 +352,6 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; - entry->state = rx_start; entry->length = 0; usb_fill_bulk_urb (urb, dev->udev, dev->in, @@ -368,7 +383,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) tasklet_schedule (&dev->bh); break; case 0: - __skb_queue_tail (&dev->rxq, skb); + __usbnet_queue_skb(&dev->rxq, skb, rx_start); } } else { netif_dbg(dev, ifdown, dev->net, "rx: stopped\n"); @@ -419,16 +434,17 @@ static void rx_complete (struct urb *urb) struct skb_data *entry = (struct skb_data *) skb->cb; struct usbnet *dev = entry->dev; int urb_status = urb->status; + enum skb_state state; skb_put (skb, urb->actual_length); - entry->state = rx_done; + state = rx_done; entry->urb = NULL; switch (urb_status) { /* success */ case 0: if (skb->len < dev->net->hard_header_len) { - entry->state = rx_cleanup; + state = rx_cleanup; dev->net->stats.rx_errors++; dev->net->stats.rx_length_errors++; netif_dbg(dev, rx_err, dev->net, @@ -467,7 +483,7 @@ static void rx_complete (struct urb *urb) "rx throttle %d\n", urb_status); } block: - entry->state = rx_cleanup; + state = rx_cleanup; entry->urb = urb; urb = NULL; break; @@ -478,17 +494,18 @@ block: // FALLTHROUGH default: - entry->state = rx_cleanup; + state = rx_cleanup; dev->net->stats.rx_errors++; netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status); break; } - defer_bh(dev, skb, &dev->rxq); + state = defer_bh(dev, skb, &dev->rxq, state); if (urb) { if (netif_running (dev->net) && - !test_bit (EVENT_RX_HALT, &dev->flags)) { + !test_bit (EVENT_RX_HALT, &dev->flags) && + state != unlink_start) { rx_submit (dev, urb, GFP_ATOMIC); usb_mark_last_busy(dev->udev); return; @@ -575,16 +592,23 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq); static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) { unsigned long flags; - struct sk_buff *skb, *skbnext; + struct sk_buff *skb; int count = 0; spin_lock_irqsave (&q->lock, flags); - skb_queue_walk_safe(q, skb, skbnext) { + while (!skb_queue_empty(q)) { struct skb_data *entry; struct urb *urb; int retval; - entry = (struct skb_data *) skb->cb; + skb_queue_walk(q, skb) { + entry = (struct skb_data *) skb->cb; + if (entry->state != unlink_start) + goto found; + } + break; +found: + entry->state = unlink_start; urb = entry->urb; /* @@ -1035,8 +1059,7 @@ static void tx_complete (struct urb *urb) } usb_autopm_put_interface_async(dev->intf); - entry->state = tx_done; - defer_bh(dev, skb, &dev->txq); + (void) defer_bh(dev, skb, &dev->txq, tx_done); } /*-------------------------------------------------------------------------*/ @@ -1089,7 +1112,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; - entry->state = tx_start; entry->length = length; usb_fill_bulk_urb (urb, dev->udev, dev->out, @@ -1148,7 +1170,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, break; case 0: net->trans_start = jiffies; - __skb_queue_tail (&dev->txq, skb); + __usbnet_queue_skb(&dev->txq, skb, tx_start); if (dev->txq.qlen >= TX_QLEN (dev)) netif_stop_queue (net); } -- cgit v1.2.3 From 7f5266b82d7a13ba905cff1d1bf039aa2b50d07b Mon Sep 17 00:00:00 2001 From: Mursalin Akon Date: Thu, 16 Aug 2012 10:01:28 -0700 Subject: net: wireless: bcmdhd: cleanup Kconfig/Make cleanup bcmdhd driver Kconfig and Makefile Bug 1029792 Bug 1029733 Change-Id: Ic947644d2159a52fab7aea1621e12e51d8ee6332 Signed-off-by: Mursalin Akon Reviewed-on: http://git-master/r/141627 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Matthew Pedro GVS: Gerrit_Virtual_Submit --- drivers/net/wireless/bcmdhd/Kconfig | 9 +++------ drivers/net/wireless/bcmdhd/Makefile | 10 +++++++--- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig index ef9ce5a00538..b02a173f98a5 100644 --- a/drivers/net/wireless/bcmdhd/Kconfig +++ b/drivers/net/wireless/bcmdhd/Kconfig @@ -26,6 +26,7 @@ config BCMDHD_NVRAM_DIR config BCMDHD_WEXT bool "Enable WEXT support" + depends on BCMDHD select WIRELESS_EXT select WEXT_PRIV help @@ -54,15 +55,11 @@ config DHD_ENABLE_P2P config BCMDHD_CFG80211 bool "Enable CFG80211 support" - depends on CFG80211 + depends on BCMDHD && CFG80211 + default n help Enables CFG80211 support -config BCMDHD_NOAPI - bool "No wireless API" - help - No wireless API is needed - config BCMDHD_WIFI_CONTROL_FUNC bool "Use bcmdhd_wlan device" depends on BCMDHD diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 50fac43948e2..4ec987da11f8 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -44,7 +44,7 @@ endif DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ dhd_linux_sched.o bcmwifi.o dhd_sdio.o bcmevent.o dhd_bta.o hndpmu.o \ bcmsdh.o dhd_cdc.o bcmsdh_linux.o dhd_common.o linux_osl.o \ - bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o dhd_cfg80211.o + bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o obj-$(CONFIG_BCMDHD) += bcmdhd.o bcmdhd-objs += $(DHDOFILES) @@ -54,17 +54,21 @@ bcmdhd-objs += wl_iw.o DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT endif -ifneq ($(CONFIG_CFG80211),) -bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o +ifneq ($(CONFIG_BCMDHD_CFG80211),) +bcmdhd-objs += wl_cfg80211.o dhd_cfg80211.o wl_cfgp2p.o wl_linux_mon.o DHDCFLAGS += -DWL_CFG80211 endif + ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) DHDCFLAGS += -DWL_SCHED_SCAN endif + ifneq ($(CONFIG_DHD_ENABLE_P2P),) DHDCFLAGS += -DWL_ENABLE_P2P_IF endif + EXTRA_CFLAGS = $(DHDCFLAGS) + ifeq ($(CONFIG_BCMDHD),m) EXTRA_LDFLAGS += --strip-debug endif -- cgit v1.2.3 From 3ae23c3dab9151cca239513888d55146515931c1 Mon Sep 17 00:00:00 2001 From: Mursalin Akon Date: Fri, 31 Aug 2012 10:46:22 -0700 Subject: bcmdhd: do not turn off mmc at probe Do not turn off mmc at probe if watchdog thread is in use. Bug 1029792 Bug 1029733 Change-Id: Ia2e303daa27688c96e7fa5941e01cf821166c498 Signed-off-by: Mursalin Akon Reviewed-on: http://git-master/r/141628 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Matthew Pedro GVS: Gerrit_Virtual_Submit --- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index 1a315b4dcf90..6ca85311b4c4 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -132,8 +132,10 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, #endif sd_trace(("F2 found, calling bcmsdh_probe_bcmdhd...\n")); ret = bcmsdh_probe_bcmdhd(&func->dev); +#ifndef DHDTHREAD if (mmc_power_save_host(func->card->host)) sd_err(("%s: card power save fail", __FUNCTION__)); +#endif } return ret; -- cgit v1.2.3 From a4a73f7bc48e771e493e6e363e5c323ce8513ae9 Mon Sep 17 00:00:00 2001 From: Mursalin Akon Date: Fri, 7 Sep 2012 10:57:02 -0700 Subject: bcmdhd: use the wiphy from private data structure (i.e., wl_priv) problem: The macro wiphy_from_scan retrieves wiphy from wl_priv->escan_info.wiphy which is in turn set by a 'scan' initiated from kernel space (like scheduled scan) or from user space (like iwlist command). Without a scan, a 'connect' crashes in function wl_inform_single_bss, with NULL pointer access, as the variable wiphy (in turn, wl->escan_info.wiphy) is not initialized properly. solution: wireless_device data structures maintained for both STA and P2P mode point to the same wiphy (see, wl_cfgp2p_register_ndev in wl_cfgp2p.c). So, irrespective of whatever wireless_device is in use, a scan will always set wl_priv->escan_info.wiphy to the same wiphy. There is no point in getting wiphy using wl_priv->escan_info.wiphy, rather use the wiphy referred by all wireless_device structures. Bug 1029792 Bug 1029733 Change-Id: I6d744950d3a053642ffa998b3c3f3ba60c0cb687 Signed-off-by: Mursalin Akon Reviewed-on: http://git-master/r/141629 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Matthew Pedro GVS: Gerrit_Virtual_Submit --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2 +- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 7f3e332dbfce..2c4e9a63dd9e 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -4693,7 +4693,7 @@ static s32 wl_inform_bss(struct wl_priv *wl) static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) { - struct wiphy *wiphy = wiphy_from_scan(wl); + struct wiphy *wiphy = wl_to_wiphy(wl); struct ieee80211_mgmt *mgmt; struct ieee80211_channel *channel; struct ieee80211_supported_band *band; diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 37c8e5850c44..21446dc10708 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -612,7 +612,6 @@ wl_get_profile_by_netdev(struct wl_priv *wl, struct net_device *ndev) #define iscan_to_wl(i) ((struct wl_priv *)(i->data)) #define wl_to_iscan(w) (w->iscan) #define wl_to_conn(w) (&w->conn_info) -#define wiphy_from_scan(w) (w->escan_info.wiphy) #define wl_get_drv_status_all(wl, stat) \ (wl_get_status_all(wl, WL_STATUS_ ## stat)) #define wl_get_drv_status(wl, stat, ndev) \ -- cgit v1.2.3