mirror of
				https://git.kernel.org/pub/scm/network/wireless/iwd.git
				synced 2025-10-31 04:57:25 +01:00 
			
		
		
		
	netdev: RSSI polling support for less capable drivers
If the kernel device driver or the kernel nl80211 version doesn't support the new RSSI threshold list CQM monitoring, implement similar logic in iwd with periodic polling. This is only active when an RSSI agent is registered to receive the events. I tested this with the same testRSSIAgent autotests that tests the driver-side rssi monitoring except with all timeouts multiplied by ~20.
This commit is contained in:
		
							parent
							
								
									417367e272
								
							
						
					
					
						commit
						452e174802
					
				
							
								
								
									
										145
									
								
								src/netdev.c
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								src/netdev.c
									
									
									
									
									
								
							| @ -86,6 +86,8 @@ struct netdev { | ||||
| 	uint8_t rssi_levels_num; | ||||
| 	uint8_t cur_rssi_level_idx; | ||||
| 	int8_t cur_rssi; | ||||
| 	struct l_timeout *rssi_poll_timeout; | ||||
| 	uint32_t rssi_poll_cmd_id; | ||||
| 
 | ||||
| 	struct l_queue *watches; | ||||
| 	uint32_t next_watch_id; | ||||
| @ -284,6 +286,117 @@ int netdev_set_powered(struct netdev *netdev, bool powered, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void netdev_set_rssi_level_idx(struct netdev *netdev) | ||||
| { | ||||
| 	uint8_t new_level; | ||||
| 
 | ||||
| 	for (new_level = 0; new_level < netdev->rssi_levels_num; new_level++) | ||||
| 		if (netdev->cur_rssi >= netdev->rssi_levels[new_level]) | ||||
| 			break; | ||||
| 
 | ||||
| 	netdev->cur_rssi_level_idx = new_level; | ||||
| } | ||||
| 
 | ||||
| static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data) | ||||
| { | ||||
| 	struct netdev *netdev = user_data; | ||||
| 	struct l_genl_attr attr, nested; | ||||
| 	uint16_t type, len; | ||||
| 	const void *data; | ||||
| 	bool found; | ||||
| 	uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx; | ||||
| 
 | ||||
| 	netdev->rssi_poll_cmd_id = 0; | ||||
| 
 | ||||
| 	if (!l_genl_attr_init(&attr, msg)) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	found = false; | ||||
| 	while (l_genl_attr_next(&attr, &type, &len, &data)) { | ||||
| 		if (type != NL80211_ATTR_STA_INFO) | ||||
| 			continue; | ||||
| 
 | ||||
| 		found = true; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!found || !l_genl_attr_recurse(&attr, &nested)) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	found = false; | ||||
| 	while (l_genl_attr_next(&nested, &type, &len, &data)) { | ||||
| 		if (type != NL80211_STA_INFO_SIGNAL_AVG) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (len != 1) | ||||
| 			continue; | ||||
| 
 | ||||
| 		found = true; | ||||
| 		netdev->cur_rssi = *(const int8_t *) data; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!found) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Note we don't have to handle LOW_SIGNAL_THRESHOLD here.  The | ||||
| 	 * CQM single threshold RSSI monitoring should work even if the | ||||
| 	 * kernel driver doesn't support multiple thresholds.  So the | ||||
| 	 * polling only handles the client-supplied threshold list. | ||||
| 	 */ | ||||
| 	netdev_set_rssi_level_idx(netdev); | ||||
| 	if (netdev->cur_rssi_level_idx != prev_rssi_level_idx) | ||||
| 		netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY, | ||||
| 					netdev->user_data); | ||||
| 
 | ||||
| done: | ||||
| 	/* Rearm timer */ | ||||
| 	l_timeout_modify(netdev->rssi_poll_timeout, 6); | ||||
| } | ||||
| 
 | ||||
| static void netdev_rssi_poll(struct l_timeout *timeout, void *user_data) | ||||
| { | ||||
| 	struct netdev *netdev = user_data; | ||||
| 	struct l_genl_msg *msg; | ||||
| 
 | ||||
| 	msg = l_genl_msg_new_sized(NL80211_CMD_GET_STATION, 64); | ||||
| 	l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); | ||||
| 	l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, | ||||
| 							netdev->handshake->aa); | ||||
| 
 | ||||
| 	netdev->rssi_poll_cmd_id = l_genl_family_send(nl80211, msg, | ||||
| 							netdev_rssi_poll_cb, | ||||
| 							netdev, NULL); | ||||
| } | ||||
| 
 | ||||
| /* To be called whenever operational or rssi_levels_num are updated */ | ||||
| static void netdev_rssi_polling_update(struct netdev *netdev) | ||||
| { | ||||
| 	if (wiphy_get_ext_feature(netdev->wiphy, | ||||
| 					NL80211_EXT_FEATURE_CQM_RSSI_LIST)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (netdev->operational && netdev->rssi_levels_num > 0) { | ||||
| 		if (netdev->rssi_poll_timeout) | ||||
| 			return; | ||||
| 
 | ||||
| 		netdev->rssi_poll_timeout = | ||||
| 			l_timeout_create(1, netdev_rssi_poll, netdev, NULL); | ||||
| 	} else { | ||||
| 		if (!netdev->rssi_poll_timeout) | ||||
| 			return; | ||||
| 
 | ||||
| 		l_timeout_remove(netdev->rssi_poll_timeout); | ||||
| 		netdev->rssi_poll_timeout = NULL; | ||||
| 
 | ||||
| 		if (netdev->rssi_poll_cmd_id) { | ||||
| 			l_genl_family_cancel(nl80211, netdev->rssi_poll_cmd_id); | ||||
| 			netdev->rssi_poll_cmd_id = 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void netdev_linkmode_dormant_cb(bool success, void *user_data) | ||||
| { | ||||
| 	struct netdev *netdev = user_data; | ||||
| @ -331,6 +444,8 @@ static void netdev_connect_free(struct netdev *netdev) | ||||
| 	netdev->result = NETDEV_RESULT_OK; | ||||
| 	netdev->in_ft = false; | ||||
| 
 | ||||
| 	netdev_rssi_polling_update(netdev); | ||||
| 
 | ||||
| 	if (netdev->pairwise_new_key_cmd_id) { | ||||
| 		l_genl_family_cancel(nl80211, netdev->pairwise_new_key_cmd_id); | ||||
| 		netdev->pairwise_new_key_cmd_id = 0; | ||||
| @ -468,23 +583,12 @@ static void netdev_cqm_event_rssi_threshold(struct netdev *netdev, | ||||
| 
 | ||||
| 	netdev->cur_rssi_low = | ||||
| 		(rssi_event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW); | ||||
| 	event = netdev->cur_rssi_low ?  NETDEV_EVENT_RSSI_THRESHOLD_LOW : | ||||
| 	event = netdev->cur_rssi_low ? NETDEV_EVENT_RSSI_THRESHOLD_LOW : | ||||
| 		NETDEV_EVENT_RSSI_THRESHOLD_HIGH; | ||||
| 
 | ||||
| 	netdev->event_filter(netdev, event, netdev->user_data); | ||||
| } | ||||
| 
 | ||||
| static void netdev_set_rssi_level_idx(struct netdev *netdev) | ||||
| { | ||||
| 	uint8_t new_level; | ||||
| 
 | ||||
| 	for (new_level = 0; new_level < netdev->rssi_levels_num; new_level++) | ||||
| 		if (netdev->cur_rssi >= netdev->rssi_levels[new_level]) | ||||
| 			break; | ||||
| 
 | ||||
| 	netdev->cur_rssi_level_idx = new_level; | ||||
| } | ||||
| 
 | ||||
| static void netdev_rssi_level_init(struct netdev *netdev) | ||||
| { | ||||
| 	if (netdev->connected && netdev->rssi_levels_num) | ||||
| @ -752,6 +856,8 @@ static void netdev_connect_ok(struct netdev *netdev) | ||||
| 		netdev->connect_cb(netdev, NETDEV_RESULT_OK, netdev->user_data); | ||||
| 		netdev->connect_cb = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	netdev_rssi_polling_update(netdev); | ||||
| } | ||||
| 
 | ||||
| static void netdev_setting_keys_failed(struct netdev *netdev, | ||||
| @ -2221,6 +2327,8 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss, | ||||
| 
 | ||||
| 	netdev->operational = false; | ||||
| 
 | ||||
| 	netdev_rssi_polling_update(netdev); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Cancel commands that could be running because of EAPoL activity | ||||
| 	 * like re-keying, this way the callbacks for those commands don't | ||||
| @ -2439,6 +2547,8 @@ int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss, | ||||
| 		netdev->group_management_new_key_cmd_id = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	netdev_rssi_polling_update(netdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| restore_snonce: | ||||
| @ -2851,13 +2961,13 @@ int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels, | ||||
| { | ||||
| 	struct l_genl_msg *cmd_set_cqm; | ||||
| 
 | ||||
| 	if (!wiphy_get_ext_feature(netdev->wiphy, | ||||
| 					NL80211_EXT_FEATURE_CQM_RSSI_LIST)) | ||||
| 		return levels_num ? -ENOTSUP : 0; | ||||
| 
 | ||||
| 	if (levels_num > L_ARRAY_SIZE(netdev->rssi_levels)) | ||||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	if (!wiphy_get_ext_feature(netdev->wiphy, | ||||
| 					NL80211_EXT_FEATURE_CQM_RSSI_LIST)) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	cmd_set_cqm = netdev_build_cmd_cqm_rssi_update(netdev, levels, | ||||
| 							levels_num); | ||||
| 	if (!cmd_set_cqm) | ||||
| @ -2872,10 +2982,13 @@ int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels, | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| done: | ||||
| 	memcpy(netdev->rssi_levels, levels, levels_num); | ||||
| 	netdev->rssi_levels_num = levels_num; | ||||
| 	netdev_rssi_level_init(netdev); | ||||
| 
 | ||||
| 	netdev_rssi_polling_update(netdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Andrew Zaborowski
						Andrew Zaborowski