From cb786ee756155411b65212d4ee76050a382ac674 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 13 Jun 2012 11:22:56 -0700 Subject: net: wireless: bcmdhd: Combined P2P fixes - Fix p2p scan - Fix p2p processing for channels 12 and 13 - Fix service discovery Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 103 +++++++++++++++++++++--------- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 6 +- 2 files changed, 77 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index bee7d55..cccc4d1 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 @@ -2343,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); } @@ -3455,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; @@ -3674,7 +3673,8 @@ 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); /* @@ -3691,6 +3691,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) { @@ -6315,7 +6327,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 */ @@ -6363,10 +6375,12 @@ static s32 wl_escan_handler(struct wl_priv *wl, } 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); @@ -7155,29 +7169,33 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } } + 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_cap) { - wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - } } wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); @@ -7559,17 +7577,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) { @@ -7586,7 +7631,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_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index a902666..bdcfa95 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -1229,6 +1229,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); } @@ -1401,13 +1402,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")); -- cgit v1.1 From 2749b2ea4f335eba5fa5fe399a8fc07b808ba5e9 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 14 Jun 2012 10:59:25 -0700 Subject: net: wireless: bcmdhd: Reload FW in case of constant scan failure Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 1 + drivers/net/wireless/bcmdhd/dhd_linux.c | 1 - drivers/net/wireless/bcmdhd/wl_cfg80211.c | 10 ++++++++++ drivers/net/wireless/bcmdhd/wl_cfg80211.h | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 3b4dc7a..3ffbf0b 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -431,6 +431,7 @@ extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +extern int net_os_send_hang_message(struct net_device *dev); #ifdef PNO_SUPPORT extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index d8c1c1b..a600b58 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -298,7 +298,6 @@ char nvram_path[MOD_PARAM_PATHLEN]; int op_mode = 0; module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); -extern int net_os_send_hang_message(struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) struct semaphore dhd_registration_sem; #define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index cccc4d1..d6bf8fa 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -72,6 +72,7 @@ u32 wl_dbg_level = WL_DBG_ERR; #define WL_SCAN_ACTIVE_TIME 40 #define WL_SCAN_PASSIVE_TIME 130 #define WL_FRAME_LEN 300 +#define WL_SCAN_BUSY_MAX 8 #define DNGL_FUNC(func, parameters) func parameters; #define COEX_DHCP @@ -1738,6 +1739,13 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); if (unlikely(err)) { WL_ERR(("scan error (%d)\n", err)); + if (err == BCME_BUSY) { + wl->scan_busy_count++; + if (wl->scan_busy_count > WL_SCAN_BUSY_MAX) { + wl->scan_busy_count = 0; + net_os_send_hang_message(ndev); + } + } return err; } @@ -6005,6 +6013,7 @@ static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) unsigned long flags; WL_DBG(("Enter \n")); + wl->scan_busy_count = 0; if (!wl_get_drv_status(wl, SCANNING, ndev)) { wl_clr_drv_status(wl, SCANNING, ndev); WL_ERR(("Scan complete while device not scanning\n")); @@ -6259,6 +6268,7 @@ static s32 wl_notify_escan_complete(struct wl_priv *wl, WL_DBG(("Enter \n")); + wl->scan_busy_count = 0; if (wl->scan_request) { if (wl->scan_request->dev == wl->p2p_net) dev = wl_to_prmry_ndev(wl); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 37c8e58..49bed70 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -456,6 +456,7 @@ struct wl_priv { bool sched_scan_running; /* scheduled scan req status */ u16 hostapd_chan; /* remember chan requested by framework for hostapd */ u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ + u16 scan_busy_count; }; static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) -- cgit v1.1 From 1ed2a48549a7e76b3790957a0d6f686e391f3aac Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 14 Jun 2012 11:32:15 -0700 Subject: net: wireless: bcmdhd: Reduce priority for dhd_dpc and watchdog Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index a600b58..b8fb0a9 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -346,14 +346,13 @@ module_param(dhd_master_mode, uint, 0); #ifdef DHDTHREAD /* Watchdog thread priority, -1 to use kernel timer */ -int dhd_watchdog_prio = 97; +int dhd_watchdog_prio = 0; module_param(dhd_watchdog_prio, int, 0); /* DPC thread priority, -1 to use tasklet */ -int dhd_dpc_prio = 98; +int dhd_dpc_prio = 1; module_param(dhd_dpc_prio, int, 0); -/* DPC thread priority, -1 to use tasklet */ extern int dhd_dongle_memsize; module_param(dhd_dongle_memsize, int, 0); #endif /* DHDTHREAD */ -- cgit v1.1 From 562490d2472d7a2074f52c98e08535abdf555393 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 20 Jun 2012 10:22:51 -0700 Subject: net: wireless: bcmdhd: Ignore error if "chanspecs" command is not supported Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index d6bf8fa..009ebed 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7182,7 +7182,10 @@ s32 wl_update_wiphybands(struct wl_priv *wl) err = wl_construct_reginfo(wl, bw_cap); if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); - return err; + if (err != BCME_UNSUPPORTED) + return err; + /* Ignore error if "chanspecs" command is not supported */ + err = 0; } for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; -- cgit v1.1 From 38fac5b1e2604aaf3ffae7ebadf219eb567cb61f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 10:25:25 -0700 Subject: net: wireless: bcmdhd: Skip inaccurate wl_construct_reginfo() call Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 009ebed..e62f6e5 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -4718,7 +4718,7 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) #endif channel = ieee80211_get_channel(wiphy, freq); if (!channel) { - WL_ERR(("No valid channel")); + WL_ERR(("No valid channel: %u\n", freq)); kfree(notif_bss_info); return -EINVAL; } @@ -7179,6 +7179,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } } +#if 0 err = wl_construct_reginfo(wl, bw_cap); if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); @@ -7187,6 +7188,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) /* Ignore error if "chanspecs" command is not supported */ err = 0; } +#endif for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { -- cgit v1.1 From d7ade6c71e6004fc2ac327eb13205684da6dcfdf Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 16:26:07 -0700 Subject: net: wireless: bcmdhd: Add wiphyband update for country change Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index b8fb0a9..2f48cbb 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4532,8 +4532,12 @@ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - if (dhd && dhd->pub.up) + if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); +#ifdef WL_CFG80211 + wl_update_wiphybands(NULL); +#endif + } } void dhd_net_if_lock(struct net_device *dev) -- cgit v1.1 From 68983530eaa27747f4dc14a981d048faeb12a46f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 16:27:31 -0700 Subject: net: wireless: bcmdhd: Return wl_construct_reginfo() call Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index e62f6e5..28c9241 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7179,7 +7179,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } } -#if 0 err = wl_construct_reginfo(wl, bw_cap); if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); @@ -7188,7 +7187,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl) /* Ignore error if "chanspecs" command is not supported */ err = 0; } -#endif for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { -- cgit v1.1 From 22baf5de1054939a81c2b35daa0663d96f06806b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 16:38:27 -0700 Subject: net: wireless: bcmdhd: Skip country setting if unnecessary Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wldev_common.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index 8270331..a1156ff 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -349,21 +349,22 @@ int wldev_set_country( __FUNCTION__, error)); return error; } - } - cspec.rev = -1; - memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); - memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); - get_customized_country_code((char *)&cspec.country_abbrev, &cspec); - error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf), NULL); - if (error < 0) { - WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", + + cspec.rev = -1; + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); + get_customized_country_code((char *)&cspec.country_abbrev, &cspec); + error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), + smbuf, sizeof(smbuf), NULL); + if (error < 0) { + WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + return error; + } + dhd_bus_country_set(dev, &cspec); + WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); - return error; } - dhd_bus_country_set(dev, &cspec); - WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", - __FUNCTION__, country_code, cspec.ccode, cspec.rev)); return 0; } -- cgit v1.1 From fa98f042f17bedb9f714193354cef67d3b253176 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 29 Jun 2012 09:31:18 -0700 Subject: net: wireless: bcmdhd: Add mutex to wl_update_wiphybands() Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 28c9241..121f3bb 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7149,11 +7149,15 @@ s32 wl_update_wiphybands(struct wl_priv *wl) int nmode = 0; int bw_cap = 0; int index = 0; + bool rollback_lock = false; WL_DBG(("Entry")); - if (wl == NULL) + if (wl == NULL) { wl = wlcfg_drv_priv; + mutex_lock(&wl->usr_sync); + rollback_lock = true; + } dev = wl_to_prmry_ndev(wl); memset(bandlist, 0, sizeof(bandlist)); @@ -7161,7 +7165,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) sizeof(bandlist), false); if (unlikely(err)) { WL_ERR(("error read bandlist (%d)\n", err)); - return err; + goto end_bands; } wiphy = wl_to_wiphy(wl); nband = bandlist[0]; @@ -7183,7 +7187,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); if (err != BCME_UNSUPPORTED) - return err; + goto end_bands; /* Ignore error if "chanspecs" command is not supported */ err = 0; } @@ -7212,6 +7216,9 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); +end_bands: + if (rollback_lock) + mutex_unlock(&wl->usr_sync); return err; } -- cgit v1.1 From f70624b6e536e99feeb793f1417375cce953966d Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 9 Jul 2012 10:31:06 -0700 Subject: net: wireless: bcm4329: Init locks in dhd_attach() at the beginning Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/dhd_linux.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 5f5b418..b466d74 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -2086,6 +2086,17 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) memcpy(netdev_priv(net), &dhd, sizeof(dhd)); dhd->pub.osh = osh; + mutex_init(&dhd->proto_sem); + mutex_init(&dhd->sdsem); + /* Initialize other structure content */ + init_waitqueue_head(&dhd->ioctl_resp_wait); + init_waitqueue_head(&dhd->ctrl_wait); + + /* Initialize the spinlocks */ + spin_lock_init(&dhd->sdlock); + spin_lock_init(&dhd->txqlock); + spin_lock_init(&dhd->dhd_lock); + /* Set network interface name if it was provided as module parameter */ if (iface_name[0]) { int len; @@ -2107,16 +2118,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) net->netdev_ops = NULL; #endif - mutex_init(&dhd->proto_sem); - /* Initialize other structure content */ - init_waitqueue_head(&dhd->ioctl_resp_wait); - init_waitqueue_head(&dhd->ctrl_wait); - - /* Initialize the spinlocks */ - spin_lock_init(&dhd->sdlock); - spin_lock_init(&dhd->txqlock); - spin_lock_init(&dhd->dhd_lock); - /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wl_lock); dhd->wl_count = 0; @@ -2154,7 +2155,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->timer.function = dhd_watchdog; /* Initialize thread based operation and lock */ - mutex_init(&dhd->sdsem); if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { dhd->threads_only = TRUE; } -- cgit v1.1 From 230efa68694bf978725fe155b4e81b1c8a9a565c Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 9 Jul 2012 10:46:55 -0700 Subject: net: wireless: bcmdhd: Init locks in dhd_attach() at the beginning Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 2f48cbb..1cee01d 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -2644,17 +2644,8 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) strcat(net->name, "%d"); } - if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) - goto fail; - dhd_state |= DHD_ATTACH_STATE_ADD_IF; - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) - net->open = NULL; -#else - net->netdev_ops = NULL; -#endif - sema_init(&dhd->proto_sem, 1); + sema_init(&dhd->sdsem, 1); #ifdef PROP_TXSTATUS spin_lock_init(&dhd->wlfc_spinlock); @@ -2670,6 +2661,17 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) spin_lock_init(&dhd->txqlock); spin_lock_init(&dhd->dhd_lock); + + if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) + goto fail; + dhd_state |= DHD_ATTACH_STATE_ADD_IF; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wakelock_spinlock); dhd->wakelock_counter = 0; @@ -2722,7 +2724,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #ifdef DHDTHREAD /* Initialize thread based operation and lock */ - sema_init(&dhd->sdsem, 1); if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { dhd->threads_only = TRUE; } -- cgit v1.1 From df06df7bfc792ee9b7ca69276d0081b2a9d0ef76 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 9 Jul 2012 11:14:42 -0700 Subject: net: wireless: bcmdhd: Update wiphy bands on band change Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 13 ++++++++++++- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 23 +++++++++++++++++++++-- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 3 ++- drivers/net/wireless/bcmdhd/wldev_common.c | 2 ++ drivers/net/wireless/bcmdhd/wldev_common.h | 1 + 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 1cee01d..4d763c9 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4536,7 +4536,18 @@ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); #ifdef WL_CFG80211 - wl_update_wiphybands(NULL); + wl_update_wiphybands(NULL, cspec->ccode); +#endif + } +} + +void dhd_bus_band_set(struct net_device *dev, uint band) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && dhd->pub.up) { +#ifdef WL_CFG80211 + wl_update_wiphybands(NULL, NULL); #endif } } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 121f3bb..5e61aa4 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7138,7 +7138,7 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) return err; } -s32 wl_update_wiphybands(struct wl_priv *wl) +s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) { struct wiphy *wiphy; struct net_device *dev; @@ -7149,6 +7149,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) int nmode = 0; int bw_cap = 0; int index = 0; + struct regulatory_request *new_request; bool rollback_lock = false; WL_DBG(("Entry")); @@ -7216,6 +7217,24 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); + + if (country_code != NULL) { + WL_DBG(("country_code[0]=%c, country_code[1]=%c\n", country_code[0], country_code[1])); + /* Allocate a requlatory request to update cfg80211 for the supported channels */ + new_request = kzalloc(sizeof(struct regulatory_request), + GFP_KERNEL); + if (!new_request) { + WL_ERR(("error Memory alloc for requlatory request failed\n")); + err = -ENOMEM; + } else { + new_request->alpha2[0] = country_code[0]; + new_request->alpha2[1] = country_code[1]; + WL_DBG(("Send an event to cfg80211 to update supplicant to take the new channel list")); + nl80211_send_reg_change_event(new_request); + kfree(new_request); + } + } + end_bands: if (rollback_lock) mutex_unlock(&wl->usr_sync); @@ -7238,7 +7257,7 @@ static s32 __wl_cfg80211_up(struct wl_priv *wl) if (unlikely(err && err != -EINPROGRESS)) { WL_ERR(("wl_config_ifmode failed\n")); } - err = wl_update_wiphybands(wl); + err = wl_update_wiphybands(wl, NULL); if (unlikely(err)) { WL_ERR(("wl_update_wiphybands failed\n")); } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 49bed70..c3281ec 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -669,6 +669,7 @@ 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_update_wiphybands(struct wl_priv *wl, char *country_code); extern s32 wl_cfg80211_if_is_group_owner(void); +extern void nl80211_send_reg_change_event(struct regulatory_request *request); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index a1156ff..51a8064 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -320,6 +320,8 @@ int wldev_set_band( if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), 1); + if (!error) + dhd_bus_band_set(dev, band); } return error; } diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h index f586609..dd3c899 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.h +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -85,6 +85,7 @@ s32 wldev_iovar_setint_bsscfg( extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); +extern void dhd_bus_band_set(struct net_device *dev, uint band); extern int wldev_set_country(struct net_device *dev, char *country_code); extern int net_os_wake_lock(struct net_device *dev); extern int net_os_wake_unlock(struct net_device *dev); -- cgit v1.1 From a5c77fe0e666320b91ad4ae7fb2d03c2f8aef41b Mon Sep 17 00:00:00 2001 From: Neeraj Kumar Garg Date: Mon, 9 Jul 2012 12:18:39 -0700 Subject: net: wireless: bcmdhd: Fix P2P GO hang issue Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/include/wlioctl.h | 1 + drivers/net/wireless/bcmdhd/wl_cfg80211.c | 64 +++++++++++++++------------ 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index 891d15c..e543bfa 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -180,6 +180,7 @@ typedef struct wlc_ssid { uchar SSID[32]; } wlc_ssid_t; +#define WL_BSS_FLAGS_FROM_BEACON 0x01 #define WL_BSSTYPE_INFRA 1 #define WL_BSSTYPE_INDEP 0 diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 5e61aa4..7049d77 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -781,8 +781,6 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("name is NULL\n")); return NULL; } - if (wl->iface_cnt == IFACE_MAX_CNT) - return ERR_PTR(-ENOMEM); if (wl->p2p_supported && (wlif_type != -1)) { if (wl_get_p2p_status(wl, IF_DELETING)) { /* wait till IF_DEL is complete @@ -810,6 +808,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("timeount < 0, return -EAGAIN\n")); return ERR_PTR(-EAGAIN); } + /* It should be now be safe to put this check here since we are sure + * by now netdev_notifier (unregister) would have been called */ + if (wl->iface_cnt == IFACE_MAX_CNT) + return ERR_PTR(-ENOMEM); } if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { p2p_on(wl) = true; @@ -1100,7 +1102,7 @@ wl_cfg80211_notify_ifdel(void) WL_DBG(("Enter \n")); wl_clr_p2p_status(wl, IF_DELETING); - + wake_up_interruptible(&wl->netif_change_event); return 0; } @@ -1578,6 +1580,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); iscan_req = false; + wl->scan_request = request; if (request) { /* scan bss */ ssids = request->ssids; if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { @@ -1658,7 +1661,6 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* we don't do iscan in ibss */ ssids = this_ssid; } - wl->scan_request = request; wl_set_drv_status(wl, SCANNING, ndev); if (iscan_req) { err = wl_do_iscan(wl, request); @@ -1722,7 +1724,12 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, scan_out: wl_clr_drv_status(wl, SCANNING, ndev); - wl->scan_request = NULL; + if (wl->scan_request) { + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } return err; } @@ -4731,29 +4738,6 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) signal = notif_bss_info->rssi * 100; -#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) - if (wl->p2p && wl->p2p_net && wl->scan_request && - ((wl->scan_request->dev == wl->p2p_net) || - (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))){ -#else - if (p2p_is_on(wl) && ( p2p_scan(wl) || - (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))) { -#endif - /* find the P2PIE, if we do not find it, we will discard this frame */ - wifi_p2p_ie_t * p2p_ie; - if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)beacon_proberesp->variable, - wl_get_ielen(wl))) == NULL) { - WL_ERR(("Couldn't find P2PIE in probe response/beacon\n")); - 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; @@ -5700,6 +5684,13 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); wl_clr_p2p_status(wl, GO_NEG_PHASE); } + + if (act_frm && (act_frm->subtype == P2P_PAF_GON_RSP)) { + /* Cancel the dwell time of req frame */ + WL_DBG(("P2P: Received GO NEG Resp frame, cancelling the dwell time\n")); + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + } } else { mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); } @@ -6337,6 +6328,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl_scan_results_t *list; u32 bi_length; u32 i; + wifi_p2p_ie_t * p2p_ie; u8 *p2p_dev_addr = NULL; WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); @@ -6403,6 +6395,22 @@ static s32 wl_escan_handler(struct wl_priv *wl, WL_ERR(("Buffer is too small: ignoring\n")); goto exit; } +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net && wl->scan_request && + wl->scan_request->dev == wl->p2p_net) { +#else + if (p2p_is_on(wl) && p2p_scan(wl)) { +#endif + /* p2p scan && allow only probe response */ + if (bi->flags & WL_BSS_FLAGS_FROM_BEACON) + goto exit; + if ((p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, + bi->ie_length)) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe" + " response/beacon\n")); + goto exit; + } + } #define WLC_BSS_RSSI_ON_CHANNEL 0x0002 for (i = 0; i < list->count; i++) { bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) -- cgit v1.1 From f4702ad7cad466dd37c0ba58dd0715fd2631535e Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 11 Jul 2012 10:17:59 -0700 Subject: net: wireless: bcmdhd: Remove country update from wl_update_wiphybands Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 4 ++-- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 22 ++-------------------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 3 +-- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 4d763c9..657a1bf 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4536,7 +4536,7 @@ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); #ifdef WL_CFG80211 - wl_update_wiphybands(NULL, cspec->ccode); + wl_update_wiphybands(NULL); #endif } } @@ -4547,7 +4547,7 @@ void dhd_bus_band_set(struct net_device *dev, uint band) if (dhd && dhd->pub.up) { #ifdef WL_CFG80211 - wl_update_wiphybands(NULL, NULL); + wl_update_wiphybands(NULL); #endif } } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 7049d77..3537138 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7146,7 +7146,7 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) return err; } -s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) +s32 wl_update_wiphybands(struct wl_priv *wl) { struct wiphy *wiphy; struct net_device *dev; @@ -7157,7 +7157,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) int nmode = 0; int bw_cap = 0; int index = 0; - struct regulatory_request *new_request; bool rollback_lock = false; WL_DBG(("Entry")); @@ -7226,23 +7225,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); - if (country_code != NULL) { - WL_DBG(("country_code[0]=%c, country_code[1]=%c\n", country_code[0], country_code[1])); - /* Allocate a requlatory request to update cfg80211 for the supported channels */ - new_request = kzalloc(sizeof(struct regulatory_request), - GFP_KERNEL); - if (!new_request) { - WL_ERR(("error Memory alloc for requlatory request failed\n")); - err = -ENOMEM; - } else { - new_request->alpha2[0] = country_code[0]; - new_request->alpha2[1] = country_code[1]; - WL_DBG(("Send an event to cfg80211 to update supplicant to take the new channel list")); - nl80211_send_reg_change_event(new_request); - kfree(new_request); - } - } - end_bands: if (rollback_lock) mutex_unlock(&wl->usr_sync); @@ -7265,7 +7247,7 @@ static s32 __wl_cfg80211_up(struct wl_priv *wl) if (unlikely(err && err != -EINPROGRESS)) { WL_ERR(("wl_config_ifmode failed\n")); } - err = wl_update_wiphybands(wl, NULL); + err = wl_update_wiphybands(wl); if (unlikely(err)) { WL_ERR(("wl_update_wiphybands failed\n")); } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index c3281ec..49bed70 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -669,7 +669,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, char *country_code); +extern s32 wl_update_wiphybands(struct wl_priv *wl); extern s32 wl_cfg80211_if_is_group_owner(void); -extern void nl80211_send_reg_change_event(struct regulatory_request *request); #endif /* _wl_cfg80211_h_ */ -- cgit v1.1 From 66943ea3526d56b35880f962094ae29b1ded1d06 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 12 Jul 2012 16:58:00 -0700 Subject: net: wireless: bcmdhd: Add info_string param with driver/fw/chip info Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 5 +++++ drivers/net/wireless/bcmdhd/dhd_linux.c | 27 +++++++++++++++++++++++---- drivers/net/wireless/bcmdhd/dhd_sdio.c | 18 +++++++++++++++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 3ffbf0b..f047c8e 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -432,6 +432,7 @@ extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); extern int net_os_send_hang_message(struct net_device *dev); +extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); #ifdef PNO_SUPPORT extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); @@ -521,6 +522,8 @@ extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *da extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); +extern uint dhd_bus_chiprev_id(dhd_pub_t *dhdp); +extern uint dhd_bus_chippkg_id(dhd_pub_t *dhdp); #if defined(KEEP_ALIVE) extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); @@ -609,6 +612,8 @@ extern uint dhd_pktgen_len; extern char fw_path[MOD_PARAM_PATHLEN]; extern char nv_path[MOD_PARAM_PATHLEN]; +#define MOD_PARAM_INFOLEN 512 + #ifdef SOFTAP extern char fw_path2[MOD_PARAM_PATHLEN]; #endif diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 657a1bf..f4dd38f 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -295,6 +295,13 @@ typedef struct dhd_info { char firmware_path[MOD_PARAM_PATHLEN]; char nvram_path[MOD_PARAM_PATHLEN]; +/* load firmware and/or nvram values from the filesystem */ +module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); +module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); + +char info_string[MOD_PARAM_INFOLEN]; +module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); + int op_mode = 0; module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); @@ -310,10 +317,6 @@ module_param(dhd_sysioc, uint, 0); /* Error bits */ module_param(dhd_msg_level, int, 0); -/* load firmware and/or nvram values from the filesystem */ -module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); -module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); - /* Watchdog interval */ uint dhd_watchdog_ms = 10; module_param(dhd_watchdog_ms, uint, 0); @@ -3314,6 +3317,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) /* Print fw version info */ DHD_ERROR(("Firmware version = %s\n", buf)); + dhd_set_version_info(dhd, buf); + DHD_BLOG(buf, strlen(buf) + 1); DHD_BLOG(dhd_version, strlen(dhd_version) + 1); @@ -4851,6 +4856,20 @@ int dhd_os_check_if_up(void *dhdp) return pub->up; } +void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) +{ + int i; + + i = snprintf(info_string, sizeof(info_string), + "WLAN:\n Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); + + if (!dhdp) + return; + i = snprintf(&info_string[i], sizeof(info_string) - i, + "\n Chip: %x Rev %x Pkg %x\n", dhd_bus_chip_id(dhdp), + dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); +} + int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) { int ifidx; diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 4834ca2..5b744f4 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -6320,7 +6320,23 @@ uint dhd_bus_chip_id(dhd_pub_t *dhdp) { dhd_bus_t *bus = dhdp->bus; - return bus->sih->chip; + return bus->sih->chip; +} + +/* Get Chip Rev ID version */ +uint dhd_bus_chiprev_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chiprev; +} + +/* Get Chip Pkg ID version */ +uint dhd_bus_chippkg_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chippkg; } int -- cgit v1.1 From b595a66a0be0e99da8854d4ff7693193e6ac1a81 Mon Sep 17 00:00:00 2001 From: German Monroy Date: Mon, 14 May 2012 17:14:19 -0700 Subject: x86: Call idle notifiers BZ: 35303 Google patched the idle loop for x86-64 (commit 1fd57f722c) and for ARM (commit 41fa406c26), but they forgot x86-32. This is preventing their interactive governor from upshifting CPU frequencies in intel's SOC platforms (e.g. Medfield, etc.). Fixing that. NOTE: The notifier calls are not located in the same exact place in the idle loop for x86-64 and ARM. The notifier is called inside the `while(!need_resched)' loop in x86-64 and outside of it in ARM. Since Google has likely tuned this governor for ARM, leaving it as in ARM. Change-Id: Ibefd0c8f08e4b4c24c4a5c32dcdc574f9090b2b9 Signed-off-by: German Monroy --- arch/x86/kernel/process_32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index fcdb1b3..12dae34 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -98,6 +98,7 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { tick_nohz_stop_sched_tick(1); + idle_notifier_call_chain(IDLE_START); while (!need_resched()) { check_pgt_cache(); @@ -112,6 +113,7 @@ void cpu_idle(void) pm_idle(); start_critical_timings(); } + idle_notifier_call_chain(IDLE_END); tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); schedule(); -- cgit v1.1 From c491722e0e6cb70bf53d43a307d24964f5970a9e Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 13 Jul 2012 14:38:32 -0700 Subject: net: wireless: bcmdhd: Adjust driver/fw/chip info format Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index f4dd38f..3aede87 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4861,12 +4861,12 @@ void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) int i; i = snprintf(info_string, sizeof(info_string), - "WLAN:\n Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); + " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); if (!dhdp) return; i = snprintf(&info_string[i], sizeof(info_string) - i, - "\n Chip: %x Rev %x Pkg %x\n", dhd_bus_chip_id(dhdp), + "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); } -- cgit v1.1 From 3421d0b81578be044b87bba5afb72d91872591ae Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 13 Jul 2012 14:30:33 -0700 Subject: net: wireless: bcmdhd: Use proper jiffie-related functions Signed-off-by: Dmitry Shmidt Conflicts: drivers/net/wireless/bcmdhd/wl_cfg80211.c drivers/net/wireless/bcmdhd/wl_iw.c --- drivers/net/wireless/bcmdhd/dhd.h | 4 ++-- drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 4 ++-- drivers/net/wireless/bcmdhd/dhd_linux.c | 22 +++++++--------------- drivers/net/wireless/bcmdhd/include/linux_osl.h | 2 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 10 +++++----- drivers/net/wireless/bcmdhd/wl_iw.c | 18 +++++++++--------- 6 files changed, 26 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index f047c8e..b35b24a 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -251,7 +251,7 @@ typedef struct dhd_cmn { SMP_RD_BARRIER_DEPENDS(); \ while (dhd_mmc_suspend && retry++ != b) { \ SMP_RD_BARRIER_DEPENDS(); \ - wait_event_interruptible_timeout(a, !dhd_mmc_suspend, HZ/100); \ + wait_event_interruptible_timeout(a, !dhd_mmc_suspend, 1); \ } \ } while (0) #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 200) @@ -263,7 +263,7 @@ typedef struct dhd_cmn { #define SPINWAIT_SLEEP(a, exp, us) do { \ uint countdown = (us) + 9999; \ while ((exp) && (countdown >= 10000)) { \ - wait_event_interruptible_timeout(a, FALSE, HZ/100); \ + wait_event_interruptible_timeout(a, FALSE, 1); \ countdown -= 10000; \ } \ } while (0) diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c index bc1be94..970216e 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -433,7 +433,7 @@ static void wl_cfg80211_bt_handler(struct work_struct *work) __FUNCTION__)); btcx_inf->bt_state = BT_DHCP_OPPR_WIN; mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); + jiffies + msecs_to_jiffies(BT_DHCP_OPPR_WIN_TIME)); btcx_inf->timer_on = 1; break; @@ -453,7 +453,7 @@ static void wl_cfg80211_bt_handler(struct work_struct *work) wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME)); btcx_inf->timer_on = 1; break; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 3aede87..cebfe98 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -646,7 +646,7 @@ dhd_timeout_start(dhd_timeout_t *tmo, uint usec) tmo->limit = usec; tmo->increment = 0; tmo->elapsed = 0; - tmo->tick = 1000000 / HZ; + tmo->tick = jiffies_to_usecs(1); } int @@ -1692,7 +1692,7 @@ dhd_watchdog_thread(void *data) /* Reschedule the watchdog */ if (dhd->wd_timer_valid) mod_timer(&dhd->timer, - jiffies + dhd_watchdog_ms * HZ / 1000); + jiffies + msecs_to_jiffies(dhd_watchdog_ms)); dhd_os_spin_unlock(&dhd->pub, flags); } dhd_os_sdunlock(&dhd->pub); @@ -1733,7 +1733,7 @@ static void dhd_watchdog(ulong data) /* Reschedule the watchdog */ if (dhd->wd_timer_valid) - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); dhd_os_spin_unlock(&dhd->pub, flags); dhd_os_sdunlock(&dhd->pub); DHD_OS_WAKE_UNLOCK(&dhd->pub); @@ -3937,14 +3937,10 @@ int dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) { dhd_info_t * dhd = (dhd_info_t *)(pub->info); - int timeout = dhd_ioctl_timeout_msec; + int timeout; /* Convert timeout in millsecond to jiffies */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - timeout = msecs_to_jiffies(timeout); -#else - timeout = timeout * HZ / 1000; -#endif + timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec); timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); return timeout; @@ -3994,7 +3990,7 @@ dhd_os_wd_timer(void *bus, uint wdtick) if (wdtick) { dhd_watchdog_ms = (uint)wdtick; /* Re arm the timer, at last watchdog period */ - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); dhd->wd_timer_valid = TRUE; } dhd_os_spin_unlock(pub, flags); @@ -4305,11 +4301,7 @@ void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) int timeout = msecs_to_jiffies(2000); -#else - int timeout = 2 * HZ; -#endif dhd_os_sdunlock(dhd); wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); dhd_os_sdlock(dhd); @@ -4634,7 +4626,7 @@ int dhd_wait_pend8021x(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - int timeout = 10 * HZ / 1000; + int timeout = msecs_to_jiffies(10); int ntimes = MAX_WAIT_FOR_8021X_TX; int pend = dhd_get_pend_8021x_cnt(dhd); diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h index 830d351..7f92966 100644 --- a/drivers/net/wireless/bcmdhd/include/linux_osl.h +++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h @@ -163,7 +163,7 @@ extern int osl_error(int bcmerror); -#define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ)) +#define OSL_SYSUPTIME() ((uint32)jiffies_to_msecs(jiffies)) #define printf(fmt, args...) printk(fmt , ## args) #include #include diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 3537138..2365bdd 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -1355,7 +1355,7 @@ static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request } wl->iscan_kickstart = true; wl_run_iscan(iscan, request, WL_SCAN_ACTION_START); - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; return err; @@ -1578,7 +1578,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } /* Arm scan timeout timer */ - mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); + mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); iscan_req = false; wl->scan_request = request; if (request) { /* scan bss */ @@ -6088,7 +6088,7 @@ static s32 wl_iscan_pending(struct wl_priv *wl) s32 err = 0; /* Reschedule the timer */ - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; return err; @@ -6104,7 +6104,7 @@ static s32 wl_iscan_inprogress(struct wl_priv *wl) wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); mutex_unlock(&wl->usr_sync); /* Reschedule the timer */ - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; return err; @@ -7551,7 +7551,7 @@ static void wl_init_eq_lock(struct wl_priv *wl) static void wl_delay(u32 ms) { - if (in_atomic() || ms < 1000 / HZ) { + if (in_atomic() || (ms < jiffies_to_msecs(1))) { mdelay(ms); } else { msleep(ms); diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 095dc86..d60c21c 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -64,7 +64,7 @@ typedef const struct si_pub si_t; #endif -#define JF2MS ((((jiffies / HZ) * 1000) + ((jiffies % HZ) * 1000) / HZ)) +#define JF2MS jiffies_to_msecs(jiffies) #ifdef COEX_DBG #define WL_TRACE_COEX(x) printf("TS:%lu ", JF2MS); \ @@ -3284,7 +3284,7 @@ _iscan_sysioc_thread(void *data) rtnl_unlock(); #endif - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; break; case WL_SCAN_RESULTS_SUCCESS: @@ -3295,7 +3295,7 @@ _iscan_sysioc_thread(void *data) case WL_SCAN_RESULTS_PENDING: WL_TRACE(("iscanresults pending\n")); - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; break; case WL_SCAN_RESULTS_ABORTED: @@ -3395,9 +3395,9 @@ wl_iw_run_ss_cache_timer(int kick_off) if (*timer) { if (kick_off) { #ifdef CONFIG_PRESCANNED - (*timer)->expires = jiffies + 70000 * HZ / 1000; + (*timer)->expires = jiffies + msecs_to_jiffies(70000); #else - (*timer)->expires = jiffies + 30000 * HZ / 1000; + (*timer)->expires = jiffies + msecs_to_jiffies(30000); #endif add_timer(*timer); WL_TRACE(("%s : timer starts \n", __FUNCTION__)); @@ -3715,7 +3715,7 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) rtnl_unlock(); #endif - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; @@ -6007,7 +6007,7 @@ wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nss iscan->list_cur = iscan->list_hdr; iscan->iscan_state = ISCAN_STATE_SCANING; wl_iw_set_event_mask(dev); - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; @@ -8639,7 +8639,7 @@ _bt_dhcp_sysioc_thread(void *data) WL_TRACE_COEX(("%s bt_dhcp stm: started \n", __FUNCTION__)); g_bt->bt_state = BT_DHCP_OPPORTUNITY_WINDOW; mod_timer(&g_bt->timer, - jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIME*HZ/1000); + jiffies + msecs_to_jiffies(BT_DHCP_OPPORTUNITY_WINDOW_TIME)); g_bt->timer_on = 1; break; @@ -8656,7 +8656,7 @@ _bt_dhcp_sysioc_thread(void *data) if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, TRUE); g_bt->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&g_bt->timer, jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + mod_timer(&g_bt->timer, jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME)); g_bt->timer_on = 1; break; -- cgit v1.1 From 62618c1b041107fdd08920ef507a9db4bc18649a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 18 Jul 2012 11:15:10 -0700 Subject: net: wireless: bcmdhd: Prevent HT Avail request failure to flood the log Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 1 + drivers/net/wireless/bcmdhd/dhd_linux.c | 26 +++++++++++++++++++------- drivers/net/wireless/bcmdhd/dhd_sdio.c | 16 +++++++++++----- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index b35b24a..725a909 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -431,6 +431,7 @@ extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +extern int dhd_os_send_hang_message(dhd_pub_t *dhdp); extern int net_os_send_hang_message(struct net_device *dev); extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index cebfe98..d32ce11 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4510,21 +4510,33 @@ static void dhd_hang_process(struct work_struct *work) #endif } } +#endif -int net_os_send_hang_message(struct net_device *dev) +int dhd_os_send_hang_message(dhd_pub_t *dhdp) { - dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; - if (dhd) { - if (!dhd->pub.hang_was_sent) { - dhd->pub.hang_was_sent = 1; - schedule_work(&dhd->work_hang); + if (dhdp) { + if (!dhdp->hang_was_sent) { + dhdp->hang_was_sent = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + schedule_work(&dhdp->info->work_hang); +#endif } } return ret; } -#endif + +int net_os_send_hang_message(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_send_hang_message(&dhd->pub); + + return ret; +} void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 5b744f4..e578d70 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -539,6 +539,8 @@ dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address) static int dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) { +#define HT_AVAIL_ERROR_MAX 10 + static int ht_avail_error = 0; int err; uint8 clkctl, clkreq, devctl; bcmsdh_info_t *sdh; @@ -551,18 +553,22 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) clkctl = 0; sdh = bus->sdh; - if (on) { /* Request HT Avail */ clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - - - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); if (err) { - DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + ht_avail_error++; + if (ht_avail_error < HT_AVAIL_ERROR_MAX) { + DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + } else { + if (ht_avail_error == HT_AVAIL_ERROR_MAX) + dhd_os_send_hang_message(bus->dhd); + } return BCME_ERROR; + } else { + ht_avail_error = 0; } if (pendok && -- cgit v1.1 From e4639327fc09d76113dc5e126c2891467821c82b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 26 Jul 2012 18:02:38 -0700 Subject: net: wireless: bcmdhd: Fill station_info packet fields Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 2365bdd..b10a5ef 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -2999,6 +2999,7 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, sta->idle * 1000)); #endif } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + get_pktcnt_t pktcnt; u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); err = -ENODEV; if (!wl_get_drv_status(wl, CONNECTED, dev) || @@ -3035,6 +3036,19 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, sinfo->signal = rssi; WL_DBG(("RSSI %d dBm\n", rssi)); + err = wldev_ioctl(dev, WLC_GET_PKTCNTS, &pktcnt, + sizeof(pktcnt), false); + if (!err) { + sinfo->filled |= (STATION_INFO_RX_PACKETS | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_TX_PACKETS | + STATION_INFO_TX_FAILED); + sinfo->rx_packets = pktcnt.rx_good_pkt; + sinfo->rx_dropped_misc = pktcnt.rx_bad_pkt; + sinfo->tx_packets = pktcnt.tx_good_pkt; + sinfo->tx_failed = pktcnt.tx_bad_pkt; + } + get_station_err: if (err && (err != -ETIMEDOUT) && (err != -EIO)) { /* Disconnect due to zero BSSID or error to get RSSI */ -- cgit v1.1 From 2fa1f1c9b55a1494462d4e0d8c591ac0aecc9f78 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 2 Aug 2012 11:22:05 -0700 Subject: USB: gadget: f_audio_source: Fix error handling and increase packet size Fixes watchdog reset on USB disconnect Larger packet size fixes performance in fullspeed mode Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_audio_source.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 23a7511..ba8e007 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -22,11 +22,9 @@ #include #define SAMPLE_RATE 44100 -/* Each frame is two 16 bit integers (one per channel) */ -#define BYTES_PER_FRAME 4 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 256 +#define IN_EP_MAX_PACKET_SIZE 384 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 @@ -414,7 +412,7 @@ static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) audio_req_put(audio, req); - if (!audio->buffer_start) + if (!audio->buffer_start || req->status) return; audio->period_offset += req->actual; -- cgit v1.1 From a1bc297a906963d7a19f98de051fea7409245ba0 Mon Sep 17 00:00:00 2001 From: Bernhard Walle Date: Sun, 26 Feb 2012 18:35:18 +0100 Subject: scripts/Kbuild.include: Fix portability problem of "echo -e" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "echo -e" is a GNU extension. When cross-compiling the kernel on a BSD-like operating system (Mac OS X in my case), this doesn't work. One could install a GNU version of echo, put that in the $PATH before the system echo and use "/usr/bin/env echo", but the solution with printf is simpler. Since it is no disadvantage on Linux, I hope that gets accepted even if cross-compiling the Linux kernel on another Unix operating system is quite a rare use case. Signed-off-by: Bernhard Walle Andreas Bießmann Signed-off-by: Michal Marek --- scripts/Kbuild.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index be39cd1..ce8844f 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -100,7 +100,7 @@ as-option = $(call try-run,\ # Usage: cflags-y += $(call as-instr,instr,option1,option2) as-instr = $(call try-run,\ - /bin/echo -e "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -xassembler -o "$$TMP" -,$(2),$(3)) + printf "%b\n" "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -xassembler -o "$$TMP" -,$(2),$(3)) # cc-option # Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586) -- cgit v1.1 From e15cb9927c7a8e16b4f95918d07d2e55f23adf6c Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 13 Aug 2012 11:37:43 -0700 Subject: net: wireless: bcmdhd: Use correct time from boot for TSF Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index b10a5ef..87d971a 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -4753,11 +4753,11 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) signal = notif_bss_info->rssi * 100; if (!mgmt->u.probe_resp.timestamp) { - struct timeval tv; + struct timespec ts; - do_gettimeofday(&tv); - mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec * 1000000) - + tv.tv_usec; + get_monotonic_boottime(&ts); + mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec * 1000000) + + ts.tv_nsec / 1000; } cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, -- cgit v1.1 From 9a6bc3422c1f8f5cd599a851d0804bb22c193c2c Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Mon, 13 Aug 2012 11:38:49 -0700 Subject: netfilter: xt_IDLETIMER: Rename INTERFACE to LABEL in netlink notification. Signed-off-by: Ashish Sharma --- net/netfilter/xt_IDLETIMER.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 43a16d0..bb4c372 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -68,15 +68,15 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; -static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) +static void notify_netlink_uevent(const char *label, struct idletimer_tg *timer) { - char iface_msg[NLMSG_MAX_SIZE]; + char label_msg[NLMSG_MAX_SIZE]; char state_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, NULL }; + char *envp[] = { label_msg, state_msg, NULL }; int res; - res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", - iface); + res = snprintf(label_msg, NLMSG_MAX_SIZE, "LABEL=%s", + label); if (NLMSG_MAX_SIZE <= res) { pr_err("message too long (%d)", res); return; @@ -87,7 +87,7 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); return; } - pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + pr_debug("putting nlmsg: <%s> <%s>\n", label_msg, state_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; -- cgit v1.1 From 11c327f59840a98bad0d57f259065faf7d22752a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 13 Aug 2012 13:59:22 -0700 Subject: net: wireless: bcm4329: Remove obsolete files Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/include/bcmspibrcm.h | 134 ------------------- drivers/net/wireless/bcm4329/include/spid.h | 153 ---------------------- 2 files changed, 287 deletions(-) delete mode 100644 drivers/net/wireless/bcm4329/include/bcmspibrcm.h delete mode 100644 drivers/net/wireless/bcm4329/include/spid.h diff --git a/drivers/net/wireless/bcm4329/include/bcmspibrcm.h b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h deleted file mode 100644 index 9dce878..0000000 --- a/drivers/net/wireless/bcm4329/include/bcmspibrcm.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * SD-SPI Protocol Conversion - BCMSDH->gSPI Translation Layer - * - * Copyright (C) 2010, Broadcom Corporation - * All Rights Reserved. - * - * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; - * the contents of this file may not be disclosed to third parties, copied - * or duplicated in any form, in whole or in part, without the prior - * written permission of Broadcom Corporation. - * - * $Id: bcmspibrcm.h,v 1.4.4.1.4.3.6.1 2008/09/27 17:03:25 Exp $ - */ - -/* global msglevel for debug messages - bitvals come from sdiovar.h */ - -#define sd_err(x) -#define sd_trace(x) -#define sd_info(x) -#define sd_debug(x) -#define sd_data(x) -#define sd_ctrl(x) - -#define sd_log(x) - -#define SDIOH_ASSERT(exp) \ - do { if (!(exp)) \ - printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ - } while (0) - -#define BLOCK_SIZE_F1 64 -#define BLOCK_SIZE_F2 2048 -#define BLOCK_SIZE_F3 2048 - -/* internal return code */ -#define SUCCESS 0 -#undef ERROR -#define ERROR 1 -#define ERROR_UF 2 -#define ERROR_OF 3 - -/* private bus modes */ -#define SDIOH_MODE_SPI 0 - -#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ -#define USE_MULTIBLOCK 0x4 - -struct sdioh_info { - uint cfg_bar; /* pci cfg address for bar */ - uint32 caps; /* cached value of capabilities reg */ - void *bar0; /* BAR0 for PCI Device */ - osl_t *osh; /* osh handler */ - void *controller; /* Pointer to SPI Controller's private data struct */ - - uint lockcount; /* nest count of spi_lock() calls */ - bool client_intr_enabled; /* interrupt connnected flag */ - bool intr_handler_valid; /* client driver interrupt handler valid */ - sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ - void *intr_handler_arg; /* argument to call interrupt handler */ - bool initialized; /* card initialized */ - uint32 target_dev; /* Target device ID */ - uint32 intmask; /* Current active interrupts */ - void *sdos_info; /* Pointer to per-OS private data */ - - uint32 controller_type; /* Host controller type */ - uint8 version; /* Host Controller Spec Compliance Version */ - uint irq; /* Client irq */ - uint32 intrcount; /* Client interrupts */ - uint32 local_intrcount; /* Controller interrupts */ - bool host_init_done; /* Controller initted */ - bool card_init_done; /* Client SDIO interface initted */ - bool polled_mode; /* polling for command completion */ - - bool sd_use_dma; /* DMA on CMD53 */ - bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ - /* Must be on for sd_multiblock to be effective */ - bool use_client_ints; /* If this is false, make sure to restore */ - /* polling hack in wl_linux.c:wl_timer() */ - int adapter_slot; /* Maybe dealing with multiple slots/controllers */ - int sd_mode; /* SD1/SD4/SPI */ - int client_block_size[SPI_MAX_IOFUNCS]; /* Blocksize */ - uint32 data_xfer_count; /* Current transfer */ - uint16 card_rca; /* Current Address */ - uint8 num_funcs; /* Supported funcs on client */ - uint32 card_dstatus; /* 32bit device status */ - uint32 com_cis_ptr; - uint32 func_cis_ptr[SPI_MAX_IOFUNCS]; - void *dma_buf; - ulong dma_phys; - int r_cnt; /* rx count */ - int t_cnt; /* tx_count */ - uint32 wordlen; /* host processor 16/32bits */ - uint32 prev_fun; - uint32 chip; - uint32 chiprev; - bool resp_delay_all; - bool dwordmode; - - struct spierrstats_t spierrstats; -}; - -/************************************************************ - * Internal interfaces: per-port references into bcmspibrcm.c - */ - -/* Global message bits */ -extern uint sd_msglevel; - -/************************************************************** - * Internal interfaces: bcmspibrcm.c references to per-port code - */ - -/* Interrupt (de)registration routines */ -extern int spi_register_irq(sdioh_info_t *sd, uint irq); -extern void spi_free_irq(uint irq, sdioh_info_t *sd); - -/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ -extern void spi_lock(sdioh_info_t *sd); -extern void spi_unlock(sdioh_info_t *sd); - -/* Allocate/init/free per-OS private data */ -extern int spi_osinit(sdioh_info_t *sd); -extern void spi_osfree(sdioh_info_t *sd); - -#define SPI_RW_FLAG_M BITFIELD_MASK(1) /* Bit [31] - R/W Command Bit */ -#define SPI_RW_FLAG_S 31 -#define SPI_ACCESS_M BITFIELD_MASK(1) /* Bit [30] - Fixed/Incr Access */ -#define SPI_ACCESS_S 30 -#define SPI_FUNCTION_M BITFIELD_MASK(2) /* Bit [29:28] - Function Number */ -#define SPI_FUNCTION_S 28 -#define SPI_REG_ADDR_M BITFIELD_MASK(17) /* Bit [27:11] - Address */ -#define SPI_REG_ADDR_S 11 -#define SPI_LEN_M BITFIELD_MASK(11) /* Bit [10:0] - Packet length */ -#define SPI_LEN_S 0 diff --git a/drivers/net/wireless/bcm4329/include/spid.h b/drivers/net/wireless/bcm4329/include/spid.h deleted file mode 100644 index c740296..0000000 --- a/drivers/net/wireless/bcm4329/include/spid.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * SPI device spec header file - * - * Copyright (C) 2010, Broadcom Corporation - * All Rights Reserved. - * - * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; - * the contents of this file may not be disclosed to third parties, copied - * or duplicated in any form, in whole or in part, without the prior - * written permission of Broadcom Corporation. - * - * $Id: spid.h,v 1.7.10.1.16.3 2009/04/09 19:23:14 Exp $ - */ - -#ifndef _SPI_H -#define _SPI_H - -/* - * Brcm SPI Device Register Map. - * - */ - -typedef volatile struct { - uint8 config; /* 0x00, len, endian, clock, speed, polarity, wakeup */ - uint8 response_delay; /* 0x01, read response delay in bytes (corerev < 3) */ - uint8 status_enable; /* 0x02, status-enable, intr with status, response_delay - * function selection, command/data error check - */ - uint8 reset_bp; /* 0x03, reset on wlan/bt backplane reset (corerev >= 1) */ - uint16 intr_reg; /* 0x04, Intr status register */ - uint16 intr_en_reg; /* 0x06, Intr mask register */ - uint32 status_reg; /* 0x08, RO, Status bits of last spi transfer */ - uint16 f1_info_reg; /* 0x0c, RO, enabled, ready for data transfer, blocksize */ - uint16 f2_info_reg; /* 0x0e, RO, enabled, ready for data transfer, blocksize */ - uint16 f3_info_reg; /* 0x10, RO, enabled, ready for data transfer, blocksize */ - uint32 test_read; /* 0x14, RO 0xfeedbead signature */ - uint32 test_rw; /* 0x18, RW */ - uint8 resp_delay_f0; /* 0x1c, read resp delay bytes for F0 (corerev >= 3) */ - uint8 resp_delay_f1; /* 0x1d, read resp delay bytes for F1 (corerev >= 3) */ - uint8 resp_delay_f2; /* 0x1e, read resp delay bytes for F2 (corerev >= 3) */ - uint8 resp_delay_f3; /* 0x1f, read resp delay bytes for F3 (corerev >= 3) */ -} spi_regs_t; - -/* SPI device register offsets */ -#define SPID_CONFIG 0x00 -#define SPID_RESPONSE_DELAY 0x01 -#define SPID_STATUS_ENABLE 0x02 -#define SPID_RESET_BP 0x03 /* (corerev >= 1) */ -#define SPID_INTR_REG 0x04 /* 16 bits - Interrupt status */ -#define SPID_INTR_EN_REG 0x06 /* 16 bits - Interrupt mask */ -#define SPID_STATUS_REG 0x08 /* 32 bits */ -#define SPID_F1_INFO_REG 0x0C /* 16 bits */ -#define SPID_F2_INFO_REG 0x0E /* 16 bits */ -#define SPID_F3_INFO_REG 0x10 /* 16 bits */ -#define SPID_TEST_READ 0x14 /* 32 bits */ -#define SPID_TEST_RW 0x18 /* 32 bits */ -#define SPID_RESP_DELAY_F0 0x1c /* 8 bits (corerev >= 3) */ -#define SPID_RESP_DELAY_F1 0x1d /* 8 bits (corerev >= 3) */ -#define SPID_RESP_DELAY_F2 0x1e /* 8 bits (corerev >= 3) */ -#define SPID_RESP_DELAY_F3 0x1f /* 8 bits (corerev >= 3) */ - -/* Bit masks for SPID_CONFIG device register */ -#define WORD_LENGTH_32 0x1 /* 0/1 16/32 bit word length */ -#define ENDIAN_BIG 0x2 /* 0/1 Little/Big Endian */ -#define CLOCK_PHASE 0x4 /* 0/1 clock phase delay */ -#define CLOCK_POLARITY 0x8 /* 0/1 Idle state clock polarity is low/high */ -#define HIGH_SPEED_MODE 0x10 /* 1/0 High Speed mode / Normal mode */ -#define INTR_POLARITY 0x20 /* 1/0 Interrupt active polarity is high/low */ -#define WAKE_UP 0x80 /* 0/1 Wake-up command from Host to WLAN */ - -/* Bit mask for SPID_RESPONSE_DELAY device register */ -#define RESPONSE_DELAY_MASK 0xFF /* Configurable rd response delay in multiples of 8 bits */ - -/* Bit mask for SPID_STATUS_ENABLE device register */ -#define STATUS_ENABLE 0x1 /* 1/0 Status sent/not sent to host after read/write */ -#define INTR_WITH_STATUS 0x2 /* 0/1 Do-not / do-interrupt if status is sent */ -#define RESP_DELAY_ALL 0x4 /* Applicability of resp delay to F1 or all func's read */ -#define DWORD_PKT_LEN_EN 0x8 /* Packet len denoted in dwords instead of bytes */ -#define CMD_ERR_CHK_EN 0x20 /* Command error check enable */ -#define DATA_ERR_CHK_EN 0x40 /* Data error check enable */ - -/* Bit mask for SPID_RESET_BP device register */ -#define RESET_ON_WLAN_BP_RESET 0x4 /* enable reset for WLAN backplane */ -#define RESET_ON_BT_BP_RESET 0x8 /* enable reset for BT backplane */ -#define RESET_SPI 0x80 /* reset the above enabled logic */ - -/* Bit mask for SPID_INTR_REG device register */ -#define DATA_UNAVAILABLE 0x0001 /* Requested data not available; Clear by writing a "1" */ -#define F2_F3_FIFO_RD_UNDERFLOW 0x0002 -#define F2_F3_FIFO_WR_OVERFLOW 0x0004 -#define COMMAND_ERROR 0x0008 /* Cleared by writing 1 */ -#define DATA_ERROR 0x0010 /* Cleared by writing 1 */ -#define F2_PACKET_AVAILABLE 0x0020 -#define F3_PACKET_AVAILABLE 0x0040 -#define F1_OVERFLOW 0x0080 /* Due to last write. Bkplane has pending write requests */ -#define MISC_INTR0 0x0100 -#define MISC_INTR1 0x0200 -#define MISC_INTR2 0x0400 -#define MISC_INTR3 0x0800 -#define MISC_INTR4 0x1000 -#define F1_INTR 0x2000 -#define F2_INTR 0x4000 -#define F3_INTR 0x8000 - -/* Bit mask for 32bit SPID_STATUS_REG device register */ -#define STATUS_DATA_NOT_AVAILABLE 0x00000001 -#define STATUS_UNDERFLOW 0x00000002 -#define STATUS_OVERFLOW 0x00000004 -#define STATUS_F2_INTR 0x00000008 -#define STATUS_F3_INTR 0x00000010 -#define STATUS_F2_RX_READY 0x00000020 -#define STATUS_F3_RX_READY 0x00000040 -#define STATUS_HOST_CMD_DATA_ERR 0x00000080 -#define STATUS_F2_PKT_AVAILABLE 0x00000100 -#define STATUS_F2_PKT_LEN_MASK 0x000FFE00 -#define STATUS_F2_PKT_LEN_SHIFT 9 -#define STATUS_F3_PKT_AVAILABLE 0x00100000 -#define STATUS_F3_PKT_LEN_MASK 0xFFE00000 -#define STATUS_F3_PKT_LEN_SHIFT 21 - -/* Bit mask for 16 bits SPID_F1_INFO_REG device register */ -#define F1_ENABLED 0x0001 -#define F1_RDY_FOR_DATA_TRANSFER 0x0002 -#define F1_MAX_PKT_SIZE 0x01FC - -/* Bit mask for 16 bits SPID_F2_INFO_REG device register */ -#define F2_ENABLED 0x0001 -#define F2_RDY_FOR_DATA_TRANSFER 0x0002 -#define F2_MAX_PKT_SIZE 0x3FFC - -/* Bit mask for 16 bits SPID_F3_INFO_REG device register */ -#define F3_ENABLED 0x0001 -#define F3_RDY_FOR_DATA_TRANSFER 0x0002 -#define F3_MAX_PKT_SIZE 0x3FFC - -/* Bit mask for 32 bits SPID_TEST_READ device register read in 16bit LE mode */ -#define TEST_RO_DATA_32BIT_LE 0xFEEDBEAD - -/* Maximum number of I/O funcs */ -#define SPI_MAX_IOFUNCS 4 - -#define SPI_MAX_PKT_LEN (2048*4) - -/* Misc defines */ -#define SPI_FUNC_0 0 -#define SPI_FUNC_1 1 -#define SPI_FUNC_2 2 -#define SPI_FUNC_3 3 - -#define WAIT_F2RXFIFORDY 100 -#define WAIT_F2RXFIFORDY_DELAY 20 - -#endif /* _SPI_H */ -- cgit v1.1 From efadaa2555535c0e0f345d071da4d547dc3f9abc Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 16 Aug 2012 10:34:01 -0700 Subject: mmc: Make sure host is disabled on suspend Change-Id: Ie0bf2004e173cef8dad66722a152658d7727ab65 Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8536095..4608634 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1851,6 +1851,7 @@ int mmc_suspend_host(struct mmc_host *host) host->pm_flags = 0; err = 0; } + flush_delayed_work(&host->disable); } mmc_bus_put(host); -- cgit v1.1 From 44099818374d63aa4b892f4ff700f97ff7ec236f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:13 +0200 Subject: HID: uhid: introduce user-space I/O driver support for HID This adds a dummy driver that will support user-space I/O drivers for the HID subsystem. This allows to write transport-level drivers like USB-HID and Bluetooth-HID in user-space. Low-Energy Bluetooth needs this to feed HID data that is parsed in user-space back into the kernel. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 21 +++++++++++++ drivers/hid/Makefile | 1 + drivers/hid/uhid.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 drivers/hid/uhid.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7e0acf4..c5fd7f7 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -50,6 +50,27 @@ config HIDRAW If unsure, say Y. +config UHID + tristate "User-space I/O driver support for HID subsystem" + depends on HID + default n + ---help--- + Say Y here if you want to provide HID I/O Drivers from user-space. + This allows to write I/O drivers in user-space and feed the data from + the device into the kernel. The kernel parses the HID reports, loads the + corresponding HID Device Driver or provides input devices on top of your + user-space device. + + This driver cannot be used to parse HID-reports in user-space and write + special HID-drivers. You should use hidraw for that. + Instead, this driver allows to write the transport-layer driver in + user-space like USB-HID and Bluetooth-HID do in kernel-space. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called uhid. + source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f8cc4ea..5a255e0 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS endif obj-$(CONFIG_HID) += hid.o +obj-$(CONFIG_UHID) += uhid.o hid-$(CONFIG_HIDRAW) += hidraw.o diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c new file mode 100644 index 0000000..5b02d6c --- /dev/null +++ b/drivers/hid/uhid.c @@ -0,0 +1,88 @@ +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * 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 the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UHID_NAME "uhid" + +static struct miscdevice uhid_misc; + +static int uhid_char_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int uhid_char_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t uhid_char_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t uhid_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static unsigned int uhid_char_poll(struct file *file, poll_table *wait) +{ + return 0; +} + +static const struct file_operations uhid_fops = { + .owner = THIS_MODULE, + .open = uhid_char_open, + .release = uhid_char_release, + .read = uhid_char_read, + .write = uhid_char_write, + .poll = uhid_char_poll, + .llseek = no_llseek, +}; + +static struct miscdevice uhid_misc = { + .fops = &uhid_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = UHID_NAME, +}; + +static int __init uhid_init(void) +{ + return misc_register(&uhid_misc); +} + +static void __exit uhid_exit(void) +{ + misc_deregister(&uhid_misc); +} + +module_init(uhid_init); +module_exit(uhid_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann "); +MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); -- cgit v1.1 From ef5006bca12286b1b92d0da4ab0d65973dc1f74d Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:14 +0200 Subject: HID: uhid: add internal message buffer When receiving messages from the HID subsystem, we need to process them and store them in an internal buffer so user-space can read() on the char device to retrieve the messages. This adds a static buffer for 32 messages to each uhid device. Each message is dynamically allocated so the uhid_device structure does not get too big. uhid_queue() adds a message to the buffer. If the buffer is full, the message is discarded. uhid_queue_event() is an helper for messages without payload. This also adds a public header: uhid.h. It contains the declarations for the user-space API. It is built around "struct uhid_event" which contains a type field which specifies the event type and each event can then add a variable-length payload. For now, there is only a dummy event but later patches will add new event types and payloads. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/Kbuild | 1 + include/linux/uhid.h | 33 ++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 include/linux/uhid.h diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5b02d6c..05ef4b0 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -25,16 +25,81 @@ #include #define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct hid_device *hid; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; +}; static struct miscdevice uhid_misc; +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + + file->private_data = uhid; + nonseekable_open(inode, file); + return 0; } static int uhid_char_release(struct inode *inode, struct file *file) { + struct uhid_device *uhid = file->private_data; + unsigned int i; + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + return 0; } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 01f6362..2d6b7ecb 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -366,6 +366,7 @@ header-y += tty.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h +header-y += uhid.h header-y += uinput.h header-y += uio.h header-y += ultrasound.h diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 0000000..16b786a --- /dev/null +++ b/include/linux/uhid.h @@ -0,0 +1,33 @@ +#ifndef __UHID_H_ +#define __UHID_H_ + +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * 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 the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Public header for user-space communication. We try to keep every structure + * aligned but to be safe we also use __attribute__((__packed__)). Therefore, + * the communication should be ABI compatible even between architectures. + */ + +#include +#include + +enum uhid_event_type { + UHID_DUMMY, +}; + +struct uhid_event { + __u32 type; +} __attribute__((__packed__)); + +#endif /* __UHID_H_ */ -- cgit v1.1 From 191b03260fc0241e355e5cb200d5a9497e67e67c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:15 +0200 Subject: HID: uhid: allow poll()'ing on uhid devices As long as the internal buffer is not empty, we return POLLIN to user-space. uhid->head and uhid->tail are no atomics so the comparison may return inexact results. However, this doesn't matter here as user-space would need to poll() in two threads simultaneously to trigger this. And in this case it doesn't matter if a cached result is returned or the exact new result as user-space does not know which thread returns first from poll() and the following read(). So it is safe to compare the values without locking. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 05ef4b0..b1a477f 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -117,6 +117,13 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, static unsigned int uhid_char_poll(struct file *file, poll_table *wait) { + struct uhid_device *uhid = file->private_data; + + poll_wait(file, &uhid->waitq, wait); + + if (uhid->head != uhid->tail) + return POLLIN | POLLRDNORM; + return 0; } -- cgit v1.1 From b8dd3d95cafc815793eb7a380be493eb5382710a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:16 +0200 Subject: HID: uhid: implement read() on uhid devices User-space can use read() to get a single event from uhid devices. read() does never return multiple events. This allows us to extend the event structure and still keep backwards compatibility. If user-space wants to get multiple events in one syscall, they should use the readv()/writev() syscalls which are supported by uhid. This introduces a new lock which helps us synchronizing simultaneous reads from user-space. We also correctly return -EINVAL/-EFAULT only on errors and retry the read() when some other thread captured the event faster than we did. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index b1a477f..9386082 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -28,6 +28,7 @@ #define UHID_BUFSIZE 32 struct uhid_device { + struct mutex devlock; struct hid_device *hid; wait_queue_head_t waitq; @@ -81,6 +82,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) if (!uhid) return -ENOMEM; + mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); @@ -106,7 +108,49 @@ static int uhid_char_release(struct inode *inode, struct file *file) static ssize_t uhid_char_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + unsigned long flags; + size_t len; + + /* they need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + +try_again: + if (file->f_flags & O_NONBLOCK) { + if (uhid->head == uhid->tail) + return -EAGAIN; + } else { + ret = wait_event_interruptible(uhid->waitq, + uhid->head != uhid->tail); + if (ret) + return ret; + } + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + if (uhid->head == uhid->tail) { + mutex_unlock(&uhid->devlock); + goto try_again; + } else { + len = min(count, sizeof(**uhid->outq)); + if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) { + ret = -EFAULT; + } else { + kfree(uhid->outq[uhid->tail]); + uhid->outq[uhid->tail] = NULL; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; + spin_unlock_irqrestore(&uhid->qlock, flags); + } + } + + mutex_unlock(&uhid->devlock); + return ret ? ret : len; } static ssize_t uhid_char_write(struct file *file, const char __user *buffer, -- cgit v1.1 From d3444a842acc1113febe0ebade74fe18aa99dfee Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:17 +0200 Subject: HID: uhid: implement write() on uhid devices Similar to read() you can only write() a single event with one call to an uhid device. To write multiple events use writev() which is supported by uhid. We currently always return -EOPNOTSUPP but other events will be added in later patches. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 9386082..31e8379 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -30,6 +30,7 @@ struct uhid_device { struct mutex devlock; struct hid_device *hid; + struct uhid_event input_buf; wait_queue_head_t waitq; spinlock_t qlock; @@ -156,7 +157,35 @@ try_again: static ssize_t uhid_char_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + size_t len; + + /* we need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); + len = min(count, sizeof(uhid->input_buf)); + if (copy_from_user(&uhid->input_buf, buffer, len)) { + ret = -EFAULT; + goto unlock; + } + + switch (uhid->input_buf.type) { + default: + ret = -EOPNOTSUPP; + } + +unlock: + mutex_unlock(&uhid->devlock); + + /* return "count" not "len" to not confuse the caller */ + return ret ? ret : count; } static unsigned int uhid_char_poll(struct file *file, poll_table *wait) -- cgit v1.1 From 6b1a4c7c9a0839648d970d7fe1ecba9a31ca87c2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:18 +0200 Subject: HID: uhid: add UHID_CREATE and UHID_DESTROY events UHID_CREATE and UHID_DESTROY are used to create and destroy a device on an open uhid char-device. Internally, we allocate and register an HID device with the HID core and immediately start the device. From now on events may be received or sent to the device. The UHID_CREATE event has a payload similar to the data used by Bluetooth-HIDP when creating a new connection. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/uhid.h | 21 +++++++- 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 31e8379..61ee7cc 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -29,6 +29,11 @@ struct uhid_device { struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + struct hid_device *hid; struct uhid_event input_buf; @@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) return 0; } +static int uhid_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_stop(struct hid_device *hid) +{ +} + +static int uhid_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_close(struct hid_device *hid) +{ +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + return 0; +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + return 0; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + return 0; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + uhid->running = false; + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + uhid->running = false; file->private_data = uhid; nonseekable_open(inode, file); @@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file) struct uhid_device *uhid = file->private_data; unsigned int i; + uhid_dev_destroy(uhid); + for (i = 0; i < UHID_BUFSIZE; ++i) kfree(uhid->outq[i]); @@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, } switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 16b786a..8a493e6 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -23,11 +23,30 @@ #include enum uhid_event_type { - UHID_DUMMY, + UHID_CREATE, + UHID_DESTROY, }; +struct uhid_create_req { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + __u8 __user *rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; + + union { + struct uhid_create_req create; + } u; } __attribute__((__packed__)); #endif /* __UHID_H_ */ -- cgit v1.1 From e0ad83067d9899b8cf47fcfc0c68a5bacf6265ec Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:19 +0200 Subject: HID: uhid: allow feeding input data into uhid devices This adds a new event type UHID_INPUT which allows user-space to feed raw HID reports into the HID subsystem. We copy the data into kernel memory and directly feed it into the HID core. There is no error handling of the events couldn't be parsed so user-space should consider all events successfull unless read() returns an error. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 14 ++++++++++++++ include/linux/uhid.h | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 61ee7cc..3d1ebda 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -210,6 +210,17 @@ static int uhid_dev_destroy(struct uhid_device *uhid) return 0; } +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -321,6 +332,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_DESTROY: ret = uhid_dev_destroy(uhid); break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 8a493e6..6eb42be 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,7 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_INPUT, }; struct uhid_create_req { @@ -41,11 +42,19 @@ struct uhid_create_req { __u32 country; } __attribute__((__packed__)); +#define UHID_DATA_MAX 4096 + +struct uhid_input_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; + struct uhid_input_req input; } u; } __attribute__((__packed__)); -- cgit v1.1 From 66454600e60af2b35fb51f88006911532862ba25 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:20 +0200 Subject: HID: uhid: forward hid report-descriptor to hid core When the uhid_hid_parse callback is called we simply forward it to hid_parse_report() with the data that we got in the UHID_CREATE event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 3d1ebda..0d011db 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -106,7 +106,9 @@ static int uhid_hid_input(struct input_dev *input, unsigned int type, static int uhid_hid_parse(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); } static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, -- cgit v1.1 From 245fd8c6b76e8b082f6d233886d35c3e27e89d53 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:21 +0200 Subject: HID: uhid: add UHID_START and UHID_STOP events We send UHID_START and UHID_STOP events to user-space when the HID core starts/stops the device. This notifies user-space about driver readiness and data-I/O can start now. This directly forwards the callbacks from hid-core to user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 8 +++++++- include/linux/uhid.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0d011db..2e7f3a0 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -82,11 +82,17 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) static int uhid_hid_start(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); } static void uhid_hid_stop(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); } static int uhid_hid_open(struct hid_device *hid) diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 6eb42be..f8ce6f7 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,8 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_START, + UHID_STOP, UHID_INPUT, }; -- cgit v1.1 From e458139e9e88d4cf9450842c2f21b4e5451faba0 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:22 +0200 Subject: HID: uhid: forward open/close events to user-space HID core notifies us with *_open/*_close callbacks when there is an actual user of our device. We forward these to user-space so they can react on this. This allows user-space to skip I/O unless they receive an OPEN event. When they receive a CLOSE event they can stop I/O again to save energy. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 ++++++- include/linux/uhid.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 2e7f3a0..0226ba3f 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -97,11 +97,16 @@ static void uhid_hid_stop(struct hid_device *hid) static int uhid_hid_open(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); } static void uhid_hid_close(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); } static int uhid_hid_input(struct input_dev *input, unsigned int type, diff --git a/include/linux/uhid.h b/include/linux/uhid.h index f8ce6f7..351650b 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -27,6 +27,8 @@ enum uhid_event_type { UHID_DESTROY, UHID_START, UHID_STOP, + UHID_OPEN, + UHID_CLOSE, UHID_INPUT, }; -- cgit v1.1 From bd46634b0b2f18a8b6f338b4c041f563ba59514e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:23 +0200 Subject: HID: uhid: forward output request to user-space If the hid-driver wants to send standardized data to the device it uses a linux input_event. We forward this to the user-space transport-level driver so they can perform the requested action on the device. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 18 ++++++++++++++++++ include/linux/uhid.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0226ba3f..4dd693e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -112,6 +112,24 @@ static void uhid_hid_close(struct hid_device *hid) static int uhid_hid_input(struct input_dev *input, unsigned int type, unsigned int code, int value) { + struct hid_device *hid = input_get_drvdata(input); + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT_EV; + ev->u.output_ev.type = type; + ev->u.output_ev.code = code; + ev->u.output_ev.value = value; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 351650b..3fa4849 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT_EV, UHID_INPUT, }; @@ -53,12 +54,19 @@ struct uhid_input_req { __u16 size; } __attribute__((__packed__)); +struct uhid_output_ev_req { + __u16 type; + __u16 code; + __s32 value; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); -- cgit v1.1 From 715ee7c3e5ccfffe08089276c632109d45d97791 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:24 +0200 Subject: HID: uhid: forward raw output reports to user-space Some drivers that use non-standard HID features require raw output reports sent to the device. We now forward these requests directly to user-space so the transport-level driver can correctly send it to the device or handle it correspondingly. There is no way to signal back whether the transmission was successful, moreover, there might be lots of messages coming out from the driver flushing the output-queue. However, there is currently no driver that causes this so we are safe. If some drivers need to transmit lots of data this way, we need a method to synchronize this and can implement another UHID_OUTPUT_SYNC event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 34 +++++++++++++++++++++++++++++++++- include/linux/uhid.h | 14 ++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 4dd693e..421c492 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -149,7 +149,39 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; } static struct hid_ll_driver uhid_hid_driver = { diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 3fa4849..2c97255 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, }; @@ -49,11 +50,23 @@ struct uhid_create_req { #define UHID_DATA_MAX 4096 +enum uhid_report_type { + UHID_FEATURE_REPORT, + UHID_OUTPUT_REPORT, + UHID_INPUT_REPORT, +}; + struct uhid_input_req { __u8 data[UHID_DATA_MAX]; __u16 size; } __attribute__((__packed__)); +struct uhid_output_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; + __u8 rtype; +} __attribute__((__packed__)); + struct uhid_output_ev_req { __u16 type; __u16 code; @@ -66,6 +79,7 @@ struct uhid_event { union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_req output; struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); -- cgit v1.1 From c12e46fb7120994085bb045ee5a885baa631aeff Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:25 +0200 Subject: HID: uhid: implement feature requests HID standard allows sending a feature request to the device which is answered by an HID report. uhid implements this by sending a UHID_FEATURE event to user-space which then must answer with UHID_FEATURE_ANSWER. If it doesn't do this in a timely manner, the request is discarded silently. We serialize the feature requests, that is, there is always only a single active feature-request sent to user-space, other requests have to wait. HIDP and USB-HID do it the same way. Because we discard feature-requests silently, we must make sure to match a response to the corresponding request. We use sequence-IDs for this so user-space must copy the ID from the request into the answer. Feature-answers are ignored if they do not contain the same ID as the currently pending feature request. Internally, we must make sure that feature-requests are synchronized with UHID_DESTROY and close() events. We must not dead-lock when closing the HID device, either, so we have to use separate locks. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/uhid.h | 17 ++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 421c492..ea560bf 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -42,6 +42,12 @@ struct uhid_device { __u8 head; __u8 tail; struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; }; static struct miscdevice uhid_misc; @@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid) static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, __u8 *buf, size_t count, unsigned char rtype) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t len; + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; } static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, @@ -265,7 +348,11 @@ static int uhid_dev_destroy(struct uhid_device *uhid) if (!uhid->running) return -EINVAL; + /* clear "running" before setting "report_done" */ uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); hid_destroy_device(uhid->hid); kfree(uhid->rd_data); @@ -284,6 +371,31 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) return 0; } +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -293,9 +405,12 @@ static int uhid_char_open(struct inode *inode, struct file *file) return -ENOMEM; mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); uhid->running = false; + atomic_set(&uhid->report_done, 1); file->private_data = uhid; nonseekable_open(inode, file); @@ -398,6 +513,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_INPUT: ret = uhid_dev_input(uhid, &uhid->input_buf); break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 2c97255..9c6974f 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -32,6 +32,8 @@ enum uhid_event_type { UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, + UHID_FEATURE, + UHID_FEATURE_ANSWER, }; struct uhid_create_req { @@ -73,6 +75,19 @@ struct uhid_output_ev_req { __s32 value; } __attribute__((__packed__)); +struct uhid_feature_req { + __u32 id; + __u8 rnum; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_feature_answer_req { + __u32 id; + __u16 err; + __u16 size; + __u8 data[UHID_DATA_MAX]; +}; + struct uhid_event { __u32 type; @@ -81,6 +96,8 @@ struct uhid_event { struct uhid_input_req input; struct uhid_output_req output; struct uhid_output_ev_req output_ev; + struct uhid_feature_req feature; + struct uhid_feature_answer_req feature_answer; } u; } __attribute__((__packed__)); -- cgit v1.1 From bd76675614d475cc9cf91eaa9ed0d65831465d08 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:26 +0200 Subject: HID: uhid: add documentation This describes the protocol used by uhid for user-space applications. It describes the details like non-blocking I/O and readv/writev for multiple events per syscall. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- Documentation/hid/uhid.txt | 169 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 Documentation/hid/uhid.txt diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt new file mode 100644 index 0000000..4627c42 --- /dev/null +++ b/Documentation/hid/uhid.txt @@ -0,0 +1,169 @@ + UHID - User-space I/O driver support for HID subsystem + ======================================================== + +The HID subsystem needs two kinds of drivers. In this document we call them: + + 1. The "HID I/O Driver" is the driver that performs raw data I/O to the + low-level device. Internally, they register an hid_ll_driver structure with + the HID core. They perform device setup, read raw data from the device and + push it into the HID subsystem and they provide a callback so the HID + subsystem can send data to the device. + + 2. The "HID Device Driver" is the driver that parses HID reports and reacts on + them. There are generic drivers like "generic-usb" and "generic-bluetooth" + which adhere to the HID specification and provide the standardizes features. + But there may be special drivers and quirks for each non-standard device out + there. Internally, they use the hid_driver structure. + +Historically, the USB stack was the first subsystem to provide an HID I/O +Driver. However, other standards like Bluetooth have adopted the HID specs and +may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O +Drivers in user-space and feed the data into the kernel HID-subsystem. + +This allows user-space to operate on the same level as USB-HID, Bluetooth-HID +and similar. It does not provide a way to write HID Device Drivers, though. Use +hidraw for this purpose. + +There is an example user-space application in ./samples/uhid/uhid-example.c + +The UHID API +------------ + +UHID is accessed through a character misc-device. The minor-number is allocated +dynamically so you need to rely on udev (or similar) to create the device node. +This is /dev/uhid by default. + +If a new device is detected by your HID I/O Driver and you want to register this +device with the HID subsystem, then you need to open /dev/uhid once for each +device you want to register. All further communication is done by read()'ing or +write()'ing "struct uhid_event" objects. Non-blocking operations are supported +by setting O_NONBLOCK. + +struct uhid_event { + __u32 type; + union { + struct uhid_create_req create; + struct uhid_data_req data; + ... + } u; +}; + +The "type" field contains the ID of the event. Depending on the ID different +payloads are sent. You must not split a single event across multiple read()'s or +multiple write()'s. A single event must always be sent as a whole. Furthermore, +only a single event can be sent per read() or write(). Pending data is ignored. +If you want to handle multiple events in a single syscall, then use vectored +I/O with readv()/writev(). + +The first thing you should do is sending an UHID_CREATE event. This will +register the device. UHID will respond with an UHID_START event. You can now +start sending data to and reading data from UHID. However, unless UHID sends the +UHID_OPEN event, the internally attached HID Device Driver has no user attached. +That is, you might put your device asleep unless you receive the UHID_OPEN +event. If you receive the UHID_OPEN event, you should start I/O. If the last +user closes the HID device, you will receive an UHID_CLOSE event. This may be +followed by an UHID_OPEN event again and so on. There is no need to perform +reference-counting in user-space. That is, you will never receive multiple +UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs +ref-counting for you. +You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even +though the device may have no users. + +If you want to send data to the HID subsystem, you send an HID_INPUT event with +your raw data payload. If the kernel wants to send data to the device, you will +read an UHID_OUTPUT or UHID_OUTPUT_EV event. + +If your device disconnects, you should send an UHID_DESTROY event. This will +unregister the device. You can now send UHID_CREATE again to register a new +device. +If you close() the fd, the device is automatically unregistered and destroyed +internally. + +write() +------- +write() allows you to modify the state of the device and feed input data into +the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and +UHID_INPUT. The kernel will parse the event immediately and if the event ID is +not supported, it will return -EOPNOTSUPP. If the payload is invalid, then +-EINVAL is returned, otherwise, the amount of data that was read is returned and +the request was handled successfully. + + UHID_CREATE: + This creates the internal HID device. No I/O is possible until you send this + event to the kernel. The payload is of type struct uhid_create_req and + contains information about your device. You can start I/O now. + + UHID_DESTROY: + This destroys the internal HID device. No further I/O will be accepted. There + may still be pending messages that you can receive with read() but no further + UHID_INPUT events can be sent to the kernel. + You can create a new device by sending UHID_CREATE again. There is no need to + reopen the character device. + + UHID_INPUT: + You must send UHID_CREATE before sending input to the kernel! This event + contains a data-payload. This is the raw data that you read from your device. + The kernel will parse the HID reports and react on it. + + UHID_FEATURE_ANSWER: + If you receive a UHID_FEATURE request you must answer with this request. You + must copy the "id" field from the request into the answer. Set the "err" field + to 0 if no error occured or to EIO if an I/O error occurred. + If "err" is 0 then you should fill the buffer of the answer with the results + of the feature request and set "size" correspondingly. + +read() +------ +read() will return a queued ouput report. These output reports can be of type +UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No +reaction is required to any of them but you should handle them according to your +needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. + + UHID_START: + This is sent when the HID device is started. Consider this as an answer to + UHID_CREATE. This is always the first event that is sent. + + UHID_STOP: + This is sent when the HID device is stopped. Consider this as an answer to + UHID_DESTROY. + If the kernel HID device driver closes the device manually (that is, you + didn't send UHID_DESTROY) then you should consider this device closed and send + an UHID_DESTROY event. You may want to reregister your device, though. This is + always the last message that is sent to you unless you reopen the device with + UHID_CREATE. + + UHID_OPEN: + This is sent when the HID device is opened. That is, the data that the HID + device provides is read by some other process. You may ignore this event but + it is useful for power-management. As long as you haven't received this event + there is actually no other process that reads your data so there is no need to + send UHID_INPUT events to the kernel. + + UHID_CLOSE: + This is sent when there are no more processes which read the HID data. It is + the counterpart of UHID_OPEN and you may as well ignore this event. + + UHID_OUTPUT: + This is sent if the HID device driver wants to send raw data to the I/O + device. You should read the payload and forward it to the device. The payload + is of type "struct uhid_data_req". + This may be received even though you haven't received UHID_OPEN, yet. + + UHID_OUTPUT_EV: + Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This + is called for force-feedback, LED or similar events which are received through + an input device by the HID subsystem. You should convert this into raw reports + and send them to your device similar to events of type UHID_OUTPUT. + + UHID_FEATURE: + This event is sent if the kernel driver wants to perform a feature request as + described in the HID specs. The report-type and report-number are available in + the payload. + The kernel serializes feature requests so there will never be two in parallel. + However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5 + seconds, then the requests will be dropped and a new one might be sent. + Therefore, the payload also contains an "id" field that identifies every + request. + +Document by: + David Herrmann -- cgit v1.1 From 9634f8109f89f26383c26c4b3781f5dd8599d365 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:27 +0200 Subject: HID: uhid: add example program This adds an example user-space program that emulates a 3 button mouse with wheel. It detects keyboard presses and moves the mouse accordingly. It register a fake HID device to feed the raw HID reports into the kernel. In this example, you could use uinput to get the same result, but this shows how to get the same behavior with uhid so you don't need HID parsers in user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- samples/uhid/Makefile | 10 ++ samples/uhid/uhid-example.c | 381 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+) create mode 100644 samples/uhid/Makefile create mode 100644 samples/uhid/uhid-example.c diff --git a/samples/uhid/Makefile b/samples/uhid/Makefile new file mode 100644 index 0000000..c95a696 --- /dev/null +++ b/samples/uhid/Makefile @@ -0,0 +1,10 @@ +# kbuild trick to avoid linker error. Can be omitted if a module is built. +obj- := dummy.o + +# List of programs to build +hostprogs-y := uhid-example + +# Tell kbuild to always build the programs +always := $(hostprogs-y) + +HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c new file mode 100644 index 0000000..03ce3c0 --- /dev/null +++ b/samples/uhid/uhid-example.c @@ -0,0 +1,381 @@ +/* + * UHID Example + * + * Copyright (c) 2012 David Herrmann + * + * The code may be used by anyone for any purpose, + * and can serve as a starting point for developing + * applications using uhid. + */ + +/* UHID Example + * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this + * program as root and then use the following keys to control the mouse: + * q: Quit the application + * 1: Toggle left button (down, up, ...) + * 2: Toggle right button + * 3: Toggle middle button + * a: Move mouse left + * d: Move mouse right + * w: Move mouse up + * s: Move mouse down + * r: Move wheel up + * f: Move wheel down + * + * If uhid is not available as /dev/uhid, then you can pass a different path as + * first argument. + * If is not installed in /usr, then compile this with: + * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c + * And ignore the warning about kernel headers. However, it is recommended to + * use the installed uhid.h if available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* HID Report Desciptor + * We emulate a basic 3 button mouse with wheel. This is the report-descriptor + * as the kernel will parse it: + * + * INPUT[INPUT] + * Field(0) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * Button.0001 + * Button.0002 + * Button.0003 + * Logical Minimum(0) + * Logical Maximum(1) + * Report Size(1) + * Report Count(3) + * Report Offset(0) + * Flags( Variable Absolute ) + * Field(1) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * GenericDesktop.X + * GenericDesktop.Y + * GenericDesktop.Wheel + * Logical Minimum(-128) + * Logical Maximum(127) + * Report Size(8) + * Report Count(3) + * Report Offset(8) + * Flags( Variable Relative ) + * + * This is the mapping that we expect: + * Button.0001 ---> Key.LeftBtn + * Button.0002 ---> Key.RightBtn + * Button.0003 ---> Key.MiddleBtn + * GenericDesktop.X ---> Relative.X + * GenericDesktop.Y ---> Relative.Y + * GenericDesktop.Wheel ---> Relative.Wheel + * + * This information can be verified by reading /sys/kernel/debug/hid//rdesc + * This file should print the same information as showed above. + */ + +static unsigned char rdesc[] = { + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, + 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, + 0x81, 0x06, 0xc0, 0xc0, +}; + +static int uhid_write(int fd, const struct uhid_event *ev) +{ + ssize_t ret; + + ret = write(fd, ev, sizeof(*ev)); + if (ret < 0) { + fprintf(stderr, "Cannot write to uhid: %m\n"); + return -errno; + } else if (ret != sizeof(*ev)) { + fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int create(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char*)ev.u.create.name, "test-uhid-device"); + ev.u.create.rd_data = rdesc; + ev.u.create.rd_size = sizeof(rdesc); + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = 0x15d9; + ev.u.create.product = 0x0a37; + ev.u.create.version = 0; + ev.u.create.country = 0; + + return uhid_write(fd, &ev); +} + +static void destroy(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +static int event(int fd) +{ + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %m\n"); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + fprintf(stderr, "UHID_START from uhid-dev\n"); + break; + case UHID_STOP: + fprintf(stderr, "UHID_STOP from uhid-dev\n"); + break; + case UHID_OPEN: + fprintf(stderr, "UHID_OPEN from uhid-dev\n"); + break; + case UHID_CLOSE: + fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); + break; + case UHID_OUTPUT: + fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + break; + case UHID_OUTPUT_EV: + fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +static bool btn1_down; +static bool btn2_down; +static bool btn3_down; +static signed char abs_hor; +static signed char abs_ver; +static signed char wheel; + +static int send_event(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = 4; + + if (btn1_down) + ev.u.input.data[0] |= 0x1; + if (btn2_down) + ev.u.input.data[0] |= 0x2; + if (btn3_down) + ev.u.input.data[0] |= 0x4; + + ev.u.input.data[1] = abs_hor; + ev.u.input.data[2] = abs_ver; + ev.u.input.data[3] = wheel; + + return uhid_write(fd, &ev); +} + +static int keyboard(int fd) +{ + char buf[128]; + ssize_t ret, i; + + ret = read(STDIN_FILENO, buf, sizeof(buf)); + if (ret == 0) { + fprintf(stderr, "Read HUP on stdin\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read stdin: %m\n"); + return -errno; + } + + for (i = 0; i < ret; ++i) { + switch (buf[i]) { + case '1': + btn1_down = !btn1_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '2': + btn2_down = !btn2_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '3': + btn3_down = !btn3_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case 'a': + abs_hor = -20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'd': + abs_hor = 20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'w': + abs_ver = -20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 's': + abs_ver = 20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 'r': + wheel = 1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'f': + wheel = -1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'q': + return -ECANCELED; + default: + fprintf(stderr, "Invalid input: %c\n", buf[i]); + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + int fd; + const char *path = "/dev/uhid"; + struct pollfd pfds[2]; + int ret; + struct termios state; + + ret = tcgetattr(STDIN_FILENO, &state); + if (ret) { + fprintf(stderr, "Cannot get tty state\n"); + } else { + state.c_lflag &= ~ICANON; + state.c_cc[VMIN] = 1; + ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); + if (ret) + fprintf(stderr, "Cannot set tty state\n"); + } + + if (argc >= 2) { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); + return EXIT_SUCCESS; + } else { + path = argv[1]; + } + } + + fprintf(stderr, "Open uhid-cdev %s\n", path); + fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); + return EXIT_FAILURE; + } + + fprintf(stderr, "Create uhid device\n"); + ret = create(fd); + if (ret) { + close(fd); + return EXIT_FAILURE; + } + + pfds[0].fd = STDIN_FILENO; + pfds[0].events = POLLIN; + pfds[1].fd = fd; + pfds[1].events = POLLIN; + + fprintf(stderr, "Press 'q' to quit...\n"); + while (1) { + ret = poll(pfds, 2, -1); + if (ret < 0) { + fprintf(stderr, "Cannot poll for fds: %m\n"); + break; + } + if (pfds[0].revents & POLLHUP) { + fprintf(stderr, "Received HUP on stdin\n"); + break; + } + if (pfds[1].revents & POLLHUP) { + fprintf(stderr, "Received HUP on uhid-cdev\n"); + break; + } + + if (pfds[0].revents & POLLIN) { + ret = keyboard(fd); + if (ret) + break; + } + if (pfds[1].revents & POLLIN) { + ret = event(fd); + if (ret) + break; + } + } + + fprintf(stderr, "Destroy uhid device\n"); + destroy(fd); + return EXIT_SUCCESS; +} -- cgit v1.1 From d33f92d28b8080264a1909568c719fd122e48d8a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:28 +0200 Subject: MAINTAINERS: add UHID entry Add an UHID entry to the MAINTAINERS file. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index de85391..b0209fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6358,6 +6358,13 @@ S: Maintained F: Documentation/filesystems/ufs.txt F: fs/ufs/ +UHID USERSPACE HID IO DRIVER: +M: David Herrmann +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/uhid.c +F: include/linux/uhid.h + ULTRA-WIDEBAND (UWB) SUBSYSTEM: L: linux-usb@vger.kernel.org S: Orphan -- cgit v1.1 From db5889812ad156019303e50e3fa73773620f79d4 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 18 Jun 2012 17:08:08 +0200 Subject: HID: uhid: silence gcc warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc is giving me: drivers/hid/uhid.c: In function ‘uhid_hid_get_raw’: drivers/hid/uhid.c:157: warning: ‘len’ may be used uninitialized in this function which is clearly bogus, as - when used as memcpy() argument, it's initialized properly - the code is structured in a way that either 'ret' or 'len' is always initialized, so the return statement always has an initialized value. Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index ea560bf..119b7e6 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -154,7 +154,7 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, struct uhid_event *ev; unsigned long flags; int ret; - size_t len; + size_t uninitialized_var(len); struct uhid_feature_answer_req *req; if (!uhid->running) -- cgit v1.1 From d4bad8b2ea1c76b0c5ea55fbbf04e2115f560c22 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Sat, 14 Jul 2012 18:59:25 -0300 Subject: HID: uhid: Fix sending events with invalid data This was detected because events with invalid types were arriving to userspace. The code before this patch would only work for the first event in the queue (when uhid->tail is 0). Signed-off-by: Vinicius Costa Gomes Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 119b7e6..714cd8c 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -465,7 +465,7 @@ try_again: goto try_again; } else { len = min(count, sizeof(**uhid->outq)); - if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) { + if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { ret = -EFAULT; } else { kfree(uhid->outq[uhid->tail]); -- cgit v1.1 From 54c7c33ce2cf80a561fcc5f83099c9150de6fe44 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 15 Aug 2012 19:58:28 -0700 Subject: USB: gadget: f_audio_source: Fix use after free in audio_unbind When USB was disconnected, we were freeing our audio_dev struct in audio_unbind before the audio system had cleaned up, resulting in a hang in audio_pcm_close. We now statically allocate the audio_dev struct to avoid this problem. Change-Id: I58ad21eaa20dcf4aa74ee614ef3b6ed2c91d52a1 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_audio_source.c | 40 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index ba8e007..fe221c5 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -243,7 +243,6 @@ struct audio_dev { struct list_head idle_reqs; struct usb_ep *in_ep; - struct usb_endpoint_descriptor *in_desc; spinlock_t lock; @@ -620,7 +619,10 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) audio_request_free(req, audio->in_ep); snd_card_free_when_closed(audio->card); - kfree(audio); + audio->card = NULL; + audio->pcm = NULL; + audio->substream = NULL; + audio->in_ep = NULL; } static void audio_pcm_playback_start(struct audio_dev *audio) @@ -736,6 +738,19 @@ static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, return ret; } +static struct audio_dev _audio_dev = { + .func = { + .name = "audio_source", + .bind = audio_bind, + .unbind = audio_unbind, + .set_alt = audio_set_alt, + .setup = audio_setup, + .disable = audio_disable, + }, + .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), + .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), +}; + static struct snd_pcm_ops audio_playback_ops = { .open = audio_pcm_open, .close = audio_pcm_close, @@ -758,27 +773,12 @@ int audio_source_bind_config(struct usb_configuration *c, config->card = -1; config->device = -1; - audio = kzalloc(sizeof *audio, GFP_KERNEL); - if (!audio) - return -ENOMEM; - - audio->func.name = "audio_source"; - - spin_lock_init(&audio->lock); - - audio->func.bind = audio_bind; - audio->func.unbind = audio_unbind; - audio->func.set_alt = audio_set_alt; - audio->func.setup = audio_setup; - audio->func.disable = audio_disable; - audio->in_desc = &fs_as_in_ep_desc; - - INIT_LIST_HEAD(&audio->idle_reqs); + audio = &_audio_dev; err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); if (err) - goto snd_card_fail; + return err; snd_card_set_dev(card, &c->cdev->gadget->dev); @@ -817,7 +817,5 @@ add_fail: register_fail: pcm_fail: snd_card_free(audio->card); -snd_card_fail: - kfree(audio); return err; } -- cgit v1.1 From 16f1d4248241c8b50b26da466b263cffecac646d Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 16 Aug 2012 13:13:20 -0700 Subject: USB: gadget: f_audio_source: Fix compilation error Change-Id: I1b785c47bda03208d396473a9d1c94631f5624d9 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_audio_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index fe221c5..c5dbf71 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -525,7 +525,7 @@ static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct audio_dev *audio = func_to_audio(f); pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); - usb_ep_enable(audio->in_ep, audio->in_desc); + usb_ep_enable(audio->in_ep, &fs_as_in_ep_desc); return 0; } -- cgit v1.1 From 8f4d8bd72fb25ef86463828774798d9a15ba90fe Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Thu, 24 May 2012 15:12:36 -0700 Subject: ARM: disable preemption in machine_shutdown Since the smp call to stop the other cpus are handled in those cpus in interrupt context, there's a potential for those smp handlers to interrupt threads holding spin locks (such as the one a mutex holds). This prevents those threads from ever releasing their spin lock, so if the cpu doing the shutdown is allowed to switch to another thread that tries to grab the same lock/mutex, we could get into a deadlock (the spin lock call is called with preemption disabled in the mutex lock code). To avoid that possibility, disable preemption before doing the smp_send_stop(). Change-Id: I7976c5382d7173fcb3cd14da8cc5083d442b2544 Signed-off-by: Mike J. Chen --- arch/arm/kernel/process.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index e5cfa6a..c132e81 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -272,6 +272,15 @@ __setup("reboot=", reboot_setup); void machine_shutdown(void) { #ifdef CONFIG_SMP + /* + * Disable preemption so we're guaranteed to + * run to power off or reboot and prevent + * the possibility of switching to another + * thread that might wind up blocking on + * one of the stopped CPUs. + */ + preempt_disable(); + smp_send_stop(); #endif } -- cgit v1.1 From eb3cd50991a0c5e8e67f54d5056033ad97cbc5aa Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 22 Aug 2012 11:29:44 -0700 Subject: net: wireless: bcmdhd: Update to Version 5.90.195.98 Change-Id: I2854300bc03b133d8276bea2f36f4c5253c551a1 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/bcmsdh.c | 7 +- drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 7 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 54 +++++++----- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 4 +- drivers/net/wireless/bcmdhd/dhd.h | 2 +- drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 9 +- drivers/net/wireless/bcmdhd/dhd_custom_gpio.c | 6 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 6 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 2 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/include/sbchipc.h | 7 +- drivers/net/wireless/bcmdhd/include/sdioh.h | 6 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 108 +++++++++++++++++------ drivers/net/wireless/bcmdhd/wl_cfg80211.h | 21 ++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 28 +++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 5 ++ drivers/net/wireless/bcmdhd/wldev_common.c | 5 +- 17 files changed, 204 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c index f67b13a..89320b6 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh.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: bcmsdh.c 300445 2011-12-03 05:37:20Z $ + * $Id: bcmsdh.c 344235 2012-07-11 23:47:18Z $ */ /** @@ -362,9 +362,10 @@ bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length) } bcopy(cis, tmp_buf, length); for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) { - ptr += sprintf((char*)ptr, "%.2x ", *tmp_ptr & 0xff); + ptr += snprintf((char*)ptr, (cis + length - ptr - 4), + "%.2x ", *tmp_ptr & 0xff); if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0) - ptr += sprintf((char *)ptr, "\n"); + ptr += snprintf((char *)ptr, (cis + length - ptr -4), "\n"); } MFREE(bcmsdh->osh, tmp_buf, length); } diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c index d257dda..edecb5f 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.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: bcmsdh_linux.c 312788 2012-02-03 23:06:32Z $ + * $Id: bcmsdh_linux.c 343302 2012-07-06 13:07:38Z $ */ /** @@ -684,6 +684,11 @@ extern int sd_uhsimode; module_param(sd_uhsimode, int, 0); #endif +#ifdef BCMSDIOH_TXGLOM +extern uint sd_txglom; +module_param(sd_txglom, uint, 0); +#endif + #ifdef BCMSDH_MODULE EXPORT_SYMBOL(bcmsdh_attach); EXPORT_SYMBOL(bcmsdh_detach); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index 374154f..67c2fbf 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.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: bcmsdh_sdmmc.c 314904 2012-02-14 21:36:04Z $ + * $Id: bcmsdh_sdmmc.c 344243 2012-07-12 00:06:23Z $ */ #include @@ -799,41 +799,49 @@ sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *by #if defined(MMC_SDIO_ABORT) /* to allow abort command through F1 */ else if (regaddr == SDIOD_CCCR_IOABORT) { - sdio_claim_host(gInstance->func[func]); - /* - * this sdio_f0_writeb() can be replaced with another api - * depending upon MMC driver change. - * As of this time, this is temporaray one - */ - sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); - sdio_release_host(gInstance->func[func]); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + /* + * this sdio_f0_writeb() can be replaced with another api + * depending upon MMC driver change. + * As of this time, this is temporaray one + */ + sdio_writeb(gInstance->func[func], + *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } } #endif /* MMC_SDIO_ABORT */ else if (regaddr < 0xF0) { sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr)); } else { /* Claim host controller, perform F0 write, and release */ - sdio_claim_host(gInstance->func[func]); - sdio_f0_writeb(gInstance->func[func], *byte, regaddr, &err_ret); - sdio_release_host(gInstance->func[func]); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + sdio_f0_writeb(gInstance->func[func], + *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } } } else { /* Claim host controller, perform Fn write, and release */ - sdio_claim_host(gInstance->func[func]); - sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); - sdio_release_host(gInstance->func[func]); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } } } else { /* CMD52 Read */ /* Claim host controller, perform Fn read, and release */ - sdio_claim_host(gInstance->func[func]); - - if (func == 0) { - *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); - } else { - *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + if (func == 0) { + *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); + } else { + *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); + } + sdio_release_host(gInstance->func[func]); } - - sdio_release_host(gInstance->func[func]); } if (err_ret) { diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index a78faeb..c93e41c 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.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: bcmsdh_sdmmc_linux.c 312783 2012-02-03 22:53:56Z $ + * $Id: bcmsdh_sdmmc_linux.c 331154 2012-05-04 00:41:40Z $ */ #include @@ -188,7 +188,6 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) return -EBUSY; - sdio_flags = sdio_get_host_pm_caps(func); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { @@ -202,7 +201,6 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); return ret; } - #if defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(0); #endif /* defined(OOB_INTR_ONLY) */ diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 725a909..6106555 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 333052 2012-05-12 02:09:28Z $ + * $Id: dhd.h 344123 2012-07-11 09:33:49Z $ */ /**************** diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c index 970216e..351c372 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -69,6 +69,9 @@ s32 dhd_cfg80211_down(struct wl_priv *wl) return 0; } +/* + * dhd_cfg80211_set_p2p_info : gets called when GO or GC created + */ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) { dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); @@ -83,7 +86,7 @@ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) dhd_arp_offload_set(dhd, 0); dhd_arp_offload_enable(dhd, false); #endif /* ARP_OFFLOAD_SUPPORT */ - + /* diable all filtering in p2p mode */ dhd_os_set_packet_filter(dhd, 0); /* Setup timeout if Beacons are lost and roam is off to report link down */ @@ -94,6 +97,9 @@ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) return 0; } +/* + * dhd_cfg80211_clean_p2p_info : gets called when GO or GC terminated + */ s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) { dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); @@ -108,7 +114,6 @@ s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) dhd_arp_offload_set(dhd, dhd_arp_mode); dhd_arp_offload_enable(dhd, true); #endif /* ARP_OFFLOAD_SUPPORT */ - dhd_os_set_packet_filter(dhd, 1); /* Setup timeout if Beacons are lost and roam is off to report link down */ diff --git a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c index 9750eeb..de519a5 100644 --- a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c @@ -20,7 +20,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_custom_gpio.c,v 1.2.42.1 2010-10-19 00:41:09 Exp $ +* $Id: dhd_custom_gpio.c 339054 2012-06-15 04:56:55Z $ */ #include @@ -97,13 +97,13 @@ int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr) #endif /* CUSTOMER_HW2 */ if (dhd_oob_gpio_num < 0) { - WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n", + WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined\n", __FUNCTION__)); return (dhd_oob_gpio_num); } WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n", - __FUNCTION__, dhd_oob_gpio_num)); + __FUNCTION__, dhd_oob_gpio_num)); #if defined CUSTOMER_HW host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index d32ce11..8aa7ed9 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 333885 2012-05-18 00:39:03Z $ + * $Id: dhd_linux.c 344350 2012-07-12 08:35:03Z $ */ #include @@ -1026,7 +1026,7 @@ dhd_op_if(dhd_if_t *ifp) #endif netif_stop_queue(ifp->net); unregister_netdev(ifp->net); - ret = DHD_DEL_IF; + ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { @@ -2648,7 +2648,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) } sema_init(&dhd->proto_sem, 1); +#ifdef DHDTHREAD sema_init(&dhd->sdsem, 1); +#endif #ifdef PROP_TXSTATUS spin_lock_init(&dhd->wlfc_spinlock); diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index e578d70..ed3da83 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.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_sdio.c 326662 2012-04-10 06:38:08Z $ + * $Id: dhd_sdio.c 338148 2012-06-11 20:35:45Z $ */ #include diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index 5df25c1..19da8f2 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -33,17 +33,17 @@ #define EPI_RC_NUMBER 195 -#define EPI_INCREMENTAL_NUMBER 75 +#define EPI_INCREMENTAL_NUMBER 98 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 195, 75 +#define EPI_VERSION 5, 90, 195, 98 -#define EPI_VERSION_NUM 0x055ac34b +#define EPI_VERSION_NUM 0x055ac362 #define EPI_VERSION_DEV 5.90.195 -#define EPI_VERSION_STR "5.90.195.75" +#define EPI_VERSION_STR "5.90.195.98" #endif diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h index aa4df44..78ced30 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 333924 2012-05-18 04:48:52Z $ + * $Id: sbchipc.h 343982 2012-07-11 00:29:37Z $ * * Copyright (C) 1999-2011, Broadcom Corporation * @@ -1726,6 +1726,11 @@ typedef volatile struct { #define SECI_MODE_SHIFT 4 #define SECI_UPD_SECI (1 << 7) +#define SECI_SLIP_ESC_CHAR 0xDB +#define SECI_SIGNOFF_0 SECI_SLIP_ESC_CHAR +#define SECI_SIGNOFF_1 0 +#define SECI_REFRESH_REQ 0xDA + #define CLKCTL_STS_SECI_CLK_REQ (1 << 8) #define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h index 5f47d6f..1d820d1 100644 --- a/drivers/net/wireless/bcmdhd/include/sdioh.h +++ b/drivers/net/wireless/bcmdhd/include/sdioh.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: sdioh.h 300017 2011-12-01 20:30:27Z $ + * $Id: sdioh.h 345478 2012-07-18 06:45:15Z $ */ #ifndef _SDIOH_H @@ -90,6 +90,10 @@ #define SD3_PresetVal_SDR50 0x06a #define SD3_PresetVal_SDR104 0x06c #define SD3_PresetVal_DDR50 0x06e +/* SDIO3.0 Revx specific Registers */ +#define SD3_Tuning_Info_Register 0x0EC +#define SD3_WL_BT_reset_register 0x0F0 + /* preset value indices */ #define SD3_PRESETVAL_INITIAL_IX 0 diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 87d971a..0468954 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -358,6 +358,7 @@ static s32 wl_iscan_done(struct wl_priv *wl); static s32 wl_iscan_pending(struct wl_priv *wl); static s32 wl_iscan_inprogress(struct wl_priv *wl); static s32 wl_iscan_aborted(struct wl_priv *wl); +static void wl_scan_timeout_process(struct work_struct *work); /* * find most significant bit set @@ -1518,6 +1519,8 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, s32 passive_scan; wl_scan_results_t *results; WL_SCAN(("Enter \n")); + + mutex_lock(&wl->usr_sync); wl->escan_info.ndev = ndev; wl->escan_info.wiphy = wiphy; wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; @@ -1526,7 +1529,7 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, &passive_scan, sizeof(passive_scan), false); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); - return err; + goto exit; } results = (wl_scan_results_t *) wl->escan_info.escan_buf; results->version = 0; @@ -1534,6 +1537,8 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, results->buflen = WL_SCAN_RESULTS_FIXED_SIZE; err = wl_run_escan(wl, ndev, request, WL_SCAN_ACTION_START); +exit: + mutex_unlock(&wl->usr_sync); return err; } @@ -1580,7 +1585,6 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); iscan_req = false; - wl->scan_request = request; if (request) { /* scan bss */ ssids = request->ssids; if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { @@ -1661,6 +1665,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* we don't do iscan in ibss */ ssids = this_ssid; } + wl->scan_request = request; wl_set_drv_status(wl, SCANNING, ndev); if (iscan_req) { err = wl_do_iscan(wl, request); @@ -1724,12 +1729,9 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, scan_out: wl_clr_drv_status(wl, SCANNING, ndev); - if (wl->scan_request) { - if (timer_pending(&wl->scan_timeout)) - del_timer_sync(&wl->scan_timeout); - cfg80211_scan_done(wl->scan_request, true); - wl->scan_request = NULL; - } + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + wl->scan_request = NULL; return err; } @@ -1750,6 +1752,7 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, wl->scan_busy_count++; if (wl->scan_busy_count > WL_SCAN_BUSY_MAX) { wl->scan_busy_count = 0; + WL_ERR(("Continuous scan failures!! Exercising FW hang recovery\n")); net_os_send_hang_message(ndev); } } @@ -3069,6 +3072,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, s32 pm; s32 err = 0; struct wl_priv *wl = wiphy_priv(wiphy); + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); CHECK_SYS_UP(wl); @@ -3077,7 +3081,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return err; } - pm = enabled ? PM_FAST : PM_OFF; + pm = enabled ? ((dhd->in_suspend) ? PM_MAX : PM_FAST) : PM_OFF; pm = htod32(pm); err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); if (unlikely(err)) { @@ -3511,6 +3515,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, wifi_p2p_ie_t *p2p_ie; wpa_ie_fixed_t *wps_ie; scb_val_t scb_val; + wifi_wfd_ie_t *wfd_ie; const struct ieee80211_mgmt *mgmt; struct wl_priv *wl = wiphy_priv(wiphy); struct net_device *dev = NULL; @@ -3518,6 +3523,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, s32 bssidx = 0; u32 p2pie_len = 0; u32 wpsie_len = 0; + u32 wfdie_len = 0; u32 id; u32 retry = 0; bool ack = false; @@ -3567,6 +3573,11 @@ 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); } + 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); + } if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Order of Vendor IE is 1) WPS IE + @@ -3578,7 +3589,7 @@ 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, - (u8 *)wps_ie, wpsie_len + p2pie_len); + (u8 *)wps_ie, wpsie_len + p2pie_len+ wfdie_len); } cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); goto exit; @@ -4087,11 +4098,13 @@ 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; + wifi_wfd_ie_t *wfd_ie; bool is_bssup = false; bool update_bss = false; bool pbc = false; u16 wpsie_len = 0; u16 p2pie_len = 0; + u32 wfdie_len = 0; u8 beacon_ie[IE_MAX_LEN]; s32 ie_offset = 0; s32 bssidx = 0; @@ -4149,10 +4162,24 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, } else { WL_ERR(("No P2PIE in beacon \n")); } + /* 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 { + WL_ERR(("Found WFD IE but there is no space, (%d)(%d)(%d)\n", + wpsie_len, p2pie_len, wfdie_len)); + wfdie_len = 0; + } + } else { + WL_ERR(("No WFDIE in beacon \n")); + } /* 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, - beacon_ie, wpsie_len + p2pie_len); + beacon_ie, wpsie_len + p2pie_len + wfdie_len); /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -4714,8 +4741,9 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) } notif_bss_info->rssi = dtoh16(bi->RSSI); memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); - mgmt_type = wl->active_scan ? - IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON; + mgmt_type = (bi->flags & WL_BSS_FLAGS_FROM_BEACON) ? + IEEE80211_STYPE_BEACON : IEEE80211_STYPE_PROBE_RESP; + if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) { mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type); } @@ -4744,7 +4772,7 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) return -EINVAL; } - WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM " + WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM" "mgmt_type %d frame_len %d\n", bi->SSID, notif_bss_info->rssi, notif_bss_info->channel, mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, @@ -6018,7 +6046,9 @@ static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) unsigned long flags; WL_DBG(("Enter \n")); - wl->scan_busy_count = 0; + if(!aborted) + wl->scan_busy_count = 0; + if (!wl_get_drv_status(wl, SCANNING, ndev)) { wl_clr_drv_status(wl, SCANNING, ndev); WL_ERR(("Scan complete while device not scanning\n")); @@ -6175,13 +6205,30 @@ static void wl_scan_timeout(unsigned long data) { struct wl_priv *wl = (struct wl_priv *)data; + schedule_work(&wl->work_scan_timeout); +} + +static void wl_scan_timeout_process(struct work_struct *work) +{ + struct wl_priv *wl; + + wl = (wl_priv_t *)container_of(work, wl_priv_t, work_scan_timeout); + if (wl->scan_request) { WL_ERR(("timer expired\n")); if (wl->escan_on) - wl_notify_escan_complete(wl, wl->escan_info.ndev, true, false); + wl_notify_escan_complete(wl, wl->escan_info.ndev, true, true); else wl_notify_iscan_complete(wl_to_iscan(wl), true); } + + /* Assume FW is in bad state if there are continuous scan timeouts */ + wl->scan_busy_count++; + if (wl->scan_busy_count > WL_SCAN_BUSY_MAX) { + wl->scan_busy_count = 0; + WL_ERR(("Continuous scan timeouts!! Exercising FW hang recovery\n")); + net_os_send_hang_message(wl->escan_info.ndev); + } } static void wl_iscan_timer(unsigned long data) @@ -6273,7 +6320,9 @@ static s32 wl_notify_escan_complete(struct wl_priv *wl, WL_DBG(("Enter \n")); - wl->scan_busy_count = 0; + if(!aborted) + wl->scan_busy_count = 0; + if (wl->scan_request) { if (wl->scan_request->dev == wl->p2p_net) dev = wl_to_prmry_ndev(wl); @@ -6344,8 +6393,11 @@ static s32 wl_escan_handler(struct wl_priv *wl, u32 i; wifi_p2p_ie_t * p2p_ie; u8 *p2p_dev_addr = NULL; + WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); + + mutex_lock(&wl->usr_sync); /* P2P SCAN is coming from primary interface */ if (wl_get_p2p_status(wl, SCANNING)) { if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) @@ -6358,7 +6410,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, !wl_get_drv_status(wl, SCANNING, ndev)) { WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); - return err; + goto exit; } if (status == WLC_E_STATUS_PARTIAL) { @@ -6448,7 +6500,6 @@ static s32 wl_escan_handler(struct wl_priv *wl, bss->RSSI = bi->RSSI; bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; } - goto exit; } } @@ -6456,7 +6507,6 @@ static s32 wl_escan_handler(struct wl_priv *wl, list->version = dtoh32(bi->version); list->buflen += bi_length; list->count++; - } } @@ -6469,12 +6519,10 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { - mutex_lock(&wl->usr_sync); WL_INFO(("ESCAN COMPLETED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); wl_notify_escan_complete(wl, ndev, false, false); - mutex_unlock(&wl->usr_sync); } } else if (status == WLC_E_STATUS_ABORT) { @@ -6486,14 +6534,15 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { - mutex_lock(&wl->usr_sync); WL_INFO(("ESCAN ABORTED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); wl_notify_escan_complete(wl, ndev, true, false); - mutex_unlock(&wl->usr_sync); } } + else if (status == WLC_E_STATUS_NEWSCAN) { + /* Do Nothing. Ignore this event */ + } else { WL_ERR(("unexpected Escan Event %d : abort\n", status)); wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; @@ -6504,14 +6553,13 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { - mutex_lock(&wl->usr_sync); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); wl_notify_escan_complete(wl, ndev, true, false); - mutex_unlock(&wl->usr_sync); } } exit: + mutex_unlock(&wl->usr_sync); return err; } @@ -6574,6 +6622,7 @@ static s32 wl_init_priv(struct wl_priv *wl) return -ENOMEM; wl_init_event_handler(wl); mutex_init(&wl->usr_sync); + INIT_WORK(&wl->work_scan_timeout, wl_scan_timeout_process); err = wl_init_scan(wl); if (err) return err; @@ -6593,6 +6642,7 @@ static void wl_deinit_priv(struct wl_priv *wl) wl_link_down(wl); del_timer_sync(&wl->scan_timeout); wl_term_iscan(wl); + cancel_work_sync(&wl->work_scan_timeout); wl_deinit_priv_mem(wl); unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); } @@ -7097,6 +7147,10 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) band = IEEE80211_BAND_5GHZ; ht40_allowed = (bw_cap == WLC_N_BW_20ALL) ? FALSE : TRUE; } + else { + WL_ERR(("Invalid Channel received %x\n", channel)); + continue; + } for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { if (band_chan_arr[j].hw_value == channel) { @@ -7225,7 +7279,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; index = IEEE80211_BAND_2GHZ; - if (bandlist[i] == WLC_BAND_2G && bw_cap == WLC_N_BW_40ALL) + if (bw_cap == WLC_N_BW_40ALL) wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } if ((index >= 0) && nmode) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 49bed70..974770e 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -61,13 +61,24 @@ struct wl_ibss; /* 0 invalidates all debug messages. default is 1 */ #define WL_DBG_LEVEL 0xFF -#define WL_ERR(args) \ +#if defined(DHD_DEBUG) +#define WL_ERR(args) \ do { \ - if (wl_dbg_level & WL_DBG_ERR) { \ + if (wl_dbg_level & WL_DBG_ERR) { \ printk(KERN_ERR "CFG80211-ERROR) %s : ", __func__); \ printk args; \ } \ } while (0) +#else /* defined(DHD_DEBUG) */ +#define WL_ERR(args) \ +do { \ + if ((wl_dbg_level & WL_DBG_ERR) && net_ratelimit()) { \ + printk(KERN_INFO "CFG80211-ERROR) %s : ", __func__); \ + printk args; \ + } \ +} while (0) +#endif /* defined(DHD_DEBUG) */ + #ifdef WL_INFO #undef WL_INFO #endif @@ -387,7 +398,7 @@ struct afx_hdl { }; /* private data of cfg80211 interface */ -struct wl_priv { +typedef struct wl_priv { struct wireless_dev *wdev; /* representing wl cfg80211 device */ struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ @@ -457,7 +468,9 @@ struct wl_priv { u16 hostapd_chan; /* remember chan requested by framework for hostapd */ u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ u16 scan_busy_count; -}; + struct work_struct work_scan_timeout; +} wl_priv_t; + static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index bdcfa95..f06ae50 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -641,8 +641,8 @@ wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, } set_ie: ret = wl_cfgp2p_set_management_ie(wl, dev, - wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), - VNDR_IE_PRBREQ_FLAG, ie, ie_len); + wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_PRBREQ_FLAG, ie, ie_len); if (unlikely(ret < 0)) { CFGP2P_ERR(("set probreq ie occurs error %d\n", ret)); @@ -846,6 +846,11 @@ 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) + /* Delete and Set a management vndr ie to firmware * Parameters: * @wl : wl_private data @@ -970,7 +975,8 @@ 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) || - wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { 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])); @@ -996,7 +1002,8 @@ 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) || - wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { 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])); @@ -1107,6 +1114,19 @@ wl_cfgp2p_find_p2pie(u8 *parse, u32 len) return NULL; } +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)) { + return (wifi_wfd_ie_t *)ie; + } + } + return NULL; +} + static s32 wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete) diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index 05323ed..03a645a 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -31,6 +31,8 @@ struct wl_priv; extern u32 wl_dbg_level; +typedef struct wifi_p2p_ie wifi_wfd_ie_t; + /* 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. @@ -192,6 +194,9 @@ wl_cfgp2p_find_wpsie(u8 *parse, u32 len); extern wifi_p2p_ie_t * wl_cfgp2p_find_p2pie(u8 *parse, u32 len); +extern wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len); + extern s32 wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index 51a8064..7bea3dc 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -334,8 +334,11 @@ int wldev_set_country( scb_val_t scbval; char smbuf[WLC_IOCTL_SMLEN]; - if (!country_code) + if (!country_code) { + WLDEV_ERROR(("%s: set country failed for %s\n", + __FUNCTION__, country_code)); return error; + } error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), smbuf, sizeof(smbuf), NULL); -- cgit v1.1 From 50701ac7b937f54b689af49de116c9cbd41b0286 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 22 Aug 2012 13:56:14 -0700 Subject: net: wireless: bcmdhd: Remove PMKID_CACHE and TXFAIL events for CFG80211 Change-Id: I82b6a1c9941485f3b47bc5978873379ab8853601 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 8aa7ed9..4faef15 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -3228,8 +3228,10 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_MIC_ERROR); setbit(eventmask, WLC_E_ASSOC_REQ_IE); setbit(eventmask, WLC_E_ASSOC_RESP_IE); +#ifndef WL_CFG80211 setbit(eventmask, WLC_E_PMKID_CACHE); setbit(eventmask, WLC_E_TXFAIL); +#endif setbit(eventmask, WLC_E_JOIN_START); setbit(eventmask, WLC_E_SCAN_COMPLETE); #ifdef WLMEDIA_HTSF -- cgit v1.1 From eca0e767ef4c74bd0b6ad6a7384fa2869a2a96b9 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 22 Aug 2012 16:09:09 -0700 Subject: net: wireless: bcmdhd: Remove WIPHY_FLAG_SUPPORTS_FW_ROAM flag If driver advertises FW_ROAM, the supplicant wouldn't send the BSSID & Freq in the connect command allowing the driver to choose the AP to connect to. But unless we support ROAM_CACHE in firware this will delay the ASSOC as the FW need to do a full scan before attempting to connect. Change-Id: Iebc88a60243196b9ad27b8302f2b1473710308f6 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 0468954..9af2c1b 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -4650,7 +4650,7 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev #endif WIPHY_FLAG_4ADDR_STATION; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) - wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + /* wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; */ #endif WL_DBG(("Registering custom regulatory)\n")); wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; -- cgit v1.1 From 97ce108d89e5b98f64c6192aad66e70fb9a0f45e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jul 2011 16:35:40 +0200 Subject: cfg80211/nl80211: support GTK rekey offload In certain circumstances, like WoWLAN scenarios, devices may implement (partial) GTK rekeying on the device to avoid waking up the host for it. In order to successfully go through GTK rekeying, the KEK, KCK and the replay counter are required. Add API to let the supplicant hand the parameters to the driver which may store it for future GTK rekey operations. Note that, of course, if GTK rekeying is done by the device, the EAP frame must not be passed up to userspace, instead a rekey event needs to be sent to let userspace update its replay counter. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Conflicts: include/linux/nl80211.h net/wireless/nl80211.c Change-Id: Icd3a157742b08c01a3be20d46d4112e5d4b93a58 --- include/linux/nl80211.h | 37 ++++++++++++++++ include/net/cfg80211.h | 26 +++++++++++ net/wireless/mlme.c | 11 +++++ net/wireless/nl80211.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 4 ++ 5 files changed, 190 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 27d2dd9..ca2cef8 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -483,6 +483,14 @@ * more background information, see * http://wireless.kernel.org/en/users/Documentation/WoWLAN. * + * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver + * the necessary information for supporting GTK rekey offload. This + * feature is typically used during WoWLAN. The configuration data + * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and + * contains the data in sub-attributes). After rekeying happened, + * this command may also be sent by the driver as an MLME event to + * inform userspace of the new replay counter. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -605,6 +613,8 @@ enum nl80211_commands { NL80211_CMD_SCHED_SCAN_RESULTS, NL80211_CMD_SCHED_SCAN_STOPPED, + NL80211_CMD_SET_REKEY_OFFLOAD, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1020,6 +1030,9 @@ enum nl80211_commands { * are managed in software: interfaces of these types aren't subject to * any restrictions in their number or combinations. * + * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -2425,4 +2438,28 @@ enum nl80211_plink_state { MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 }; +#define NL80211_KCK_LEN 16 +#define NL80211_KEK_LEN 16 +#define NL80211_REPLAY_CTR_LEN 8 + +/** + * enum nl80211_rekey_data - attributes for GTK rekey offload + * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes + * @NL80211_REKEY_DATA_KEK: key encryption key (binary) + * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) + * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) + * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) + * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) + */ +enum nl80211_rekey_data { + __NL80211_REKEY_DATA_INVALID, + NL80211_REKEY_DATA_KEK, + NL80211_REKEY_DATA_KCK, + NL80211_REKEY_DATA_REPLAY_CTR, + + /* keep last */ + NUM_NL80211_REKEY_DATA, + MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a2a97f3..66886ac 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1185,6 +1185,18 @@ struct cfg80211_wowlan { }; /** + * struct cfg80211_gtk_rekey_data - rekey data + * @kek: key encryption key + * @kck: key confirmation key + * @replay_ctr: replay counter + */ +struct cfg80211_gtk_rekey_data { + u8 kek[NL80211_KEK_LEN]; + u8 kck[NL80211_KCK_LEN]; + u8 replay_ctr[NL80211_REPLAY_CTR_LEN]; +}; + +/** * struct cfg80211_ops - backend description for wireless configuration * * This struct is registered by fullmac card drivers and/or wireless stacks @@ -1228,6 +1240,8 @@ struct cfg80211_wowlan { * * @set_default_mgmt_key: set the default management frame key on an interface * + * @set_rekey_data: give the data necessary for GTK rekeying to the driver + * * @add_beacon: Add a beacon with given parameters, @head, @interval * and @dtim_period will be valid, @tail is optional. * @set_beacon: Change the beacon parameters for an access point mode @@ -1521,6 +1535,9 @@ struct cfg80211_ops { struct net_device *dev, struct cfg80211_sched_scan_request *request); int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev); + + int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data); }; /* @@ -3063,6 +3080,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, void cfg80211_cqm_pktloss_notify(struct net_device *dev, const u8 *peer, u32 num_packets, gfp_t gfp); +/** + * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying + * @dev: network device + * @bssid: BSSID of AP (to avoid races) + * @replay_ctr: new replay counter + */ +void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 493b939..c960a81 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1082,3 +1082,14 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); } EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); + +void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); +} +EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4e84e22..e4e8954 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -220,6 +220,14 @@ nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { .len = IEEE80211_MAX_SSID_LEN }, }; +/* policy for GTK rekey offload attributes */ +static const struct nla_policy +nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN }, + [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN }, +}; + /* ifidx get helper */ static int nl80211_get_ifidx(struct netlink_callback *cb) { @@ -5399,6 +5407,57 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct nlattr *tb[NUM_NL80211_REKEY_DATA]; + struct cfg80211_gtk_rekey_data rekey_data; + int err; + + if (!info->attrs[NL80211_ATTR_REKEY_DATA]) + return -EINVAL; + + err = nla_parse(tb, MAX_NL80211_REKEY_DATA, + nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), + nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), + nl80211_rekey_policy); + if (err) + return err; + + if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) + return -ERANGE; + if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) + return -ERANGE; + if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) + return -ERANGE; + + memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), + NL80211_KEK_LEN); + memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), + NL80211_KCK_LEN); + memcpy(rekey_data.replay_ctr, + nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), + NL80211_REPLAY_CTR_LEN); + + wdev_lock(wdev); + if (!wdev->current_bss) { + err = -ENOTCONN; + goto out; + } + + if (!rdev->ops->set_rekey_data) { + err = -EOPNOTSUPP; + goto out; + } + + err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); + out: + wdev_unlock(wdev); + return err; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -5929,6 +5988,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, + .doit = nl80211_set_rekey_data, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -6873,6 +6940,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *rekey_attr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + + rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); + if (!rekey_attr) + goto nla_put_failure; + + NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, + NL80211_REPLAY_CTR_LEN, replay_ctr); + + nla_nest_end(msg, rekey_attr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2f1bfb8..5d69c56 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -109,4 +109,8 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, u32 num_packets, gfp_t gfp); +void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.1 From 931201c5f741e10b75354668f522f7636e4c9e82 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 16 Sep 2011 18:56:23 +0300 Subject: cfg80211/nl80211: Add PMKSA caching candidate event When the driver (or most likely firmware) decides which AP to use for roaming based on internal scan result processing, user space needs to be notified of PMKSA caching candidates to allow RSN pre-authentication to be used. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville Conflicts: include/linux/nl80211.h Change-Id: I31aa113747b75f5f35658b857fdfe8d9a75e4534 --- include/linux/nl80211.h | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ include/net/cfg80211.h | 11 ++++++ net/wireless/mlme.c | 11 ++++++ net/wireless/nl80211.c | 46 ++++++++++++++++++++++++ net/wireless/nl80211.h | 4 +++ 5 files changed, 167 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ca2cef8..b5db3c6 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -491,6 +491,9 @@ * this command may also be sent by the driver as an MLME event to * inform userspace of the new replay counter. * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -615,6 +618,8 @@ enum nl80211_commands { NL80211_CMD_SET_REKEY_OFFLOAD, + NL80211_CMD_PMKSA_CANDIDATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1033,6 +1038,36 @@ enum nl80211_commands { * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. * + * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, + * nested array attribute containing an entry for each band, with the entry + * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but + * without the length restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon + * and Probe Response (when response to wildcard Probe Request); see + * &enum nl80211_hidden_ssid, represented as a u32 + * + * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. + * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to + * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the + * driver (or firmware) replies to Probe Request frames. + * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association + * Response frames. This is used with %NL80211_CMD_NEW_BEACON and + * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into + * (Re)Association Response frames when the driver (or firmware) replies to + * (Re)Association Request frames. + * + * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration + * of the station, see &enum nl80211_sta_wme_attr. + * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working + * as AP. + * + * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of + * roaming to another AP in the same ESS if the signal lever is low. + * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1251,6 +1286,8 @@ enum nl80211_attrs { NL80211_ATTR_SCHED_SCAN_MATCH, NL80211_ATTR_MAX_MATCH_SETS, + NL80211_ATTR_PMKSA_CANDIDATE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2462,4 +2499,62 @@ enum nl80211_rekey_data { MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 }; +/** + * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in + * Beacon frames) + * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element + * in Beacon frames + * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID + * element in Beacon frames but zero out each byte in the SSID + */ +enum nl80211_hidden_ssid { + NL80211_HIDDEN_SSID_NOT_IN_USE, + NL80211_HIDDEN_SSID_ZERO_LEN, + NL80211_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * enum nl80211_sta_wme_attr - station WME attributes + * @__NL80211_STA_WME_INVALID: invalid number for nested attribute + * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format + * is the same as the AC bitmap in the QoS info field. + * @NL80211_STA_WME_MAX_SP: max service period. the format is the same + * as the MAX_SP field in the QoS info field (but already shifted down). + * @__NL80211_STA_WME_AFTER_LAST: internal + * @NL80211_STA_WME_MAX: highest station WME attribute + */ +enum nl80211_sta_wme_attr { + __NL80211_STA_WME_INVALID, + NL80211_STA_WME_UAPSD_QUEUES, + NL80211_STA_WME_MAX_SP, + + /* keep last */ + __NL80211_STA_WME_AFTER_LAST, + NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +}; + +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 66886ac..9355b6a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3089,6 +3089,17 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); +/** + * cfg80211_pmksa_candidate_notify - notify about PMKSA caching candidate + * @dev: network device + * @index: candidate index (the smaller the index, the higher the priority) + * @bssid: BSSID of AP + * @preauth: Whether AP advertises support for RSN pre-authentication + * @gfp: allocation flags + */ +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index c960a81..6630846 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -1093,3 +1093,14 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); + +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); +} +EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e4e8954..a51cdf0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6985,6 +6985,52 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *attr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); + if (!attr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index); + NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid); + if (preauth) + NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH); + + nla_nest_end(msg, attr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5d69c56..f24a1fb 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -113,4 +113,8 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.1 From e350606366b0aeb540c7e943ccdb50c4c4205b25 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:50 +0300 Subject: nl80211: support sending TDLS commands/frames Add support for sending high-level TDLS commands and TDLS frames via NL80211_CMD_TDLS_OPER and NL80211_CMD_TDLS_MGMT, respectively. Add appropriate cfg80211 callbacks for lower level drivers. Add wiphy capability flags for TDLS support and advertise them via nl80211. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville Conflicts: include/linux/nl80211.h include/net/cfg80211.h net/wireless/nl80211.c Change-Id: I08e4de6e92680aed35b98838aa999d31963b6d50 --- include/linux/nl80211.h | 51 +++++++++++++++++++++++++++++ include/net/cfg80211.h | 22 +++++++++++++ net/wireless/nl80211.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index b5db3c6..59a1104 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -494,6 +494,9 @@ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace * of PMKSA caching dandidates. * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -620,6 +623,9 @@ enum nl80211_commands { NL80211_CMD_PMKSA_CANDIDATE, + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1068,6 +1074,27 @@ enum nl80211_commands { * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching * candidate information, see &enum nl80211_pmksa_candidate_attr. * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1288,6 +1315,14 @@ enum nl80211_attrs { NL80211_ATTR_PMKSA_CANDIDATE, + NL80211_ATTR_TX_NO_CCK_RATE, + + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2557,4 +2592,20 @@ enum nl80211_pmksa_candidate_attr { MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 }; +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9355b6a..394fd02 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1359,6 +1359,9 @@ struct cfg80211_gtk_rekey_data { * @set_ringparam: Set tx and rx ring sizes. * * @get_ringparam: Get tx and rx ring current and maximum sizes. + * + * @tdls_mgmt: Transmit a TDLS management frame. + * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1538,6 +1541,12 @@ struct cfg80211_ops { int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data); + + int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper); }; /* @@ -1587,6 +1596,15 @@ struct cfg80211_ops { * @WIPHY_FLAG_MESH_AUTH: The device supports mesh authentication by routing * auth frames to userspace. See @NL80211_MESH_SETUP_USERSPACE_AUTH. * @WIPHY_FLAG_SUPPORTS_SCHED_SCAN: The device supports scheduled scans. + * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the + * firmware. + * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP. + * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation. + * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z) + * link setup/discovery operations internally. Setup, discovery and + * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT + * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be + * used for asking the driver/firmware to perform a TDLS operation. */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1601,6 +1619,10 @@ enum wiphy_flags { WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), WIPHY_FLAG_ENFORCE_COMBINATIONS = BIT(12), + WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), + WIPHY_FLAG_AP_UAPSD = BIT(14), + WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), + WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a51cdf0..53890d3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -184,6 +184,12 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, + [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -718,6 +724,14 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); + if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) + NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) + NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP); NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * dev->wiphy.n_cipher_suites, @@ -860,6 +874,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } CMD(set_channel, SET_CHANNEL); CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { + CMD(tdls_mgmt, TDLS_MGMT); + CMD(tdls_oper, TDLS_OPER); + } if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); @@ -4720,6 +4738,57 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) return rdev->ops->flush_pmksa(&rdev->wiphy, dev); } +static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 action_code, dialog_token; + u16 status_code; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_mgmt) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || + !info->attrs[NL80211_ATTR_STATUS_CODE] || + !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || + !info->attrs[NL80211_ATTR_IE] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); + status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + + return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, + dialog_token, status_code, + nla_data(info->attrs[NL80211_ATTR_IE]), + nla_len(info->attrs[NL80211_ATTR_IE])); +} + +static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + enum nl80211_tdls_operation operation; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_oper) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + + return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); +} + static int nl80211_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { @@ -5996,6 +6065,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_TDLS_MGMT, + .doit = nl80211_tdls_mgmt, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TDLS_OPER, + .doit = nl80211_tdls_oper, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { -- cgit v1.1 From 1bd42d5ea786795ee253847e03e0ab617f41739d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jul 2011 10:48:55 +0200 Subject: nl80211: advertise GTK rekey support, new triggers Since we now have the necessary API in place to support GTK rekeying, applications will need to know whether it is supported by a device. Add a pseudo-trigger that is used only to advertise that capability. Also, add some new triggers that match what iwlagn devices can do. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 19 +++++++++++++++++-- include/net/cfg80211.h | 24 ++++++++++++++++++++---- net/wireless/core.c | 4 ++++ net/wireless/nl80211.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 6 deletions(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 59a1104..e077580 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1005,8 +1005,8 @@ enum nl80211_commands { * driving the peer link management state machine. * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. * - * @NL80211_ATTR_WOWLAN_SUPPORTED: indicates, as part of the wiphy capabilities, - * the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy + * capabilities, the supported WoWLAN triggers * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN @@ -2388,6 +2388,16 @@ struct nl80211_wowlan_pattern_support { * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute * carrying a &struct nl80211_wowlan_pattern_support. + * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be + * used when setting, used only to indicate that GTK rekeying is supported + * by the device (flag) + * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if + * done by the device) (flag) + * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request + * packet (flag) + * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) + * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released + * (on devices that have rfkill in the device) (flag) * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number */ @@ -2397,6 +2407,11 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_DISCONNECT, NL80211_WOWLAN_TRIG_MAGIC_PKT, NL80211_WOWLAN_TRIG_PKT_PATTERN, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE, /* keep last */ NUM_NL80211_WOWLAN_TRIG, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 394fd02..7c206b6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1177,9 +1177,15 @@ struct cfg80211_wowlan_trig_pkt_pattern { * @magic_pkt: wake up on receiving magic packet * @patterns: wake up on receiving packet matching a pattern * @n_patterns: number of patterns + * @gtk_rekey_failure: wake up on GTK rekey failure + * @eap_identity_req: wake up on EAP identity request packet + * @four_way_handshake: wake up on 4-way handshake + * @rfkill_release: wake up when rfkill is released */ struct cfg80211_wowlan { - bool any, disconnect, magic_pkt; + bool any, disconnect, magic_pkt, gtk_rekey_failure, + eap_identity_req, four_way_handshake, + rfkill_release; struct cfg80211_wowlan_trig_pkt_pattern *patterns; int n_patterns; }; @@ -1717,11 +1723,21 @@ struct ieee80211_txrx_stypes { * @WIPHY_WOWLAN_MAGIC_PKT: supports wakeup on magic packet * (see nl80211.h) * @WIPHY_WOWLAN_DISCONNECT: supports wakeup on disconnect + * @WIPHY_WOWLAN_SUPPORTS_GTK_REKEY: supports GTK rekeying while asleep + * @WIPHY_WOWLAN_GTK_REKEY_FAILURE: supports wakeup on GTK rekey failure + * @WIPHY_WOWLAN_EAP_IDENTITY_REQ: supports wakeup on EAP identity request + * @WIPHY_WOWLAN_4WAY_HANDSHAKE: supports wakeup on 4-way handshake failure + * @WIPHY_WOWLAN_RFKILL_RELEASE: supports wakeup on RF-kill release */ enum wiphy_wowlan_support_flags { - WIPHY_WOWLAN_ANY = BIT(0), - WIPHY_WOWLAN_MAGIC_PKT = BIT(1), - WIPHY_WOWLAN_DISCONNECT = BIT(2), + WIPHY_WOWLAN_ANY = BIT(0), + WIPHY_WOWLAN_MAGIC_PKT = BIT(1), + WIPHY_WOWLAN_DISCONNECT = BIT(2), + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY = BIT(3), + WIPHY_WOWLAN_GTK_REKEY_FAILURE = BIT(4), + WIPHY_WOWLAN_EAP_IDENTITY_REQ = BIT(5), + WIPHY_WOWLAN_4WAY_HANDSHAKE = BIT(6), + WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7), }; /** diff --git a/net/wireless/core.c b/net/wireless/core.c index 880dbe2..112959d 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -488,6 +488,10 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; + if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && + !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) + return -EINVAL; + if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 53890d3..0a78297 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -218,6 +218,10 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, + [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, }; static const struct nla_policy @@ -966,6 +970,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); if (dev->wiphy.wowlan.n_patterns) { struct nl80211_wowlan_pattern_support pat = { .max_patterns = dev->wiphy.wowlan.n_patterns, @@ -5319,6 +5333,14 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); if (rdev->wowlan->magic_pkt) NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (rdev->wowlan->gtk_rekey_failure) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (rdev->wowlan->eap_identity_req) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (rdev->wowlan->four_way_handshake) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (rdev->wowlan->rfkill_release) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); if (rdev->wowlan->n_patterns) { struct nlattr *nl_pats, *nl_pat; int i, pat_len; @@ -5395,6 +5417,33 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) new_triggers.magic_pkt = true; } + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) + return -EINVAL; + + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) + return -EINVAL; + new_triggers.gtk_rekey_failure = true; + } + + if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { + if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) + return -EINVAL; + new_triggers.eap_identity_req = true; + } + + if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) + return -EINVAL; + new_triggers.four_way_handshake = true; + } + + if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { + if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) + return -EINVAL; + new_triggers.rfkill_release = true; + } + if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { struct nlattr *pat; int n_patterns = 0; -- cgit v1.1 From 6c3e830bd84af1b8e125490dc194fe099490dc46 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 7 Nov 2011 12:39:33 +0100 Subject: nl80211: advertise device AP SME Add the ability to advertise that the device contains the AP SME and what features it can support. There are currently no features in the bitmap -- probe response offload will be advertised by a few patches Arik is working on now (who took over from Guy Eilam) and a device with AP SME will typically implement and require response offload. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Conflicts: drivers/net/wireless/ath/ath6kl/init.c Change-Id: Ib1a65814860cf97cadd142c17be0e91f43743832 --- include/linux/nl80211.h | 15 +++++++++++++++ include/net/cfg80211.h | 6 ++++++ net/wireless/core.c | 4 ++++ net/wireless/nl80211.c | 4 ++++ 4 files changed, 29 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e077580..8cc5987 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1095,6 +1095,11 @@ enum nl80211_commands { * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be * used for asking the driver to perform a TDLS operation. * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1323,6 +1328,8 @@ enum nl80211_attrs { NL80211_ATTR_TDLS_SUPPORT, NL80211_ATTR_TDLS_EXTERNAL_SETUP, + NL80211_ATTR_DEVICE_AP_SME, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2623,4 +2630,12 @@ enum nl80211_tdls_operation { NL80211_TDLS_DISABLE_LINK, }; +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7c206b6..b258e5c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1611,6 +1611,7 @@ struct cfg80211_ops { * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be * used for asking the driver/firmware to perform a TDLS operation. + * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1629,6 +1630,7 @@ enum wiphy_flags { WIPHY_FLAG_AP_UAPSD = BIT(14), WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), + WIPHY_FLAG_HAVE_AP_SME = BIT(17), }; /** @@ -1836,6 +1838,8 @@ struct wiphy_wowlan_support { * may request, if implemented. * * @wowlan: WoWLAN support information + * + * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -1859,6 +1863,8 @@ struct wiphy { u32 flags; + u32 ap_sme_capa; + enum cfg80211_signal_type signal_type; int bss_priv_size; diff --git a/net/wireless/core.c b/net/wireless/core.c index 112959d..8ba1553 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -492,6 +492,10 @@ int wiphy_register(struct wiphy *wiphy) !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) return -EINVAL; + if (WARN_ON(wiphy->ap_sme_capa && + !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) + return -EINVAL; + if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0a78297..b346860 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1002,6 +1002,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nl80211_put_iface_combinations(&dev->wiphy, msg)) goto nla_put_failure; + if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) + NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME, + dev->wiphy.ap_sme_capa); + return genlmsg_end(msg, hdr); nla_put_failure: -- cgit v1.1 From eef4c1efee2f8138ba91143a00adea178bcd5411 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 4 Nov 2011 11:18:12 +0100 Subject: nl80211: allow subscribing to unexpected class3 frames To implement AP mode without monitor interfaces we need to be able to send a deauth to stations that send frames without being associated. Enable this by adding a new nl80211 event for such frames that an application can subscribe to. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 12 +++++++++ include/net/cfg80211.h | 17 +++++++++++++ net/wireless/mlme.c | 16 ++++++++++++ net/wireless/nl80211.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 3 +++ 5 files changed, 114 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 8cc5987..445613c 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -497,6 +497,16 @@ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -626,6 +636,8 @@ enum nl80211_commands { NL80211_CMD_TDLS_OPER, NL80211_CMD_TDLS_MGMT, + NL80211_CMD_UNEXPECTED_FRAME, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b258e5c..918dc1f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2115,6 +2115,8 @@ struct wireless_dev { int beacon_interval; + u32 ap_unexpected_nlpid; + #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { @@ -3144,6 +3146,21 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, const u8 *bssid, bool preauth, gfp_t gfp); +/** + * cfg80211_rx_spurious_frame - inform userspace about a spurious frame + * @dev: The device the frame matched to + * @addr: the transmitter address + * @gfp: context flags + * + * This function is used in AP mode (only!) to inform userspace that + * a spurious class 3 frame was received, to be able to deauth the + * sender. + * Returns %true if the frame was passed to userspace (or this failed + * for a reason other than not having a subscription.) + */ +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 6630846..31a3dad 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -877,6 +877,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) } spin_unlock_bh(&wdev->mgmt_registrations_lock); + + if (nlpid == wdev->ap_unexpected_nlpid) + wdev->ap_unexpected_nlpid = 0; } void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) @@ -1104,3 +1107,16 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); + +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + return false; + + return nl80211_unexpected_frame(dev, addr, gfp); +} +EXPORT_SYMBOL(cfg80211_rx_spurious_frame); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b346860..25ed0fb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5580,6 +5580,23 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_register_unexpected_frame(struct sk_buff *skb, + struct genl_info *info) +{ + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + + if (wdev->ap_unexpected_nlpid) + return -EBUSY; + + wdev->ap_unexpected_nlpid = info->snd_pid; + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -6134,6 +6151,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_UNEXPECTED_FRAME, + .doit = nl80211_register_unexpected_frame, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -6954,6 +6979,47 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid); + + if (!nlpid) + return false; + + msg = nlmsg_new(100, gfp); + if (!msg) + return true; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_UNEXPECTED_FRAME); + if (!hdr) { + nlmsg_free(msg); + return true; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return true; + } + + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return true; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return true; +} + int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, int freq, const u8 *buf, size_t len, gfp_t gfp) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f24a1fb..d94456e 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -117,4 +117,7 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, int index, const u8 *bssid, bool preauth, gfp_t gfp); +bool nl80211_unexpected_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.1 From 82342154be417fc39c3afe54788314c5055b889e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 4 Nov 2011 11:18:15 +0100 Subject: nl80211: add API to probe a client When the AP SME in hostapd is used it wants to probe the clients when they have been idle for some time. Add explicit API to support this. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 10 +++++ include/net/cfg80211.h | 17 ++++++++ net/wireless/nl80211.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 445613c..fa921b7 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -507,6 +507,14 @@ * If used as the command it must have an interface index and you can * only unsubscribe from the event by closing the socket. * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -638,6 +646,8 @@ enum nl80211_commands { NL80211_CMD_UNEXPECTED_FRAME, + NL80211_CMD_PROBE_CLIENT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 918dc1f..f6f9fb4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1368,6 +1368,9 @@ struct cfg80211_gtk_rekey_data { * * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). + * + * @probe_client: probe an associated client, must return a cookie that it + * later passes to cfg80211_probe_status(). */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1553,6 +1556,9 @@ struct cfg80211_ops { u16 status_code, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper); + + int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie); }; /* @@ -3161,6 +3167,17 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); +/** + * cfg80211_probe_status - notify userspace about probe status + * @dev: the device the probe was sent on + * @addr: the address of the peer + * @cookie: the cookie filled in @probe_client previously + * @acked: indicates whether probe was acked or not + * @gfp: allocation flags + */ +void cfg80211_probe_status(struct net_device *dev, const u8 *addr, + u64 cookie, bool acked, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 25ed0fb..f0a03b6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -884,6 +884,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); + CMD(probe_client, PROBE_CLIENT); #undef CMD @@ -5597,6 +5598,59 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, return 0; } +static int nl80211_probe_client(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct sk_buff *msg; + void *hdr; + const u8 *addr; + u64 cookie; + int err; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!rdev->ops->probe_client) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_PROBE_CLIENT); + + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto free_msg; + } + + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); + if (err) + goto free_msg; + + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -6159,6 +6213,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_PROBE_CLIENT, + .doit = nl80211_probe_client, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -7280,6 +7342,48 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void cfg80211_probe_status(struct net_device *dev, const u8 *addr, + u64 cookie, bool acked, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + if (acked) + NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_probe_status); + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) -- cgit v1.1 From 26ae87e169689d445bcaed699e38875bc1134790 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 4 Nov 2011 11:18:17 +0100 Subject: cfg80211: allow registering to beacons Add the ability to register to received beacon frames to allow implementing OLBC logic in userspace. The registration is per wiphy since there's no point in receiving the same frame multiple times. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 7 +++++ include/net/cfg80211.h | 20 ++++++++++++++ net/wireless/core.h | 2 ++ net/wireless/nl80211.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index fa921b7..da5a98b 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -515,6 +515,11 @@ * up the event with the request. The event includes the same data and * has %NL80211_ATTR_ACK set if the frame was ACKed. * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -648,6 +653,8 @@ enum nl80211_commands { NL80211_CMD_PROBE_CLIENT, + NL80211_CMD_REGISTER_BEACONS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f6f9fb4..748de7b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1618,6 +1618,9 @@ struct cfg80211_ops { * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be * used for asking the driver/firmware to perform a TDLS operation. * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME + * @WIPHY_FLAG_REPORTS_OBSS: the device will report beacons from other BSSes + * when there are virtual interfaces in AP mode by calling + * cfg80211_report_obss_beacon(). */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1637,6 +1640,7 @@ enum wiphy_flags { WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), WIPHY_FLAG_HAVE_AP_SME = BIT(17), + WIPHY_FLAG_REPORTS_OBSS = BIT(18), }; /** @@ -3178,6 +3182,22 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev, void cfg80211_probe_status(struct net_device *dev, const u8 *addr, u64 cookie, bool acked, gfp_t gfp); +/** + * cfg80211_report_obss_beacon - report beacon from other APs + * @wiphy: The wiphy that received the beacon + * @frame: the frame + * @len: length of the frame + * @freq: frequency the frame was received on + * @gfp: allocation flags + * + * Use this function to report to userspace when a beacon was + * received. It is not useful to call this when there is no + * netdev that is in AP/GO mode. + */ +void cfg80211_report_obss_beacon(struct wiphy *wiphy, + const u8 *frame, size_t len, + int freq, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/core.h b/net/wireless/core.h index a570ff9..a020d38 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -54,6 +54,8 @@ struct cfg80211_registered_device { int opencount; /* also protected by devlist_mtx */ wait_queue_head_t dev_wait; + u32 ap_beacons_nlpid; + /* BSSes/scanning */ spinlock_t bss_lock; struct list_head bss_list; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f0a03b6..752d542 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -885,6 +885,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); CMD(probe_client, PROBE_CLIENT); + if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS); + } #undef CMD @@ -5651,6 +5655,21 @@ static int nl80211_probe_client(struct sk_buff *skb, return err; } +static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) + return -EOPNOTSUPP; + + if (rdev->ap_beacons_nlpid) + return -EBUSY; + + rdev->ap_beacons_nlpid = info->snd_pid; + + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -6221,6 +6240,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_REGISTER_BEACONS, + .doit = nl80211_register_beacons, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -7384,6 +7411,44 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, } EXPORT_SYMBOL(cfg80211_probe_status); +void cfg80211_report_obss_beacon(struct wiphy *wiphy, + const u8 *frame, size_t len, + int freq, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct sk_buff *msg; + void *hdr; + u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid); + + if (!nlpid) + return; + + msg = nlmsg_new(len + 100, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + if (freq) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame); + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_obss_beacon); + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) @@ -7397,9 +7462,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); - list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) + list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) cfg80211_mlme_unregister_socket(wdev, notify->pid); + if (rdev->ap_beacons_nlpid == notify->pid) + rdev->ap_beacons_nlpid = 0; + } rcu_read_unlock(); -- cgit v1.1 From 8b056ddc94d30fdb5bc5cd981c050b25cd5eabc7 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 27 Aug 2012 13:55:15 -0700 Subject: net: wireless: bcmdhd: Update to version 5.90.195.104 - Fix p2p support for new wpa_supplicant - Propagate disconnect error code for cfg80211 Change-Id: I9643fbad7b6170b0e794fd7854201931d5eefb22 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 6 ++- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 10 ++++- drivers/net/wireless/bcmdhd/dhd_linux.c | 20 ++++++--- drivers/net/wireless/bcmdhd/dhd_sdio.c | 11 +++-- drivers/net/wireless/bcmdhd/include/epivers.h | 8 ++-- drivers/net/wireless/bcmdhd/include/wlioctl.h | 61 +++------------------------ drivers/net/wireless/bcmdhd/wl_android.c | 2 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 18 ++++++-- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 2 + 9 files changed, 60 insertions(+), 78 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c index edecb5f..91232bd 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.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: bcmsdh_linux.c 343302 2012-07-06 13:07:38Z $ + * $Id: bcmsdh_linux.c 352863 2012-08-24 04:48:50Z $ */ /** @@ -404,6 +404,10 @@ bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* match this pci device with what we support */ /* we can't solely rely on this to believe it is our SDIO Host Controller! */ if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) { + if (pdev->vendor == VENDOR_BROADCOM) { + SDLX_MSG(("%s: Unknown Broadcom device (vendor: %#x, device: %#x).\n", + __FUNCTION__, pdev->vendor, pdev->device)); + } return -ENODEV; } diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index 67c2fbf..db051a4 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.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: bcmsdh_sdmmc.c 344243 2012-07-12 00:06:23Z $ + * $Id: bcmsdh_sdmmc.c 351910 2012-08-21 22:39:46Z $ */ #include @@ -1020,7 +1020,13 @@ sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, pkt_len -= xfred_len; xfred_len = 0; } - pkt_len = (pkt_len + 3) & 0xFFFFFFFC; + + /* Align Patch */ + if (!write || pkt_len < 32) + pkt_len = (pkt_len + 3) & 0xFFFFFFFC; + else if (pkt_len % DHD_SDALIGN) + pkt_len += DHD_SDALIGN - (pkt_len % DHD_SDALIGN); + #ifdef CONFIG_MMC_MSM7X00A if ((pkt_len % 64) == 32) { sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 4faef15..cf6d226 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 344350 2012-07-12 08:35:03Z $ + * $Id: dhd_linux.c 352789 2012-08-24 00:01:33Z $ */ #include @@ -327,8 +327,8 @@ uint dhd_console_ms = 0; module_param(dhd_console_ms, uint, 0644); #endif /* defined(DHD_DEBUG) */ -/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ -uint dhd_arp_mode = 0xb; +/* ARP offload agent mode : enable ARP Peer Auto-Reply */ +uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; module_param(dhd_arp_mode, uint, 0); /* ARP offload enable */ @@ -1361,10 +1361,11 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) done: - if (ret) + if (ret) { dhd->pub.dstats.tx_dropped++; - else + } else { dhd->pub.tx_packets++; + } DHD_OS_WAKE_UNLOCK(&dhd->pub); @@ -4330,6 +4331,13 @@ dhd_dev_reset(struct net_device *dev, uint8 flag) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + if (flag == TRUE) { + /* Issue wl down command before resetting the chip */ + if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { + DHD_TRACE(("%s: wl down failed\n", __FUNCTION__)); + } + } + ret = dhd_bus_devreset(&dhd->pub, flag); if (ret) { DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); @@ -4510,7 +4518,7 @@ static void dhd_hang_process(struct work_struct *work) wl_iw_send_priv_event(dev, "HANG"); #endif #if defined(WL_CFG80211) - wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); + wl_cfg80211_hang(dev, WLAN_REASON_DRIVER_ERROR); #endif } } diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index ed3da83..6049e16 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.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_sdio.c 338148 2012-06-11 20:35:45Z $ + * $Id: dhd_sdio.c 352730 2012-08-23 20:55:11Z $ */ #include @@ -1487,7 +1487,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) dhd_os_sdunlock(bus->dhd); #endif /* DHD_DEBUG */ } else if (pending == TRUE) { - /* signal pending */ + /* possibly fw hangs so never responsed back */ DHD_ERROR(("%s: signal pending\n", __FUNCTION__)); return -EINTR; } else { @@ -5192,7 +5192,7 @@ done: } #endif /* DHD_DEBUG */ -#ifdef DHD_DEBUG +#if (defined DHD_DEBUG) static void dhd_dump_cis(uint fn, uint8 *cis) { @@ -5468,12 +5468,11 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) { - DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", - err, DHD_INIT_CLKCTL1, clkctl)); + DHD_ERROR(("%s: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", + __FUNCTION__, err, DHD_INIT_CLKCTL1, clkctl)); goto fail; } - #ifdef DHD_DEBUG if (DHD_INFO_ON()) { uint fn, numfn; diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index 19da8f2..37c07e6 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -33,17 +33,17 @@ #define EPI_RC_NUMBER 195 -#define EPI_INCREMENTAL_NUMBER 98 +#define EPI_INCREMENTAL_NUMBER 104 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 195, 98 +#define EPI_VERSION 5, 90, 195, 104 -#define EPI_VERSION_NUM 0x055ac362 +#define EPI_VERSION_NUM 0x055ac368 #define EPI_VERSION_DEV 5.90.195 -#define EPI_VERSION_STR "5.90.195.98" +#define EPI_VERSION_STR "5.90.195.104" #endif diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index e543bfa..2038e20 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 331292 2012-05-04 09:04:23Z $ + * $Id: wlioctl.h 353331 2012-08-27 06:04:47Z $ */ @@ -83,44 +83,6 @@ typedef struct wl_af_params { #include - - - -#define LEGACY2_WL_BSS_INFO_VERSION 108 - - -typedef struct wl_bss_info_108 { - uint32 version; - uint32 length; - struct ether_addr BSSID; - uint16 beacon_period; - uint16 capability; - uint8 SSID_len; - uint8 SSID[32]; - struct { - uint count; - uint8 rates[16]; - } rateset; - chanspec_t chanspec; - uint16 atim_window; - uint8 dtim_period; - int16 RSSI; - int8 phy_noise; - - uint8 n_cap; - uint32 nbss_cap; - uint8 ctl_ch; - uint32 reserved32[1]; - uint8 flags; - uint8 reserved[3]; - uint8 basic_mcs[MCSSET_LEN]; - - uint16 ie_offset; - uint32 ie_length; - - -} wl_bss_info_108_t; - #define WL_BSS_INFO_VERSION 109 @@ -157,30 +119,19 @@ typedef struct wl_bss_info { } wl_bss_info_t; -typedef struct wl_bsscfg { - uint32 wsec; - uint32 WPA_auth; - uint32 wsec_index; - uint32 associated; - uint32 BSS; - uint32 phytest_on; - struct ether_addr prev_BSSID; - struct ether_addr BSSID; -} wl_bsscfg_t; -typedef struct wl_bss_config { - uint32 atim_window; - uint32 beacon_period; - uint32 chanspec; -} wl_bss_config_t; +#define WL_BSS_FLAGS_FROM_BEACON 0x01 +#define WL_BSS_FLAGS_FROM_CACHE 0x02 +#define WL_BSS_FLAGS_RSSI_ONCHANNEL 0x04 + +#define VHT_BI_SGI_80MHZ 0x00000100 typedef struct wlc_ssid { uint32 SSID_len; uchar SSID[32]; } wlc_ssid_t; -#define WL_BSS_FLAGS_FROM_BEACON 0x01 #define WL_BSSTYPE_INFRA 1 #define WL_BSSTYPE_INDEP 0 diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 4fcdcd3..ce74f0f 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -736,7 +736,7 @@ int wifi_set_power(int on, unsigned long msec) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) int wifi_get_mac_addr(unsigned char *buf) { - DHD_ERROR(("%s\n", __FUNCTION__)); + DHD_TRACE(("%s\n", __FUNCTION__)); if (!buf) return -EINVAL; if (wifi_control_data && wifi_control_data->get_mac_addr) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 9af2c1b..548cc05 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -188,6 +188,8 @@ static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx); static s32 wl_cfg80211_resume(struct wiphy *wiphy); +static s32 wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); #else @@ -952,7 +954,7 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) struct net_device *ndev = wl_to_prmry_ndev(wl); WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" "HANG Notification sent to %s\n", ret, ndev->name)); - wl_cfg80211_hang(ndev, WLAN_REASON_UNSPECIFIED); + wl_cfg80211_hang(ndev, WLAN_REASON_DRIVER_ERROR); } /* Wait for any pending scan req to get aborted from the sysioc context */ @@ -2663,7 +2665,7 @@ wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); return err; } - if (wsec & WEP_ENABLED) { + if (wsec == WEP_ENABLED) { /* Just select a new current key */ index = (u32) key_idx; index = htod32(index); @@ -4582,6 +4584,7 @@ static struct cfg80211_ops wl_cfg80211_ops = { .set_channel = wl_cfg80211_set_channel, .set_beacon = wl_cfg80211_add_set_beacon, .add_beacon = wl_cfg80211_add_set_beacon, + .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, #ifdef WL_SCHED_SCAN .sched_scan_start = wl_cfg80211_sched_scan_start, .sched_scan_stop = wl_cfg80211_sched_scan_stop, @@ -4652,6 +4655,8 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) /* wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; */ #endif + /* AP_SME flag can be advertised to remove patch from wpa_supplicant */ + wdev->wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; WL_DBG(("Registering custom regulatory)\n")); wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(wdev->wiphy, &brcm_regdom); @@ -5449,7 +5454,7 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, - completed ? WLAN_STATUS_SUCCESS : WLAN_STATUS_AUTH_TIMEOUT, + completed ? WLAN_STATUS_SUCCESS : e->reason, GFP_KERNEL); if (completed) WL_INFO(("Report connect result - connection succeeded\n")); @@ -7825,3 +7830,10 @@ void wl_cfg80211_enable_trace(int level) { wl_dbg_level |= WL_DBG_DBG; } + +static s32 +wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +{ + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 974770e..8886968 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -153,6 +153,8 @@ do { \ #define WL_SCB_TIMEOUT 20 #endif +#define WLAN_REASON_DRIVER_ERROR WLAN_REASON_UNSPECIFIED + /* driver status */ enum wl_status { WL_STATUS_READY = 0, -- cgit v1.1 From d7cdb87a5ea2979a86355d05290893335bf08ea3 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 27 Aug 2012 17:38:43 -0700 Subject: net: wireless: bcmdhd: Add ASSOC event processing Change-Id: Ide0b887ba52af56d0aaa415111df5ba16265c8d8 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index cf6d226..f4ae2a3 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -3215,6 +3215,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_SET_SSID); setbit(eventmask, WLC_E_PRUNE); setbit(eventmask, WLC_E_AUTH); + setbit(eventmask, WLC_E_ASSOC); setbit(eventmask, WLC_E_REASSOC); setbit(eventmask, WLC_E_REASSOC_IND); setbit(eventmask, WLC_E_DEAUTH); -- cgit v1.1 From ddb45c7e8572c73ba6b0367dcd27ca4e2d5bcee4 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 28 Aug 2012 16:17:37 -0700 Subject: net: wireless: bcmdhd: Fix sending ctrl packet - Schedule dpc thread to send ctrl frame if we cannot send ctrl packet immediately. Change-Id: I5ae8b705a6ccddcc1f5c86b4c094342b57d26c7f Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_sdio.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 6049e16..09dc45a 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -1375,7 +1375,13 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) /* Send from dpc */ bus->ctrl_frame_buf = frame; bus->ctrl_frame_len = len; - dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); + if (!bus->dpc_sched) { + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + } + if (bus->ctrl_frame_stat) { + dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); + } if (bus->ctrl_frame_stat == FALSE) { DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ret = 0; -- cgit v1.1 From 1dfbc281b4a77cd19dd65fa447a8567b3c0b93f2 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 28 Aug 2012 16:53:32 -0700 Subject: netfilter: xt_qtaguid: report only uid tags to non-privileged processes In the past, a process could only see its own stats (uid-based summary, and details). Now we allow any process to see other UIDs uid-based stats, but still hide the detailed stats. Change-Id: I7666961ed244ac1d9359c339b048799e5db9facc Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index f6d4cfc..ea716b3 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2588,8 +2588,9 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); - - if (!can_read_other_uid_stats(stat_uid)) { + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u\n", -- cgit v1.1 From 6a5fe817ac903697a9e996af18bc74f0d8e82ce9 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 7 Sep 2012 11:22:54 -0700 Subject: net: wireless: bcmdhd: Report proper mcs rate mask Setting proper mcs rate mask allows to connect in 802.11n-only mode Change-Id: Iffd47a60c92a74d29e42d81d79fa64463f5895c5 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 548cc05..ea6aedd 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7293,6 +7293,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) 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; + wiphy->bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; } } -- cgit v1.1 From 477f1d90566c9e6c4252d83e1ccef359aa3d9501 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 13 Sep 2012 13:56:32 -0700 Subject: net: wireless: bcmdhd: Add ROAM setting per platform from Makefle Change-Id: Ia6bc3025e3641cb6b91022ab1c9976c0f6ad16a4 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/Makefile | 2 ++ drivers/net/wireless/bcmdhd/dhd.h | 14 ++++++++++++++ drivers/net/wireless/bcmdhd/dhd_linux.c | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index eda803e..0e9ca24 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -25,6 +25,8 @@ endif ifneq ($(CONFIG_CFG80211),) bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o DHDCFLAGS += -DWL_CFG80211 +DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-70 +DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 endif ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) DHDCFLAGS += -DWL_SCHED_SCAN diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 6106555..34616d3 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -608,6 +608,20 @@ extern uint dhd_pktgen_len; #endif +/* hooks for custom Roaming Trigger setting via Makefile */ +#define DEFAULT_ROAM_TRIGGER_VALUE -75 /* dBm default roam trigger all band */ +#define DEFAULT_ROAM_TRIGGER_SETTING -1 +#ifndef CUSTOM_ROAM_TRIGGER_SETTING +#define CUSTOM_ROAM_TRIGGER_SETTING DEFAULT_ROAM_TRIGGER_VALUE +#endif + +/* hooks for custom Roaming Romaing setting via Makefile */ +#define DEFAULT_ROAM_DELTA_VALUE 10 /* dBm default roam delta all band */ +#define DEFAULT_ROAM_DELTA_SETTING -1 +#ifndef CUSTOM_ROAM_DELTA_SETTING +#define CUSTOM_ROAM_DELTA_SETTING DEFAULT_ROAM_DELTA_VALUE +#endif + /* optionally set by a module_param_string() */ #define MOD_PARAM_PATHLEN 2048 extern char fw_path[MOD_PARAM_PATHLEN]; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index f4ae2a3..a189d63 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -2990,6 +2990,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) char *ptr; uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ uint16 chipID; + int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; + int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL}; #if defined(SOFTAP) uint dtim = 1; #endif @@ -3157,6 +3159,14 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); + /* custom romaing setting */ + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, + sizeof(roam_trigger), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret)); + if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, + sizeof(roam_delta), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret)); + /* Set PowerSave mode */ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); -- cgit v1.1