diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
17 files changed, 410 insertions, 110 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index c35adf4bc70b..90a977fe9a64 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -30,16 +30,18 @@ brcmfmac-objs += \ fwsignal.o \ p2p.o \ proto.o \ - bcdc.o \ - commonring.o \ - flowring.o \ - msgbuf.o \ dhd_common.o \ dhd_linux.o \ firmware.o \ feature.o \ btcoex.o \ vendor.o +brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \ + bcdc.o +brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ + commonring.o \ + flowring.o \ + msgbuf.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ bcmsdh.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h index 17e8c039ff32..6003179c0ceb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h @@ -16,9 +16,12 @@ #ifndef BRCMFMAC_BCDC_H #define BRCMFMAC_BCDC_H - +#ifdef CONFIG_BRCMFMAC_PROTO_BCDC int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); - +#else +static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } +static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} +#endif #endif /* BRCMFMAC_BCDC_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 3122b86050a1..80e73a1262be 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -67,6 +67,7 @@ struct brcmf_bus_dcmd { * @txctl: transmit a control request message to dongle. * @rxctl: receive a control response message from dongle. * @gettxq: obtain a reference of bus transmit queue (optional). + * @wowl_config: specify if dongle is configured for wowl when going to suspend * * This structure provides an abstract interface towards the * bus specific driver. For control messages to common driver @@ -80,6 +81,7 @@ struct brcmf_bus_ops { int (*txctl)(struct device *dev, unsigned char *msg, uint len); int (*rxctl)(struct device *dev, unsigned char *msg, uint len); struct pktq * (*gettxq)(struct device *dev); + void (*wowl_config)(struct device *dev, bool enabled); }; @@ -114,6 +116,7 @@ struct brcmf_bus_msgbuf { * @dstats: dongle-based statistical data. * @dcmd_list: bus/device specific dongle initialization commands. * @chip: device identifier of the dongle chip. + * @wowl_supported: is wowl supported by bus driver. * @chiprev: revision of the dongle chip. */ struct brcmf_bus { @@ -131,6 +134,7 @@ struct brcmf_bus { u32 chip; u32 chiprev; bool always_use_fws_queue; + bool wowl_supported; struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; @@ -177,6 +181,13 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) return bus->ops->gettxq(bus->dev); } +static inline +void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) +{ + if (bus->ops->wowl_config) + bus->ops->wowl_config(bus->dev, enabled); +} + static inline bool brcmf_bus_ready(struct brcmf_bus *bus) { return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index f55f625fd06b..d20d4e6f391a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -670,7 +670,6 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, struct brcmf_sdio_dev *sdiodev) { int i; - uint fw_len, nv_len; char end; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { @@ -684,25 +683,25 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, return -ENODEV; } - fw_len = sizeof(sdiodev->fw_name) - 1; - nv_len = sizeof(sdiodev->nvram_name) - 1; /* check if firmware path is provided by module parameter */ if (brcmf_firmware_path[0] != '\0') { - strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len); - strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len); - fw_len -= strlen(sdiodev->fw_name); - nv_len -= strlen(sdiodev->nvram_name); + strlcpy(sdiodev->fw_name, brcmf_firmware_path, + sizeof(sdiodev->fw_name)); + strlcpy(sdiodev->nvram_name, brcmf_firmware_path, + sizeof(sdiodev->nvram_name)); end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; if (end != '/') { - strncat(sdiodev->fw_name, "/", fw_len); - strncat(sdiodev->nvram_name, "/", nv_len); - fw_len--; - nv_len--; + strlcat(sdiodev->fw_name, "/", + sizeof(sdiodev->fw_name)); + strlcat(sdiodev->nvram_name, "/", + sizeof(sdiodev->nvram_name)); } } - strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len); - strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len); + strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin, + sizeof(sdiodev->fw_name)); + strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, + sizeof(sdiodev->nvram_name)); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 50877e3c5d2f..aed53acef456 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -107,6 +107,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) struct brcmf_if *ifp = drvr->iflist[0]; brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + if (drvr->bus_if->wowl_supported) + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index 961d175f8afb..b9a796d0a44d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -22,7 +22,8 @@ * MCHAN: multi-channel for concurrent P2P. */ #define BRCMF_FEAT_LIST \ - BRCMF_FEAT_DEF(MCHAN) + BRCMF_FEAT_DEF(MCHAN) \ + BRCMF_FEAT_DEF(WOWL) /* * Quirks: * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index a1016b811284..1faa929f5fff 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -354,7 +354,7 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) struct brcmf_flowring *flow; u32 i; - flow = kzalloc(sizeof(*flow), GFP_ATOMIC); + flow = kzalloc(sizeof(*flow), GFP_KERNEL); if (flow) { flow->dev = dev; flow->nrofrings = nrofrings; @@ -364,7 +364,7 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) for (i = 0; i < ARRAY_SIZE(flow->hash); i++) flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), - GFP_ATOMIC); + GFP_KERNEL); if (!flow->rings) { kfree(flow); flow = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 4f1daabc551b..44fc85f68f7a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -185,7 +185,13 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, ifevent->action, ifevent->ifidx, ifevent->bssidx, ifevent->flags, ifevent->role); - if (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) { + /* The P2P Device interface event must not be ignored + * contrary to what firmware tells us. The only way to + * distinguish the P2P Device is by looking at the ifidx + * and bssidx received. + */ + if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) && + (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { brcmf_dbg(EVENT, "event can be ignored\n"); return; } @@ -210,12 +216,12 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, return; } - if (ifevent->action == BRCMF_E_IF_CHANGE) + if (ifp && ifevent->action == BRCMF_E_IF_CHANGE) brcmf_fws_reset_interface(ifp); err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); - if (ifevent->action == BRCMF_E_IF_DEL) { + if (ifp && ifevent->action == BRCMF_E_IF_DEL) { brcmf_fws_del_interface(ifp); brcmf_del_if(drvr, ifevent->bssidx); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index dd20b1862d44..cbf033f59109 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -172,6 +172,8 @@ enum brcmf_fweh_event_code { #define BRCMF_E_IF_ROLE_STA 0 #define BRCMF_E_IF_ROLE_AP 1 #define BRCMF_E_IF_ROLE_WDS 2 +#define BRCMF_E_IF_ROLE_P2P_GO 3 +#define BRCMF_E_IF_ROLE_P2P_CLIENT 4 /** * definitions for event packet validation. diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 2bc68a2137fc..5ff5cd0bb032 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -53,6 +53,62 @@ #define BRCMF_OBSS_COEX_OFF 0 #define BRCMF_OBSS_COEX_ON 1 +/* WOWL bits */ +/* Wakeup on Magic packet: */ +#define WL_WOWL_MAGIC (1 << 0) +/* Wakeup on Netpattern */ +#define WL_WOWL_NET (1 << 1) +/* Wakeup on loss-of-link due to Disassoc/Deauth: */ +#define WL_WOWL_DIS (1 << 2) +/* Wakeup on retrograde TSF: */ +#define WL_WOWL_RETR (1 << 3) +/* Wakeup on loss of beacon: */ +#define WL_WOWL_BCN (1 << 4) +/* Wakeup after test: */ +#define WL_WOWL_TST (1 << 5) +/* Wakeup after PTK refresh: */ +#define WL_WOWL_M1 (1 << 6) +/* Wakeup after receipt of EAP-Identity Req: */ +#define WL_WOWL_EAPID (1 << 7) +/* Wakeind via PME(0) or GPIO(1): */ +#define WL_WOWL_PME_GPIO (1 << 8) +/* need tkip phase 1 key to be updated by the driver: */ +#define WL_WOWL_NEEDTKIP1 (1 << 9) +/* enable wakeup if GTK fails: */ +#define WL_WOWL_GTK_FAILURE (1 << 10) +/* support extended magic packets: */ +#define WL_WOWL_EXTMAGPAT (1 << 11) +/* support ARP/NS/keepalive offloading: */ +#define WL_WOWL_ARPOFFLOAD (1 << 12) +/* read protocol version for EAPOL frames: */ +#define WL_WOWL_WPA2 (1 << 13) +/* If the bit is set, use key rotaton: */ +#define WL_WOWL_KEYROT (1 << 14) +/* If the bit is set, frm received was bcast frame: */ +#define WL_WOWL_BCAST (1 << 15) +/* If the bit is set, scan offload is enabled: */ +#define WL_WOWL_SCANOL (1 << 16) +/* Wakeup on tcpkeep alive timeout: */ +#define WL_WOWL_TCPKEEP_TIME (1 << 17) +/* Wakeup on mDNS Conflict Resolution: */ +#define WL_WOWL_MDNS_CONFLICT (1 << 18) +/* Wakeup on mDNS Service Connect: */ +#define WL_WOWL_MDNS_SERVICE (1 << 19) +/* tcp keepalive got data: */ +#define WL_WOWL_TCPKEEP_DATA (1 << 20) +/* Firmware died in wowl mode: */ +#define WL_WOWL_FW_HALT (1 << 21) +/* Enable detection of radio button changes: */ +#define WL_WOWL_ENAB_HWRADIO (1 << 22) +/* Offloads detected MIC failure(s): */ +#define WL_WOWL_MIC_FAIL (1 << 23) +/* Wakeup in Unassociated state (Net/Magic Pattern): */ +#define WL_WOWL_UNASSOC (1 << 24) +/* Wakeup if received matched secured pattern: */ +#define WL_WOWL_SECURE (1 << 25) +/* Link Down indication in WoWL mode: */ +#define WL_WOWL_LINKDOWN (1 << 31) + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index d42f7d04b65f..183f08d7fc8c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1636,7 +1636,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, if (!signal_len) return 0; /* if flow control disabled, skip to packet data and leave */ - if (!fws->fw_signals) { + if ((!fws) || (!fws->fw_signals)) { skb_pull(skb, signal_len); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 535c7eb01b3a..11cc051f97cd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -208,6 +208,14 @@ struct msgbuf_flowring_flush_resp { __le32 rsvd0[3]; }; +struct brcmf_msgbuf_work_item { + struct list_head queue; + u32 flowid; + int ifidx; + u8 sa[ETH_ALEN]; + u8 da[ETH_ALEN]; +}; + struct brcmf_msgbuf { struct brcmf_pub *drvr; @@ -230,7 +238,7 @@ struct brcmf_msgbuf { dma_addr_t ioctbuf_handle; u32 ioctbuf_phys_hi; u32 ioctbuf_phys_lo; - u32 ioctl_resp_status; + int ioctl_resp_status; u32 ioctl_resp_ret_len; u32 ioctl_resp_pktid; @@ -248,6 +256,10 @@ struct brcmf_msgbuf { struct work_struct txflow_work; unsigned long *flow_map; unsigned long *txstatus_done_map; + + struct work_struct flowring_work; + spinlock_t flowring_work_lock; + struct list_head work_queue; }; struct brcmf_msgbuf_pktid { @@ -284,11 +296,11 @@ brcmf_msgbuf_init_pktids(u32 nr_array_entries, struct brcmf_msgbuf_pktid *array; struct brcmf_msgbuf_pktids *pktids; - array = kcalloc(nr_array_entries, sizeof(*array), GFP_ATOMIC); + array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL); if (!array) return NULL; - pktids = kzalloc(sizeof(*pktids), GFP_ATOMIC); + pktids = kzalloc(sizeof(*pktids), GFP_KERNEL); if (!pktids) { kfree(array); return NULL; @@ -544,11 +556,29 @@ brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) } -static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, - struct sk_buff *skb) +static struct brcmf_msgbuf_work_item * +brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf) +{ + struct brcmf_msgbuf_work_item *work = NULL; + ulong flags; + + spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); + if (!list_empty(&msgbuf->work_queue)) { + work = list_first_entry(&msgbuf->work_queue, + struct brcmf_msgbuf_work_item, queue); + list_del(&work->queue); + } + spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); + + return work; +} + + +static u32 +brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, + struct brcmf_msgbuf_work_item *work) { struct msgbuf_tx_flowring_create_req *create; - struct ethhdr *eh = (struct ethhdr *)(skb->data); struct brcmf_commonring *commonring; void *ret_ptr; u32 flowid; @@ -557,16 +587,11 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, long long address; int err; - flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, - skb->priority, ifidx); - if (flowid == BRCMF_FLOWRING_INVALID_ID) - return flowid; - + flowid = work->flowid; dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; - dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz, &msgbuf->flowring_dma_handle[flowid], - GFP_ATOMIC); + GFP_KERNEL); if (!dma_buf) { brcmf_err("dma_alloc_coherent failed\n"); brcmf_flowring_delete(msgbuf->flow, flowid); @@ -589,13 +614,13 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, create = (struct msgbuf_tx_flowring_create_req *)ret_ptr; create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; - create->msg.ifidx = ifidx; + create->msg.ifidx = work->ifidx; create->msg.request_id = 0; create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); create->flow_ring_id = cpu_to_le16(flowid + BRCMF_NROF_H2D_COMMON_MSGRINGS); - memcpy(create->sa, eh->h_source, ETH_ALEN); - memcpy(create->da, eh->h_dest, ETH_ALEN); + memcpy(create->sa, work->sa, ETH_ALEN); + memcpy(create->da, work->da, ETH_ALEN); address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); @@ -603,7 +628,7 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE); brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n", - flowid, eh->h_dest, create->tid, ifidx); + flowid, work->da, create->tid, work->ifidx); err = brcmf_commonring_write_complete(commonring); brcmf_commonring_unlock(commonring); @@ -617,6 +642,53 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, } +static void brcmf_msgbuf_flowring_worker(struct work_struct *work) +{ + struct brcmf_msgbuf *msgbuf; + struct brcmf_msgbuf_work_item *create; + + msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work); + + while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) { + brcmf_msgbuf_flowring_create_worker(msgbuf, create); + kfree(create); + } +} + + +static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, + struct sk_buff *skb) +{ + struct brcmf_msgbuf_work_item *create; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + u32 flowid; + ulong flags; + + create = kzalloc(sizeof(*create), GFP_ATOMIC); + if (create == NULL) + return BRCMF_FLOWRING_INVALID_ID; + + flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, + skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) { + kfree(create); + return flowid; + } + + create->flowid = flowid; + create->ifidx = ifidx; + memcpy(create->sa, eh->h_source, ETH_ALEN); + memcpy(create->da, eh->h_dest, ETH_ALEN); + + spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); + list_add_tail(&create->queue, &msgbuf->work_queue); + spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); + schedule_work(&msgbuf->flowring_work); + + return flowid; +} + + static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) { struct brcmf_flowring *flow = msgbuf->flow; @@ -767,7 +839,8 @@ brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf; - msgbuf->ioctl_resp_status = le16_to_cpu(ioctl_resp->compl_hdr.status); + msgbuf->ioctl_resp_status = + (s16)le16_to_cpu(ioctl_resp->compl_hdr.status); msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len); msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id); @@ -1271,7 +1344,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) u32 count; if_msgbuf = drvr->bus_if->msgbuf; - msgbuf = kzalloc(sizeof(*msgbuf), GFP_ATOMIC); + msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); if (!msgbuf) goto fail; @@ -1282,11 +1355,11 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) } INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); - msgbuf->flow_map = kzalloc(count, GFP_ATOMIC); + msgbuf->flow_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->flow_map) goto fail; - msgbuf->txstatus_done_map = kzalloc(count, GFP_ATOMIC); + msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->txstatus_done_map) goto fail; @@ -1294,7 +1367,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev, BRCMF_TX_IOCTL_MAX_MSG_SIZE, &msgbuf->ioctbuf_handle, - GFP_ATOMIC); + GFP_KERNEL); if (!msgbuf->ioctbuf) goto fail; address = (long long)(long)msgbuf->ioctbuf_handle; @@ -1317,7 +1390,9 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * - sizeof(*msgbuf->flowring_dma_handle), GFP_ATOMIC); + sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); + if (!msgbuf->flowring_dma_handle) + goto fail; msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset; msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost; @@ -1355,6 +1430,10 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) brcmf_msgbuf_rxbuf_event_post(msgbuf); brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); + INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker); + spin_lock_init(&msgbuf->flowring_work_lock); + INIT_LIST_HEAD(&msgbuf->work_queue); + return 0; fail: @@ -1362,6 +1441,7 @@ fail: kfree(msgbuf->flow_map); kfree(msgbuf->txstatus_done_map); brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf->flowring_dma_handle); if (msgbuf->ioctbuf) dma_free_coherent(drvr->bus_if->dev, BRCMF_TX_IOCTL_MAX_MSG_SIZE, @@ -1376,11 +1456,19 @@ fail: void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) { struct brcmf_msgbuf *msgbuf; + struct brcmf_msgbuf_work_item *work; brcmf_dbg(TRACE, "Enter\n"); if (drvr->proto->pd) { msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - + cancel_work_sync(&msgbuf->flowring_work); + while (!list_empty(&msgbuf->work_queue)) { + work = list_first_entry(&msgbuf->work_queue, + struct brcmf_msgbuf_work_item, + queue); + list_del(&work->queue); + kfree(work); + } kfree(msgbuf->flow_map); kfree(msgbuf->txstatus_done_map); if (msgbuf->txflow_wq) @@ -1391,6 +1479,7 @@ void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) BRCMF_TX_IOCTL_MAX_MSG_SIZE, msgbuf->ioctbuf, msgbuf->ioctbuf_handle); brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf->flowring_dma_handle); kfree(msgbuf); drvr->proto->pd = NULL; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h index f901ae52bf2b..77a51b8c1e12 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h @@ -15,6 +15,7 @@ #ifndef BRCMFMAC_MSGBUF_H #define BRCMFMAC_MSGBUF_H +#ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20 #define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256 @@ -32,9 +33,15 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev); +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); -void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); - +#else +static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) +{ + return 0; +} +static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} +#endif #endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 057b982ea8b3..d54c58a32faa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -440,8 +440,11 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) /* In case of COB type, firmware has default mac address * After Initializing firmware, we have to set current mac address to - * firmware for P2P device address + * firmware for P2P device address. This must be done with discovery + * disabled. */ + brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0); + ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac, ETH_ALEN); if (ret) @@ -1431,8 +1434,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, IEEE80211_BAND_5GHZ); wdev = &ifp->vif->wdev; - cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0, - GFP_ATOMIC); + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0); kfree(mgmt_frame); return 0; @@ -1896,8 +1898,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0, - GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", mgmt_frame_len, e->datalen, chanspec, freq); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index bc972c0ba5f8..8c0632ec9f7a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -165,6 +165,8 @@ enum brcmf_pcie_state { #define BRCMF_H2D_HOST_D3_INFORM 0x00000001 #define BRCMF_H2D_HOST_DS_ACK 0x00000002 +#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008 +#define BRCMF_H2D_HOST_D0_INFORM 0x00000010 #define BRCMF_PCIE_MBDATA_TIMEOUT 2000 @@ -243,6 +245,7 @@ struct brcmf_pciedev_info { wait_queue_head_t mbdata_resp_wait; bool mbdata_completed; bool irq_allocated; + bool wowl_enabled; }; struct brcmf_pcie_ringbuf { @@ -537,7 +540,7 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, } -static void +static int brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) { struct brcmf_pcie_shared_info *shared; @@ -558,13 +561,15 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) msleep(10); i++; if (i > 100) - break; + return -EIO; cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); } brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + + return 0; } @@ -591,12 +596,13 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) } if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); - if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) + if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); if (waitqueue_active(&devinfo->mbdata_resp_wait)) { devinfo->mbdata_completed = true; wake_up(&devinfo->mbdata_resp_wait); } + } } @@ -1228,11 +1234,27 @@ static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg, } +static void brcmf_pcie_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled); + devinfo->wowl_enabled = enabled; + if (enabled) + device_set_wakeup_enable(&devinfo->pdev->dev, true); + else + device_set_wakeup_enable(&devinfo->pdev->dev, false); +} + + static struct brcmf_bus_ops brcmf_pcie_bus_ops = { .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, .rxctl = brcmf_pcie_rx_ctlpkt, + .wowl_config = brcmf_pcie_wowl_config, }; @@ -1667,6 +1689,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) bus->ops = &brcmf_pcie_bus_ops; bus->proto_type = BRCMF_PROTO_MSGBUF; bus->chip = devinfo->coreid; + bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot); dev_set_drvdata(&pdev->dev, bus); ret = brcmf_pcie_get_fwnames(devinfo); @@ -1758,36 +1781,62 @@ static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) brcmf_err("Timeout on response for entering D3 substate\n"); return -EIO; } - brcmf_pcie_release_irq(devinfo); + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE); err = pci_save_state(pdev); - if (err) { + if (err) brcmf_err("pci_save_state failed, err=%d\n", err); - return err; + if ((err) || (!devinfo->wowl_enabled)) { + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + brcmf_pcie_remove(pdev); + return 0; } - brcmf_chip_detach(devinfo->ci); - devinfo->ci = NULL; - - brcmf_pcie_remove(pdev); - return pci_prepare_to_sleep(pdev); } - static int brcmf_pcie_resume(struct pci_dev *pdev) { + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; int err; - brcmf_dbg(PCIE, "Enter, pdev=%p\n", pdev); + bus = dev_get_drvdata(&pdev->dev); + brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus); err = pci_set_power_state(pdev, PCI_D0); if (err) { brcmf_err("pci_set_power_state failed, err=%d\n", err); - return err; + goto cleanup; } pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, false); + pci_enable_wake(pdev, PCI_D3cold, false); + + /* Check if device is still up and running, if so we are ready */ + if (bus) { + devinfo = bus->bus_priv.pcie->devinfo; + if (brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { + if (brcmf_pcie_send_mb_data(devinfo, + BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + brcmf_dbg(PCIE, "Hot resume, continue....\n"); + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_bus_change_state(bus, BRCMF_BUS_DATA); + brcmf_pcie_intr_enable(devinfo); + return 0; + } + } +cleanup: + if (bus) { + devinfo = bus->bus_priv.pcie->devinfo; + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + brcmf_pcie_remove(pdev); + } err = brcmf_pcie_probe(pdev, NULL); if (err) brcmf_err("probe after resume failed, err=%d\n", err); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 02fe706fc9ec..28fa25b509db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -37,6 +37,7 @@ #include "fwil.h" #include "proto.h" #include "vendor.h" +#include "dhd_bus.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -497,8 +498,11 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) static void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) { - struct net_device *ndev = wdev->netdev; - struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || (wdev->iftype == NL80211_IFTYPE_AP) || @@ -2394,9 +2398,13 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); brcmf_dbg(CONN, "Signal: %d\n", notify_signal); - bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID, - 0, notify_capability, notify_interval, notify_ie, - notify_ielen, notify_signal, GFP_KERNEL); + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, + (const u8 *)bi->BSSID, + 0, notify_capability, + notify_interval, notify_ie, + notify_ielen, notify_signal, + GFP_KERNEL); if (!bss) return -ENOMEM; @@ -2422,7 +2430,7 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) s32 err = 0; int i; - bss_list = cfg->bss_list; + bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; if (bss_list->count != 0 && bss_list->version != BRCMF_BSS_INFO_VERSION) { brcmf_err("Version %d != WL_BSS_INFO_VERSION\n", @@ -2498,9 +2506,11 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); brcmf_dbg(CONN, "signal: %d\n", notify_signal); - bss = cfg80211_inform_bss(wiphy, notify_channel, bssid, - 0, notify_capability, notify_interval, - notify_ie, notify_ielen, notify_signal, GFP_KERNEL); + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, + GFP_KERNEL); if (!bss) { err = -ENOMEM; @@ -2596,6 +2606,7 @@ static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) container_of(work, struct brcmf_cfg80211_info, escan_timeout_work); + brcmf_inform_bss(cfg); brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); } @@ -2734,12 +2745,9 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) goto exit; if (cfg->scan_request) { - cfg->bss_list = (struct brcmf_scan_results *) - cfg->escan_info.escan_buf; brcmf_inform_bss(cfg); aborted = status != BRCMF_E_STATUS_SUCCESS; - brcmf_notify_escan_complete(cfg, ifp, aborted, - false); + brcmf_notify_escan_complete(cfg, ifp, aborted, false); } else brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", status); @@ -2773,50 +2781,91 @@ static __always_inline void brcmf_delay(u32 ms) static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + brcmf_dbg(TRACE, "Enter\n"); + if (cfg->wowl_enabled) { + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, + cfg->pre_wowl_pmmode); + brcmf_fil_iovar_data_set(ifp, "wowl_pattern", "clr", 4); + brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + cfg->wowl_enabled = false; + } return 0; } +static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, + struct cfg80211_wowlan *wowl) +{ + u32 wowl_config; + + brcmf_dbg(TRACE, "Suspend, wowl config.\n"); + + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); + + wowl_config = 0; + if (wowl->disconnect) + wowl_config |= WL_WOWL_DIS | WL_WOWL_BCN | WL_WOWL_RETR; + /* Note: if "wowl" target and not "wowlpf" then wowl_bcn_loss + * should be configured. This paramater is not supported by + * wowlpf. + */ + if (wowl->magic_pkt) + wowl_config |= WL_WOWL_MAGIC; + brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); + brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); + brcmf_bus_wowl_config(cfg->pub->bus_if, true); + cfg->wowl_enabled = true; +} + static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wow) + struct cfg80211_wowlan *wowl) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_vif *vif; brcmf_dbg(TRACE, "Enter\n"); - /* - * if the primary net_device is not READY there is nothing + /* if the primary net_device is not READY there is nothing * we can do but pray resume goes smoothly. */ - vif = ((struct brcmf_if *)netdev_priv(ndev))->vif; - if (!check_vif_up(vif)) + if (!check_vif_up(ifp->vif)) goto exit; - list_for_each_entry(vif, &cfg->vif_list, list) { - if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) - continue; - /* - * While going to suspend if associated with AP disassociate - * from AP to save power while system is in suspended state - */ - brcmf_link_down(vif); - - /* Make sure WPA_Supplicant receives all the event - * generated due to DISASSOC call to the fw to keep - * the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - /* end any scanning */ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); - /* Turn off watchdog timer */ - brcmf_set_mpc(netdev_priv(ndev), 1); + if (wowl == NULL) { + brcmf_bus_wowl_config(cfg->pub->bus_if, false); + list_for_each_entry(vif, &cfg->vif_list, list) { + if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) + continue; + /* While going to suspend if associated with AP + * disassociate from AP to save power while system is + * in suspended state + */ + brcmf_link_down(vif); + /* Make sure WPA_Supplicant receives all the event + * generated due to DISASSOC call to the fw to keep + * the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); + } + /* Configure MPC */ + brcmf_set_mpc(ifp, 1); + + } else { + /* Configure WOWL paramaters */ + brcmf_configure_wowl(cfg, ifp, wowl); + } exit: brcmf_dbg(TRACE, "Exit\n"); @@ -4918,7 +4967,7 @@ static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, struct brcmu_chan ch; int i; - for (i = 0; i <= total; i++) { + for (i = 0; i < total; i++) { ch.chspec = (u16)le32_to_cpu(chlist->element[i]); cfg->d11inf.decchspec(&ch); @@ -5143,6 +5192,7 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) ch.band = BRCMU_CHAN_BAND_2G; ch.bw = BRCMU_CHAN_BW_40; + ch.sb = BRCMU_CHAN_SB_NONE; ch.chnum = 0; cfg->d11inf.encchspec(&ch); @@ -5176,6 +5226,7 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) brcmf_update_bw40_channel_flag(&band->channels[j], &ch); } + kfree(pbuf); } return err; } @@ -5389,6 +5440,21 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support brcmf_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) +{ +#ifdef CONFIG_PM + /* wowl settings */ + wiphy->wowlan = &brcmf_wowlan_support; +#endif +} + static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { struct ieee80211_iface_combination ifc_combo; @@ -5426,6 +5492,9 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + brcmf_wiphy_wowl_params(wiphy); + return brcmf_setup_wiphybands(wiphy); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index f9fb10998e79..6abf94e41d3d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -35,7 +35,7 @@ #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_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ #define WL_ESCAN_ACTION_START 1 #define WL_ESCAN_ACTION_CONTINUE 2 @@ -363,6 +363,8 @@ struct brcmf_cfg80211_vif_event { * @vif_list: linked list of vif instances. * @vif_cnt: number of vif instances. * @vif_event: vif event signalling. + * @wowl_enabled; set during suspend, is wowl used. + * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. */ struct brcmf_cfg80211_info { struct wiphy *wiphy; @@ -371,7 +373,6 @@ struct brcmf_cfg80211_info { struct brcmf_btcoex_info *btcoex; struct cfg80211_scan_request *scan_request; struct mutex usr_sync; - struct brcmf_scan_results *bss_list; struct brcmf_cfg80211_scan_req scan_req_int; struct wl_cfg80211_bss_info *bss_info; struct brcmf_cfg80211_ie ie; @@ -397,6 +398,8 @@ struct brcmf_cfg80211_info { struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; struct brcmu_d11inf d11inf; + bool wowl_enabled; + u32 pre_wowl_pmmode; }; /** |