mwifiex: add set_cqm_rssi_config handler support
authorAmitkumar Karwar <akarwar@marvell.com>
Fri, 16 Mar 2012 03:51:51 +0000 (20:51 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 9 Apr 2012 20:06:00 +0000 (16:06 -0400)
In this handler LOW_RSSI and HIGH_RSSI events are subscribed
to FW using provided threshold value so that FW will monitor
connection quality and trigger any of these events.

Driver will notify cfg80211 about connection quality based on
inputs from FW and provided hysteresis.

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Yogesh Ashok Powar <yogeshp@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c

index 96c1e3b24da37bde60358ab65a21bfac6f8edbb0..c7e89188c35078cf6cc3e2410544e91f01548529 100644 (file)
@@ -765,6 +765,45 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
        return 0;
 }
 
+/*
+ * CFG802.11 operation handler for connection quality monitoring.
+ *
+ * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI
+ * events to FW.
+ */
+static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+                                               struct net_device *dev,
+                                               s32 rssi_thold, u32 rssi_hyst)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_ds_misc_subsc_evt subsc_evt;
+
+       priv->cqm_rssi_thold = rssi_thold;
+       priv->cqm_rssi_hyst = rssi_hyst;
+
+       memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
+       subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+
+       /* Subscribe/unsubscribe low and high rssi events */
+       if (rssi_thold && rssi_hyst) {
+               subsc_evt.action = HostCmd_ACT_BITWISE_SET;
+               subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold);
+               subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
+               subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+               subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+               return mwifiex_send_cmd_sync(priv,
+                                            HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+                                            0, 0, &subsc_evt);
+       } else {
+               subsc_evt.action = HostCmd_ACT_BITWISE_CLR;
+               return mwifiex_send_cmd_sync(priv,
+                                            HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+                                            0, 0, &subsc_evt);
+       }
+
+       return 0;
+}
+
 /*
  * CFG802.11 operation handler for disconnection request.
  *
@@ -1367,6 +1406,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
        .set_tx_power = mwifiex_cfg80211_set_tx_power,
        .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask,
+       .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
 };
 
 /*
index 930ad2f4b1e3f04d1acefa005a2a357f9493089c..e3b8c7062dbe251eb38715f931bbc76a7242da77 100644 (file)
@@ -92,10 +92,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_KEY_MATERIAL       (PROPRIETARY_TLV_BASE_ID + 0)
 #define TLV_TYPE_CHANLIST           (PROPRIETARY_TLV_BASE_ID + 1)
 #define TLV_TYPE_NUMPROBES          (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW           (PROPRIETARY_TLV_BASE_ID + 4)
 #define TLV_TYPE_PASSTHROUGH        (PROPRIETARY_TLV_BASE_ID + 10)
 #define TLV_TYPE_WMMQSTATUS         (PROPRIETARY_TLV_BASE_ID + 16)
 #define TLV_TYPE_WILDCARDSSID       (PROPRIETARY_TLV_BASE_ID + 18)
 #define TLV_TYPE_TSFTIMESTAMP       (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 22)
 #define TLV_TYPE_AUTH_TYPE          (PROPRIETARY_TLV_BASE_ID + 31)
 #define TLV_TYPE_CHANNELBANDLIST    (PROPRIETARY_TLV_BASE_ID + 42)
 #define TLV_TYPE_RATE_DROP_CONTROL  (PROPRIETARY_TLV_BASE_ID + 82)
@@ -194,6 +196,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_802_11_KEY_MATERIAL               0x005e
 #define HostCmd_CMD_802_11_BG_SCAN_QUERY              0x006c
 #define HostCmd_CMD_WMM_GET_STATUS                    0x0071
+#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT            0x0075
 #define HostCmd_CMD_802_11_TX_RATE_QUERY              0x007f
 #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS     0x0083
 #define HostCmd_CMD_VERSION_EXT                       0x0097
@@ -228,6 +231,8 @@ enum ENH_PS_MODES {
 #define HostCmd_RET_BIT                       0x8000
 #define HostCmd_ACT_GEN_GET                   0x0000
 #define HostCmd_ACT_GEN_SET                   0x0001
+#define HostCmd_ACT_BITWISE_SET               0x0002
+#define HostCmd_ACT_BITWISE_CLR               0x0003
 #define HostCmd_RESULT_OK                     0x0000
 
 #define HostCmd_ACT_MAC_RX_ON                 0x0001
@@ -1146,6 +1151,17 @@ struct host_cmd_ds_pcie_details {
        u32 sleep_cookie_addr_hi;
 } __packed;
 
+struct mwifiex_ie_types_rssi_threshold {
+       struct mwifiex_ie_types_header header;
+       u8 abs_value;
+       u8 evt_freq;
+} __packed;
+
+struct host_cmd_ds_802_11_subsc_evt {
+       __le16 action;
+       __le16 events;
+} __packed;
+
 struct host_cmd_ds_command {
        __le16 command;
        __le16 size;
@@ -1195,6 +1211,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_set_bss_mode bss_mode;
                struct host_cmd_ds_pcie_details pcie_host_spec;
                struct host_cmd_ds_802_11_eeprom_access eeprom;
+               struct host_cmd_ds_802_11_subsc_evt subsc_evt;
        } params;
 } __packed;
 
index 58fe054370467bc5b84ff589427aa59762fec5b3..99c06649f94cbbf39a5ad9197c463acf649c1521 100644 (file)
@@ -280,6 +280,27 @@ struct mwifiex_ds_misc_cmd {
        u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER];
 };
 
+#define BITMASK_BCN_RSSI_LOW   BIT(0)
+#define BITMASK_BCN_RSSI_HIGH  BIT(4)
+
+enum subsc_evt_rssi_state {
+       EVENT_HANDLED,
+       RSSI_LOW_RECVD,
+       RSSI_HIGH_RECVD
+};
+
+struct subsc_evt_cfg {
+       u8 abs_value;
+       u8 evt_freq;
+};
+
+struct mwifiex_ds_misc_subsc_evt {
+       u16 action;
+       u16 events;
+       struct subsc_evt_cfg bcn_l_rssi_cfg;
+       struct subsc_evt_cfg bcn_h_rssi_cfg;
+};
+
 #define MWIFIEX_MAX_VSIE_LEN       (256)
 #define MWIFIEX_MAX_VSIE_NUM       (8)
 #define MWIFIEX_VSIE_MASK_SCAN     0x01
index 964570ad2f30d5e3ef28102228f31ae24825598a..3bbe163647315d2a971d617bea7e8458030533f0 100644 (file)
@@ -458,6 +458,9 @@ struct mwifiex_private {
        u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
        struct wps wps;
        u8 scan_block;
+       s32 cqm_rssi_thold;
+       u32 cqm_rssi_hyst;
+       u8 subsc_evt_rssi_state;
 };
 
 enum mwifiex_ba_status {
index 6c8e4594b48bed9bbaa148fe3888e3311ba48d58..e90c34d9c63d550223914e30c4087cd825e644a9 100644 (file)
@@ -906,6 +906,101 @@ mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv,
        return 0;
 }
 
+/*
+ * This function prepares command for event subscription, configuration
+ * and query. Events can be subscribed or unsubscribed. Current subscribed
+ * events can be queried. Also, current subscribed events are reported in
+ * every FW response.
+ */
+static int
+mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv,
+                            struct host_cmd_ds_command *cmd,
+                            struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg)
+{
+       struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt;
+       struct mwifiex_ie_types_rssi_threshold *rssi_tlv;
+       u16 event_bitmap;
+       u8 *pos;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT);
+       cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) +
+                               S_DS_GEN);
+
+       subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action);
+       dev_dbg(priv->adapter->dev, "cmd: action: %d\n", subsc_evt_cfg->action);
+
+       /*For query requests, no configuration TLV structures are to be added.*/
+       if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET)
+               return 0;
+
+       subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events);
+
+       event_bitmap = subsc_evt_cfg->events;
+       dev_dbg(priv->adapter->dev, "cmd: event bitmap : %16x\n",
+               event_bitmap);
+
+       if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) ||
+            (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) &&
+           (event_bitmap == 0)) {
+               dev_dbg(priv->adapter->dev, "Error: No event specified "
+                       "for bitwise action type\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Append TLV structures for each of the specified events for
+        * subscribing or re-configuring. This is not required for
+        * bitwise unsubscribing request.
+        */
+       if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR)
+               return 0;
+
+       pos = ((u8 *)subsc_evt) +
+                       sizeof(struct host_cmd_ds_802_11_subsc_evt);
+
+       if (event_bitmap & BITMASK_BCN_RSSI_LOW) {
+               rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos;
+
+               rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW);
+               rssi_tlv->header.len =
+                   cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) -
+                               sizeof(struct mwifiex_ie_types_header));
+               rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value;
+               rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq;
+
+               dev_dbg(priv->adapter->dev, "Cfg Beacon Low Rssi event, "
+                       "RSSI:-%d dBm, Freq:%d\n",
+                       subsc_evt_cfg->bcn_l_rssi_cfg.abs_value,
+                       subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq);
+
+               pos += sizeof(struct mwifiex_ie_types_rssi_threshold);
+               le16_add_cpu(&cmd->size,
+                            sizeof(struct mwifiex_ie_types_rssi_threshold));
+       }
+
+       if (event_bitmap & BITMASK_BCN_RSSI_HIGH) {
+               rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos;
+
+               rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH);
+               rssi_tlv->header.len =
+                   cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) -
+                               sizeof(struct mwifiex_ie_types_header));
+               rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value;
+               rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq;
+
+               dev_dbg(priv->adapter->dev, "Cfg Beacon Low Rssi event, "
+                       "RSSI:-%d dBm, Freq:%d\n",
+                       subsc_evt_cfg->bcn_h_rssi_cfg.abs_value,
+                       subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq);
+
+               pos += sizeof(struct mwifiex_ie_types_rssi_threshold);
+               le16_add_cpu(&cmd->size,
+                            sizeof(struct mwifiex_ie_types_rssi_threshold));
+       }
+
+       return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1086,6 +1181,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
        case HostCmd_CMD_PCIE_DESC_DETAILS:
                ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action);
                break;
+       case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
+               ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);
index cd90b6f0969f2925430385f350c3d5648de10ed4..3aa54243dea979b5a11b3ed440840f9b51855a57 100644 (file)
@@ -123,6 +123,7 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
 {
        struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp =
                                                &resp->params.rssi_info_rsp;
+       struct mwifiex_ds_misc_subsc_evt subsc_evt;
 
        priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last);
        priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last);
@@ -136,6 +137,30 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
        priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg);
        priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg);
 
+       if (priv->subsc_evt_rssi_state == EVENT_HANDLED)
+               return 0;
+
+       /* Resubscribe low and high rssi events with new thresholds */
+       memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
+       subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+       subsc_evt.action = HostCmd_ACT_BITWISE_SET;
+       if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) {
+               subsc_evt.bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg -
+                               priv->cqm_rssi_hyst);
+               subsc_evt.bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold);
+       } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) {
+               subsc_evt.bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold);
+               subsc_evt.bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg +
+                               priv->cqm_rssi_hyst);
+       }
+       subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+       subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+
+       priv->subsc_evt_rssi_state = EVENT_HANDLED;
+
+       mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+                              0, 0, &subsc_evt);
+
        return 0;
 }
 
@@ -754,6 +779,28 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
        return 0;
 }
 
+/*
+ * This function handles the command response for subscribe event command.
+ */
+static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv,
+                                struct host_cmd_ds_command *resp,
+                                struct mwifiex_ds_misc_subsc_evt *sub_event)
+{
+       struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event =
+               (struct host_cmd_ds_802_11_subsc_evt *)&resp->params.subsc_evt;
+
+       /* For every subscribe event command (Get/Set/Clear), FW reports the
+        * current set of subscribed events*/
+       dev_dbg(priv->adapter->dev, "Bitmap of currently subscribed events: %16x\n",
+               le16_to_cpu(cmd_sub_event->events));
+
+       /*Return the subscribed event info for a Get request*/
+       if (sub_event)
+               sub_event->events = le16_to_cpu(cmd_sub_event->events);
+
+       return 0;
+}
+
 /*
  * This function handles the command responses.
  *
@@ -894,6 +941,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                break;
        case HostCmd_CMD_PCIE_DESC_DETAILS:
                break;
+       case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
+               ret = mwifiex_ret_subsc_evt(priv, resp, data_buf);
+               break;
        default:
                dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
                        resp->command);
index 33b311ce125383cbbfc3376e01374056ed70fc10..f6bbb9307f868c98a45078045eb973597ddc5a8c 100644 (file)
@@ -314,6 +314,12 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                break;
 
        case EVENT_RSSI_LOW:
+               cfg80211_cqm_rssi_notify(priv->netdev,
+                                        NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                                        GFP_KERNEL);
+               mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO,
+                                      HostCmd_ACT_GEN_GET, 0, NULL);
+               priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
                dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n");
                break;
        case EVENT_SNR_LOW:
@@ -323,6 +329,12 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                dev_dbg(adapter->dev, "event: MAX_FAIL\n");
                break;
        case EVENT_RSSI_HIGH:
+               cfg80211_cqm_rssi_notify(priv->netdev,
+                                        NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                                        GFP_KERNEL);
+               mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO,
+                                      HostCmd_ACT_GEN_GET, 0, NULL);
+               priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
                dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n");
                break;
        case EVENT_SNR_HIGH: