summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/brcm80211
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/brcm80211')
-rw-r--r--drivers/net/wireless/brcm80211/Kconfig8
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd.h25
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c8
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c24
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c10
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c17
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c237
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c598
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h25
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/aiutils.c3
-rw-r--r--drivers/net/wireless/brcm80211/include/brcm_hw_ids.h1
13 files changed, 800 insertions, 160 deletions
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig
index b480088b3dbe..c9d811eb6556 100644
--- a/drivers/net/wireless/brcm80211/Kconfig
+++ b/drivers/net/wireless/brcm80211/Kconfig
@@ -55,6 +55,14 @@ config BRCMFMAC_USB
IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to
use the driver for an USB wireless card.
+config BRCMISCAN
+ bool "Broadcom I-Scan (OBSOLETE)"
+ depends on BRCMFMAC
+ ---help---
+ This option enables the I-Scan method. By default fullmac uses the
+ new E-Scan method which uses less memory in firmware and gives no
+ limitation on the number of scan results.
+
config BRCMDBG
bool "Broadcom driver debug functions"
depends on BRCMSMAC || BRCMFMAC
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 49765d34b4e0..e0b313c7f5ce 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -42,6 +42,7 @@
#define DMA_ALIGN_MASK 0x03
+#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
@@ -51,6 +52,7 @@
/* devices we support, null terminated */
static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index a11fe54f5950..4766d9f35696 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -130,6 +130,10 @@
#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
#define BRCMF_EVENT_MSG_GROUP 0x04
+#define BRCMF_ESCAN_REQ_VERSION 1
+
+#define WLC_BSS_RSSI_ON_CHANNEL 0x0002
+
struct brcmf_event_msg {
__be16 version;
__be16 flags;
@@ -140,6 +144,8 @@ struct brcmf_event_msg {
__be32 datalen;
u8 addr[ETH_ALEN];
char ifname[IFNAMSIZ];
+ u8 ifidx;
+ u8 bsscfgidx;
} __packed;
struct brcm_ethhdr {
@@ -454,6 +460,24 @@ struct brcmf_scan_results_le {
__le32 count;
};
+struct brcmf_escan_params_le {
+ __le32 version;
+ __le16 action;
+ __le16 sync_id;
+ struct brcmf_scan_params_le params_le;
+};
+
+struct brcmf_escan_result_le {
+ __le32 buflen;
+ __le32 version;
+ __le16 sync_id;
+ __le16 bss_count;
+ struct brcmf_bss_info_le bss_info_le;
+};
+
+#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \
+ sizeof(struct brcmf_bss_info_le))
+
/* used for association with a specific BSSID and chanspec list */
struct brcmf_assoc_params_le {
/* 00:00:00:00:00:00: broadcast scan */
@@ -638,6 +662,7 @@ extern uint brcmf_c_mkiovar(char *name, char *data, uint datalen,
extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
extern s32 brcmf_exec_dcmd(struct net_device *dev, u32 cmd, void *arg, u32 len);
+extern int brcmf_netlink_dcmd(struct net_device *ndev, struct brcmf_dcmd *dcmd);
/* Return pointer to interface name */
extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
index 537f499cc5d2..9b8ee19ea55d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
@@ -103,7 +103,7 @@ extern int brcmf_attach(uint bus_hdrlen, struct device *dev);
extern void brcmf_detach(struct device *dev);
/* Indication from bus module to change flow-control state */
-extern void brcmf_txflowcontrol(struct device *dev, int ifidx, bool on);
+extern void brcmf_txflowblock(struct device *dev, bool state);
/* Notify tx completion */
extern void brcmf_txcomplete(struct device *dev, struct sk_buff *txp,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
index 2621dd3d7dcd..f6b862d77986 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -205,7 +205,8 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data)
BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, {
BRCMF_E_IF, "IF"}, {
BRCMF_E_RSSI, "RSSI"}, {
- BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
+ BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}, {
+ BRCMF_E_ESCAN_RESULT, "ESCAN_RESULT"}
};
uint event_type, flags, auth_type, datalen;
static u32 seqnum_prev;
@@ -350,6 +351,11 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data)
brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name);
break;
+ case BRCMF_E_ESCAN_RESULT:
+ brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name);
+ datalen = 0;
+ break;
+
case BRCMF_E_PFN_NET_FOUND:
case BRCMF_E_PFN_NET_LOST:
case BRCMF_E_PFN_SCAN_COMPLETE:
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 9ab24528f9b9..b08f3474d8e7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -350,19 +350,23 @@ done:
return 0;
}
-void brcmf_txflowcontrol(struct device *dev, int ifidx, bool state)
+void brcmf_txflowblock(struct device *dev, bool state)
{
struct net_device *ndev;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
+ int i;
brcmf_dbg(TRACE, "Enter\n");
- ndev = drvr->iflist[ifidx]->ndev;
- if (state == ON)
- netif_stop_queue(ndev);
- else
- netif_wake_queue(ndev);
+ for (i = 0; i < BRCMF_MAX_IFS; i++)
+ if (drvr->iflist[i]) {
+ ndev = drvr->iflist[i]->ndev;
+ if (state)
+ netif_stop_queue(ndev);
+ else
+ netif_wake_queue(ndev);
+ }
}
static int brcmf_host_event(struct brcmf_pub *drvr, int *ifidx,
@@ -775,6 +779,14 @@ done:
return err;
}
+int brcmf_netlink_dcmd(struct net_device *ndev, struct brcmf_dcmd *dcmd)
+{
+ brcmf_dbg(TRACE, "enter: cmd %x buf %p len %d\n",
+ dcmd->cmd, dcmd->buf, dcmd->len);
+
+ return brcmf_exec_dcmd(ndev, dcmd->cmd, dcmd->buf, dcmd->len);
+}
+
static int brcmf_netdev_stop(struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 472f2ef5c652..4580ff34c2d0 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -2235,8 +2235,8 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
if (bus->sdiodev->bus_if->drvr_up &&
(bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) &&
bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
- bus->txoff = OFF;
- brcmf_txflowcontrol(bus->sdiodev->dev, 0, OFF);
+ bus->txoff = false;
+ brcmf_txflowblock(bus->sdiodev->dev, false);
}
return cnt;
@@ -2672,8 +2672,8 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
spin_unlock_bh(&bus->txqlock);
if (pktq_len(&bus->txq) >= TXHI) {
- bus->txoff = ON;
- brcmf_txflowcontrol(bus->sdiodev->dev, 0, ON);
+ bus->txoff = true;
+ brcmf_txflowblock(bus->sdiodev->dev, true);
}
#ifdef DEBUG
@@ -3881,6 +3881,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
static bool brcmf_sdbrcm_chipmatch(u16 chipid)
{
+ if (chipid == BCM43241_CHIP_ID)
+ return true;
if (chipid == BCM4329_CHIP_ID)
return true;
if (chipid == BCM4330_CHIP_ID)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
index 58155e23d220..9434440bbc65 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
@@ -377,6 +377,23 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
/* Address of cores for new chips should be added here */
switch (ci->chip) {
+ case BCM43241_CHIP_ID:
+ ci->c_inf[0].wrapbase = 0x18100000;
+ ci->c_inf[0].cib = 0x2a084411;
+ ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
+ ci->c_inf[1].base = 0x18002000;
+ ci->c_inf[1].wrapbase = 0x18102000;
+ ci->c_inf[1].cib = 0x0e004211;
+ ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
+ ci->c_inf[2].base = 0x18004000;
+ ci->c_inf[2].wrapbase = 0x18104000;
+ ci->c_inf[2].cib = 0x14080401;
+ ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
+ ci->c_inf[3].base = 0x18003000;
+ ci->c_inf[3].wrapbase = 0x18103000;
+ ci->c_inf[3].cib = 0x07004211;
+ ci->ramsize = 0x90000;
+ break;
case BCM4329_CHIP_ID:
ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
ci->c_inf[1].base = BCM4329_CORE_BUS_BASE;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index a299d42da8e7..c6d5aeb27a02 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -66,7 +66,9 @@
#define BRCMF_USB_CBCTL_READ 1
#define BRCMF_USB_MAX_PKT_SIZE 1600
+#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
+#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
enum usbdev_suspend_state {
USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow
@@ -78,20 +80,6 @@ enum usbdev_suspend_state {
USBOS_SUSPEND_STATE_SUSPENDED /* Device suspended */
};
-struct brcmf_usb_probe_info {
- void *usbdev_info;
- struct usb_device *usb; /* USB device pointer from OS */
- uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2;
- int intr_size; /* Size of interrupt message */
- int interval; /* Interrupt polling interval */
- int vid;
- int pid;
- enum usb_device_speed device_speed;
- enum usbdev_suspend_state suspend_state;
- struct usb_interface *intf;
-};
-static struct brcmf_usb_probe_info usbdev_probe_info;
-
struct brcmf_usb_image {
void *data;
u32 len;
@@ -117,9 +105,8 @@ struct brcmf_usbdev_info {
int rx_low_watermark;
int tx_low_watermark;
int tx_high_watermark;
- bool txoff;
- bool rxoff;
- bool txoverride;
+ int tx_freecount;
+ bool tx_flowblock;
struct brcmf_usbreq *tx_reqs;
struct brcmf_usbreq *rx_reqs;
@@ -133,7 +120,6 @@ struct brcmf_usbdev_info {
struct usb_device *usbdev;
struct device *dev;
- enum usb_device_speed device_speed;
int ctl_in_pipe, ctl_out_pipe;
struct urb *ctl_urb; /* URB for control endpoint */
@@ -153,9 +139,6 @@ struct brcmf_usbdev_info {
int intr_size; /* Size of interrupt message */
int interval; /* Interrupt polling interval */
struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */
-
- struct brcmf_usb_probe_info probe_info;
-
};
static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
@@ -177,14 +160,6 @@ static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev)
return brcmf_usb_get_buspub(dev)->devinfo;
}
-#if 0
-static void
-brcmf_usb_txflowcontrol(struct brcmf_usbdev_info *devinfo, bool onoff)
-{
- dhd_txflowcontrol(devinfo->bus_pub.netdev, 0, onoff);
-}
-#endif
-
static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo,
uint *condition, bool *pending)
{
@@ -366,13 +341,13 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
if (test_and_set_bit(0, &devinfo->ctl_op))
return -EIO;
+ devinfo->ctl_completed = false;
err = brcmf_usb_send_ctl(devinfo, buf, len);
if (err) {
brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len);
return err;
}
- devinfo->ctl_completed = false;
timeout = brcmf_usb_ioctl_resp_wait(devinfo, &devinfo->ctl_completed,
&pending);
clear_bit(0, &devinfo->ctl_op);
@@ -418,7 +393,7 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
}
static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo,
- struct list_head *q)
+ struct list_head *q, int *counter)
{
unsigned long flags;
struct brcmf_usbreq *req;
@@ -429,17 +404,22 @@ static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo,
}
req = list_entry(q->next, struct brcmf_usbreq, list);
list_del_init(q->next);
+ if (counter)
+ (*counter)--;
spin_unlock_irqrestore(&devinfo->qlock, flags);
return req;
}
static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo,
- struct list_head *q, struct brcmf_usbreq *req)
+ struct list_head *q, struct brcmf_usbreq *req,
+ int *counter)
{
unsigned long flags;
spin_lock_irqsave(&devinfo->qlock, flags);
list_add_tail(&req->list, q);
+ if (counter)
+ (*counter)++;
spin_unlock_irqrestore(&devinfo->qlock, flags);
}
@@ -519,10 +499,14 @@ static void brcmf_usb_tx_complete(struct urb *urb)
else
devinfo->bus_pub.bus->dstats.tx_errors++;
- dev_kfree_skb(req->skb);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
- brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
-
+ brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
+ if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
+ devinfo->tx_flowblock) {
+ brcmf_txflowblock(devinfo->dev, false);
+ devinfo->tx_flowblock = false;
+ }
}
static void brcmf_usb_rx_complete(struct urb *urb)
@@ -540,8 +524,8 @@ static void brcmf_usb_rx_complete(struct urb *urb)
devinfo->bus_pub.bus->dstats.rx_packets++;
} else {
devinfo->bus_pub.bus->dstats.rx_errors++;
- dev_kfree_skb(skb);
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
return;
}
@@ -551,12 +535,12 @@ static void brcmf_usb_rx_complete(struct urb *urb)
brcmf_dbg(ERROR, "rx protocol error\n");
brcmu_pkt_buf_free_skb(skb);
devinfo->bus_pub.bus->dstats.rx_errors++;
- } else {
+ } else
brcmf_rx_packet(devinfo->dev, ifidx, skb);
- brcmf_usb_rx_refill(devinfo, req);
- }
+ brcmf_usb_rx_refill(devinfo, req);
} else {
- dev_kfree_skb(skb);
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
}
return;
@@ -573,7 +557,7 @@ static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu);
if (!skb) {
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
return;
}
req->skb = skb;
@@ -581,16 +565,15 @@ static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
req);
- req->urb->transfer_flags |= URB_ZERO_PACKET;
req->devinfo = devinfo;
+ brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL);
ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (ret == 0) {
- brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
- } else {
- dev_kfree_skb(req->skb);
+ if (ret) {
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
- brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
}
return;
}
@@ -603,7 +586,7 @@ static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo)
brcmf_dbg(ERROR, "bus is not up\n");
return;
}
- while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq)) != NULL)
+ while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL)
brcmf_usb_rx_refill(devinfo, req);
}
@@ -681,27 +664,34 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
return -EIO;
}
- req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq);
+ req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq,
+ &devinfo->tx_freecount);
if (!req) {
+ brcmu_pkt_buf_free_skb(skb);
brcmf_dbg(ERROR, "no req to send\n");
return -ENOMEM;
}
- if (!req->urb) {
- brcmf_dbg(ERROR, "no urb for req %p\n", req);
- return -ENOBUFS;
- }
req->skb = skb;
req->devinfo = devinfo;
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
skb->data, skb->len, brcmf_usb_tx_complete, req);
req->urb->transfer_flags |= URB_ZERO_PACKET;
+ brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL);
ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (!ret) {
- brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
- } else {
+ if (ret) {
+ brcmf_dbg(ERROR, "brcmf_usb_tx usb_submit_urb FAILED\n");
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
- brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
+ brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req,
+ &devinfo->tx_freecount);
+ } else {
+ if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
+ !devinfo->tx_flowblock) {
+ brcmf_txflowblock(dev, true);
+ devinfo->tx_flowblock = true;
+ }
}
return ret;
@@ -1112,10 +1102,14 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
static bool brcmf_usb_chip_support(int chipid, int chiprev)
{
switch(chipid) {
+ case 43143:
+ return true;
case 43235:
case 43236:
case 43238:
return (chiprev == 3);
+ case 43242:
+ return true;
default:
break;
}
@@ -1154,11 +1148,8 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
}
-static void brcmf_usb_detach(const struct brcmf_usbdev *bus_pub)
+static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo)
{
- struct brcmf_usbdev_info *devinfo =
- (struct brcmf_usbdev_info *)bus_pub;
-
brcmf_dbg(TRACE, "devinfo %p\n", devinfo);
/* store the image globally */
@@ -1175,7 +1166,6 @@ static void brcmf_usb_detach(const struct brcmf_usbdev *bus_pub)
kfree(devinfo->tx_reqs);
kfree(devinfo->rx_reqs);
- kfree(devinfo);
}
#define TRX_MAGIC 0x30524448 /* "HDR0" */
@@ -1228,7 +1218,22 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo)
if (devinfo->image)
return 0;
- fwname = BRCMF_USB_43236_FW_NAME;
+ switch (devinfo->bus_pub.devid) {
+ case 43143:
+ fwname = BRCMF_USB_43143_FW_NAME;
+ break;
+ case 43235:
+ case 43236:
+ case 43238:
+ fwname = BRCMF_USB_43236_FW_NAME;
+ break;
+ case 43242:
+ fwname = BRCMF_USB_43242_FW_NAME;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
err = request_firmware(&fw, fwname, devinfo->dev);
if (!fw) {
@@ -1253,14 +1258,9 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo)
static
-struct brcmf_usbdev *brcmf_usb_attach(int nrxq, int ntxq, struct device *dev)
+struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
+ int nrxq, int ntxq)
{
- struct brcmf_usbdev_info *devinfo;
-
- devinfo = kzalloc(sizeof(struct brcmf_usbdev_info), GFP_ATOMIC);
- if (devinfo == NULL)
- return NULL;
-
devinfo->bus_pub.nrxq = nrxq;
devinfo->rx_low_watermark = nrxq / 2;
devinfo->bus_pub.devinfo = devinfo;
@@ -1269,18 +1269,6 @@ struct brcmf_usbdev *brcmf_usb_attach(int nrxq, int ntxq, struct device *dev)
/* flow control when too many tx urbs posted */
devinfo->tx_low_watermark = ntxq / 4;
devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3;
- devinfo->dev = dev;
- devinfo->usbdev = usbdev_probe_info.usb;
- devinfo->tx_pipe = usbdev_probe_info.tx_pipe;
- devinfo->rx_pipe = usbdev_probe_info.rx_pipe;
- devinfo->rx_pipe2 = usbdev_probe_info.rx_pipe2;
- devinfo->intr_pipe = usbdev_probe_info.intr_pipe;
-
- devinfo->interval = usbdev_probe_info.interval;
- devinfo->intr_size = usbdev_probe_info.intr_size;
-
- memcpy(&devinfo->probe_info, &usbdev_probe_info,
- sizeof(struct brcmf_usb_probe_info));
devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE;
/* Initialize other structure content */
@@ -1295,6 +1283,8 @@ struct brcmf_usbdev *brcmf_usb_attach(int nrxq, int ntxq, struct device *dev)
INIT_LIST_HEAD(&devinfo->tx_freeq);
INIT_LIST_HEAD(&devinfo->tx_postq);
+ devinfo->tx_flowblock = false;
+
devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq);
if (!devinfo->rx_reqs)
goto error;
@@ -1302,6 +1292,7 @@ struct brcmf_usbdev *brcmf_usb_attach(int nrxq, int ntxq, struct device *dev)
devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq);
if (!devinfo->tx_reqs)
goto error;
+ devinfo->tx_freecount = ntxq;
devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!devinfo->intr_urb) {
@@ -1336,19 +1327,19 @@ struct brcmf_usbdev *brcmf_usb_attach(int nrxq, int ntxq, struct device *dev)
error:
brcmf_dbg(ERROR, "failed!\n");
- brcmf_usb_detach(&devinfo->bus_pub);
+ brcmf_usb_detach(devinfo);
return NULL;
}
-static int brcmf_usb_probe_cb(struct device *dev, const char *desc,
- u32 bustype, u32 hdrlen)
+static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo,
+ const char *desc, u32 bustype, u32 hdrlen)
{
struct brcmf_bus *bus = NULL;
struct brcmf_usbdev *bus_pub = NULL;
int ret;
+ struct device *dev = devinfo->dev;
-
- bus_pub = brcmf_usb_attach(BRCMF_USB_NRXQ, BRCMF_USB_NTXQ, dev);
+ bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
if (!bus_pub) {
ret = -ENODEV;
goto fail;
@@ -1387,23 +1378,21 @@ static int brcmf_usb_probe_cb(struct device *dev, const char *desc,
return 0;
fail:
/* Release resources in reverse order */
- if (bus_pub)
- brcmf_usb_detach(bus_pub);
kfree(bus);
+ brcmf_usb_detach(devinfo);
return ret;
}
static void
-brcmf_usb_disconnect_cb(struct brcmf_usbdev *bus_pub)
+brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
{
- if (!bus_pub)
+ if (!devinfo)
return;
- brcmf_dbg(TRACE, "enter: bus_pub %p\n", bus_pub);
-
- brcmf_detach(bus_pub->devinfo->dev);
- kfree(bus_pub->bus);
- brcmf_usb_detach(bus_pub);
+ brcmf_dbg(TRACE, "enter: bus_pub %p\n", devinfo);
+ brcmf_detach(devinfo->dev);
+ kfree(devinfo->bus_pub.bus);
+ brcmf_usb_detach(devinfo);
}
static int
@@ -1415,18 +1404,18 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
struct usb_device *usb = interface_to_usbdev(intf);
int num_of_eps;
u8 endpoint_num;
+ struct brcmf_usbdev_info *devinfo;
brcmf_dbg(TRACE, "enter\n");
- usbdev_probe_info.usb = usb;
- usbdev_probe_info.intf = intf;
+ devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
+ if (devinfo == NULL)
+ return -ENOMEM;
- if (id != NULL) {
- usbdev_probe_info.vid = id->idVendor;
- usbdev_probe_info.pid = id->idProduct;
- }
+ devinfo->usbdev = usb;
+ devinfo->dev = &usb->dev;
- usb_set_intfdata(intf, &usbdev_probe_info);
+ usb_set_intfdata(intf, devinfo);
/* Check that the device supports only one configuration */
if (usb->descriptor.bNumConfigurations != 1) {
@@ -1475,11 +1464,11 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
}
endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- usbdev_probe_info.intr_pipe = usb_rcvintpipe(usb, endpoint_num);
+ devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num);
- usbdev_probe_info.rx_pipe = 0;
- usbdev_probe_info.rx_pipe2 = 0;
- usbdev_probe_info.tx_pipe = 0;
+ devinfo->rx_pipe = 0;
+ devinfo->rx_pipe2 = 0;
+ devinfo->tx_pipe = 0;
num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1;
/* Check data endpoints and get pipes */
@@ -1496,35 +1485,33 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
USB_ENDPOINT_NUMBER_MASK;
if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
== USB_DIR_IN) {
- if (!usbdev_probe_info.rx_pipe) {
- usbdev_probe_info.rx_pipe =
+ if (!devinfo->rx_pipe) {
+ devinfo->rx_pipe =
usb_rcvbulkpipe(usb, endpoint_num);
} else {
- usbdev_probe_info.rx_pipe2 =
+ devinfo->rx_pipe2 =
usb_rcvbulkpipe(usb, endpoint_num);
}
} else {
- usbdev_probe_info.tx_pipe =
- usb_sndbulkpipe(usb, endpoint_num);
+ devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num);
}
}
/* Allocate interrupt URB and data buffer */
/* RNDIS says 8-byte intr, our old drivers used 4-byte */
if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16))
- usbdev_probe_info.intr_size = 8;
+ devinfo->intr_size = 8;
else
- usbdev_probe_info.intr_size = 4;
+ devinfo->intr_size = 4;
- usbdev_probe_info.interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval;
+ devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval;
- usbdev_probe_info.device_speed = usb->speed;
if (usb->speed == USB_SPEED_HIGH)
brcmf_dbg(INFO, "Broadcom high speed USB wireless device detected\n");
else
brcmf_dbg(INFO, "Broadcom full speed USB wireless device detected\n");
- ret = brcmf_usb_probe_cb(&usb->dev, "", USB_BUS, 0);
+ ret = brcmf_usb_probe_cb(devinfo, "", USB_BUS, 0);
if (ret)
goto fail;
@@ -1533,6 +1520,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
fail:
brcmf_dbg(ERROR, "failed with errno %d\n", ret);
+ kfree(devinfo);
usb_set_intfdata(intf, NULL);
return ret;
@@ -1541,11 +1529,12 @@ fail:
static void
brcmf_usb_disconnect(struct usb_interface *intf)
{
- struct usb_device *usb = interface_to_usbdev(intf);
+ struct brcmf_usbdev_info *devinfo;
brcmf_dbg(TRACE, "enter\n");
- brcmf_usb_disconnect_cb(brcmf_usb_get_buspub(&usb->dev));
- usb_set_intfdata(intf, NULL);
+ devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
+ brcmf_usb_disconnect_cb(devinfo);
+ kfree(devinfo);
}
/*
@@ -1577,17 +1566,23 @@ static int brcmf_usb_resume(struct usb_interface *intf)
}
#define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c
+#define BRCMF_USB_DEVICE_ID_43143 0xbd1e
#define BRCMF_USB_DEVICE_ID_43236 0xbd17
+#define BRCMF_USB_DEVICE_ID_43242 0xbd1f
#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc
static struct usb_device_id brcmf_usb_devid_table[] = {
+ { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
+ { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
/* special entry for device with firmware loaded and running */
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
{ }
};
MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
+MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
+MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
/* TODO: suspend and resume entries */
static struct usb_driver brcmf_usbdrvr = {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index 28c5fbb4af26..65cf8f92cb3e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -28,6 +28,7 @@
#include <linux/ieee80211.h>
#include <linux/uaccess.h>
#include <net/cfg80211.h>
+#include <net/netlink.h>
#include <brcmu_utils.h>
#include <defs.h>
@@ -489,8 +490,8 @@ static void brcmf_set_mpc(struct net_device *ndev, int mpc)
}
}
-static void wl_iscan_prep(struct brcmf_scan_params_le *params_le,
- struct brcmf_ssid *ssid)
+static void brcmf_iscan_prep(struct brcmf_scan_params_le *params_le,
+ struct brcmf_ssid *ssid)
{
memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
params_le->bss_type = DOT11_BSSTYPE_ANY;
@@ -544,7 +545,7 @@ brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
return -ENOMEM;
BUG_ON(params_size >= BRCMF_DCMD_SMLEN);
- wl_iscan_prep(&params->params_le, ssid);
+ brcmf_iscan_prep(&params->params_le, ssid);
params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION);
params->action = cpu_to_le16(action);
@@ -597,9 +598,9 @@ static s32 brcmf_do_iscan(struct brcmf_cfg80211_priv *cfg_priv)
}
static s32
-__brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_scan_request *request,
- struct cfg80211_ssid *this_ssid)
+brcmf_cfg80211_iscan(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_scan_request *request,
+ struct cfg80211_ssid *this_ssid)
{
struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
struct cfg80211_ssid *ssids;
@@ -690,11 +691,342 @@ scan_out:
return err;
}
+static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
+ struct cfg80211_scan_request *request)
+{
+ u32 n_ssids;
+ u32 n_channels;
+ s32 i;
+ s32 offset;
+ __le16 chanspec;
+ u16 channel;
+ struct ieee80211_channel *req_channel;
+ char *ptr;
+ struct brcmf_ssid ssid;
+
+ memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
+ params_le->bss_type = DOT11_BSSTYPE_ANY;
+ params_le->scan_type = 0;
+ params_le->channel_num = 0;
+ params_le->nprobes = cpu_to_le32(-1);
+ params_le->active_time = cpu_to_le32(-1);
+ params_le->passive_time = cpu_to_le32(-1);
+ params_le->home_time = cpu_to_le32(-1);
+ memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
+
+ /* if request is null exit so it will be all channel broadcast scan */
+ if (!request)
+ return;
+
+ n_ssids = request->n_ssids;
+ n_channels = request->n_channels;
+ /* Copy channel array if applicable */
+ WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
+ if (n_channels > 0) {
+ for (i = 0; i < n_channels; i++) {
+ chanspec = 0;
+ req_channel = request->channels[i];
+ channel = ieee80211_frequency_to_channel(
+ req_channel->center_freq);
+ if (req_channel->band == IEEE80211_BAND_2GHZ)
+ chanspec |= WL_CHANSPEC_BAND_2G;
+ else
+ chanspec |= WL_CHANSPEC_BAND_5G;
+
+ if (req_channel->flags & IEEE80211_CHAN_NO_HT40) {
+ chanspec |= WL_CHANSPEC_BW_20;
+ chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+ } else {
+ chanspec |= WL_CHANSPEC_BW_40;
+ if (req_channel->flags &
+ IEEE80211_CHAN_NO_HT40PLUS)
+ chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
+ else
+ chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
+ }
+
+ params_le->channel_list[i] =
+ (channel & WL_CHANSPEC_CHAN_MASK) |
+ chanspec;
+ WL_SCAN("Chan : %d, Channel spec: %x\n",
+ channel, params_le->channel_list[i]);
+ params_le->channel_list[i] =
+ cpu_to_le16(params_le->channel_list[i]);
+ }
+ } else {
+ WL_SCAN("Scanning all channels\n");
+ }
+ /* Copy ssid array if applicable */
+ WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
+ if (n_ssids > 0) {
+ offset = offsetof(struct brcmf_scan_params_le, channel_list) +
+ n_channels * sizeof(u16);
+ offset = roundup(offset, sizeof(u32));
+ ptr = (char *)params_le + offset;
+ for (i = 0; i < n_ssids; i++) {
+ memset(&ssid, 0, sizeof(ssid));
+ ssid.SSID_len = cpu_to_le32(request->ssids[i].ssid_len);
+ memcpy(ssid.SSID, request->ssids[i].ssid,
+ request->ssids[i].ssid_len);
+ if (!ssid.SSID_len)
+ WL_SCAN("%d: Broadcast scan\n", i);
+ else
+ WL_SCAN("%d: scan for %s size =%d\n", i,
+ ssid.SSID, ssid.SSID_len);
+ memcpy(ptr, &ssid, sizeof(ssid));
+ ptr += sizeof(ssid);
+ }
+ } else {
+ WL_SCAN("Broadcast scan %p\n", request->ssids);
+ if ((request->ssids) && request->ssids->ssid_len) {
+ WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
+ request->ssids->ssid_len);
+ params_le->ssid_le.SSID_len =
+ cpu_to_le32(request->ssids->ssid_len);
+ memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
+ request->ssids->ssid_len);
+ }
+ }
+ /* Adding mask to channel numbers */
+ params_le->channel_num =
+ cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
+ (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
+}
+
+static s32
+brcmf_notify_escan_complete(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ bool aborted, bool fw_abort)
+{
+ struct brcmf_scan_params_le params_le;
+ struct cfg80211_scan_request *scan_request;
+ s32 err = 0;
+
+ WL_SCAN("Enter\n");
+
+ /* clear scan request, because the FW abort can cause a second call */
+ /* to this functon and might cause a double cfg80211_scan_done */
+ scan_request = cfg_priv->scan_request;
+ cfg_priv->scan_request = NULL;
+
+ if (timer_pending(&cfg_priv->escan_timeout))
+ del_timer_sync(&cfg_priv->escan_timeout);
+
+ if (fw_abort) {
+ /* Do a scan abort to stop the driver's scan engine */
+ WL_SCAN("ABORT scan in firmware\n");
+ memset(&params_le, 0, sizeof(params_le));
+ memcpy(params_le.bssid, ether_bcast, ETH_ALEN);
+ params_le.bss_type = DOT11_BSSTYPE_ANY;
+ params_le.scan_type = 0;
+ params_le.channel_num = cpu_to_le32(1);
+ params_le.nprobes = cpu_to_le32(1);
+ params_le.active_time = cpu_to_le32(-1);
+ params_le.passive_time = cpu_to_le32(-1);
+ params_le.home_time = cpu_to_le32(-1);
+ /* Scan is aborted by setting channel_list[0] to -1 */
+ params_le.channel_list[0] = cpu_to_le16(-1);
+ /* E-Scan (or anyother type) can be aborted by SCAN */
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &params_le,
+ sizeof(params_le));
+ if (err)
+ WL_ERR("Scan abort failed\n");
+ }
+ if (scan_request) {
+ WL_SCAN("ESCAN Completed scan: %s\n",
+ aborted ? "Aborted" : "Done");
+ cfg80211_scan_done(scan_request, aborted);
+ brcmf_set_mpc(ndev, 1);
+ }
+ if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+ WL_ERR("Scan complete while device not scanning\n");
+ return -EPERM;
+ }
+
+ return err;
+}
+
+static s32
+brcmf_run_escan(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev,
+ struct cfg80211_scan_request *request, u16 action)
+{
+ s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
+ offsetof(struct brcmf_escan_params_le, params_le);
+ struct brcmf_escan_params_le *params;
+ s32 err = 0;
+
+ WL_SCAN("E-SCAN START\n");
+
+ if (request != NULL) {
+ /* Allocate space for populating ssids in struct */
+ params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
+
+ /* Allocate space for populating ssids in struct */
+ params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
+ }
+
+ params = kzalloc(params_size, GFP_KERNEL);
+ if (!params) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
+ brcmf_escan_prep(&params->params_le, request);
+ params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
+ params->action = cpu_to_le16(action);
+ params->sync_id = cpu_to_le16(0x1234);
+
+ err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size,
+ cfg_priv->escan_ioctl_buf, BRCMF_DCMD_MEDLEN);
+ if (err) {
+ if (err == -EBUSY)
+ WL_INFO("system busy : escan canceled\n");
+ else
+ WL_ERR("error (%d)\n", err);
+ }
+
+ kfree(params);
+exit:
+ return err;
+}
+
+static s32
+brcmf_do_escan(struct brcmf_cfg80211_priv *cfg_priv, struct wiphy *wiphy,
+ struct net_device *ndev, struct cfg80211_scan_request *request)
+{
+ s32 err;
+ __le32 passive_scan;
+ struct brcmf_scan_results *results;
+
+ WL_SCAN("Enter\n");
+ cfg_priv->escan_info.ndev = ndev;
+ cfg_priv->escan_info.wiphy = wiphy;
+ cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
+ passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
+ &passive_scan, sizeof(passive_scan));
+ if (err) {
+ WL_ERR("error (%d)\n", err);
+ return err;
+ }
+ brcmf_set_mpc(ndev, 0);
+ results = (struct brcmf_scan_results *)cfg_priv->escan_info.escan_buf;
+ results->version = 0;
+ results->count = 0;
+ results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
+
+ err = brcmf_run_escan(cfg_priv, ndev, request, WL_ESCAN_ACTION_START);
+ if (err)
+ brcmf_set_mpc(ndev, 1);
+ return err;
+}
+
+static s32
+brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_scan_request *request,
+ struct cfg80211_ssid *this_ssid)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+ struct cfg80211_ssid *ssids;
+ struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int;
+ __le32 passive_scan;
+ bool escan_req;
+ bool spec_scan;
+ s32 err;
+ u32 SSID_len;
+
+ WL_SCAN("START ESCAN\n");
+
+ if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+ WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status);
+ return -EAGAIN;
+ }
+ if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status)) {
+ WL_ERR("Scanning being aborted : status (%lu)\n",
+ cfg_priv->status);
+ return -EAGAIN;
+ }
+ if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) {
+ WL_ERR("Connecting : status (%lu)\n",
+ cfg_priv->status);
+ return -EAGAIN;
+ }
+
+ /* Arm scan timeout timer */
+ mod_timer(&cfg_priv->escan_timeout, jiffies +
+ WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
+
+ escan_req = false;
+ if (request) {
+ /* scan bss */
+ ssids = request->ssids;
+ escan_req = true;
+ } else {
+ /* scan in ibss */
+ /* we don't do escan in ibss */
+ ssids = this_ssid;
+ }
+
+ cfg_priv->scan_request = request;
+ set_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+ if (escan_req) {
+ err = brcmf_do_escan(cfg_priv, wiphy, ndev, request);
+ if (!err)
+ return err;
+ else
+ goto scan_out;
+ } else {
+ WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
+ ssids->ssid, ssids->ssid_len);
+ memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
+ SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
+ sr->ssid_le.SSID_len = cpu_to_le32(0);
+ spec_scan = false;
+ if (SSID_len) {
+ memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
+ sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
+ spec_scan = true;
+ } else
+ WL_SCAN("Broadcast scan\n");
+
+ passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
+ &passive_scan, sizeof(passive_scan));
+ if (err) {
+ WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
+ goto scan_out;
+ }
+ brcmf_set_mpc(ndev, 0);
+ err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
+ sizeof(sr->ssid_le));
+ if (err) {
+ if (err == -EBUSY)
+ WL_INFO("BUSY: scan for \"%s\" canceled\n",
+ sr->ssid_le.SSID);
+ else
+ WL_ERR("WLC_SCAN error (%d)\n", err);
+
+ brcmf_set_mpc(ndev, 1);
+ goto scan_out;
+ }
+ }
+
+ return 0;
+
+scan_out:
+ clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+ if (timer_pending(&cfg_priv->escan_timeout))
+ del_timer_sync(&cfg_priv->escan_timeout);
+ cfg_priv->scan_request = NULL;
+ return err;
+}
+
static s32
brcmf_cfg80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
struct net_device *ndev = request->wdev->netdev;
+ struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
s32 err = 0;
WL_TRACE("Enter\n");
@@ -702,7 +1034,11 @@ brcmf_cfg80211_scan(struct wiphy *wiphy,
if (!check_sys_up(wiphy))
return -EIO;
- err = __brcmf_cfg80211_scan(wiphy, ndev, request, NULL);
+ if (cfg_priv->iscan_on)
+ err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL);
+ else if (cfg_priv->escan_on)
+ err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
+
if (err)
WL_ERR("scan error (%d)\n", err);
@@ -1876,16 +2212,17 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
}
if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) {
- scb_val.val = cpu_to_le32(0);
+ memset(&scb_val, 0, sizeof(scb_val));
err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
sizeof(struct brcmf_scb_val_le));
- if (err)
+ if (err) {
WL_ERR("Could not get rssi (%d)\n", err);
-
- rssi = le32_to_cpu(scb_val.val);
- sinfo->filled |= STATION_INFO_SIGNAL;
- sinfo->signal = rssi;
- WL_CONN("RSSI %d dBm\n", rssi);
+ } else {
+ rssi = le32_to_cpu(scb_val.val);
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = rssi;
+ WL_CONN("RSSI %d dBm\n", rssi);
+ }
}
done:
@@ -2470,6 +2807,175 @@ static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv)
return err;
}
+static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
+{
+ struct brcmf_cfg80211_priv *cfg_priv =
+ container_of(work, struct brcmf_cfg80211_priv,
+ escan_timeout_work);
+
+ brcmf_notify_escan_complete(cfg_priv,
+ cfg_priv->escan_info.ndev, true, true);
+}
+
+static void brcmf_escan_timeout(unsigned long data)
+{
+ struct brcmf_cfg80211_priv *cfg_priv =
+ (struct brcmf_cfg80211_priv *)data;
+
+ if (cfg_priv->scan_request) {
+ WL_ERR("timer expired\n");
+ if (cfg_priv->escan_on)
+ schedule_work(&cfg_priv->escan_timeout_work);
+ }
+}
+
+static s32
+brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
+ struct brcmf_bss_info_le *bss_info_le)
+{
+ if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
+ (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
+ CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
+ bss_info_le->SSID_len == bss->SSID_len &&
+ !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
+ if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
+ (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
+ /* preserve max RSSI if the measurements are
+ * both on-channel or both off-channel
+ */
+ if (bss_info_le->RSSI > bss->RSSI)
+ bss->RSSI = bss_info_le->RSSI;
+ } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
+ (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
+ /* preserve the on-channel rssi measurement
+ * if the new measurement is off channel
+ */
+ bss->RSSI = bss_info_le->RSSI;
+ bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static s32
+brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_priv *cfg_priv,
+ struct net_device *ndev,
+ const struct brcmf_event_msg *e, void *data)
+{
+ s32 status;
+ s32 err = 0;
+ struct brcmf_escan_result_le *escan_result_le;
+ struct brcmf_bss_info_le *bss_info_le;
+ struct brcmf_bss_info_le *bss = NULL;
+ u32 bi_length;
+ struct brcmf_scan_results *list;
+ u32 i;
+
+ status = be32_to_cpu(e->status);
+
+ if (!ndev || !cfg_priv->escan_on ||
+ !test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+ WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n",
+ ndev, cfg_priv->escan_on,
+ !test_bit(WL_STATUS_SCANNING, &cfg_priv->status));
+ return -EPERM;
+ }
+
+ if (status == BRCMF_E_STATUS_PARTIAL) {
+ WL_SCAN("ESCAN Partial result\n");
+ escan_result_le = (struct brcmf_escan_result_le *) data;
+ if (!escan_result_le) {
+ WL_ERR("Invalid escan result (NULL pointer)\n");
+ goto exit;
+ }
+ if (!cfg_priv->scan_request) {
+ WL_SCAN("result without cfg80211 request\n");
+ goto exit;
+ }
+
+ if (le16_to_cpu(escan_result_le->bss_count) != 1) {
+ WL_ERR("Invalid bss_count %d: ignoring\n",
+ escan_result_le->bss_count);
+ goto exit;
+ }
+ bss_info_le = &escan_result_le->bss_info_le;
+
+ bi_length = le32_to_cpu(bss_info_le->length);
+ if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
+ WL_ESCAN_RESULTS_FIXED_SIZE)) {
+ WL_ERR("Invalid bss_info length %d: ignoring\n",
+ bi_length);
+ goto exit;
+ }
+
+ if (!(cfg_to_wiphy(cfg_priv)->interface_modes &
+ BIT(NL80211_IFTYPE_ADHOC))) {
+ if (le16_to_cpu(bss_info_le->capability) &
+ WLAN_CAPABILITY_IBSS) {
+ WL_ERR("Ignoring IBSS result\n");
+ goto exit;
+ }
+ }
+
+ list = (struct brcmf_scan_results *)
+ cfg_priv->escan_info.escan_buf;
+ if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
+ WL_ERR("Buffer is too small: ignoring\n");
+ goto exit;
+ }
+
+ for (i = 0; i < list->count; i++) {
+ bss = bss ? (struct brcmf_bss_info_le *)
+ ((unsigned char *)bss +
+ le32_to_cpu(bss->length)) : list->bss_info_le;
+ if (brcmf_compare_update_same_bss(bss, bss_info_le))
+ goto exit;
+ }
+ memcpy(&(cfg_priv->escan_info.escan_buf[list->buflen]),
+ bss_info_le, bi_length);
+ list->version = le32_to_cpu(bss_info_le->version);
+ list->buflen += bi_length;
+ list->count++;
+ } else {
+ cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
+ if (cfg_priv->scan_request) {
+ cfg_priv->bss_list = (struct brcmf_scan_results *)
+ cfg_priv->escan_info.escan_buf;
+ brcmf_inform_bss(cfg_priv);
+ if (status == BRCMF_E_STATUS_SUCCESS) {
+ WL_SCAN("ESCAN Completed\n");
+ brcmf_notify_escan_complete(cfg_priv, ndev,
+ false, false);
+ } else {
+ WL_ERR("ESCAN Aborted, Event 0x%x\n", status);
+ brcmf_notify_escan_complete(cfg_priv, ndev,
+ true, false);
+ }
+ brcmf_set_mpc(ndev, 1);
+ } else
+ WL_ERR("Unexpected scan result 0x%x\n", status);
+ }
+exit:
+ return err;
+}
+
+static void brcmf_init_escan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+
+ if (cfg_priv->escan_on) {
+ cfg_priv->el.handler[BRCMF_E_ESCAN_RESULT] =
+ brcmf_cfg80211_escan_handler;
+ cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
+ /* Init scan_timeout timer */
+ init_timer(&cfg_priv->escan_timeout);
+ cfg_priv->escan_timeout.data = (unsigned long) cfg_priv;
+ cfg_priv->escan_timeout.function = brcmf_escan_timeout;
+ INIT_WORK(&cfg_priv->escan_timeout_work,
+ brcmf_cfg80211_escan_timeout_worker);
+ }
+}
+
static __always_inline void brcmf_delay(u32 ms)
{
if (ms < 1000 / HZ) {
@@ -2545,10 +3051,8 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
/* Turn off watchdog timer */
- if (test_bit(WL_STATUS_READY, &cfg_priv->status)) {
- WL_INFO("Enable MPC\n");
+ if (test_bit(WL_STATUS_READY, &cfg_priv->status))
brcmf_set_mpc(ndev, 1);
- }
WL_TRACE("Exit\n");
@@ -2723,6 +3227,25 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
}
+#ifdef CONFIG_NL80211_TESTMODE
+static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
+{
+ struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_priv->wdev->netdev;
+ struct brcmf_dcmd *dcmd = data;
+ struct sk_buff *reply;
+ int ret;
+
+ ret = brcmf_netlink_dcmd(ndev, dcmd);
+ if (ret == 0) {
+ reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
+ nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
+ ret = cfg80211_testmode_reply(reply);
+ }
+ return ret;
+}
+#endif
+
static struct cfg80211_ops wl_cfg80211_ops = {
.change_virtual_intf = brcmf_cfg80211_change_iface,
.scan = brcmf_cfg80211_scan,
@@ -2745,7 +3268,10 @@ static struct cfg80211_ops wl_cfg80211_ops = {
.resume = brcmf_cfg80211_resume,
.set_pmksa = brcmf_cfg80211_set_pmksa,
.del_pmksa = brcmf_cfg80211_del_pmksa,
- .flush_pmksa = brcmf_cfg80211_flush_pmksa
+ .flush_pmksa = brcmf_cfg80211_flush_pmksa,
+#ifdef CONFIG_NL80211_TESTMODE
+ .testmode_cmd = brcmf_cfg80211_testmode
+#endif
};
static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
@@ -3170,10 +3696,8 @@ brcmf_notify_scan_status(struct brcmf_cfg80211_priv *cfg_priv,
cfg_priv->scan_results->count = le32_to_cpu(bss_list_le->count);
err = brcmf_inform_bss(cfg_priv);
- if (err) {
+ if (err)
scan_abort = true;
- goto scan_done_out;
- }
scan_done_out:
if (cfg_priv->scan_request) {
@@ -3220,6 +3744,8 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
cfg_priv->profile = NULL;
kfree(cfg_priv->scan_req_int);
cfg_priv->scan_req_int = NULL;
+ kfree(cfg_priv->escan_ioctl_buf);
+ cfg_priv->escan_ioctl_buf = NULL;
kfree(cfg_priv->dcmd_buf);
cfg_priv->dcmd_buf = NULL;
kfree(cfg_priv->extra_buf);
@@ -3248,6 +3774,9 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
GFP_KERNEL);
if (!cfg_priv->scan_req_int)
goto init_priv_mem_out;
+ cfg_priv->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+ if (!cfg_priv->escan_ioctl_buf)
+ goto init_priv_mem_out;
cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
if (!cfg_priv->dcmd_buf)
goto init_priv_mem_out;
@@ -3297,18 +3826,28 @@ static struct brcmf_cfg80211_event_q *brcmf_deq_event(
static s32
brcmf_enq_event(struct brcmf_cfg80211_priv *cfg_priv, u32 event,
- const struct brcmf_event_msg *msg)
+ const struct brcmf_event_msg *msg, void *data)
{
struct brcmf_cfg80211_event_q *e;
s32 err = 0;
ulong flags;
+ u32 data_len;
+ u32 total_len;
- e = kzalloc(sizeof(struct brcmf_cfg80211_event_q), GFP_ATOMIC);
+ total_len = sizeof(struct brcmf_cfg80211_event_q);
+ if (data)
+ data_len = be32_to_cpu(msg->datalen);
+ else
+ data_len = 0;
+ total_len += data_len;
+ e = kzalloc(total_len, GFP_ATOMIC);
if (!e)
return -ENOMEM;
e->etype = event;
memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
+ if (data)
+ memcpy(&e->edata, data, data_len);
spin_lock_irqsave(&cfg_priv->evt_q_lock, flags);
list_add_tail(&e->evt_q_list, &cfg_priv->evt_q_list);
@@ -3374,8 +3913,17 @@ static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv)
cfg_priv->scan_request = NULL;
cfg_priv->pwr_save = true;
+#ifdef CONFIG_BRCMISCAN
cfg_priv->iscan_on = true; /* iscan on & off switch.
we enable iscan per default */
+ cfg_priv->escan_on = false; /* escan on & off switch.
+ we disable escan per default */
+#else
+ cfg_priv->iscan_on = false; /* iscan on & off switch.
+ we disable iscan per default */
+ cfg_priv->escan_on = true; /* escan on & off switch.
+ we enable escan per default */
+#endif
cfg_priv->roam_on = true; /* roam on & off switch.
we enable roam per default */
@@ -3393,6 +3941,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv)
err = brcmf_init_iscan(cfg_priv);
if (err)
return err;
+ brcmf_init_escan(cfg_priv);
brcmf_init_conf(cfg_priv->conf);
brcmf_init_prof(cfg_priv->profile);
brcmf_link_down(cfg_priv);
@@ -3477,7 +4026,7 @@ brcmf_cfg80211_event(struct net_device *ndev,
u32 event_type = be32_to_cpu(e->event_type);
struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
- if (!brcmf_enq_event(cfg_priv, event_type, e))
+ if (!brcmf_enq_event(cfg_priv, event_type, e, data))
schedule_work(&cfg_priv->event_work);
}
@@ -3551,6 +4100,7 @@ static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
setbit(eventmask, BRCMF_E_TXFAIL);
setbit(eventmask, BRCMF_E_JOIN_START);
setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
+ setbit(eventmask, BRCMF_E_ESCAN_RESULT);
brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
iovbuf, sizeof(iovbuf));
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index b5d9b36df3d0..3b2129738d30 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -123,6 +123,13 @@ do { \
#define WL_SCAN_UNASSOC_TIME 40
#define WL_SCAN_PASSIVE_TIME 120
+#define WL_ESCAN_BUF_SIZE (1024 * 64)
+#define WL_ESCAN_TIMER_INTERVAL_MS 8000 /* E-Scan timeout */
+
+#define WL_ESCAN_ACTION_START 1
+#define WL_ESCAN_ACTION_CONTINUE 2
+#define WL_ESCAN_ACTION_ABORT 3
+
/* dongle status */
enum wl_status {
WL_STATUS_READY,
@@ -275,6 +282,19 @@ struct brcmf_cfg80211_pmk_list {
struct pmkid foo[MAXPMKID - 1];
};
+/* dongle escan state */
+enum wl_escan_state {
+ WL_ESCAN_STATE_IDLE,
+ WL_ESCAN_STATE_SCANNING
+};
+
+struct escan_info {
+ u32 escan_state;
+ u8 escan_buf[WL_ESCAN_BUF_SIZE];
+ struct wiphy *wiphy;
+ struct net_device *ndev;
+};
+
/* dongle private data of cfg80211 interface */
struct brcmf_cfg80211_priv {
struct wireless_dev *wdev; /* representing wl cfg80211 device */
@@ -315,6 +335,11 @@ struct brcmf_cfg80211_priv {
u8 *dcmd_buf; /* dcmd buffer */
u8 *extra_buf; /* maily to grab assoc information */
struct dentry *debugfsdir;
+ bool escan_on; /* escan on/off switch */
+ struct escan_info escan_info; /* escan information */
+ struct timer_list escan_timeout; /* Timer for catch scan timeout */
+ struct work_struct escan_timeout_work; /* scan timeout worker */
+ u8 *escan_ioctl_buf;
u8 ci[0] __aligned(NETDEV_ALIGN);
};
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
index 8c9345dd37d2..b89f1272b93f 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
@@ -535,9 +535,6 @@ void ai_detach(struct si_pub *sih)
{
struct si_info *sii;
- struct si_pub *si_local = NULL;
- memcpy(&si_local, &sih, sizeof(struct si_pub **));
-
sii = container_of(sih, struct si_info, pub);
if (sii == NULL)
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
index bcc79b4e3267..e8682855b73a 100644
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
@@ -34,6 +34,7 @@
#define BCM43235_CHIP_ID 43235
#define BCM43236_CHIP_ID 43236
#define BCM43238_CHIP_ID 43238
+#define BCM43241_CHIP_ID 0x4324
#define BCM4329_CHIP_ID 0x4329
#define BCM4330_CHIP_ID 0x4330
#define BCM4331_CHIP_ID 0x4331