Browse Source

qcacld-3.0: Use gratuitous ARP keepalive after IP address change

Currently, a null data packet keepalive method is used by default. Once
ipv4 address information is received, dynamically switch to using
gratuitous ARP for keepalive instead of null packets.

Change-Id: I475a4bf71376ab6be92fd81070ba7dce48f45151
CRs-Fixed: 2001249
Dustin Brown 8 years ago
parent
commit
3c31cebf4a
1 changed files with 152 additions and 41 deletions
  1. 152 41
      core/hdd/src/wlan_hdd_power.c

+ 152 - 41
core/hdd/src/wlan_hdd_power.c

@@ -669,6 +669,133 @@ void hdd_conf_hostoffload(hdd_adapter_t *pAdapter, bool fenable)
 }
 #endif
 
+/**
+ * hdd_lookup_ifaddr() - Lookup interface address data by name
+ * @adapter: the adapter whose name should be searched for
+ *
+ * return in_ifaddr pointer on success, NULL for failure
+ */
+static struct in_ifaddr *hdd_lookup_ifaddr(hdd_adapter_t *adapter)
+{
+	struct in_ifaddr *ifa;
+	struct in_device *in_dev;
+
+	if (!adapter) {
+		hdd_err("adapter is null");
+		return NULL;
+	}
+
+	in_dev = __in_dev_get_rtnl(adapter->dev);
+	if (!in_dev) {
+		hdd_err("Failed to get in_device");
+		return NULL;
+	}
+
+	/* lookup address data by interface name */
+	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+		if (!strcmp(adapter->dev->name, ifa->ifa_label))
+			return ifa;
+	}
+
+	return NULL;
+}
+
+/**
+ * hdd_populate_ipv4_addr() - Populates the adapter's IPv4 address
+ * @adapter: the adapter whose IPv4 address is desired
+ * @ipv4_addr: the address of the array to copy the IPv4 address into
+ *
+ * return: zero for success; non-zero for failure
+ */
+static int hdd_populate_ipv4_addr(hdd_adapter_t *adapter, uint8_t *ipv4_addr)
+{
+	struct in_ifaddr *ifa;
+	int i;
+
+	if (!adapter) {
+		hdd_err("adapter is null");
+		return -EINVAL;
+	}
+
+	if (!ipv4_addr) {
+		hdd_err("ipv4_addr is null");
+		return -EINVAL;
+	}
+
+	ifa = hdd_lookup_ifaddr(adapter);
+	if (!ifa || !ifa->ifa_local) {
+		hdd_err("ipv4 address not found");
+		return -EINVAL;
+	}
+
+	/* convert u32 to byte array */
+	for (i = 0; i < 4; i++)
+		ipv4_addr[i] = (ifa->ifa_local >> i * 8) & 0xff;
+
+	return 0;
+}
+
+/**
+ * hdd_set_grat_arp_keepalive() - Enable grat APR keepalive
+ * @adapter: the HDD adapter to configure
+ *
+ * This configures gratuitous APR keepalive based on the adapter's current
+ * connection information, specifically IPv4 address and BSSID
+ *
+ * return: zero for success, non-zero for failure
+ */
+static int hdd_set_grat_arp_keepalive(hdd_adapter_t *adapter)
+{
+	QDF_STATUS status;
+	int exit_code;
+	hdd_context_t *hdd_ctx;
+	hdd_station_ctx_t *sta_ctx;
+	tSirKeepAliveReq req = {
+		.packetType = SIR_KEEP_ALIVE_UNSOLICIT_ARP_RSP,
+		.destIpv4Addr = {0xff, 0xff, 0xff, 0xff},
+		.dest_macaddr = QDF_MAC_ADDR_BROADCAST_INITIALIZER,
+	};
+
+	if (!adapter) {
+		hdd_err("adapter is null");
+		return -EINVAL;
+	}
+
+	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	if (!hdd_ctx) {
+		hdd_err("hdd_ctx is null");
+		return -EINVAL;
+	}
+
+	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	if (!sta_ctx) {
+		hdd_err("sta_ctx is null");
+		return -EINVAL;
+	}
+
+	exit_code = hdd_populate_ipv4_addr(adapter, req.hostIpv4Addr);
+	if (exit_code) {
+		hdd_err("Failed to populate ipv4 address");
+		return exit_code;
+	}
+
+	qdf_copy_macaddr(&req.bssid, &sta_ctx->conn_info.bssId);
+	req.timePeriod = hdd_ctx->config->infraStaKeepAlivePeriod;
+	req.sessionId = adapter->sessionId;
+
+	hdd_info("Setting gratuitous ARP keepalive; ipv4_addr:%u.%u.%u.%u",
+		 req.hostIpv4Addr[0], req.hostIpv4Addr[1],
+		 req.hostIpv4Addr[2], req.hostIpv4Addr[3]);
+
+	status = sme_set_keep_alive(hdd_ctx->hHal, req.sessionId, &req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to set keepalive");
+		return qdf_status_to_os_return(status);
+	}
+
+	return 0;
+}
+
 /**
  * __hdd_ipv4_notifier_work_queue() - IPv4 notification work function
  * @work: registered work item
@@ -685,10 +812,13 @@ static void __hdd_ipv4_notifier_work_queue(struct work_struct *work)
 	hdd_adapter_t *pAdapter =
 		container_of(work, hdd_adapter_t, ipv4NotifierWorkQueue);
 	hdd_context_t *pHddCtx;
+	hdd_station_ctx_t *sta_ctx;
 	int status;
-	bool ndi_connected = false;
+	bool ndi_connected;
+	bool sta_associated;
 
 	hdd_info("Configuring ARP Offload");
+
 	pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
 	status = wlan_hdd_validate_context(pHddCtx);
 	if (status)
@@ -705,19 +835,24 @@ static void __hdd_ipv4_notifier_work_queue(struct work_struct *work)
 		pHddCtx->sus_res_mcastbcast_filter_valid = true;
 	}
 
-	/* check if the device is in NAN data mode */
-	if (WLAN_HDD_IS_NDI(pAdapter))
-		ndi_connected = WLAN_HDD_IS_NDI_CONNECTED(pAdapter);
+	ndi_connected = WLAN_HDD_IS_NDI(pAdapter) &&
+		WLAN_HDD_IS_NDI_CONNECTED(pAdapter);
 
-	if (eConnectionState_Associated ==
-	     (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState ||
-		ndi_connected)
-		/*
-		 * This invocation being part of the IPv4 registration callback,
-		 * we are passing second parameter as 2 to avoid registration
-		 * of IPv4 notifier again.
-		 */
-		hdd_conf_arp_offload(pAdapter, true);
+	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+	sta_associated = sta_ctx->conn_info.connState ==
+		eConnectionState_Associated;
+
+	if (!ndi_connected && !sta_associated)
+		return;
+
+	/*
+	 * This invocation being part of the IPv4 registration callback,
+	 * we are passing second parameter as 2 to avoid registration
+	 * of IPv4 notifier again.
+	 */
+	hdd_conf_arp_offload(pAdapter, true);
+
+	hdd_set_grat_arp_keepalive(pAdapter);
 }
 
 /**
@@ -750,9 +885,6 @@ static int __wlan_hdd_ipv4_changed(struct notifier_block *nb,
 				 unsigned long data, void *arg)
 {
 	struct in_ifaddr *ifa = (struct in_ifaddr *)arg;
-	struct in_ifaddr **ifap = NULL;
-	struct in_device *in_dev;
-
 	struct net_device *ndev = ifa->ifa_dev->dev;
 	hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(ndev);
 	hdd_context_t *pHddCtx;
@@ -790,19 +922,9 @@ static int __wlan_hdd_ipv4_changed(struct notifier_block *nb,
 			return NOTIFY_DONE;
 		}
 
-		in_dev = __in_dev_get_rtnl(pAdapter->dev);
-		if (in_dev) {
-			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
-			     ifap = &ifa->ifa_next) {
-				if (!strcmp(pAdapter->dev->name,
-					    ifa->ifa_label)) {
-					break;  /* found */
-				}
-			}
-		}
-		if (ifa && ifa->ifa_local) {
+		ifa = hdd_lookup_ifaddr(pAdapter);
+		if (ifa && ifa->ifa_local)
 			schedule_work(&pAdapter->ipv4NotifierWorkQueue);
-		}
 	}
 	EXIT();
 	return NOTIFY_DONE;
@@ -843,9 +965,7 @@ int wlan_hdd_ipv4_changed(struct notifier_block *nb,
  */
 QDF_STATUS hdd_conf_arp_offload(hdd_adapter_t *pAdapter, bool fenable)
 {
-	struct in_ifaddr **ifap = NULL;
-	struct in_ifaddr *ifa = NULL;
-	struct in_device *in_dev;
+	struct in_ifaddr *ifa;
 	int i = 0;
 	tSirHostOffloadReq offLoadRequest;
 	hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
@@ -863,16 +983,7 @@ QDF_STATUS hdd_conf_arp_offload(hdd_adapter_t *pAdapter, bool fenable)
 	}
 
 	if (fenable) {
-		in_dev = __in_dev_get_rtnl(pAdapter->dev);
-		if (in_dev) {
-			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
-			     ifap = &ifa->ifa_next) {
-				if (!strcmp(pAdapter->dev->name,
-					    ifa->ifa_label)) {
-					break;  /* found */
-				}
-			}
-		}
+		ifa = hdd_lookup_ifaddr(pAdapter);
 		if (ifa && ifa->ifa_local) {
 			offLoadRequest.offloadType = SIR_IPV4_ARP_REPLY_OFFLOAD;
 			offLoadRequest.enableOrDisable = SIR_OFFLOAD_ENABLE;