diff options
Diffstat (limited to 'drivers/net/wireless/quantenna')
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 106 | ||||
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/commands.c | 211 | ||||
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/commands.h | 3 | ||||
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/core.c | 26 | ||||
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/core.h | 3 | ||||
| -rw-r--r-- | drivers/net/wireless/quantenna/qtnfmac/qlink.h | 105 | 
7 files changed, 430 insertions, 26 deletions
| diff --git a/drivers/net/wireless/quantenna/qtnfmac/Kconfig b/drivers/net/wireless/quantenna/qtnfmac/Kconfig index 025fa6018550..8d1492a90bd1 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/Kconfig +++ b/drivers/net/wireless/quantenna/qtnfmac/Kconfig @@ -7,7 +7,7 @@ config QTNFMAC  config QTNFMAC_PEARL_PCIE  	tristate "Quantenna QSR10g PCIe support"  	default n -	depends on HAS_DMA && PCI && CFG80211 +	depends on PCI && CFG80211  	select QTNFMAC  	select FW_LOADER  	select CRC32 diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 220e2b710208..4aa332f4646b 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -654,8 +654,7 @@ qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev,  	vif = qtnf_mac_get_base_vif(mac);  	if (!vif) {  		pr_err("MAC%u: primary VIF is not configured\n", mac->macid); -		ret = -EFAULT; -		goto out; +		return -EFAULT;  	}  	if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { @@ -844,6 +843,88 @@ static int qtnf_set_mac_acl(struct wiphy *wiphy,  	return ret;  } +static int qtnf_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, +			       bool enabled, int timeout) +{ +	struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); +	int ret; + +	ret = qtnf_cmd_send_pm_set(vif, enabled ? QLINK_PM_AUTO_STANDBY : +				   QLINK_PM_OFF, timeout); +	if (ret) { +		pr_err("%s: failed to set PM mode ret=%d\n", dev->name, ret); +		return ret; +	} + +	return ret; +} + +#ifdef CONFIG_PM +static int qtnf_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) +{ +	struct qtnf_wmac *mac = wiphy_priv(wiphy); +	struct qtnf_vif *vif; +	int ret = 0; + +	vif = qtnf_mac_get_base_vif(mac); +	if (!vif) { +		pr_err("MAC%u: primary VIF is not configured\n", mac->macid); +		ret = -EFAULT; +		goto exit; +	} + +	if (!wowlan) { +		pr_debug("WoWLAN triggers are not enabled\n"); +		qtnf_virtual_intf_cleanup(vif->netdev); +		goto exit; +	} + +	qtnf_scan_done(vif->mac, true); + +	ret = qtnf_cmd_send_wowlan_set(vif, wowlan); +	if (ret) { +		pr_err("MAC%u: failed to set WoWLAN triggers\n", +		       mac->macid); +		goto exit; +	} + +exit: +	return ret; +} + +static int qtnf_resume(struct wiphy *wiphy) +{ +	struct qtnf_wmac *mac = wiphy_priv(wiphy); +	struct qtnf_vif *vif; +	int ret = 0; + +	vif = qtnf_mac_get_base_vif(mac); +	if (!vif) { +		pr_err("MAC%u: primary VIF is not configured\n", mac->macid); +		ret = -EFAULT; +		goto exit; +	} + +	ret = qtnf_cmd_send_wowlan_set(vif, NULL); +	if (ret) { +		pr_err("MAC%u: failed to reset WoWLAN triggers\n", +		       mac->macid); +		goto exit; +	} + +exit: +	return ret; +} + +static void qtnf_set_wakeup(struct wiphy *wiphy, bool enabled) +{ +	struct qtnf_wmac *mac = wiphy_priv(wiphy); +	struct qtnf_bus *bus = mac->bus; + +	device_set_wakeup_enable(bus->dev, enabled); +} +#endif +  static struct cfg80211_ops qtn_cfg80211_ops = {  	.add_virtual_intf	= qtnf_add_virtual_intf,  	.change_virtual_intf	= qtnf_change_virtual_intf, @@ -870,6 +951,12 @@ static struct cfg80211_ops qtn_cfg80211_ops = {  	.channel_switch		= qtnf_channel_switch,  	.start_radar_detection	= qtnf_start_radar_detection,  	.set_mac_acl		= qtnf_set_mac_acl, +	.set_power_mgmt		= qtnf_set_power_mgmt, +#ifdef CONFIG_PM +	.suspend		= qtnf_suspend, +	.resume			= qtnf_resume, +	.set_wakeup		= qtnf_set_wakeup, +#endif  };  static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, @@ -922,6 +1009,9 @@ struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus)  	if (bus->hw_info.hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD)  		qtn_cfg80211_ops.start_radar_detection = NULL; +	if (!(bus->hw_info.hw_capab & QLINK_HW_CAPAB_PWR_MGMT)) +		qtn_cfg80211_ops.set_power_mgmt	= NULL; +  	wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac));  	if (!wiphy)  		return NULL; @@ -976,7 +1066,8 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)  	wiphy->retry_long = macinfo->lretry_limit;  	wiphy->coverage_class = macinfo->coverage_class; -	wiphy->max_scan_ssids = QTNF_MAX_SSID_LIST_LENGTH; +	wiphy->max_scan_ssids = +		(hw_info->max_scan_ssids) ? hw_info->max_scan_ssids : 1;  	wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN;  	wiphy->mgmt_stypes = qtnf_mgmt_stypes;  	wiphy->max_remain_on_channel_duration = 5000; @@ -995,6 +1086,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)  			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |  			WIPHY_FLAG_AP_UAPSD |  			WIPHY_FLAG_HAS_CHANNEL_SWITCH; +	wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;  	if (hw_info->hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD)  		wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD); @@ -1014,6 +1106,14 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)  	if (hw_info->hw_capab & QLINK_HW_CAPAB_STA_INACT_TIMEOUT)  		wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; +	if (hw_info->hw_capab & QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR) +		wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + +#ifdef CONFIG_PM +	if (macinfo->wowlan) +		wiphy->wowlan = macinfo->wowlan; +#endif +  	if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) {  		wiphy->regulatory_flags |= REGULATORY_STRICT_REG |  			REGULATORY_CUSTOM_REG; diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index c5d94a95e21a..ae9e77300533 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -640,83 +640,83 @@ qtnf_cmd_sta_info_parse(struct station_info *sinfo,  		return;  	if (qtnf_sta_stat_avail(inactive_time, QLINK_STA_INFO_INACTIVE_TIME)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);  		sinfo->inactive_time = le32_to_cpu(stats->inactive_time);  	}  	if (qtnf_sta_stat_avail(connected_time,  				QLINK_STA_INFO_CONNECTED_TIME)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);  		sinfo->connected_time = le32_to_cpu(stats->connected_time);  	}  	if (qtnf_sta_stat_avail(signal, QLINK_STA_INFO_SIGNAL)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);  		sinfo->signal = stats->signal - QLINK_RSSI_OFFSET;  	}  	if (qtnf_sta_stat_avail(signal_avg, QLINK_STA_INFO_SIGNAL_AVG)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);  		sinfo->signal_avg = stats->signal_avg - QLINK_RSSI_OFFSET;  	}  	if (qtnf_sta_stat_avail(rxrate, QLINK_STA_INFO_RX_BITRATE)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);  		qtnf_sta_info_parse_rate(&sinfo->rxrate, &stats->rxrate);  	}  	if (qtnf_sta_stat_avail(txrate, QLINK_STA_INFO_TX_BITRATE)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);  		qtnf_sta_info_parse_rate(&sinfo->txrate, &stats->txrate);  	}  	if (qtnf_sta_stat_avail(sta_flags, QLINK_STA_INFO_STA_FLAGS)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_STA_FLAGS); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);  		qtnf_sta_info_parse_flags(&sinfo->sta_flags, &stats->sta_flags);  	}  	if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);  		sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);  	}  	if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);  		sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);  	}  	if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES64)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);  		sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes);  	}  	if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES64)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);  		sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes);  	}  	if (qtnf_sta_stat_avail(rx_packets, QLINK_STA_INFO_RX_PACKETS)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);  		sinfo->rx_packets = le32_to_cpu(stats->rx_packets);  	}  	if (qtnf_sta_stat_avail(tx_packets, QLINK_STA_INFO_TX_PACKETS)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);  		sinfo->tx_packets = le32_to_cpu(stats->tx_packets);  	}  	if (qtnf_sta_stat_avail(rx_beacon, QLINK_STA_INFO_BEACON_RX)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX);  		sinfo->rx_beacon = le64_to_cpu(stats->rx_beacon);  	}  	if (qtnf_sta_stat_avail(rx_dropped_misc, QLINK_STA_INFO_RX_DROP_MISC)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_RX_DROP_MISC); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC);  		sinfo->rx_dropped_misc = le32_to_cpu(stats->rx_dropped_misc);  	}  	if (qtnf_sta_stat_avail(tx_failed, QLINK_STA_INFO_TX_FAILED)) { -		sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED); +		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);  		sinfo->tx_failed = le32_to_cpu(stats->tx_failed);  	} @@ -1092,6 +1092,9 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,  		case QTN_TLV_ID_UBOOT_VER:  			uboot_ver = (const void *)tlv->val;  			break; +		case QTN_TLV_ID_MAX_SCAN_SSIDS: +			hwinfo->max_scan_ssids = *tlv->val; +			break;  		default:  			break;  		} @@ -1135,6 +1138,37 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,  	return 0;  } +static void +qtnf_parse_wowlan_info(struct qtnf_wmac *mac, +		       const struct qlink_wowlan_capab_data *wowlan) +{ +	struct qtnf_mac_info *mac_info = &mac->macinfo; +	const struct qlink_wowlan_support *data1; +	struct wiphy_wowlan_support *supp; + +	supp = kzalloc(sizeof(*supp), GFP_KERNEL); +	if (!supp) +		return; + +	switch (le16_to_cpu(wowlan->version)) { +	case 0x1: +		data1 = (struct qlink_wowlan_support *)wowlan->data; + +		supp->flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT; +		supp->n_patterns = le32_to_cpu(data1->n_patterns); +		supp->pattern_max_len = le32_to_cpu(data1->pattern_max_len); +		supp->pattern_min_len = le32_to_cpu(data1->pattern_min_len); + +		mac_info->wowlan = supp; +		break; +	default: +		pr_warn("MAC%u: unsupported WoWLAN version 0x%x\n", +			mac->macid, le16_to_cpu(wowlan->version)); +		kfree(supp); +		break; +	} +} +  static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,  					const u8 *tlv_buf, size_t tlv_buf_size)  { @@ -1144,6 +1178,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,  	const struct qlink_iface_comb_num *comb_num;  	const struct qlink_iface_limit_record *rec;  	const struct qlink_iface_limit *lim; +	const struct qlink_wowlan_capab_data *wowlan;  	u16 rec_len;  	u16 tlv_type;  	u16 tlv_value_len; @@ -1252,7 +1287,31 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,  			ext_capa_mask = (u8 *)tlv->val;  			ext_capa_mask_len = tlv_value_len;  			break; +		case QTN_TLV_ID_WOWLAN_CAPAB: +			if (tlv_value_len < sizeof(*wowlan)) +				return -EINVAL; + +			wowlan = (void *)tlv->val; +			if (!le16_to_cpu(wowlan->len)) { +				pr_warn("MAC%u: skip empty WoWLAN data\n", +					mac->macid); +				break; +			} + +			rec_len = sizeof(*wowlan) + le16_to_cpu(wowlan->len); +			if (unlikely(tlv_value_len != rec_len)) { +				pr_warn("MAC%u: WoWLAN data size mismatch\n", +					mac->macid); +				return -EINVAL; +			} + +			kfree(mac->macinfo.wowlan); +			mac->macinfo.wowlan = NULL; +			qtnf_parse_wowlan_info(mac, wowlan); +			break;  		default: +			pr_warn("MAC%u: unknown TLV type %u\n", +				mac->macid, tlv_type);  			break;  		} @@ -2234,6 +2293,22 @@ static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb,  	qchan->chan.flags = cpu_to_le32(flags);  } +static void qtnf_cmd_randmac_tlv_add(struct sk_buff *cmd_skb, +				     const u8 *mac_addr, +				     const u8 *mac_addr_mask) +{ +	struct qlink_random_mac_addr *randmac; +	struct qlink_tlv_hdr *hdr = +		skb_put(cmd_skb, sizeof(*hdr) + sizeof(*randmac)); + +	hdr->type = cpu_to_le16(QTN_TLV_ID_RANDOM_MAC_ADDR); +	hdr->len = cpu_to_le16(sizeof(*randmac)); +	randmac = (struct qlink_random_mac_addr *)hdr->val; + +	memcpy(randmac->mac_addr, mac_addr, ETH_ALEN); +	memcpy(randmac->mac_addr_mask, mac_addr_mask, ETH_ALEN); +} +  int qtnf_cmd_send_scan(struct qtnf_wmac *mac)  {  	struct sk_buff *cmd_skb; @@ -2244,11 +2319,6 @@ int qtnf_cmd_send_scan(struct qtnf_wmac *mac)  	int count = 0;  	int ret; -	if (scan_req->n_ssids > QTNF_MAX_SSID_LIST_LENGTH) { -		pr_err("MAC%u: too many SSIDs in scan request\n", mac->macid); -		return -EINVAL; -	} -  	cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,  					    QLINK_CMD_SCAN,  					    sizeof(struct qlink_cmd)); @@ -2291,6 +2361,15 @@ int qtnf_cmd_send_scan(struct qtnf_wmac *mac)  		}  	} +	if (scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { +		pr_debug("MAC%u: scan with random addr=%pM, mask=%pM\n", +			 mac->macid, +			 scan_req->mac_addr, scan_req->mac_addr_mask); + +		qtnf_cmd_randmac_tlv_add(cmd_skb, scan_req->mac_addr, +					 scan_req->mac_addr_mask); +	} +  	ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code);  	if (unlikely(ret)) @@ -2774,3 +2853,93 @@ int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,  	return ret;  } + +int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout) +{ +	struct qtnf_bus *bus = vif->mac->bus; +	struct sk_buff *cmd_skb; +	u16 res_code = QLINK_CMD_RESULT_OK; +	struct qlink_cmd_pm_set *cmd; +	int ret = 0; + +	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, +					    QLINK_CMD_PM_SET, sizeof(*cmd)); +	if (!cmd_skb) +		return -ENOMEM; + +	cmd = (struct qlink_cmd_pm_set *)cmd_skb->data; +	cmd->pm_mode = pm_mode; +	cmd->pm_standby_timer = cpu_to_le32(timeout); + +	qtnf_bus_lock(bus); + +	ret = qtnf_cmd_send(bus, cmd_skb, &res_code); + +	if (unlikely(ret)) +		goto out; + +	if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { +		pr_err("cmd exec failed: 0x%.4X\n", res_code); +		ret = -EFAULT; +	} + +out: +	qtnf_bus_unlock(bus); +	return ret; +} + +int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif, +			     const struct cfg80211_wowlan *wowl) +{ +	struct qtnf_bus *bus = vif->mac->bus; +	struct sk_buff *cmd_skb; +	u16 res_code = QLINK_CMD_RESULT_OK; +	struct qlink_cmd_wowlan_set *cmd; +	u32 triggers = 0; +	int count = 0; +	int ret = 0; + +	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, +					    QLINK_CMD_WOWLAN_SET, sizeof(*cmd)); +	if (!cmd_skb) +		return -ENOMEM; + +	qtnf_bus_lock(bus); + +	cmd = (struct qlink_cmd_wowlan_set *)cmd_skb->data; + +	if (wowl) { +		if (wowl->disconnect) +			triggers |=  QLINK_WOWLAN_TRIG_DISCONNECT; + +		if (wowl->magic_pkt) +			triggers |= QLINK_WOWLAN_TRIG_MAGIC_PKT; + +		if (wowl->n_patterns && wowl->patterns) { +			triggers |= QLINK_WOWLAN_TRIG_PATTERN_PKT; +			while (count < wowl->n_patterns) { +				qtnf_cmd_skb_put_tlv_arr(cmd_skb, +					QTN_TLV_ID_WOWLAN_PATTERN, +					wowl->patterns[count].pattern, +					wowl->patterns[count].pattern_len); +				count++; +			} +		} +	} + +	cmd->triggers = cpu_to_le32(triggers); + +	ret = qtnf_cmd_send(bus, cmd_skb, &res_code); + +	if (unlikely(ret)) +		goto out; + +	if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { +		pr_err("cmd exec failed: 0x%.4X\n", res_code); +		ret = -EFAULT; +	} + +out: +	qtnf_bus_unlock(bus); +	return ret; +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h index cf9274add26d..1ac41156c192 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.h +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h @@ -76,5 +76,8 @@ int qtnf_cmd_start_cac(const struct qtnf_vif *vif,  		       u32 cac_time_ms);  int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,  			 const struct cfg80211_acl_data *params); +int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout); +int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif, +			     const struct cfg80211_wowlan *wowl);  #endif /* QLINK_COMMANDS_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c index a6a450984f9a..19abbc4e23e0 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.c +++ b/drivers/net/wireless/quantenna/qtnfmac/core.c @@ -179,6 +179,30 @@ static void qtnf_netdev_tx_timeout(struct net_device *ndev)  	}  } +static int qtnf_netdev_set_mac_address(struct net_device *ndev, void *addr) +{ +	struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); +	struct sockaddr *sa = addr; +	int ret; +	unsigned char old_addr[ETH_ALEN]; + +	memcpy(old_addr, sa->sa_data, sizeof(old_addr)); + +	ret = eth_mac_addr(ndev, sa); +	if (ret) +		return ret; + +	qtnf_scan_done(vif->mac, true); + +	ret = qtnf_cmd_send_change_intf_type(vif, vif->wdev.iftype, +					     sa->sa_data); + +	if (ret) +		memcpy(ndev->dev_addr, old_addr, ETH_ALEN); + +	return ret; +} +  /* Network device ops handlers */  const struct net_device_ops qtnf_netdev_ops = {  	.ndo_open = qtnf_netdev_open, @@ -186,6 +210,7 @@ const struct net_device_ops qtnf_netdev_ops = {  	.ndo_start_xmit = qtnf_netdev_hard_start_xmit,  	.ndo_tx_timeout = qtnf_netdev_tx_timeout,  	.ndo_get_stats64 = qtnf_netdev_get_stats64, +	.ndo_set_mac_address = qtnf_netdev_set_mac_address,  };  static int qtnf_mac_init_single_band(struct wiphy *wiphy, @@ -470,6 +495,7 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)  	qtnf_mac_iface_comb_free(mac);  	kfree(mac->macinfo.extended_capabilities);  	kfree(mac->macinfo.extended_capabilities_mask); +	kfree(mac->macinfo.wowlan);  	wiphy_free(wiphy);  	bus->mac[macid] = NULL;  } diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index 214435448335..a1e338a1f055 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -40,7 +40,6 @@  #undef pr_fmt  #define pr_fmt(fmt)	KBUILD_MODNAME ": %s: " fmt, __func__ -#define QTNF_MAX_SSID_LIST_LENGTH	2  #define QTNF_MAX_VSIE_LEN		255  #define QTNF_MAX_INTF			8  #define QTNF_MAX_EVENT_QUEUE_LEN	255 @@ -111,6 +110,7 @@ struct qtnf_mac_info {  	u8 *extended_capabilities;  	u8 *extended_capabilities_mask;  	u8 extended_capabilities_len; +	struct wiphy_wowlan_support *wowlan;  };  struct qtnf_chan_stats { @@ -145,6 +145,7 @@ struct qtnf_hw_info {  	u8 total_rx_chain;  	char fw_version[ETHTOOL_FWVERS_LEN];  	u32 hw_version; +	u8 max_scan_ssids;  };  struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac); diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index f85deda703fb..99d37e3efba6 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -69,11 +69,15 @@ struct qlink_msg_header {   *	associated STAs due to inactivity. Inactivity timeout period is taken   *	from QLINK_CMD_START_AP parameters.   * @QLINK_HW_CAPAB_DFS_OFFLOAD: device implements DFS offload functionality + * @QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR: device supports MAC Address + *	Randomization in probe requests.   */  enum qlink_hw_capab {  	QLINK_HW_CAPAB_REG_UPDATE		= BIT(0),  	QLINK_HW_CAPAB_STA_INACT_TIMEOUT	= BIT(1),  	QLINK_HW_CAPAB_DFS_OFFLOAD		= BIT(2), +	QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR	= BIT(3), +	QLINK_HW_CAPAB_PWR_MGMT			= BIT(4),  };  enum qlink_iface_type { @@ -253,6 +257,8 @@ enum qlink_cmd_type {  	QLINK_CMD_CHAN_STATS		= 0x0054,  	QLINK_CMD_CONNECT		= 0x0060,  	QLINK_CMD_DISCONNECT		= 0x0061, +	QLINK_CMD_PM_SET		= 0x0062, +	QLINK_CMD_WOWLAN_SET		= 0x0063,  };  /** @@ -665,6 +671,54 @@ struct qlink_acl_data {  	struct qlink_mac_address mac_addrs[0];  } __packed; +/** + * enum qlink_pm_mode - Power Management mode + * + * @QLINK_PM_OFF: normal mode, no power saving enabled + * @QLINK_PM_AUTO_STANDBY: enable auto power save mode + */ +enum qlink_pm_mode { +	QLINK_PM_OFF		= 0, +	QLINK_PM_AUTO_STANDBY	= 1, +}; + +/** + * struct qlink_cmd_pm_set - data for QLINK_CMD_PM_SET command + * + * @pm_standby timer: period of network inactivity in seconds before + *	putting a radio in power save mode + * @pm_mode: power management mode + */ +struct qlink_cmd_pm_set { +	struct qlink_cmd chdr; +	__le32 pm_standby_timer; +	u8 pm_mode; +} __packed; + +/** + * enum qlink_wowlan_trigger + * + * @QLINK_WOWLAN_TRIG_DISCONNECT: wakeup on disconnect + * @QLINK_WOWLAN_TRIG_MAGIC_PKT: wakeup on magic packet + * @QLINK_WOWLAN_TRIG_PATTERN_PKT: wakeup on user-defined packet + */ +enum qlink_wowlan_trigger { +	QLINK_WOWLAN_TRIG_DISCONNECT	= BIT(0), +	QLINK_WOWLAN_TRIG_MAGIC_PKT	= BIT(1), +	QLINK_WOWLAN_TRIG_PATTERN_PKT	= BIT(2), +}; + +/** + * struct qlink_cmd_wowlan_set - data for QLINK_CMD_WOWLAN_SET command + * + * @triggers: requested bitmask of WoWLAN triggers + */ +struct qlink_cmd_wowlan_set { +	struct qlink_cmd chdr; +	__le32 triggers; +	u8 data[0]; +} __packed; +  /* QLINK Command Responses messages related definitions   */ @@ -1062,6 +1116,8 @@ struct qlink_event_radar {   * @QTN_TLV_ID_STA_STATS: per-STA statistics as defined by   *	&struct qlink_sta_stats. Valid values are marked as such in a bitmap   *	carried by QTN_TLV_ID_STA_STATS_MAP. + * @QTN_TLV_ID_MAX_SCAN_SSIDS: maximum number of SSIDs the device can scan + *	for in any given scan.   */  enum qlink_tlv_id {  	QTN_TLV_ID_FRAG_THRESH		= 0x0201, @@ -1089,6 +1145,10 @@ enum qlink_tlv_id {  	QTN_TLV_ID_HW_ID		= 0x0405,  	QTN_TLV_ID_CALIBRATION_VER	= 0x0406,  	QTN_TLV_ID_UBOOT_VER		= 0x0407, +	QTN_TLV_ID_RANDOM_MAC_ADDR	= 0x0408, +	QTN_TLV_ID_MAX_SCAN_SSIDS	= 0x0409, +	QTN_TLV_ID_WOWLAN_CAPAB		= 0x0410, +	QTN_TLV_ID_WOWLAN_PATTERN	= 0x0411,  };  struct qlink_tlv_hdr { @@ -1360,4 +1420,49 @@ struct qlink_sta_stats {  	u8 rsvd[1];  }; +/** + * struct qlink_random_mac_addr - data for QTN_TLV_ID_RANDOM_MAC_ADDR TLV + * + * Specifies MAC address mask/value for generation random MAC address + * during scan. + * + * @mac_addr: MAC address used with randomisation + * @mac_addr_mask: MAC address mask used with randomisation, bits that + *	are 0 in the mask should be randomised, bits that are 1 should + *	be taken from the @mac_addr + */ +struct qlink_random_mac_addr { +	u8 mac_addr[ETH_ALEN]; +	u8 mac_addr_mask[ETH_ALEN]; +} __packed; + +/** + * struct qlink_wowlan_capab_data - data for QTN_TLV_ID_WOWLAN_CAPAB TLV + * + * WoWLAN capabilities supported by cards. + * + * @version: version of WoWLAN data structure, to ensure backward + *	compatibility for firmwares with limited WoWLAN support + * @len: Total length of WoWLAN data + * @data: supported WoWLAN features + */ +struct qlink_wowlan_capab_data { +	__le16 version; +	__le16 len; +	u8 data[0]; +} __packed; + +/** + * struct qlink_wowlan_support - supported WoWLAN capabilities + * + * @n_patterns: number of supported wakeup patterns + * @pattern_max_len: maximum length of each pattern + * @pattern_min_len: minimum length of each pattern + */ +struct qlink_wowlan_support { +	__le32 n_patterns; +	__le32 pattern_max_len; +	__le32 pattern_min_len; +} __packed; +  #endif /* _QTN_QLINK_H_ */ |