qcacmn: P2P protocol changes to Disable/Enable NOA
P2P-GO whenever goes offchannel, issues NOA. With this behavior, we
have issue whenever legacy STA connects to P2P-GO since legacy STA
does not understand NOA. To handle this case, changes are done in p2p
protocol component and the revised behavior will be:
1. By default no change in NOA.
2. Whenever first legacy STA connects to GO, p2p protocol component
disables NOA (i.e. P2P-GO will issue Self-CTS whenever it goes
off-channel).
3. Whenever last legacy STA disconnects from GO, p2p protocol
component enables NOA (i.e. P2P-GO will issue NOA whenever it goes
off-channel).
Change-Id: I113950ac3fa99ca68fcafcfe4c6095cf4b2def43
CRs-Fixed: 2035609
diff --git a/umac/cmn_services/inc/wlan_cmn.h b/umac/cmn_services/inc/wlan_cmn.h
index e5484f1..9f771b6 100644
--- a/umac/cmn_services/inc/wlan_cmn.h
+++ b/umac/cmn_services/inc/wlan_cmn.h
@@ -198,7 +198,9 @@
* enum wlan_peer_type - peer type
* @WLAN_PEER_SELF: for AP mode, SELF PEER or AP PEER are same
* @WLAN_PEER_AP: BSS peer for STA mode, Self peer for AP mode
+ * @WLAN_PEER_P2P_GO: BSS peer for P2P CLI mode, Self peer for P2P GO mode
* @WLAN_PEER_STA: Self Peer for STA mode, STA peer for AP mode
+ * @WLAN_PEER_P2P_CLI: Self peer for P2P CLI mode, P2P CLI peer for P2P GO mode
* @WLAN_PEER_TDLS: TDLS Peer
* @WLAN_PEER_NAWDS: NAWDS Peer
* @WLAN_PEER_STA_TEMP: STA Peer Temp (its host only node)
@@ -207,11 +209,13 @@
enum wlan_peer_type {
WLAN_PEER_SELF = 1,
WLAN_PEER_AP = 2,
- WLAN_PEER_STA = 3,
- WLAN_PEER_TDLS = 4,
- WLAN_PEER_NAWDS = 5,
- WLAN_PEER_STA_TEMP = 6,
- WLAN_PEER_IBSS = 7,
+ WLAN_PEER_P2P_GO = 3,
+ WLAN_PEER_STA = 4,
+ WLAN_PEER_P2P_CLI = 5,
+ WLAN_PEER_TDLS = 6,
+ WLAN_PEER_NAWDS = 7,
+ WLAN_PEER_STA_TEMP = 8,
+ WLAN_PEER_IBSS = 9,
};
/**
diff --git a/umac/cmn_services/obj_mgr/src/wlan_objmgr_peer_obj.c b/umac/cmn_services/obj_mgr/src/wlan_objmgr_peer_obj.c
index bc130c0..7eb5692 100644
--- a/umac/cmn_services/obj_mgr/src/wlan_objmgr_peer_obj.c
+++ b/umac/cmn_services/obj_mgr/src/wlan_objmgr_peer_obj.c
@@ -164,7 +164,8 @@
qdf_spinlock_create(&peer->peer_lock);
wlan_objmgr_peer_get_ref(peer, WLAN_OBJMGR_ID);
/* Increment ref count for BSS peer, so that BSS peer deletes last*/
- if ((type == WLAN_PEER_STA) || (type == WLAN_PEER_STA_TEMP))
+ if ((type == WLAN_PEER_STA) || (type == WLAN_PEER_STA_TEMP)
+ || (type == WLAN_PEER_P2P_CLI))
wlan_objmgr_peer_get_ref(wlan_vdev_get_bsspeer(vdev),
WLAN_OBJMGR_ID);
/* TODO init other parameters */
diff --git a/umac/cmn_services/obj_mgr/src/wlan_objmgr_vdev_obj.c b/umac/cmn_services/obj_mgr/src/wlan_objmgr_vdev_obj.c
index fe6732e..510610a 100644
--- a/umac/cmn_services/obj_mgr/src/wlan_objmgr_vdev_obj.c
+++ b/umac/cmn_services/obj_mgr/src/wlan_objmgr_vdev_obj.c
@@ -580,7 +580,8 @@
wlan_obj_vdev_peerlist_add_tail(&objmgr->wlan_peer_list, peer);
objmgr->wlan_peer_count++;
- if (wlan_peer_get_peer_type(peer) == WLAN_PEER_AP) {
+ if ((wlan_peer_get_peer_type(peer) == WLAN_PEER_AP) ||
+ (wlan_peer_get_peer_type(peer) == WLAN_PEER_P2P_GO)) {
if (WLAN_ADDR_EQ(wlan_peer_get_macaddr(peer),
wlan_vdev_mlme_get_macaddr(vdev)) ==
QDF_STATUS_SUCCESS) {
@@ -615,7 +616,8 @@
return QDF_STATUS_E_FAILURE;
}
- if (wlan_peer_get_peer_type(peer) == WLAN_PEER_AP) {
+ if ((wlan_peer_get_peer_type(peer) == WLAN_PEER_AP) ||
+ (wlan_peer_get_peer_type(peer) == WLAN_PEER_P2P_GO)) {
if (wlan_vdev_get_selfpeer(vdev) == peer) {
/*
* There might be instances where new node is created
diff --git a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h
index 1070ad0..0955c3b 100644
--- a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h
+++ b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h
@@ -253,6 +253,7 @@
* @set_ps: function pointer to set power save
* @lo_start: function pointer to start listen offload
* @lo_stop: function pointer to stop listen offload
+ * @set_noa: function pointer to disable/enable NOA
* @reg_lo_ev_handler: function pointer to register lo event handler
* @reg_noa_ev_handler: function pointer to register noa event handler
* @unreg_lo_ev_handler: function pointer to unregister lo event handler
@@ -265,6 +266,8 @@
struct p2p_lo_start *lo_start);
QDF_STATUS (*lo_stop)(struct wlan_objmgr_psoc *psoc,
uint32_t vdev_id);
+ QDF_STATUS (*set_noa)(struct wlan_objmgr_psoc *psoc,
+ uint32_t vdev_id, bool disable_noa);
QDF_STATUS (*reg_lo_ev_handler)(struct wlan_objmgr_psoc *psoc,
void *arg);
QDF_STATUS (*reg_noa_ev_handler)(struct wlan_objmgr_psoc *psoc,
diff --git a/umac/p2p/core/src/wlan_p2p_main.c b/umac/p2p/core/src/wlan_p2p_main.c
index bc613c6..9084c75 100644
--- a/umac/p2p/core/src/wlan_p2p_main.c
+++ b/umac/p2p/core/src/wlan_p2p_main.c
@@ -211,6 +211,8 @@
}
p2p_vdev_obj->vdev = vdev;
+ p2p_vdev_obj->noa_status = true;
+ p2p_vdev_obj->non_p2p_peer_count = 0;
status = wlan_objmgr_vdev_component_obj_attach(vdev,
WLAN_UMAC_COMP_P2P, p2p_vdev_obj,
@@ -286,6 +288,127 @@
}
/**
+ * p2p_peer_obj_create_notification() - manages peer details per vdev
+ * @peer: peer object
+ * @arg: Pointer to private argument - NULL
+ *
+ * This function gets called from object manager when peer is being
+ * created.
+ *
+ * Return: QDF_STATUS_SUCCESS - in case of success
+ */
+static QDF_STATUS p2p_peer_obj_create_notification(
+ struct wlan_objmgr_peer *peer, void *arg)
+{
+ struct wlan_objmgr_vdev *vdev;
+ struct p2p_vdev_priv_obj *p2p_vdev_obj;
+ enum tQDF_ADAPTER_MODE mode;
+ enum wlan_peer_type peer_type;
+
+ if (!peer) {
+ p2p_err("peer context passed is NULL");
+ return QDF_STATUS_E_INVAL;
+ }
+
+ wlan_peer_obj_lock(peer);
+ vdev = wlan_peer_get_vdev(peer);
+ wlan_peer_obj_unlock(peer);
+ p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
+ WLAN_UMAC_COMP_P2P);
+ wlan_peer_obj_lock(peer);
+ peer_type = wlan_peer_get_peer_type(peer);
+ wlan_peer_obj_unlock(peer);
+ if ((peer_type == WLAN_PEER_STA) && p2p_vdev_obj) {
+
+ wlan_vdev_obj_lock(vdev);
+ mode = wlan_vdev_mlme_get_opmode(vdev);
+ wlan_vdev_obj_unlock(vdev);
+ if (mode == QDF_P2P_GO_MODE) {
+ p2p_vdev_obj->non_p2p_peer_count++;
+ p2p_debug("Non P2P peer count: %d",
+ p2p_vdev_obj->non_p2p_peer_count);
+ }
+ }
+ p2p_debug("p2p peer object create successful");
+
+ return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * p2p_peer_obj_destroy_notification() - clears peer details per vdev
+ * @peer: peer object
+ * @arg: Pointer to private argument - NULL
+ *
+ * This function gets called from object manager when peer is being
+ * destroyed.
+ *
+ * Return: QDF_STATUS_SUCCESS - in case of success
+ */
+static QDF_STATUS p2p_peer_obj_destroy_notification(
+ struct wlan_objmgr_peer *peer, void *arg)
+{
+ struct wlan_objmgr_vdev *vdev;
+ struct p2p_vdev_priv_obj *p2p_vdev_obj;
+ struct wlan_objmgr_psoc *psoc;
+ enum tQDF_ADAPTER_MODE mode;
+ enum wlan_peer_type peer_type;
+ uint8_t vdev_id;
+
+ if (!peer) {
+ p2p_err("peer context passed is NULL");
+ return QDF_STATUS_E_INVAL;
+ }
+
+ wlan_peer_obj_lock(peer);
+ vdev = wlan_peer_get_vdev(peer);
+ wlan_peer_obj_unlock(peer);
+ p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
+ WLAN_UMAC_COMP_P2P);
+ wlan_vdev_obj_lock(vdev);
+ psoc = wlan_vdev_get_psoc(vdev);
+ wlan_vdev_obj_unlock(vdev);
+ if (!p2p_vdev_obj || !psoc) {
+ p2p_err("p2p_vdev_obj:%p psoc:%p", p2p_vdev_obj, psoc);
+ return QDF_STATUS_E_INVAL;
+ }
+
+ wlan_vdev_obj_lock(vdev);
+ mode = wlan_vdev_mlme_get_opmode(vdev);
+ wlan_vdev_obj_unlock(vdev);
+
+ wlan_peer_obj_lock(peer);
+ peer_type = wlan_peer_get_peer_type(peer);
+ wlan_peer_obj_unlock(peer);
+
+ if ((peer_type == WLAN_PEER_STA) && (mode == QDF_P2P_GO_MODE)) {
+
+ p2p_vdev_obj->non_p2p_peer_count--;
+
+ if (!p2p_vdev_obj->non_p2p_peer_count &&
+ (p2p_vdev_obj->noa_status == false)) {
+
+ wlan_vdev_obj_lock(vdev);
+ vdev_id = wlan_vdev_get_id(vdev);
+ wlan_vdev_obj_unlock(vdev);
+
+ if (ucfg_p2p_set_noa(psoc, vdev_id,
+ false) == QDF_STATUS_SUCCESS)
+ p2p_vdev_obj->noa_status = true;
+ else
+ p2p_vdev_obj->noa_status = false;
+
+ p2p_debug("Non p2p peer disconnected from GO,NOA status: %d.",
+ p2p_vdev_obj->noa_status);
+ }
+ p2p_debug("Non P2P peer count: %d",
+ p2p_vdev_obj->non_p2p_peer_count);
+ }
+ p2p_debug("p2p peer object destroy successful");
+
+ return QDF_STATUS_SUCCESS;
+}
+
+/**
* p2p_send_noa_to_pe() - send noa information to pe
* @noa_info: vdev context
*
@@ -348,6 +471,68 @@
return QDF_STATUS_SUCCESS;
}
+/**
+ * process_peer_for_noa() - disable NoA
+ * @vdev: vdev object
+ * @psoc: soc object
+ * @peer: peer object
+ *
+ * This function disables NoA
+ *
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS process_peer_for_noa(struct wlan_objmgr_vdev *vdev,
+ struct wlan_objmgr_psoc *psoc,
+ struct wlan_objmgr_peer *peer)
+{
+ struct p2p_vdev_priv_obj *p2p_vdev_obj = NULL;
+ enum tQDF_ADAPTER_MODE mode;
+ enum wlan_peer_type peer_type;
+ bool disable_noa;
+ uint8_t vdev_id;
+
+ if (!vdev || !psoc || !peer) {
+ p2p_err("vdev:%p psoc:%p peer:%p", vdev, psoc, peer);
+ return QDF_STATUS_E_INVAL;
+ }
+ p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
+ WLAN_UMAC_COMP_P2P);
+ if (!p2p_vdev_obj) {
+ p2p_err("p2p_vdev_obj:%p", p2p_vdev_obj);
+ return QDF_STATUS_E_INVAL;
+ }
+ wlan_vdev_obj_lock(vdev);
+ mode = wlan_vdev_mlme_get_opmode(vdev);
+ wlan_vdev_obj_unlock(vdev);
+
+ wlan_peer_obj_lock(peer);
+ peer_type = wlan_peer_get_peer_type(peer);
+ wlan_peer_obj_unlock(peer);
+
+ disable_noa = ((mode == QDF_P2P_GO_MODE)
+ && p2p_vdev_obj->non_p2p_peer_count
+ && p2p_vdev_obj->noa_status);
+
+ if (disable_noa && (peer_type == WLAN_PEER_STA)) {
+
+ wlan_vdev_obj_lock(vdev);
+ vdev_id = wlan_vdev_get_id(vdev);
+ wlan_vdev_obj_unlock(vdev);
+
+ if (ucfg_p2p_set_noa(psoc, vdev_id,
+ true) == QDF_STATUS_SUCCESS) {
+ p2p_vdev_obj->noa_status = false;
+ } else {
+ p2p_vdev_obj->noa_status = true;
+ }
+ p2p_debug("NoA status: %d", p2p_vdev_obj->noa_status);
+ }
+ p2p_debug("process_peer_for_noa");
+
+ return QDF_STATUS_SUCCESS;
+}
+
QDF_STATUS p2p_component_init(void)
{
QDF_STATUS status;
@@ -388,9 +573,33 @@
goto err_reg_vdev_delete;
}
+ status = wlan_objmgr_register_peer_create_handler(
+ WLAN_UMAC_COMP_P2P,
+ p2p_peer_obj_create_notification,
+ NULL);
+ if (status != QDF_STATUS_SUCCESS) {
+ p2p_err("Failed to register p2p peer create handler");
+ goto err_reg_peer_create;
+ }
+
+ status = wlan_objmgr_register_peer_destroy_handler(
+ WLAN_UMAC_COMP_P2P,
+ p2p_peer_obj_destroy_notification,
+ NULL);
+ if (status != QDF_STATUS_SUCCESS) {
+ p2p_err("Failed to register p2p peer destroy handler");
+ goto err_reg_peer_destroy;
+ }
+
p2p_debug("Register p2p obj handler successful");
return QDF_STATUS_SUCCESS;
+err_reg_peer_destroy:
+ wlan_objmgr_unregister_peer_create_handler(WLAN_UMAC_COMP_P2P,
+ p2p_peer_obj_create_notification, NULL);
+err_reg_peer_create:
+ wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_P2P,
+ p2p_vdev_obj_destroy_notification, NULL);
err_reg_vdev_delete:
wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_P2P,
p2p_vdev_obj_create_notification, NULL);
@@ -810,3 +1019,35 @@
return status;
}
+
+void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr)
+{
+ QDF_STATUS status;
+ struct wlan_objmgr_psoc *psoc;
+ struct wlan_objmgr_peer *peer;
+
+ if (!vdev) {
+ p2p_err("vdev:%p", vdev);
+ return;
+ }
+ wlan_vdev_obj_lock(vdev);
+ psoc = wlan_vdev_get_psoc(vdev);
+ wlan_vdev_obj_unlock(vdev);
+ if (!psoc) {
+ p2p_err("psoc:%p", psoc);
+ return;
+ }
+ peer = wlan_objmgr_get_peer(psoc, mac_addr, WLAN_P2P_ID);
+ if (!peer) {
+ p2p_debug("peer info not found");
+ return;
+ }
+ status = process_peer_for_noa(vdev, psoc, peer);
+ wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID);
+
+ if (status != QDF_STATUS_SUCCESS) {
+ p2p_err("status:%u", status);
+ return;
+ }
+ p2p_debug("peer is authorized");
+}
diff --git a/umac/p2p/core/src/wlan_p2p_main.h b/umac/p2p/core/src/wlan_p2p_main.h
index c13068c..9aabc6a 100644
--- a/umac/p2p/core/src/wlan_p2p_main.h
+++ b/umac/p2p/core/src/wlan_p2p_main.h
@@ -154,12 +154,16 @@
/**
* struct p2p_vdev_priv_obj - Per vdev p2p private object
- * @vdev: Pointer to vdev context
- * @noa_info: NoA information
+ * @vdev: Pointer to vdev context
+ * @noa_info: NoA information
+ * @noa_status: NoA status i.e. Enabled / Disabled (TRUE/FALSE)
+ * @non_p2p_peer_count: Number of legacy stations connected to this GO
*/
struct p2p_vdev_priv_obj {
- struct wlan_objmgr_vdev *vdev;
- struct p2p_noa_info *noa_info;
+ struct wlan_objmgr_vdev *vdev;
+ struct p2p_noa_info *noa_info;
+ bool noa_status;
+ uint16_t non_p2p_peer_count;
};
/**
diff --git a/umac/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h b/umac/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h
index 185d949..6e86130 100644
--- a/umac/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h
+++ b/umac/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h
@@ -262,4 +262,29 @@
QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc,
uint32_t vdev_id);
+/**
+ * p2p_peer_authorized() - Process peer authorized event
+ * @vdev: vdev structure to which peer is associated
+ * @mac_addr: peer mac address
+ *
+ * This function handles disables noa whenever a legacy station
+ * complete 4-way handshake after association.
+ *
+ * Return: void
+ */
+void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr);
+
+/**
+ * ucfg_p2p_set_noa() - Disable/Enable NOA
+ * @soc: soc context
+ * @vdev_id: vdev id
+ * @disable_noa: TRUE - Disable NoA, FALSE - Enable NoA
+ *
+ * This function send wmi command to enable / disable NoA.
+ *
+ * Return: QDF_STATUS_SUCCESS - in case of success
+ */
+QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc,
+ uint32_t vdev_id, bool disable_noa);
+
#endif /* _WLAN_P2P_UCFG_API_H_ */
diff --git a/umac/p2p/dispatcher/src/wlan_p2p_ucfg_api.c b/umac/p2p/dispatcher/src/wlan_p2p_ucfg_api.c
index 543ef54..4278d8c 100644
--- a/umac/p2p/dispatcher/src/wlan_p2p_ucfg_api.c
+++ b/umac/p2p/dispatcher/src/wlan_p2p_ucfg_api.c
@@ -35,6 +35,48 @@
return &(psoc->soc_cb.tx_ops.p2p);
}
+/**
+ * is_p2p_ps_allowed() - If P2P power save is allowed or not
+ * @vdev: vdev object
+ * @id: umac component id
+ *
+ * This function returns TRUE if P2P power-save is allowed
+ * else returns FALSE.
+ *
+ * Return: bool
+ */
+static bool is_p2p_ps_allowed(struct wlan_objmgr_vdev *vdev,
+ enum wlan_umac_comp_id id)
+{
+ struct p2p_vdev_priv_obj *p2p_vdev_obj;
+ uint8_t is_p2pgo = 0;
+
+ if (!vdev) {
+ p2p_err("vdev:%p", vdev);
+ return true;
+ }
+ p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
+ WLAN_UMAC_COMP_P2P);
+
+ if (wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_GO_MODE)
+ is_p2pgo = 1;
+
+ if (!p2p_vdev_obj || !is_p2pgo) {
+ p2p_err("p2p_vdev_obj:%p is_p2pgo:%u",
+ p2p_vdev_obj, is_p2pgo);
+ return false;
+ }
+ if (p2p_vdev_obj->non_p2p_peer_count &&
+ p2p_vdev_obj->noa_status == false) {
+ p2p_debug("non_p2p_peer_count: %u, noa_status: %d",
+ p2p_vdev_obj->non_p2p_peer_count,
+ p2p_vdev_obj->noa_status);
+ return false;
+ }
+
+ return true;
+}
+
QDF_STATUS ucfg_p2p_init(void)
{
return p2p_component_init();
@@ -249,6 +291,8 @@
{
struct wlan_lmac_if_p2p_tx_ops *p2p_ops;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
+ uint16_t obj_id;
+ struct wlan_objmgr_vdev *vdev;
p2p_debug("soc:%p, vdev_id:%d, opp_ps:%d, ct_window:%d, count:%d, duration:%d, duration:%d, ps_selection:%d",
soc, ps_config->vdev_id, ps_config->opp_ps,
@@ -261,6 +305,20 @@
return QDF_STATUS_E_INVAL;
}
+ for (obj_id = 0; obj_id < WLAN_UMAC_PSOC_MAX_VDEVS; obj_id++) {
+
+ vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, obj_id,
+ WLAN_P2P_ID);
+ if (vdev) {
+ if (is_p2p_ps_allowed(vdev,
+ WLAN_UMAC_COMP_P2P) == false) {
+ p2p_debug("p2p set ps, NoA is disabled as legacy STA is connected to GO.");
+ return QDF_STATUS_E_BUSY;
+ }
+ wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID);
+ }
+ }
+
p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc);
if (p2p_ops->set_ps) {
status = p2p_ops->set_ps(soc, ps_config);
@@ -318,3 +376,18 @@
return status;
}
+
+QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc,
+ uint32_t vdev_id, bool disable_noa)
+{
+ struct wlan_lmac_if_p2p_tx_ops *p2p_ops;
+ QDF_STATUS status = QDF_STATUS_E_INVAL;
+
+ p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc);
+ if (p2p_ops->set_noa) {
+ status = p2p_ops->set_noa(soc, vdev_id, disable_noa);
+ p2p_debug("p2p set noa, status:%d", status);
+ }
+
+ return status;
+}