staging: brcm80211: implementation of RFKILL functionality
authorArend van Spriel <arend@broadcom.com>
Fri, 21 Jan 2011 14:44:12 +0000 (15:44 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 4 Feb 2011 21:01:03 +0000 (13:01 -0800)
Resubmitted the patch to align with staging-next tree. This change
depends on suspend/resume patch as sent on Wed, Jan 12, 2011.

Only hardware switch state needs to be handled by driver. RFKILL is
informed when hardware switch is activated. MAC80211 rfkill_poll
callback is used to check hardware switch deactivation.

Reviewed-by: Brett Rudley <brudley@broadcom.com>
Reviewed-by: Henry Ptasinski <henryp@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/brcm80211/brcmsmac/wl_export.h
drivers/staging/brcm80211/brcmsmac/wl_mac80211.c
drivers/staging/brcm80211/brcmsmac/wlc_bmac.c
drivers/staging/brcm80211/brcmsmac/wlc_mac80211.c
drivers/staging/brcm80211/brcmsmac/wlc_pub.h

index aa8b5a3..16c626a 100644 (file)
@@ -34,6 +34,7 @@ extern void wl_down(struct wl_info *wl);
 extern void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
                             int prio);
 extern bool wl_alloc_dma_resources(struct wl_info *wl, uint dmaddrwidth);
+extern bool wl_rfkill_set_hw_state(struct wl_info *wl);
 
 /* timer functions */
 struct wl_timer;
index add0046..5ce0f07 100644 (file)
@@ -142,6 +142,7 @@ static int wl_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 static int wl_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                           enum ieee80211_ampdu_mlme_action action,
                           struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+static void wl_ops_rfkill_poll(struct ieee80211_hw *hw);
 
 static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
@@ -162,6 +163,7 @@ static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 static int wl_ops_start(struct ieee80211_hw *hw)
 {
        struct wl_info *wl = hw->priv;
+       bool blocked;
        /*
          struct ieee80211_channel *curchan = hw->conf.channel;
          WL_NONE("%s : Initial channel: %d\n", __func__, curchan->hw_value);
@@ -170,6 +172,9 @@ static int wl_ops_start(struct ieee80211_hw *hw)
        WL_LOCK(wl);
        ieee80211_wake_queues(hw);
        WL_UNLOCK(wl);
+       blocked = wl_rfkill_set_hw_state(wl);
+       if (!blocked)
+               wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
 
        return 0;
 }
@@ -205,8 +210,9 @@ wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        err = wl_up(wl);
        WL_UNLOCK(wl);
 
-       if (err != 0)
+       if (err != 0) {
                WL_ERROR("%s: wl_up() returned %d\n", __func__, err);
+       }
        return err;
 }
 
@@ -583,6 +589,19 @@ wl_ampdu_action(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void wl_ops_rfkill_poll(struct ieee80211_hw *hw)
+{
+       struct wl_info *wl = HW_TO_WL(hw);
+       bool blocked;
+
+       WL_LOCK(wl);
+       blocked = wlc_check_radio_disabled(wl->wlc);
+       WL_UNLOCK(wl);
+
+       WL_ERROR("wl: rfkill_poll: %d\n", blocked);
+       wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
+}
+
 static const struct ieee80211_ops wl_ops = {
        .tx = wl_ops_tx,
        .start = wl_ops_start,
@@ -604,6 +623,7 @@ static const struct ieee80211_ops wl_ops = {
        .sta_add = wl_sta_add,
        .sta_remove = wl_sta_remove,
        .ampdu_action = wl_ampdu_action,
+       .rfkill_poll = wl_ops_rfkill_poll,
 };
 
 static int wl_set_hint(struct wl_info *wl, char *abbrev)
@@ -1137,6 +1157,11 @@ static void wl_remove(struct pci_dev *pdev)
                WL_ERROR("wl: wl_remove: pci_get_drvdata failed\n");
                return;
        }
+
+       /* make sure rfkill is not using driver */
+       wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
+       wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
+
        if (!wlc_chipmatch(pdev->vendor, pdev->device)) {
                WL_ERROR("wl: wl_remove: wlc_chipmatch failed\n");
                return;
@@ -1815,3 +1840,13 @@ int wl_check_firmwares(struct wl_info *wl)
        return rc;
 }
 
+bool wl_rfkill_set_hw_state(struct wl_info *wl)
+{
+       bool blocked = wlc_check_radio_disabled(wl->wlc);
+
+       WL_ERROR("%s: update hw state: blocked=%s\n", __func__, blocked ? "true" : "false");
+       wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
+       if (blocked)
+               wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
+       return blocked;
+}
index 80716c5..631ee73 100644 (file)
@@ -428,14 +428,10 @@ bool BCMFASTPATH wlc_dpc(struct wlc_info *wlc, bool bounded)
        }
 
        if (macintstatus & MI_RFDISABLE) {
-#if defined(BCMDBG)
-               u32 rfd = R_REG(wlc_hw->osh, &regs->phydebug) & PDBG_RFD;
-#endif
-
-               WL_ERROR("wl%d: MAC Detected a change on the RF Disable Input 0x%x\n",
-                        wlc_hw->unit, rfd);
+               WL_TRACE("wl%d: BMAC Detected a change on the RF Disable Input\n", wlc_hw->unit);
 
                WLCNTINCR(wlc->pub->_cnt->rfdisable);
+               wl_rfkill_set_hw_state(wlc->wl);
        }
 
        /* send any enq'd tx packets. Just makes sure to jump start tx */
index 683bc5f..bba84eb 100644 (file)
@@ -2304,10 +2304,6 @@ void wlc_radio_mpc_upd(struct wlc_info *wlc)
  */
 static void wlc_radio_upd(struct wlc_info *wlc)
 {
-       if (wlc->pub->radio_disabled)
-               wlc_radio_disable(wlc);
-       else
-               wlc_radio_enable(wlc);
 }
 
 /* maintain LED behavior in down state */
@@ -2324,6 +2320,14 @@ static void wlc_down_led_upd(struct wlc_info *wlc)
        }
 }
 
+/* update hwradio status and return it */
+bool wlc_check_radio_disabled(struct wlc_info *wlc)
+{
+       wlc_radio_hwdisable_upd(wlc);
+
+       return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ? true : false;
+}
+
 void wlc_radio_disable(struct wlc_info *wlc)
 {
        if (!wlc->pub->up) {
index e8b252a..0e39414 100644 (file)
@@ -598,6 +598,7 @@ extern void wlc_getrand(struct wlc_info *wlc, u8 *buf, int len);
 struct scb;
 extern void wlc_ps_on(struct wlc_info *wlc, struct scb *scb);
 extern void wlc_ps_off(struct wlc_info *wlc, struct scb *scb, bool discard);
+extern bool wlc_check_radio_disabled(struct wlc_info *wlc);
 extern bool wlc_radio_monitor_stop(struct wlc_info *wlc);
 
 #if defined(BCMDBG)