Browse Source

qcacld-3.0: Add monitor mode support

Add monitor mode support. Configure target to deliver 802.11 packets
in raw mode. Below is the procedure to start the monitor mode.
insmod /system/lib/modules/wlan.ko con_mode=4
ifconfig wlan0 up
"iwpriv wlan0 setMonChan 36 2"
or
"iw dev mon0 set channel 36 HT40+"
tcpdump -i wlan0 -w <tcpdump.pcap>

In this mode concurrency is not supported and module doesnot support Tx.

Change-Id: I211ece0a66e2d43bc111e523714942e1557e36f4
CRs-Fixed: 963060
Manjunathappa Prakash 9 years ago
parent
commit
59f861d11d

+ 16 - 0
core/hdd/inc/wlan_hdd_main.h

@@ -631,6 +631,20 @@ typedef struct hdd_cfg80211_state_s {
 	eP2PActionFrameState actionFrmState;
 } hdd_cfg80211_state_t;
 
+/**
+ * struct hdd_mon_set_ch_info - Holds monitor mode channel switch params
+ * @channel: Channel number.
+ * @cb_mode: Channel bonding
+ * @channel_width: Channel width 0/1/2 for 20/40/80MHz respectively.
+ * @phy_mode: PHY mode
+ */
+struct hdd_mon_set_ch_info {
+	uint8_t channel;
+	uint8_t cb_mode;
+	uint32_t channel_width;
+	eCsrPhyMode phy_mode;
+};
+
 struct hdd_station_ctx {
 	/** Handle to the Wireless Extension State */
 	hdd_wext_state_t WextState;
@@ -665,6 +679,8 @@ struct hdd_station_ctx {
 	int staDebugState;
 
 	uint8_t broadcast_ibss_staid;
+
+	struct hdd_mon_set_ch_info ch_info;
 };
 
 #define BSS_STOP    0

+ 1 - 3
core/hdd/inc/wlan_hdd_tx_rx.h

@@ -128,11 +128,9 @@ static inline void wlan_hdd_log_eapol(struct sk_buff *skb,
 }
 #endif /* FEATURE_WLAN_DIAG_SUPPORT */
 
-
 const char *hdd_reason_type_to_string(enum netif_reason_type reason);
 const char *hdd_action_type_to_string(enum netif_action_type action);
 void wlan_hdd_netif_queue_control(hdd_adapter_t *adapter,
 		enum netif_action_type action, enum netif_reason_type reason);
-
-
+int hdd_set_mon_rx_cb(struct net_device *dev);
 #endif /* end #if !defined(WLAN_HDD_TX_RX_H) */

+ 63 - 0
core/hdd/src/wlan_hdd_cfg.c

@@ -44,6 +44,7 @@
 #include <csr_api.h>
 #include <wlan_hdd_misc.h>
 #include <wlan_hdd_napi.h>
+#include <cds_concurrency.h>
 
 static void
 cb_notify_set_roam_prefer5_g_hz(hdd_context_t *pHddCtx, unsigned long notifyId)
@@ -5474,6 +5475,66 @@ config_exit:
 	return qdf_status;
 }
 
+/**
+ * hdd_disable_runtime_pm() - Override to disable runtime_pm.
+ * @cfg_ini: Handle to struct hdd_config
+ *
+ * Return: None
+ */
+#ifdef FEATURE_RUNTIME_PM
+static void hdd_disable_runtime_pm(struct hdd_config *cfg_ini)
+{
+	cfg_ini->runtime_pm = 0;
+}
+#else
+static void hdd_disable_runtime_pm(struct hdd_config *cfg_ini)
+{
+}
+#endif
+
+/**
+ * hdd_disable_auto_shutdown() - Override to disable auto_shutdown.
+ * @cfg_ini: Handle to struct hdd_config
+ *
+ * Return: None
+ */
+#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
+static void hdd_disable_auto_shutdown(struct hdd_config *cfg_ini)
+{
+	cfg_ini->WlanAutoShutdown = 0;
+}
+#else
+static void hdd_disable_auto_shutdown(struct hdd_config *cfg_ini)
+{
+}
+#endif
+
+/**
+ * hdd_override_all_ps() - overrides to disables all the powersave features.
+ * @hdd_ctx: Pointer to HDD context.
+ * Overrides below powersave ini configurations.
+ * gEnableImps=0
+ * gEnableBmps=0
+ * gRuntimePM=0
+ * gWlanAutoShutdown = 0
+ * gEnableSuspend=0
+ * gEnablePowerSaveOffload=0
+ * gEnableWoW=0
+ *
+ * Return: None
+ */
+static void hdd_override_all_ps(hdd_context_t *hdd_ctx)
+{
+	struct hdd_config *cfg_ini = hdd_ctx->config;
+
+	cfg_ini->fIsImpsEnabled = 0;
+	cfg_ini->is_ps_enabled = 0;
+	hdd_disable_runtime_pm(cfg_ini);
+	hdd_disable_auto_shutdown(cfg_ini);
+	cfg_ini->enablePowersaveOffload = 0;
+	cfg_ini->wowEnable = 0;
+}
+
 /**
  * hdd_parse_config_ini() - parse the ini configuration file
  * @pHddCtx: the pointer to hdd context
@@ -5577,6 +5638,8 @@ QDF_STATUS hdd_parse_config_ini(hdd_context_t *pHddCtx)
 		hdd_napi_event(NAPI_EVT_INI_FILE,
 			       (void *)pHddCtx->config->napi_enable);
 #endif /* FEATURE_NAPI */
+	if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam())
+		hdd_override_all_ps(pHddCtx);
 
 config_exit:
 	release_firmware(fw);

+ 147 - 18
core/hdd/src/wlan_hdd_cfg80211.c

@@ -505,6 +505,24 @@ wlan_hdd_p2p_p2p_iface_limit[] = {
 	},
 };
 
+static const struct ieee80211_iface_limit
+	wlan_hdd_mon_iface_limit[] = {
+	{
+		.max = 3,       /* Monitor interface */
+		.types = BIT(NL80211_IFTYPE_MONITOR),
+	},
+};
+
+static struct ieee80211_iface_combination
+	wlan_hdd_mon_iface[] = {
+	{
+		.limits = wlan_hdd_mon_iface_limit,
+		.max_interfaces = 3,
+		.num_different_channels = 2,
+		.n_limits = ARRAY_SIZE(wlan_hdd_mon_iface_limit),
+	},
+};
+
 static struct ieee80211_iface_combination
 	wlan_hdd_iface_combination[] = {
 	/* STA */
@@ -5908,26 +5926,35 @@ int wlan_hdd_cfg80211_init(struct device *dev,
 
 	wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRESS;
 
-	/* Supports STATION & AD-HOC modes right now */
-	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
-				 | BIT(NL80211_IFTYPE_ADHOC)
-				 | BIT(NL80211_IFTYPE_P2P_CLIENT)
-				 | BIT(NL80211_IFTYPE_P2P_GO)
-				 | BIT(NL80211_IFTYPE_AP);
-
-	if (pCfg->advertiseConcurrentOperation) {
-		if (pCfg->enableMCC) {
-			int i;
-			for (i = 0; i < ARRAY_SIZE(wlan_hdd_iface_combination);
-			     i++) {
-				if (!pCfg->allowMCCGODiffBI)
-					wlan_hdd_iface_combination[i].
-					beacon_int_infra_match = true;
+	if (cds_get_conparam() != QDF_GLOBAL_MONITOR_MODE) {
+		/* Supports STATION & AD-HOC modes right now */
+		wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
+					 | BIT(NL80211_IFTYPE_ADHOC)
+					 | BIT(NL80211_IFTYPE_P2P_CLIENT)
+					 | BIT(NL80211_IFTYPE_P2P_GO)
+					 | BIT(NL80211_IFTYPE_AP);
+
+		if (pCfg->advertiseConcurrentOperation) {
+			if (pCfg->enableMCC) {
+				int i;
+
+				for (i = 0;
+				     i < ARRAY_SIZE(wlan_hdd_iface_combination);
+				     i++) {
+					if (!pCfg->allowMCCGODiffBI)
+						wlan_hdd_iface_combination[i].
+						beacon_int_infra_match = true;
+				}
 			}
+			wiphy->n_iface_combinations =
+				ARRAY_SIZE(wlan_hdd_iface_combination);
+			wiphy->iface_combinations = wlan_hdd_iface_combination;
 		}
+	} else {
+		wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
 		wiphy->n_iface_combinations =
-			ARRAY_SIZE(wlan_hdd_iface_combination);
-		wiphy->iface_combinations = wlan_hdd_iface_combination;
+			ARRAY_SIZE(wlan_hdd_mon_iface);
+		wiphy->iface_combinations = wlan_hdd_mon_iface;
 	}
 
 	/* Before registering we need to update the ht capabilitied based
@@ -8242,6 +8269,10 @@ void hdd_select_cbmode(hdd_adapter_t *pAdapter, uint8_t operationChannel)
 	uint8_t iniDot11Mode = (WLAN_HDD_GET_CTX(pAdapter))->config->dot11Mode;
 	eHddDot11Mode hddDot11Mode = iniDot11Mode;
 	struct ch_params_s ch_params;
+	hdd_station_ctx_t *station_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+	uint32_t cb_mode;
+	struct hdd_mon_set_ch_info *ch_info = &station_ctx->ch_info;
+
 	ch_params.ch_width =
 			(WLAN_HDD_GET_CTX(pAdapter))->config->vhtChannelWidth;
 
@@ -8264,10 +8295,20 @@ void hdd_select_cbmode(hdd_adapter_t *pAdapter, uint8_t operationChannel)
 		break;
 	}
 	/* This call decides required channel bonding mode */
-	sme_set_ch_params((WLAN_HDD_GET_CTX(pAdapter)->hHal),
+	cb_mode = sme_set_ch_params((WLAN_HDD_GET_CTX(pAdapter)->hHal),
 				hdd_cfg_xlate_to_csr_phy_mode(hddDot11Mode),
 				operationChannel, 0,
 				&ch_params);
+
+	if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam()) {
+		ch_info->channel_width = ch_params.ch_width;
+		ch_info->phy_mode = hdd_cfg_xlate_to_csr_phy_mode(hddDot11Mode);
+		ch_info->channel = operationChannel;
+		ch_info->cb_mode = cb_mode;
+		hdd_info("ch_info width %d, phymode %d channel %d",
+			 ch_info->channel_width, ch_info->phy_mode,
+			 ch_info->channel);
+	}
 }
 
 /**
@@ -11841,6 +11882,93 @@ enum cds_con_mode wlan_hdd_convert_nl_iftype_to_hdd_type(
 	return mode;
 }
 
+/**
+ * wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel
+ * @wiphy: Handle to struct wiphy to get handle to module context.
+ * @chandef: Contains information about the capture channel to be set.
+ *
+ * This interface is called if and only if monitor mode interface alone is
+ * active.
+ *
+ * Return: 0 success or error code on failure.
+ */
+static int __wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy,
+				       struct cfg80211_chan_def *chandef)
+{
+	hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+	hdd_adapter_t *adapter;
+	hdd_station_ctx_t *sta_ctx;
+	struct hdd_mon_set_ch_info *ch_info;
+	QDF_STATUS status;
+	tHalHandle hal_hdl;
+	struct qdf_mac_addr bssid;
+	tCsrRoamProfile roam_profile;
+	struct ch_params_s ch_params;
+	int ret;
+	uint16_t chan_num = cds_freq_to_chan(chandef->chan->center_freq);
+
+	ENTER();
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret)
+		return ret;
+
+	hal_hdl = hdd_ctx->hHal;
+
+	adapter = hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE);
+	if (!adapter)
+		return -EIO;
+
+	hdd_info("%s: set monitor mode Channel %d and freq %d",
+		 adapter->dev->name, chan_num, chandef->chan->center_freq);
+
+	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	ch_info = &sta_ctx->ch_info;
+	hdd_select_cbmode(adapter, chan_num);
+	roam_profile.ChannelInfo.ChannelList = &ch_info->channel;
+	roam_profile.ChannelInfo.numOfChannels = 1;
+	roam_profile.phyMode = ch_info->phy_mode;
+	roam_profile.ch_params.ch_width = chandef->width;
+
+	qdf_mem_copy(bssid.bytes, adapter->macAddressCurrent.bytes,
+		     QDF_MAC_ADDR_SIZE);
+
+	ch_params.ch_width = chandef->width;
+	sme_set_ch_params(hal_hdl, ch_info->phy_mode, chan_num, 0,
+			  &ch_params);
+	status = sme_roam_channel_change_req(hal_hdl, bssid, &ch_params,
+						 &roam_profile);
+	if (status) {
+		hdd_err("Status: %d Failed to set sme_RoamChannel for monitor mode",
+			status);
+		ret = qdf_status_to_os_return(status);
+		return ret;
+	}
+	EXIT();
+	return 0;
+}
+
+/**
+ * wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel
+ * @wiphy: Handle to struct wiphy to get handle to module context.
+ * @chandef: Contains information about the capture channel to be set.
+ *
+ * This interface is called if and only if monitor mode interface alone is
+ * active.
+ *
+ * Return: 0 success or error code on failure.
+ */
+static int wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy,
+				       struct cfg80211_chan_def *chandef)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_set_mon_ch(wiphy, chandef);
+	cds_ssr_unprotect(__func__);
+	return ret;
+}
+
 /**
  * struct cfg80211_ops - cfg80211_ops
  *
@@ -11956,4 +12084,5 @@ static struct cfg80211_ops wlan_hdd_cfg80211_ops = {
 #ifdef CHANNEL_SWITCH_SUPPORTED
 	.channel_switch = wlan_hdd_cfg80211_channel_switch,
 #endif
+	.set_monitor_channel = wlan_hdd_cfg80211_set_mon_ch,
 };

+ 97 - 3
core/hdd/src/wlan_hdd_main.c

@@ -1435,6 +1435,42 @@ bool hdd_is_valid_mac_address(const uint8_t *pMacAddr)
 	return xdigit == 12 && (separator == 5 || separator == 0);
 }
 
+/**
+ * __hdd__mon_open() - HDD Open function
+ * @dev: Pointer to net_device structure
+ *
+ * This is called in response to ifconfig up
+ *
+ * Return: 0 for success; non-zero for failure
+ */
+static int __hdd_mon_open(struct net_device *dev)
+{
+	int ret;
+
+	ENTER_DEV(dev);
+	ret = hdd_set_mon_rx_cb(dev);
+	return ret;
+}
+
+/**
+ * hdd_mon_open() - Wrapper function for __hdd_mon_open to protect it from SSR
+ * @dev:	Pointer to net_device structure
+ *
+ * This is called in response to ifconfig up
+ *
+ * Return: 0 for success; non-zero for failure
+ */
+int hdd_mon_open(struct net_device *dev)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __hdd_mon_open(dev);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
+
 /**
  * __hdd_open() - HDD Open function
  * @dev:	Pointer to net_device structure
@@ -1892,9 +1928,44 @@ static struct net_device_ops wlan_drv_ops = {
 #endif
 };
 
+/* Monitor mode net_device_ops, doesnot Tx and most of operations. */
+static struct net_device_ops wlan_mon_drv_ops = {
+	.ndo_open = hdd_mon_open,
+	.ndo_stop = hdd_stop,
+	.ndo_get_stats = hdd_get_stats,
+};
+
+/**
+ * hdd_set_station_ops() - update net_device ops for monitor mode
+ * @pWlanDev: Handle to struct net_device to be updated.
+ * Return: None
+ */
 void hdd_set_station_ops(struct net_device *pWlanDev)
 {
-	pWlanDev->netdev_ops = &wlan_drv_ops;
+	if (QDF_GLOBAL_MONITOR_MODE == cds_get_conparam())
+		pWlanDev->netdev_ops = &wlan_mon_drv_ops;
+	else
+		pWlanDev->netdev_ops = &wlan_drv_ops;
+}
+
+/**
+ * hdd_mon_mode_ether_setup() - Update monitor mode struct net_device.
+ * @dev: Handle to struct net_device to be updated.
+ *
+ * Return: None
+ */
+static void hdd_mon_mode_ether_setup(struct net_device *dev)
+{
+	dev->header_ops         = NULL;
+	dev->type               = ARPHRD_IEEE80211_RADIOTAP;
+	dev->hard_header_len    = ETH_HLEN;
+	dev->mtu                = ETH_DATA_LEN;
+	dev->addr_len           = ETH_ALEN;
+	dev->tx_queue_len       = 1000; /* Ethernet wants good queues */
+	dev->flags              = IFF_BROADCAST|IFF_MULTICAST;
+	dev->priv_flags        |= IFF_TX_SKB_SHARING;
+
+	memset(dev->broadcast, 0xFF, ETH_ALEN);
 }
 
 /**
@@ -1922,7 +1993,9 @@ static hdd_adapter_t *hdd_alloc_station_adapter(hdd_context_t *hdd_ctx,
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS)
 				   name_assign_type,
 #endif
-				   ether_setup, NUM_TX_QUEUES);
+				(QDF_GLOBAL_MONITOR_MODE == cds_get_conparam() ?
+				hdd_mon_mode_ether_setup : ether_setup),
+				NUM_TX_QUEUES);
 
 	if (pWlanDev != NULL) {
 
@@ -2434,6 +2507,7 @@ hdd_adapter_t *hdd_open_adapter(hdd_context_t *hdd_ctx, uint8_t session_type,
 	case QDF_P2P_CLIENT_MODE:
 	case QDF_P2P_DEVICE_MODE:
 	case QDF_OCB_MODE:
+	case QDF_MONITOR_MODE:
 	{
 		adapter = hdd_alloc_station_adapter(hdd_ctx, macAddr,
 						    name_assign_type,
@@ -2450,6 +2524,8 @@ hdd_adapter_t *hdd_open_adapter(hdd_context_t *hdd_ctx, uint8_t session_type,
 			adapter->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT;
 		else if (QDF_P2P_DEVICE_MODE == session_type)
 			adapter->wdev.iftype = NL80211_IFTYPE_P2P_DEVICE;
+		else if (QDF_MONITOR_MODE == session_type)
+			adapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
 		else
 			adapter->wdev.iftype = NL80211_IFTYPE_STATION;
 
@@ -5635,6 +5711,21 @@ static inline int hdd_open_p2p_interface(struct hdd_context_t *hdd_ctx,
 }
 #endif
 
+/**
+ * hdd_open_monitor_interface() - Open monitor mode interface
+ * @hdd_ctx: HDD context
+ * @rtnl_held: True if RTNL lock is held
+ *
+ * Return: Primary adapter on success and PTR_ERR on failure
+ */
+static hdd_adapter_t *hdd_open_monitor_interface(hdd_context_t *hdd_ctx,
+						 bool rtnl_held)
+{
+	return hdd_open_adapter(hdd_ctx, QDF_MONITOR_MODE, "wlan%d",
+				wlan_hdd_get_intf_addr(hdd_ctx),
+				NET_NAME_UNKNOWN, rtnl_held);
+}
+
 /**
  * hdd_open_interfaces - Open all required interfaces
  * hdd_ctx:	HDD context
@@ -6059,7 +6150,10 @@ int hdd_wlan_startup(struct device *dev, void *hif_sc)
 
 	rtnl_held = hdd_hold_rtnl_lock();
 
-	adapter = hdd_open_interfaces(hdd_ctx, rtnl_held);
+	if (QDF_GLOBAL_MONITOR_MODE == hdd_get_conparam())
+		adapter = hdd_open_monitor_interface(hdd_ctx, rtnl_held);
+	else
+		adapter = hdd_open_interfaces(hdd_ctx, rtnl_held);
 
 	if (IS_ERR(adapter)) {
 		ret = PTR_ERR(adapter);

+ 121 - 0
core/hdd/src/wlan_hdd_tx_rx.c

@@ -55,6 +55,7 @@
 #include "wlan_hdd_lro.h"
 
 #include "cdp_txrx_peer_ops.h"
+#include "ol_txrx.h"
 
 #ifdef FEATURE_WLAN_DIAG_SUPPORT
 #define HDD_EAPOL_ETHER_TYPE             (0x888E)
@@ -669,6 +670,84 @@ QDF_STATUS hdd_deinit_tx_rx(hdd_adapter_t *pAdapter)
 	return status;
 }
 
+/**
+ * hdd_mon_rx_packet_cbk() - Receive callback registered with OL layer.
+ * @context: [in] pointer to qdf context
+ * @rxBuf:      [in] pointer to rx qdf_nbuf
+ *
+ * TL will call this to notify the HDD when one or more packets were
+ * received for a registered STA.
+ *
+ * Return: QDF_STATUS_E_FAILURE if any errors encountered, QDF_STATUS_SUCCESS
+ * otherwise
+ */
+static QDF_STATUS hdd_mon_rx_packet_cbk(void *context, qdf_nbuf_t rxbuf)
+{
+	hdd_adapter_t *adapter;
+	int rxstat;
+	struct sk_buff *skb;
+	struct sk_buff *skb_next;
+	unsigned int cpu_index;
+
+	/* Sanity check on inputs */
+	if ((NULL == context) || (NULL == rxbuf)) {
+		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Null params being passed", __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	adapter = (hdd_adapter_t *)context;
+	if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
+		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
+			  "invalid adapter %p", adapter);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	cpu_index = wlan_hdd_get_cpu();
+
+	/* walk the chain until all are processed */
+	skb = (struct sk_buff *) rxbuf;
+	while (NULL != skb) {
+		skb_next = skb->next;
+		skb->dev = adapter->dev;
+
+		++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index];
+		++adapter->stats.rx_packets;
+		adapter->stats.rx_bytes += skb->len;
+
+		/* Remove SKB from internal tracking table before submitting
+		 * it to stack
+		 */
+		qdf_net_buf_debug_release_skb(skb);
+
+		/*
+		 * If this is not a last packet on the chain
+		 * Just put packet into backlog queue, not scheduling RX sirq
+		 */
+		if (skb->next) {
+			rxstat = netif_rx(skb);
+		} else {
+			/*
+			 * This is the last packet on the chain
+			 * Scheduling rx sirq
+			 */
+			rxstat = netif_rx_ni(skb);
+		}
+
+		if (NET_RX_SUCCESS == rxstat)
+			++adapter->
+				hdd_stats.hddTxRxStats.rxDelivered[cpu_index];
+		else
+			++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index];
+
+		skb = skb_next;
+	}
+
+	adapter->dev->last_rx = jiffies;
+
+	return QDF_STATUS_SUCCESS;
+}
+
 /**
  * hdd_rx_packet_cbk() - Receive packet handler
  * @context: pointer to HDD context
@@ -1108,3 +1187,45 @@ void wlan_hdd_netif_queue_control(hdd_adapter_t *adapter,
 	return;
 }
 
+/**
+ * hdd_set_mon_rx_cb() - Set Monitor mode Rx callback
+ * @dev:        Pointer to net_device structure
+ *
+ * Return: 0 for success; non-zero for failure
+ */
+int hdd_set_mon_rx_cb(struct net_device *dev)
+{
+	hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	hdd_context_t *hdd_ctx =  WLAN_HDD_GET_CTX(adapter);
+	int ret;
+	QDF_STATUS qdf_status;
+	struct ol_txrx_desc_type sta_desc = {0};
+	struct ol_txrx_ops txrx_ops;
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (0 != ret)
+		return ret;
+
+	qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
+	txrx_ops.rx.rx = hdd_mon_rx_packet_cbk;
+	ol_txrx_vdev_register(
+		 ol_txrx_get_vdev_from_vdev_id(adapter->sessionId),
+		 adapter, &txrx_ops);
+	/* peer is created wma_vdev_attach->wma_create_peer */
+	qdf_status = ol_txrx_register_peer(&sta_desc);
+	if (QDF_STATUS_SUCCESS != qdf_status) {
+		hdd_err("WLANTL_RegisterSTAClient() failed to register. Status= %d [0x%08X]",
+			qdf_status, qdf_status);
+		goto exit;
+	}
+
+	qdf_status = sme_create_mon_session(hdd_ctx->hHal,
+				     adapter->macAddressCurrent.bytes);
+	if (QDF_STATUS_SUCCESS != qdf_status) {
+		hdd_err("sme_create_mon_session() failed to register. Status= %d [0x%08X]",
+			qdf_status, qdf_status);
+	}
+exit:
+	ret = qdf_status_to_os_return(qdf_status);
+	return ret;
+}

+ 55 - 0
core/hdd/src/wlan_hdd_wext.c

@@ -367,6 +367,7 @@ static const hdd_freq_chan_map_t freq_chan_map[] = {
 
 #define WE_SET_DUAL_MAC_SCAN_CONFIG    21
 #define WE_SET_DUAL_MAC_FW_MODE_CONFIG 22
+#define WE_SET_MON_MODE_CHAN 23
 
 #ifdef FEATURE_WLAN_TDLS
 #undef  MAX_VAR_ARGS
@@ -9728,6 +9729,53 @@ static int iw_set_band_config(struct net_device *dev,
 	return ret;
 }
 
+/**
+ * wlan_hdd_set_mon_chan() - Set capture channel on the monitor mode interface.
+ * @adapter: Handle to adapter
+ * @chan: Monitor mode channel
+ * @bandwidth: Capture channel bandwidth
+ *
+ * Return: 0 on success else error code.
+ */
+static int wlan_hdd_set_mon_chan(hdd_adapter_t *adapter, uint32_t chan,
+				 uint32_t bandwidth)
+{
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	struct hdd_mon_set_ch_info *ch_info = &sta_ctx->ch_info;
+	QDF_STATUS status;
+	tHalHandle hal_hdl = hdd_ctx->hHal;
+	struct qdf_mac_addr bssid;
+	tCsrRoamProfile roam_profile;
+	struct ch_params_s ch_params;
+
+	if (QDF_GLOBAL_MONITOR_MODE != hdd_get_conparam()) {
+		hdd_err("Not supported, device is not in monitor mode");
+		return -EINVAL;
+	}
+
+	hdd_info("Set monitor mode Channel %d", chan);
+	hdd_select_cbmode(adapter, chan);
+	roam_profile.ChannelInfo.ChannelList = &ch_info->channel;
+	roam_profile.ChannelInfo.numOfChannels = 1;
+	roam_profile.phyMode = ch_info->phy_mode;
+	roam_profile.ch_params.ch_width = bandwidth;
+
+	qdf_mem_copy(bssid.bytes, adapter->macAddressCurrent.bytes,
+		     QDF_MAC_ADDR_SIZE);
+
+	ch_params.ch_width = bandwidth;
+	sme_set_ch_params(hal_hdl, ch_info->phy_mode, chan, 0, &ch_params);
+	status = sme_roam_channel_change_req(hal_hdl, bssid, &ch_params,
+					     &roam_profile);
+	if (status) {
+		hdd_err("Status: %d Failed to set sme_roam Channel for monitor mode",
+			status);
+	}
+
+	return qdf_status_to_os_return(status);
+}
+
 static int __iw_set_two_ints_getnone(struct net_device *dev,
 				     struct iw_request_info *info,
 				     union iwreq_data *wrqu, char *extra)
@@ -9793,6 +9841,9 @@ static int __iw_set_two_ints_getnone(struct net_device *dev,
 		if (value[1] == DUMP_DP_TRACE)
 			qdf_dp_trace_dump_all(value[2]);
 		break;
+	case WE_SET_MON_MODE_CHAN:
+		ret = wlan_hdd_set_mon_chan(pAdapter, value[1], value[2]);
+		break;
 	default:
 		hddLog(LOGE, "%s: Invalid IOCTL command %d", __func__, sub_cmd);
 		break;
@@ -11014,6 +11065,10 @@ static const struct iw_priv_args we_private_args[] = {
 	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
 	 0, "dump_dp_trace"}
 	,
+	{WE_SET_MON_MODE_CHAN,
+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
+	 0, "setMonChan"}
+	,
 };
 
 const struct iw_handler_def we_handler_def = {

+ 13 - 0
core/mac/inc/sir_api.h

@@ -494,6 +494,7 @@ typedef enum eSirBssType {
 	eSIR_INFRA_AP_MODE,     /* Added for softAP support */
 	eSIR_IBSS_MODE,
 	eSIR_AUTO_MODE,
+	eSIR_MONITOR_MODE,
 	eSIR_DONOT_USE_BSS_TYPE = SIR_MAX_ENUM_SIZE
 } tSirBssType;
 
@@ -2429,6 +2430,18 @@ typedef struct sSirUpdateParams {
 	uint8_t ssidHidden;     /* Hide SSID */
 } tSirUpdateParams, *tpSirUpdateParams;
 
+/**
+ * struct sir_create_session - Used for creating session in monitor mode
+ * @type: SME host message type.
+ * @msg_len: Length of the message.
+ * @bss_id: bss_id for creating the session.
+ */
+struct sir_create_session {
+	uint16_t type;
+	uint16_t msg_len;
+	struct qdf_mac_addr bss_id;
+};
+
 /* Beacon Interval */
 typedef struct sSirChangeBIParams {
 	uint16_t messageType;

+ 1 - 0
core/mac/inc/wni_api.h

@@ -249,6 +249,7 @@ enum eWniMsgTypes {
 	eWNI_SME_SET_ANTENNA_MODE_REQ,
 	eWNI_SME_SET_ANTENNA_MODE_RESP,
 	eWNI_SME_TSF_EVENT,
+	eWNI_SME_MON_INIT_SESSION,
 	eWNI_SME_MSG_TYPES_END
 };
 

+ 1 - 0
core/sme/inc/sme_api.h

@@ -1125,4 +1125,5 @@ QDF_STATUS sme_get_bpf_offload_capabilities(tHalHandle hal);
 QDF_STATUS sme_set_bpf_instructions(tHalHandle hal,
 				struct sir_bpf_set_offload *);
 
+QDF_STATUS sme_create_mon_session(tHalHandle hal_handle, uint8_t *bssid);
 #endif /* #if !defined( __SME_API_H ) */

+ 23 - 1
core/sme/src/common/sme_api.c

@@ -15630,7 +15630,6 @@ QDF_STATUS sme_remove_beacon_filter(tHalHandle hal, uint32_t session_id)
 	return qdf_status;
 }
 
-
 /**
  * sme_get_bpf_offload_capabilities() - Get length for BPF offload
  * @hal: Global HAL handle
@@ -15754,3 +15753,26 @@ QDF_STATUS sme_bpf_offload_register_callback(tHalHandle hal,
 	}
 	return status;
 }
+
+/**
+ * sme_create_mon_session() - post message to create PE session for monitormode
+ * operation
+ * @hal_handle: Handle to the HAL
+ * @bssid: pointer to bssid
+ *
+ * Return: QDF_STATUS_SUCCESS on success, non-zero error code on failure.
+ */
+QDF_STATUS sme_create_mon_session(tHalHandle hal_handle, tSirMacAddr bss_id)
+{
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
+	struct sir_create_session *msg;
+
+	msg = qdf_mem_malloc(sizeof(*msg));
+	if (NULL != msg) {
+		msg->type = eWNI_SME_MON_INIT_SESSION;
+		msg->msg_len = sizeof(*msg);
+		qdf_mem_copy(msg->bss_id.bytes, bss_id, QDF_MAC_ADDR_SIZE);
+		status = cds_send_mb_message_to_mac(msg);
+	}
+	return status;
+}