mac80211: update to wireless-testing 2017-01-31
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		| @@ -10,13 +10,13 @@ include $(INCLUDE_DIR)/kernel.mk | ||||
|  | ||||
| PKG_NAME:=mac80211 | ||||
|  | ||||
| PKG_VERSION:=2016-10-08 | ||||
| PKG_VERSION:=2017-01-31 | ||||
| PKG_RELEASE:=1 | ||||
| PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources | ||||
| PKG_BACKPORT_VERSION:= | ||||
| PKG_HASH:=4f6350e3b75815060bfdf47ef266ad613c7bfea5b7b1dc4552dee69e1bebe4fb | ||||
| PKG_HASH:=75e6d39e34cf156212a2509172a4a62b673b69eb4a1d9aaa565f7fa719fa2317 | ||||
|  | ||||
| PKG_SOURCE:=compat-wireless-$(PKG_VERSION)$(PKG_BACKPORT_VERSION).tar.bz2 | ||||
| PKG_SOURCE:=compat-wireless-$(PKG_VERSION)$(PKG_BACKPORT_VERSION).tar.xz | ||||
| PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION) | ||||
| PKG_BUILD_PARALLEL:=1 | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/kconf/conf.c | ||||
| +++ b/kconf/conf.c | ||||
| @@ -593,40 +593,12 @@ int main(int ac, char **av) | ||||
| @@ -594,40 +594,12 @@ int main(int ac, char **av) | ||||
|  	case oldconfig: | ||||
|  	case listnewconfig: | ||||
|  	case olddefconfig: | ||||
| @@ -44,7 +44,7 @@ | ||||
|  		break; | ||||
| --- a/kconf/confdata.c | ||||
| +++ b/kconf/confdata.c | ||||
| @@ -1169,6 +1169,8 @@ bool conf_set_all_new_symbols(enum conf_ | ||||
| @@ -1170,6 +1170,8 @@ bool conf_set_all_new_symbols(enum conf_ | ||||
|  	} | ||||
|  	bool has_changed = false; | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|  	for_all_symbols(i, sym) { | ||||
|  		if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID)) | ||||
|  			continue; | ||||
| @@ -1212,8 +1214,6 @@ bool conf_set_all_new_symbols(enum conf_ | ||||
| @@ -1213,8 +1215,6 @@ bool conf_set_all_new_symbols(enum conf_ | ||||
|   | ||||
|  	} | ||||
|   | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/compat/Makefile | ||||
| +++ b/compat/Makefile | ||||
| @@ -35,8 +35,6 @@ compat-$(CPTCFG_KERNEL_4_6) += backport- | ||||
|   | ||||
|  compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o | ||||
|  compat-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o | ||||
| -skcipher-objs += crypto-skcipher.o | ||||
| -obj-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += skcipher.o | ||||
|  compat-$(CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP) += drivers-base-devcoredump.o | ||||
|  compat-$(CPTCFG_BPAUTO_RHASHTABLE) += lib-rhashtable.o | ||||
|  cordic-objs += lib-cordic.o | ||||
| @@ -0,0 +1,28 @@ | ||||
| --- a/backport-include/linux/kconfig.h | ||||
| +++ b/backport-include/linux/kconfig.h | ||||
| @@ -5,6 +5,8 @@ | ||||
|  #include_next <linux/kconfig.h> | ||||
|  #endif | ||||
|   | ||||
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) | ||||
| + | ||||
|  #ifndef __ARG_PLACEHOLDER_1 | ||||
|  #define __ARG_PLACEHOLDER_1 0, | ||||
|  #define config_enabled(cfg) _config_enabled(cfg) | ||||
| @@ -16,6 +18,7 @@ | ||||
|   * 3.1 - 3.3 had a broken version of this, so undef | ||||
|   * (they didn't have __ARG_PLACEHOLDER_1) | ||||
|   */ | ||||
| + | ||||
|  #undef IS_ENABLED | ||||
|  #define IS_ENABLED(option) \ | ||||
|          (config_enabled(option) || config_enabled(option##_MODULE)) | ||||
| @@ -24,6 +27,8 @@ | ||||
|  #undef IS_BUILTIN | ||||
|  #define IS_BUILTIN(option) config_enabled(option) | ||||
|   | ||||
| +#endif | ||||
| + | ||||
|  #ifndef IS_REACHABLE | ||||
|  /* | ||||
|   * IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled | ||||
| @@ -1,22 +0,0 @@ | ||||
| --- a/backport-include/linux/skbuff.h | ||||
| +++ b/backport-include/linux/skbuff.h | ||||
| @@ -305,6 +305,19 @@ static inline void skb_free_frag(void *d | ||||
|  { | ||||
|  	put_page(virt_to_head_page(data)); | ||||
|  } | ||||
| + | ||||
| +#include <net/flow_keys.h> | ||||
| +#include <linux/jhash.h> | ||||
| + | ||||
| +static inline u32 skb_get_hash_perturb(struct sk_buff *skb, u32 key) | ||||
| +{ | ||||
| +	struct flow_keys keys; | ||||
| + | ||||
| +	skb_flow_dissect(skb, &keys); | ||||
| +	return jhash_3words((__force u32)keys.dst, | ||||
| +			    (__force u32)keys.src ^ keys.ip_proto, | ||||
| +			    (__force u32)keys.ports, key); | ||||
| +} | ||||
|  #endif | ||||
|   | ||||
|  #endif /* __BACKPORT_SKBUFF_H */ | ||||
							
								
								
									
										152
									
								
								package/kernel/mac80211/patches/005-revert-devcoredump.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								package/kernel/mac80211/patches/005-revert-devcoredump.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| --- a/compat/Makefile | ||||
| +++ b/compat/Makefile | ||||
| @@ -35,8 +35,6 @@ compat-$(CPTCFG_KERNEL_4_6) += backport- | ||||
|   | ||||
|  compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o | ||||
|  compat-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o | ||||
| -skcipher-objs += crypto-skcipher.o | ||||
| -obj-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += skcipher.o | ||||
|  compat-$(CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP) += drivers-base-devcoredump.o | ||||
|  compat-$(CPTCFG_BPAUTO_RHASHTABLE) += lib-rhashtable.o | ||||
|  cordic-objs += lib-cordic.o | ||||
| --- a/compat/drivers-base-devcoredump.c | ||||
| +++ b/compat/drivers-base-devcoredump.c | ||||
| @@ -31,6 +31,7 @@ | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/fs.h> | ||||
|  #include <linux/workqueue.h> | ||||
| +#include "backports.h" | ||||
|   | ||||
|  static struct class devcd_class; | ||||
|   | ||||
| @@ -40,6 +41,10 @@ static bool devcd_disabled; | ||||
|  /* if data isn't read by userspace after 5 minutes then delete it */ | ||||
|  #define DEVCD_TIMEOUT	(HZ * 60 * 5) | ||||
|   | ||||
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) | ||||
| +static struct bin_attribute devcd_attr_data; | ||||
| +#endif | ||||
| + | ||||
|  struct devcd_entry { | ||||
|  	struct device devcd_dev; | ||||
|  	void *data; | ||||
| @@ -69,8 +74,7 @@ static void devcd_dev_release(struct dev | ||||
|  	 * a struct device to know when it goes away? | ||||
|  	 */ | ||||
|  	if (devcd->failing_dev->kobj.sd) | ||||
| -		sysfs_delete_link(&devcd->failing_dev->kobj, &dev->kobj, | ||||
| -				  "devcoredump"); | ||||
| +		sysfs_remove_link(&devcd->failing_dev->kobj, "devcoredump"); | ||||
|   | ||||
|  	put_device(devcd->failing_dev); | ||||
|  	kfree(devcd); | ||||
| @@ -82,6 +86,9 @@ static void devcd_del(struct work_struct | ||||
|   | ||||
|  	devcd = container_of(wk, struct devcd_entry, del_wk.work); | ||||
|   | ||||
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) | ||||
| +	device_remove_bin_file(&devcd->devcd_dev, &devcd_attr_data); | ||||
| +#endif | ||||
|  	device_del(&devcd->devcd_dev); | ||||
|  	put_device(&devcd->devcd_dev); | ||||
|  } | ||||
| @@ -115,6 +122,7 @@ static struct bin_attribute devcd_attr_d | ||||
|  	.write = devcd_data_write, | ||||
|  }; | ||||
|   | ||||
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) | ||||
|  static struct bin_attribute *devcd_dev_bin_attrs[] = { | ||||
|  	&devcd_attr_data, NULL, | ||||
|  }; | ||||
| @@ -126,6 +134,7 @@ static const struct attribute_group devc | ||||
|  static const struct attribute_group *devcd_dev_groups[] = { | ||||
|  	&devcd_dev_group, NULL, | ||||
|  }; | ||||
| +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) */ | ||||
|   | ||||
|  static int devcd_free(struct device *dev, void *data) | ||||
|  { | ||||
| @@ -160,18 +169,11 @@ static ssize_t disabled_store(struct cla | ||||
|   | ||||
|  	return count; | ||||
|  } | ||||
| -static CLASS_ATTR_RW(disabled); | ||||
|   | ||||
| -static struct attribute *devcd_class_attrs[] = { | ||||
| -	&class_attr_disabled.attr, | ||||
| -	NULL, | ||||
| +static struct class_attribute devcd_class_attrs[] = { | ||||
| +	__ATTR_RW(disabled), | ||||
| +	__ATTR_NULL | ||||
|  }; | ||||
| -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) | ||||
| -ATTRIBUTE_GROUPS(devcd_class); | ||||
| -#else | ||||
| -#define BP_ATTR_GRP_STRUCT device_attribute | ||||
| -ATTRIBUTE_GROUPS_BACKPORT(devcd_class); | ||||
| -#endif | ||||
|   | ||||
|  static struct class devcd_class = { | ||||
|  	.name		= "devcoredump", | ||||
| @@ -179,10 +181,8 @@ static struct class devcd_class = { | ||||
|  	.dev_release	= devcd_dev_release, | ||||
|  #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) | ||||
|  	.dev_groups	= devcd_dev_groups, | ||||
| -#else | ||||
| -	.dev_attrs = devcd_class_dev_attrs, | ||||
|  #endif | ||||
| -	.class_groups	= devcd_class_groups, | ||||
| +	.class_attrs	= devcd_class_attrs, | ||||
|  }; | ||||
|   | ||||
|  static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, | ||||
| @@ -325,6 +325,11 @@ void dev_coredumpm(struct device *dev, s | ||||
|  	if (device_add(&devcd->devcd_dev)) | ||||
|  		goto put_device; | ||||
|   | ||||
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) | ||||
| +	if (device_create_bin_file(&devcd->devcd_dev, &devcd_attr_data)) | ||||
| +		goto put_device; | ||||
| +#endif | ||||
| + | ||||
|  	if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj, | ||||
|  			      "failing_device")) | ||||
|  		/* nothing - symlink will be missing */; | ||||
| @@ -367,16 +372,13 @@ void dev_coredumpsg(struct device *dev, | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(dev_coredumpsg); | ||||
|   | ||||
| -static int __init devcoredump_init(void) | ||||
| +int __init devcoredump_init(void) | ||||
|  { | ||||
| -	init_devcd_class_attrs(); | ||||
|  	return class_register(&devcd_class); | ||||
|  } | ||||
| -__initcall(devcoredump_init); | ||||
|   | ||||
| -static void __exit devcoredump_exit(void) | ||||
| +void __exit devcoredump_exit(void) | ||||
|  { | ||||
|  	class_for_each_device(&devcd_class, NULL, NULL, devcd_free); | ||||
|  	class_unregister(&devcd_class); | ||||
|  } | ||||
| -__exitcall(devcoredump_exit); | ||||
| --- a/include/linux/backport-devcoredump.h | ||||
| +++ b/include/linux/backport-devcoredump.h | ||||
| @@ -66,7 +66,7 @@ static inline void _devcd_free_sgtable(s | ||||
|  } | ||||
|   | ||||
|   | ||||
| -#ifdef CONFIG_DEV_COREDUMP | ||||
| +#ifdef CPTCFG_BPAUTO_WANT_DEV_COREDUMP | ||||
|  void dev_coredumpv(struct device *dev, void *data, size_t datalen, | ||||
|  		   gfp_t gfp); | ||||
|   | ||||
| @@ -100,6 +100,6 @@ static inline void dev_coredumpsg(struct | ||||
|  { | ||||
|  	_devcd_free_sgtable(table); | ||||
|  } | ||||
| -#endif /* CONFIG_DEV_COREDUMP */ | ||||
| +#endif /* CPTCFG_BPAUTO_WANT_DEV_COREDUMP */ | ||||
|   | ||||
|  #endif /* __DEVCOREDUMP_H */ | ||||
| @@ -0,0 +1,20 @@ | ||||
| --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c | ||||
| +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c | ||||
| @@ -177,7 +177,7 @@ static bool rt2800usb_tx_sta_fifo_read_c | ||||
|  	if (rt2800usb_txstatus_pending(rt2x00dev)) { | ||||
|  		/* Read register after 1 ms */ | ||||
|  		hrtimer_start(&rt2x00dev->txstatus_timer, | ||||
| -			      TXSTATUS_READ_INTERVAL, | ||||
| +			      ktime_set(0, TXSTATUS_READ_INTERVAL), | ||||
|  			      HRTIMER_MODE_REL); | ||||
|  		return false; | ||||
|  	} | ||||
| @@ -204,7 +204,7 @@ static void rt2800usb_async_read_tx_stat | ||||
|   | ||||
|  	/* Read TX_STA_FIFO register after 2 ms */ | ||||
|  	hrtimer_start(&rt2x00dev->txstatus_timer, | ||||
| -		      2 * TXSTATUS_READ_INTERVAL, | ||||
| +		      ktime_set(0, 2*TXSTATUS_READ_INTERVAL), | ||||
|  		      HRTIMER_MODE_REL); | ||||
|  } | ||||
|   | ||||
| @@ -0,0 +1,266 @@ | ||||
| --- a/net/wireless/nl80211.c | ||||
| +++ b/net/wireless/nl80211.c | ||||
| @@ -32,8 +32,22 @@ static int nl80211_crypto_settings(struc | ||||
|  				   struct cfg80211_crypto_settings *settings, | ||||
|  				   int cipher_limit); | ||||
|   | ||||
| +static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||||
| +			    struct genl_info *info); | ||||
| +static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||||
| +			      struct genl_info *info); | ||||
| + | ||||
|  /* the netlink family */ | ||||
| -static struct genl_family nl80211_fam; | ||||
| +static struct genl_family nl80211_fam = { | ||||
| +	.id = GENL_ID_GENERATE,		/* don't bother with a hardcoded ID */ | ||||
| +	.name = NL80211_GENL_NAME,	/* have users key off the name instead */ | ||||
| +	.hdrsize = 0,			/* no private header */ | ||||
| +	.version = 1,			/* no particular meaning now */ | ||||
| +	.maxattr = NL80211_ATTR_MAX, | ||||
| +	.netnsok = true, | ||||
| +	.pre_doit = nl80211_pre_doit, | ||||
| +	.post_doit = nl80211_post_doit, | ||||
| +}; | ||||
|   | ||||
|  /* multicast groups */ | ||||
|  enum nl80211_multicast_groups { | ||||
| @@ -549,14 +563,13 @@ static int nl80211_prepare_wdev_dump(str | ||||
|   | ||||
|  	if (!cb->args[0]) { | ||||
|  		err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||||
| -				  genl_family_attrbuf(&nl80211_fam), | ||||
| -				  nl80211_fam.maxattr, nl80211_policy); | ||||
| +				  nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||||
| +				  nl80211_policy); | ||||
|  		if (err) | ||||
|  			goto out_unlock; | ||||
|   | ||||
| -		*wdev = __cfg80211_wdev_from_attrs( | ||||
| -					sock_net(skb->sk), | ||||
| -					genl_family_attrbuf(&nl80211_fam)); | ||||
| +		*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), | ||||
| +						   nl80211_fam.attrbuf); | ||||
|  		if (IS_ERR(*wdev)) { | ||||
|  			err = PTR_ERR(*wdev); | ||||
|  			goto out_unlock; | ||||
| @@ -1903,7 +1916,7 @@ static int nl80211_dump_wiphy_parse(stru | ||||
|  				    struct netlink_callback *cb, | ||||
|  				    struct nl80211_dump_wiphy_state *state) | ||||
|  { | ||||
| -	struct nlattr **tb = genl_family_attrbuf(&nl80211_fam); | ||||
| +	struct nlattr **tb = nl80211_fam.attrbuf; | ||||
|  	int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||||
|  			      tb, nl80211_fam.maxattr, nl80211_policy); | ||||
|  	/* ignore parse errors for backward compatibility */ | ||||
| @@ -7733,7 +7746,6 @@ static int nl80211_send_survey(struct sk | ||||
|   | ||||
|  static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) | ||||
|  { | ||||
| -	struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); | ||||
|  	struct survey_info survey; | ||||
|  	struct cfg80211_registered_device *rdev; | ||||
|  	struct wireless_dev *wdev; | ||||
| @@ -7746,7 +7758,7 @@ static int nl80211_dump_survey(struct sk | ||||
|  		return res; | ||||
|   | ||||
|  	/* prepare_wdev_dump parsed the attributes */ | ||||
| -	radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; | ||||
| +	radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; | ||||
|   | ||||
|  	if (!wdev->netdev) { | ||||
|  		res = -EINVAL; | ||||
| @@ -8594,14 +8606,14 @@ static int nl80211_testmode_dump(struct | ||||
|  		 */ | ||||
|  		phy_idx = cb->args[0] - 1; | ||||
|  	} else { | ||||
| -		struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); | ||||
| - | ||||
|  		err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||||
| -				  attrbuf, nl80211_fam.maxattr, nl80211_policy); | ||||
| +				  nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||||
| +				  nl80211_policy); | ||||
|  		if (err) | ||||
|  			goto out_err; | ||||
|   | ||||
| -		rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf); | ||||
| +		rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), | ||||
| +						  nl80211_fam.attrbuf); | ||||
|  		if (IS_ERR(rdev)) { | ||||
|  			err = PTR_ERR(rdev); | ||||
|  			goto out_err; | ||||
| @@ -8609,8 +8621,9 @@ static int nl80211_testmode_dump(struct | ||||
|  		phy_idx = rdev->wiphy_idx; | ||||
|  		rdev = NULL; | ||||
|   | ||||
| -		if (attrbuf[NL80211_ATTR_TESTDATA]) | ||||
| -			cb->args[1] = (long)attrbuf[NL80211_ATTR_TESTDATA]; | ||||
| +		if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) | ||||
| +			cb->args[1] = | ||||
| +				(long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; | ||||
|  	} | ||||
|   | ||||
|  	if (cb->args[1]) { | ||||
| @@ -10814,7 +10827,8 @@ static int handle_nan_filter(struct nlat | ||||
|   | ||||
|  	i = 0; | ||||
|  	nla_for_each_nested(attr, attr_filter, rem) { | ||||
| -		filter[i].filter = nla_memdup(attr, GFP_KERNEL); | ||||
| +		filter[i].filter = kmemdup(nla_data(attr), nla_len(attr), | ||||
| +					   GFP_KERNEL); | ||||
|  		filter[i].len = nla_len(attr); | ||||
|  		i++; | ||||
|  	} | ||||
| @@ -11450,7 +11464,6 @@ static int nl80211_prepare_vendor_dump(s | ||||
|  				       struct cfg80211_registered_device **rdev, | ||||
|  				       struct wireless_dev **wdev) | ||||
|  { | ||||
| -	struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); | ||||
|  	u32 vid, subcmd; | ||||
|  	unsigned int i; | ||||
|  	int vcmd_idx = -1; | ||||
| @@ -11486,28 +11499,31 @@ static int nl80211_prepare_vendor_dump(s | ||||
|  	} | ||||
|   | ||||
|  	err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||||
| -			  attrbuf, nl80211_fam.maxattr, nl80211_policy); | ||||
| +			  nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||||
| +			  nl80211_policy); | ||||
|  	if (err) | ||||
|  		goto out_unlock; | ||||
|   | ||||
| -	if (!attrbuf[NL80211_ATTR_VENDOR_ID] || | ||||
| -	    !attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { | ||||
| +	if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] || | ||||
| +	    !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { | ||||
|  		err = -EINVAL; | ||||
|  		goto out_unlock; | ||||
|  	} | ||||
|   | ||||
| -	*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf); | ||||
| +	*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), | ||||
| +					   nl80211_fam.attrbuf); | ||||
|  	if (IS_ERR(*wdev)) | ||||
|  		*wdev = NULL; | ||||
|   | ||||
| -	*rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf); | ||||
| +	*rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), | ||||
| +					   nl80211_fam.attrbuf); | ||||
|  	if (IS_ERR(*rdev)) { | ||||
|  		err = PTR_ERR(*rdev); | ||||
|  		goto out_unlock; | ||||
|  	} | ||||
|   | ||||
| -	vid = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_ID]); | ||||
| -	subcmd = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); | ||||
| +	vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]); | ||||
| +	subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); | ||||
|   | ||||
|  	for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) { | ||||
|  		const struct wiphy_vendor_command *vcmd; | ||||
| @@ -11531,9 +11547,9 @@ static int nl80211_prepare_vendor_dump(s | ||||
|  		goto out_unlock; | ||||
|  	} | ||||
|   | ||||
| -	if (attrbuf[NL80211_ATTR_VENDOR_DATA]) { | ||||
| -		data = nla_data(attrbuf[NL80211_ATTR_VENDOR_DATA]); | ||||
| -		data_len = nla_len(attrbuf[NL80211_ATTR_VENDOR_DATA]); | ||||
| +	if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) { | ||||
| +		data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); | ||||
| +		data_len = nla_len(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); | ||||
|  	} | ||||
|   | ||||
|  	/* 0 is the first index - add 1 to parse only once */ | ||||
| @@ -12795,21 +12811,6 @@ static __genl_const struct genl_ops nl80 | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| -static struct genl_family nl80211_fam __ro_after_init = { | ||||
| -	.name = NL80211_GENL_NAME,	/* have users key off the name instead */ | ||||
| -	.hdrsize = 0,			/* no private header */ | ||||
| -	.version = 1,			/* no particular meaning now */ | ||||
| -	.maxattr = NL80211_ATTR_MAX, | ||||
| -	.netnsok = true, | ||||
| -	.pre_doit = nl80211_pre_doit, | ||||
| -	.post_doit = nl80211_post_doit, | ||||
| -	.module = THIS_MODULE, | ||||
| -	.ops = nl80211_ops, | ||||
| -	.n_ops = ARRAY_SIZE(nl80211_ops), | ||||
| -	.mcgrps = nl80211_mcgrps, | ||||
| -	.n_mcgrps = ARRAY_SIZE(nl80211_mcgrps), | ||||
| -}; | ||||
| - | ||||
|  /* notification functions */ | ||||
|   | ||||
|  void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, | ||||
| @@ -14759,11 +14760,12 @@ void nl80211_send_ap_stopped(struct wire | ||||
|   | ||||
|  /* initialisation/exit functions */ | ||||
|   | ||||
| -int __init nl80211_init(void) | ||||
| +int nl80211_init(void) | ||||
|  { | ||||
|  	int err; | ||||
|   | ||||
| -	err = genl_register_family(&nl80211_fam); | ||||
| +	err = genl_register_family_with_ops_groups(&nl80211_fam, nl80211_ops, | ||||
| +						   nl80211_mcgrps); | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| --- a/drivers/net/wireless/mac80211_hwsim.c | ||||
| +++ b/drivers/net/wireless/mac80211_hwsim.c | ||||
| @@ -587,8 +587,15 @@ struct hwsim_radiotap_ack_hdr { | ||||
|  	__le16 rt_chbitmask; | ||||
|  } __packed; | ||||
|   | ||||
| -/* MAC80211_HWSIM netlink family */ | ||||
| -static struct genl_family hwsim_genl_family; | ||||
| +/* MAC80211_HWSIM netlinf family */ | ||||
| +static struct genl_family hwsim_genl_family = { | ||||
| +	.id = GENL_ID_GENERATE, | ||||
| +	.hdrsize = 0, | ||||
| +	.name = "MAC80211_HWSIM", | ||||
| +	.version = 1, | ||||
| +	.maxattr = HWSIM_ATTR_MAX, | ||||
| +	.netnsok = true, | ||||
| +}; | ||||
|   | ||||
|  enum hwsim_multicast_groups { | ||||
|  	HWSIM_MCGRP_CONFIG, | ||||
| @@ -3250,18 +3257,6 @@ static __genl_const struct genl_ops hwsi | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| -static struct genl_family hwsim_genl_family __ro_after_init = { | ||||
| -	.name = "MAC80211_HWSIM", | ||||
| -	.version = 1, | ||||
| -	.maxattr = HWSIM_ATTR_MAX, | ||||
| -	.netnsok = true, | ||||
| -	.module = THIS_MODULE, | ||||
| -	.ops = hwsim_ops, | ||||
| -	.n_ops = ARRAY_SIZE(hwsim_ops), | ||||
| -	.mcgrps = hwsim_mcgrps, | ||||
| -	.n_mcgrps = ARRAY_SIZE(hwsim_mcgrps), | ||||
| -}; | ||||
| - | ||||
|  static void destroy_radio(struct work_struct *work) | ||||
|  { | ||||
|  	struct mac80211_hwsim_data *data = | ||||
| @@ -3309,13 +3304,15 @@ static struct notifier_block hwsim_netli | ||||
|  	.notifier_call = mac80211_hwsim_netlink_notify, | ||||
|  }; | ||||
|   | ||||
| -static int __init hwsim_init_netlink(void) | ||||
| +static int hwsim_init_netlink(void) | ||||
|  { | ||||
|  	int rc; | ||||
|   | ||||
|  	printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); | ||||
|   | ||||
| -	rc = genl_register_family(&hwsim_genl_family); | ||||
| +	rc = genl_register_family_with_ops_groups(&hwsim_genl_family, | ||||
| +						  hwsim_ops, | ||||
| +						  hwsim_mcgrps); | ||||
|  	if (rc) | ||||
|  		goto failure; | ||||
|   | ||||
| @@ -0,0 +1,20 @@ | ||||
| --- a/net/mac80211/iface.c | ||||
| +++ b/net/mac80211/iface.c | ||||
| @@ -1133,7 +1133,7 @@ static u16 ieee80211_netdev_select_queue | ||||
|  	return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb); | ||||
|  } | ||||
|   | ||||
| -static void | ||||
| +static struct rtnl_link_stats64 * | ||||
|  ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) | ||||
|  { | ||||
|  	int i; | ||||
| @@ -1158,6 +1158,8 @@ ieee80211_get_stats64(struct net_device | ||||
|  		stats->rx_bytes   += rx_bytes; | ||||
|  		stats->tx_bytes   += tx_bytes; | ||||
|  	} | ||||
| + | ||||
| +	return stats; | ||||
|  } | ||||
|   | ||||
|  static const struct net_device_ops ieee80211_dataif_ops = { | ||||
							
								
								
									
										338
									
								
								package/kernel/mac80211/patches/009-revert-mtu-changes.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								package/kernel/mac80211/patches/009-revert-mtu-changes.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | ||||
| --- a/net/mac80211/iface.c | ||||
| +++ b/net/mac80211/iface.c | ||||
| @@ -151,6 +151,15 @@ void ieee80211_recalc_idle(struct ieee80 | ||||
|  		ieee80211_hw_config(local, change); | ||||
|  } | ||||
|   | ||||
| +static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) | ||||
| +{ | ||||
| +	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	dev->mtu = new_mtu; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr, | ||||
|  				bool check_dup) | ||||
|  { | ||||
| @@ -1168,6 +1177,7 @@ static const struct net_device_ops ieee8 | ||||
|  	.ndo_uninit		= ieee80211_uninit, | ||||
|  	.ndo_start_xmit		= ieee80211_subif_start_xmit, | ||||
|  	.ndo_set_rx_mode	= ieee80211_set_multicast_list, | ||||
| +	.ndo_change_mtu 	= ieee80211_change_mtu, | ||||
|  	.ndo_set_mac_address 	= ieee80211_change_mac, | ||||
|  	.ndo_select_queue	= ieee80211_netdev_select_queue, | ||||
|  	.ndo_get_stats64	= ieee80211_get_stats64, | ||||
| @@ -1211,6 +1221,7 @@ static const struct net_device_ops ieee8 | ||||
|  	.ndo_uninit		= ieee80211_uninit, | ||||
|  	.ndo_start_xmit		= ieee80211_monitor_start_xmit, | ||||
|  	.ndo_set_rx_mode	= ieee80211_set_multicast_list, | ||||
| +	.ndo_change_mtu 	= ieee80211_change_mtu, | ||||
|  	.ndo_set_mac_address 	= ieee80211_change_mac, | ||||
|  	.ndo_select_queue	= ieee80211_monitor_select_queue, | ||||
|  	.ndo_get_stats64	= ieee80211_get_stats64, | ||||
| @@ -1919,10 +1930,6 @@ int ieee80211_if_add(struct ieee80211_lo | ||||
|   | ||||
|  		netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); | ||||
|   | ||||
| -		/* MTU range: 256 - 2304 */ | ||||
| -		ndev->min_mtu = 256; | ||||
| -		ndev->max_mtu = IEEE80211_MAX_DATA_LEN; | ||||
| - | ||||
|  		ret = register_netdevice(ndev); | ||||
|  		if (ret) { | ||||
|  			ieee80211_if_free(ndev); | ||||
| --- a/drivers/net/wireless/ath/wil6210/netdev.c | ||||
| +++ b/drivers/net/wireless/ath/wil6210/netdev.c | ||||
| @@ -42,6 +42,21 @@ static int wil_stop(struct net_device *n | ||||
|  	return wil_down(wil); | ||||
|  } | ||||
|   | ||||
| +static int wil_change_mtu(struct net_device *ndev, int new_mtu) | ||||
| +{ | ||||
| +	struct wil6210_priv *wil = ndev_to_wil(ndev); | ||||
| + | ||||
| +	if (new_mtu < 68 || new_mtu > mtu_max) { | ||||
| +		wil_err(wil, "invalid MTU %d\n", new_mtu); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu); | ||||
| +	ndev->mtu = new_mtu; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) | ||||
|  { | ||||
|  	struct wil6210_priv *wil = ndev_to_wil(ndev); | ||||
| @@ -55,6 +70,7 @@ static const struct net_device_ops wil_n | ||||
|  	.ndo_start_xmit		= wil_start_xmit, | ||||
|  	.ndo_set_mac_address	= eth_mac_addr, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
| +	.ndo_change_mtu		= wil_change_mtu, | ||||
|  	.ndo_do_ioctl		= wil_do_ioctl, | ||||
|  }; | ||||
|   | ||||
| @@ -111,7 +127,6 @@ static int wil6210_netdev_poll_tx(struct | ||||
|  static void wil_dev_setup(struct net_device *dev) | ||||
|  { | ||||
|  	ether_setup(dev); | ||||
| -	dev->max_mtu = mtu_max; | ||||
|  	dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/atmel/atmel.c | ||||
| +++ b/drivers/net/wireless/atmel/atmel.c | ||||
| @@ -1295,6 +1295,14 @@ static struct iw_statistics *atmel_get_w | ||||
|  	return &priv->wstats; | ||||
|  } | ||||
|   | ||||
| +static int atmel_change_mtu(struct net_device *dev, int new_mtu) | ||||
| +{ | ||||
| +	if ((new_mtu < 68) || (new_mtu > 2312)) | ||||
| +		return -EINVAL; | ||||
| +	dev->mtu = new_mtu; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int atmel_set_mac_address(struct net_device *dev, void *p) | ||||
|  { | ||||
|  	struct sockaddr *addr = p; | ||||
| @@ -1498,6 +1506,7 @@ static const struct file_operations atme | ||||
|  static const struct net_device_ops atmel_netdev_ops = { | ||||
|  	.ndo_open 		= atmel_open, | ||||
|  	.ndo_stop		= atmel_close, | ||||
| +	.ndo_change_mtu 	= atmel_change_mtu, | ||||
|  	.ndo_set_mac_address 	= atmel_set_mac_address, | ||||
|  	.ndo_start_xmit 	= start_tx, | ||||
|  	.ndo_do_ioctl 		= atmel_ioctl, | ||||
| @@ -1591,10 +1600,6 @@ struct net_device *init_atmel_card(unsig | ||||
|  	dev->irq = irq; | ||||
|  	dev->base_addr = port; | ||||
|   | ||||
| -	/* MTU range: 68 - 2312 */ | ||||
| -	dev->min_mtu = 68; | ||||
| -	dev->max_mtu = MAX_WIRELESS_BODY - ETH_FCS_LEN; | ||||
| - | ||||
|  	SET_NETDEV_DEV(dev, sys_dev); | ||||
|   | ||||
|  	if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { | ||||
| --- a/drivers/net/wireless/cisco/airo.c | ||||
| +++ b/drivers/net/wireless/cisco/airo.c | ||||
| @@ -2329,6 +2329,14 @@ static int airo_set_mac_address(struct n | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int airo_change_mtu(struct net_device *dev, int new_mtu) | ||||
| +{ | ||||
| +	if ((new_mtu < 68) || (new_mtu > 2400)) | ||||
| +		return -EINVAL; | ||||
| +	dev->mtu = new_mtu; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static LIST_HEAD(airo_devices); | ||||
|   | ||||
|  static void add_airo_dev(struct airo_info *ai) | ||||
| @@ -2648,6 +2656,7 @@ static const struct net_device_ops airo1 | ||||
|  	.ndo_get_stats 		= airo_get_stats, | ||||
|  	.ndo_set_mac_address	= airo_set_mac_address, | ||||
|  	.ndo_do_ioctl		= airo_ioctl, | ||||
| +	.ndo_change_mtu		= airo_change_mtu, | ||||
|  }; | ||||
|   | ||||
|  static void wifi_setup(struct net_device *dev) | ||||
| @@ -2659,8 +2668,6 @@ static void wifi_setup(struct net_device | ||||
|  	dev->type               = ARPHRD_IEEE80211; | ||||
|  	dev->hard_header_len    = ETH_HLEN; | ||||
|  	dev->mtu                = AIRO_DEF_MTU; | ||||
| -	dev->min_mtu            = 68; | ||||
| -	dev->max_mtu            = MIC_MSGLEN_MAX; | ||||
|  	dev->addr_len           = ETH_ALEN; | ||||
|  	dev->tx_queue_len       = 100;  | ||||
|   | ||||
| @@ -2747,6 +2754,7 @@ static const struct net_device_ops airo_ | ||||
|  	.ndo_set_rx_mode	= airo_set_multicast_list, | ||||
|  	.ndo_set_mac_address	= airo_set_mac_address, | ||||
|  	.ndo_do_ioctl		= airo_ioctl, | ||||
| +	.ndo_change_mtu		= airo_change_mtu, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
|   | ||||
| @@ -2758,6 +2766,7 @@ static const struct net_device_ops mpi_n | ||||
|  	.ndo_set_rx_mode	= airo_set_multicast_list, | ||||
|  	.ndo_set_mac_address	= airo_set_mac_address, | ||||
|  	.ndo_do_ioctl		= airo_ioctl, | ||||
| +	.ndo_change_mtu		= airo_change_mtu, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
|   | ||||
| @@ -2813,7 +2822,6 @@ static struct net_device *_init_airo_car | ||||
|  	dev->irq = irq; | ||||
|  	dev->base_addr = port; | ||||
|  	dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||||
| -	dev->max_mtu = MIC_MSGLEN_MAX; | ||||
|   | ||||
|  	SET_NETDEV_DEV(dev, dmdev); | ||||
|   | ||||
| --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c | ||||
| +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c | ||||
| @@ -6039,6 +6039,7 @@ static const struct net_device_ops ipw21 | ||||
|  	.ndo_open		= ipw2100_open, | ||||
|  	.ndo_stop		= ipw2100_close, | ||||
|  	.ndo_start_xmit		= libipw_xmit, | ||||
| +	.ndo_change_mtu		= libipw_change_mtu, | ||||
|  	.ndo_tx_timeout		= ipw2100_tx_timeout, | ||||
|  	.ndo_set_mac_address	= ipw2100_set_address, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
| @@ -6074,8 +6075,6 @@ static struct net_device *ipw2100_alloc_ | ||||
|  	dev->wireless_data = &priv->wireless_data; | ||||
|  	dev->watchdog_timeo = 3 * HZ; | ||||
|  	dev->irq = 0; | ||||
| -	dev->min_mtu = 68; | ||||
| -	dev->max_mtu = LIBIPW_DATA_LEN; | ||||
|   | ||||
|  	/* NOTE: We don't use the wireless_handlers hook | ||||
|  	 * in dev as the system will start throwing WX requests | ||||
| --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c | ||||
| +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c | ||||
| @@ -11561,6 +11561,7 @@ static const struct net_device_ops ipw_p | ||||
|  	.ndo_open 		= ipw_prom_open, | ||||
|  	.ndo_stop		= ipw_prom_stop, | ||||
|  	.ndo_start_xmit		= ipw_prom_hard_start_xmit, | ||||
| +	.ndo_change_mtu		= libipw_change_mtu, | ||||
|  	.ndo_set_mac_address 	= eth_mac_addr, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
| @@ -11586,9 +11587,6 @@ static int ipw_prom_alloc(struct ipw_pri | ||||
|  	priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP; | ||||
|  	priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops; | ||||
|   | ||||
| -	priv->prom_net_dev->min_mtu = 68; | ||||
| -	priv->prom_net_dev->max_mtu = LIBIPW_DATA_LEN; | ||||
| - | ||||
|  	priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR; | ||||
|  	SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev); | ||||
|   | ||||
| @@ -11621,6 +11619,7 @@ static const struct net_device_ops ipw_n | ||||
|  	.ndo_set_rx_mode	= ipw_net_set_multicast_list, | ||||
|  	.ndo_set_mac_address	= ipw_net_set_mac_address, | ||||
|  	.ndo_start_xmit		= libipw_xmit, | ||||
| +	.ndo_change_mtu		= libipw_change_mtu, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
|   | ||||
| @@ -11730,9 +11729,6 @@ static int ipw_pci_probe(struct pci_dev | ||||
|  	net_dev->wireless_handlers = &ipw_wx_handler_def; | ||||
|  	net_dev->ethtool_ops = &ipw_ethtool_ops; | ||||
|   | ||||
| -	net_dev->min_mtu = 68; | ||||
| -	net_dev->max_mtu = LIBIPW_DATA_LEN; | ||||
| - | ||||
|  	err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group); | ||||
|  	if (err) { | ||||
|  		IPW_ERROR("failed to create sysfs device attributes\n"); | ||||
| --- a/drivers/net/wireless/intel/ipw2x00/libipw.h | ||||
| +++ b/drivers/net/wireless/intel/ipw2x00/libipw.h | ||||
| @@ -948,6 +948,7 @@ static inline int libipw_is_cck_rate(u8 | ||||
|  /* libipw.c */ | ||||
|  void free_libipw(struct net_device *dev, int monitor); | ||||
|  struct net_device *alloc_libipw(int sizeof_priv, int monitor); | ||||
| +int libipw_change_mtu(struct net_device *dev, int new_mtu); | ||||
|   | ||||
|  void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); | ||||
|   | ||||
| --- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c | ||||
| +++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c | ||||
| @@ -118,6 +118,15 @@ static void libipw_networks_initialize(s | ||||
|  			      &ieee->network_free_list); | ||||
|  } | ||||
|   | ||||
| +int libipw_change_mtu(struct net_device *dev, int new_mtu) | ||||
| +{ | ||||
| +	if ((new_mtu < 68) || (new_mtu > LIBIPW_DATA_LEN)) | ||||
| +		return -EINVAL; | ||||
| +	dev->mtu = new_mtu; | ||||
| +	return 0; | ||||
| +} | ||||
| +EXPORT_SYMBOL(libipw_change_mtu); | ||||
| + | ||||
|  struct net_device *alloc_libipw(int sizeof_priv, int monitor) | ||||
|  { | ||||
|  	struct libipw_device *ieee; | ||||
| --- a/drivers/net/wireless/intersil/hostap/hostap_main.c | ||||
| +++ b/drivers/net/wireless/intersil/hostap/hostap_main.c | ||||
| @@ -765,6 +765,16 @@ static void hostap_set_multicast_list(st | ||||
|  } | ||||
|   | ||||
|   | ||||
| +static int prism2_change_mtu(struct net_device *dev, int new_mtu) | ||||
| +{ | ||||
| +	if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	dev->mtu = new_mtu; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| + | ||||
|  static void prism2_tx_timeout(struct net_device *dev) | ||||
|  { | ||||
|  	struct hostap_interface *iface; | ||||
| @@ -803,6 +813,7 @@ static const struct net_device_ops hosta | ||||
|  	.ndo_do_ioctl		= hostap_ioctl, | ||||
|  	.ndo_set_mac_address	= prism2_set_mac_address, | ||||
|  	.ndo_set_rx_mode	= hostap_set_multicast_list, | ||||
| +	.ndo_change_mtu 	= prism2_change_mtu, | ||||
|  	.ndo_tx_timeout 	= prism2_tx_timeout, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
| @@ -815,6 +826,7 @@ static const struct net_device_ops hosta | ||||
|  	.ndo_do_ioctl		= hostap_ioctl, | ||||
|  	.ndo_set_mac_address	= prism2_set_mac_address, | ||||
|  	.ndo_set_rx_mode	= hostap_set_multicast_list, | ||||
| +	.ndo_change_mtu 	= prism2_change_mtu, | ||||
|  	.ndo_tx_timeout 	= prism2_tx_timeout, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
| @@ -827,6 +839,7 @@ static const struct net_device_ops hosta | ||||
|  	.ndo_do_ioctl		= hostap_ioctl, | ||||
|  	.ndo_set_mac_address	= prism2_set_mac_address, | ||||
|  	.ndo_set_rx_mode	= hostap_set_multicast_list, | ||||
| +	.ndo_change_mtu 	= prism2_change_mtu, | ||||
|  	.ndo_tx_timeout 	= prism2_tx_timeout, | ||||
|  	.ndo_validate_addr	= eth_validate_addr, | ||||
|  }; | ||||
| @@ -838,8 +851,6 @@ void hostap_setup_dev(struct net_device | ||||
|   | ||||
|  	iface = netdev_priv(dev); | ||||
|  	ether_setup(dev); | ||||
| -	dev->min_mtu = PRISM2_MIN_MTU; | ||||
| -	dev->max_mtu = PRISM2_MAX_MTU; | ||||
|  	dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||||
|   | ||||
|  	/* kernel callbacks */ | ||||
| --- a/drivers/net/wireless/intersil/orinoco/main.c | ||||
| +++ b/drivers/net/wireless/intersil/orinoco/main.c | ||||
| @@ -322,6 +322,9 @@ int orinoco_change_mtu(struct net_device | ||||
|  { | ||||
|  	struct orinoco_private *priv = ndev_priv(dev); | ||||
|   | ||||
| +	if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
|  	/* MTU + encapsulation + header length */ | ||||
|  	if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) > | ||||
|  	     (priv->nicbuf_size - ETH_HLEN)) | ||||
| @@ -2285,9 +2288,6 @@ int orinoco_if_add(struct orinoco_privat | ||||
|  	dev->base_addr = base_addr; | ||||
|  	dev->irq = irq; | ||||
|   | ||||
| -	dev->min_mtu = ORINOCO_MIN_MTU; | ||||
| -	dev->max_mtu = ORINOCO_MAX_MTU; | ||||
| - | ||||
|  	SET_NETDEV_DEV(dev, priv->dev); | ||||
|  	ret = register_netdev(dev); | ||||
|  	if (ret) | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/.local-symbols | ||||
| +++ b/.local-symbols | ||||
| @@ -477,45 +477,6 @@ USB_IPHETH= | ||||
| @@ -482,45 +482,6 @@ USB_IPHETH= | ||||
|  USB_SIERRA_NET= | ||||
|  USB_VL600= | ||||
|  USB_NET_CH9200= | ||||
| @@ -88,7 +88,7 @@ | ||||
|  	gpiodev = bus->chipco.dev ? : pcidev; | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile | ||||
| @@ -43,6 +43,6 @@ brcmsmac-y := \ | ||||
| @@ -42,6 +42,6 @@ brcmsmac-y := \ | ||||
|  	brcms_trace_events.o \ | ||||
|  	debug.o | ||||
|   | ||||
|   | ||||
| @@ -18,12 +18,12 @@ | ||||
| -	aes_gcm.o \ | ||||
|  	aes_cmac.o \ | ||||
| -	aes_gmac.o \ | ||||
|  	fils_aead.o \ | ||||
|  	cfg.o \ | ||||
|  	ethtool.o \ | ||||
|  	rx.o \ | ||||
| --- a/net/mac80211/aes_ccm.c | ||||
| +++ b/net/mac80211/aes_ccm.c | ||||
| @@ -13,89 +13,132 @@ | ||||
| @@ -13,103 +13,132 @@ | ||||
|  #include <linux/types.h> | ||||
|  #include <linux/err.h> | ||||
|  #include <crypto/aead.h> | ||||
| @@ -33,76 +33,101 @@ | ||||
|  #include "key.h" | ||||
|  #include "aes_ccm.h" | ||||
|   | ||||
| -void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| -int ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic, | ||||
| -			      size_t mic_len) | ||||
| +static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0, | ||||
| +			    u8 *a, u8 *b) | ||||
|  { | ||||
| -	struct scatterlist sg[3]; | ||||
| -	struct aead_request *aead_req; | ||||
| -	int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); | ||||
| -	u8 *__aad; | ||||
| +	int i; | ||||
| + | ||||
|   | ||||
| -	aead_req = kzalloc(reqsize + CCM_AAD_LEN, GFP_ATOMIC); | ||||
| -	if (!aead_req) | ||||
| -		return -ENOMEM; | ||||
| +	crypto_cipher_encrypt_one(tfm, b, b_0); | ||||
| + | ||||
|   | ||||
| -	__aad = (u8 *)aead_req + reqsize; | ||||
| -	memcpy(__aad, aad, CCM_AAD_LEN); | ||||
| +	/* Extra Authenticate-only data (always two AES blocks) */ | ||||
| +	for (i = 0; i < AES_BLOCK_SIZE; i++) | ||||
| +		aad[i] ^= b[i]; | ||||
| +	crypto_cipher_encrypt_one(tfm, b, aad); | ||||
| + | ||||
| +	aad += AES_BLOCK_SIZE; | ||||
| + | ||||
| +	for (i = 0; i < AES_BLOCK_SIZE; i++) | ||||
| +		aad[i] ^= b[i]; | ||||
| +	crypto_cipher_encrypt_one(tfm, a, aad); | ||||
|   | ||||
| -	char aead_req_data[sizeof(struct aead_request) + | ||||
| -			   crypto_aead_reqsize(tfm)] | ||||
| -		__aligned(__alignof__(struct aead_request)); | ||||
| -	struct aead_request *aead_req = (void *) aead_req_data; | ||||
| +	/* Mask out bits from auth-only-b_0 */ | ||||
| +	b_0[0] &= 0x07; | ||||
|   | ||||
| -	memset(aead_req, 0, sizeof(aead_req_data)); | ||||
| +	/* S_0 is used to encrypt T (= MIC) */ | ||||
| +	b_0[14] = 0; | ||||
| +	b_0[15] = 0; | ||||
| +	crypto_cipher_encrypt_one(tfm, s_0, b_0); | ||||
| +} | ||||
|   | ||||
| -	sg_init_table(sg, 3); | ||||
| -	sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad)); | ||||
| -	sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad)); | ||||
| -	sg_set_buf(&sg[1], data, data_len); | ||||
| -	sg_set_buf(&sg[2], mic, mic_len); | ||||
| +	aad += AES_BLOCK_SIZE; | ||||
|   | ||||
| -	aead_request_set_tfm(aead_req, tfm); | ||||
| -	aead_request_set_crypt(aead_req, sg, sg, data_len, b_0); | ||||
| -	aead_request_set_ad(aead_req, sg[0].length); | ||||
| +	for (i = 0; i < AES_BLOCK_SIZE; i++) | ||||
| +		aad[i] ^= b[i]; | ||||
| +	crypto_cipher_encrypt_one(tfm, a, aad); | ||||
|   | ||||
| -	crypto_aead_encrypt(aead_req); | ||||
| -	kzfree(aead_req); | ||||
| +	/* Mask out bits from auth-only-b_0 */ | ||||
| +	b_0[0] &= 0x07; | ||||
|   | ||||
| -	return 0; | ||||
| +	/* S_0 is used to encrypt T (= MIC) */ | ||||
| +	b_0[14] = 0; | ||||
| +	b_0[15] = 0; | ||||
| +	crypto_cipher_encrypt_one(tfm, s_0, b_0); | ||||
|  } | ||||
|   | ||||
| -int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic, | ||||
| -			      size_t mic_len) | ||||
| + | ||||
| +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
| +			       u8 *data, size_t data_len, u8 *mic, | ||||
| +			       size_t mic_len) | ||||
| +{ | ||||
|  { | ||||
| -	struct scatterlist sg[3]; | ||||
| -	struct aead_request *aead_req; | ||||
| -	int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); | ||||
| -	u8 *__aad; | ||||
| -	int err; | ||||
| +	int i, j, last_len, num_blocks; | ||||
| +	u8 b[AES_BLOCK_SIZE]; | ||||
| +	u8 s_0[AES_BLOCK_SIZE]; | ||||
| +	u8 e[AES_BLOCK_SIZE]; | ||||
| +	u8 *pos, *cpos; | ||||
| + | ||||
|   | ||||
| -	if (data_len == 0) | ||||
| -		return -EINVAL; | ||||
| +	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | ||||
| +	last_len = data_len % AES_BLOCK_SIZE; | ||||
| +	aes_ccm_prepare(tfm, b_0, aad, s_0, b, b); | ||||
| + | ||||
|   | ||||
| -	aead_req = kzalloc(reqsize + CCM_AAD_LEN, GFP_ATOMIC); | ||||
| -	if (!aead_req) | ||||
| -		return -ENOMEM; | ||||
| +	/* Process payload blocks */ | ||||
| +	pos = data; | ||||
| +	cpos = data; | ||||
| +	for (j = 1; j <= num_blocks; j++) { | ||||
| +		int blen = (j == num_blocks && last_len) ? | ||||
| +			last_len : AES_BLOCK_SIZE; | ||||
| + | ||||
|   | ||||
| -	__aad = (u8 *)aead_req + reqsize; | ||||
| -	memcpy(__aad, aad, CCM_AAD_LEN); | ||||
| +		/* Authentication followed by encryption */ | ||||
| +		for (i = 0; i < blen; i++) | ||||
| +			b[i] ^= pos[i]; | ||||
| +		crypto_cipher_encrypt_one(tfm, b, b); | ||||
| + | ||||
|   | ||||
| -	sg_init_table(sg, 3); | ||||
| -	sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad)); | ||||
| -	sg_set_buf(&sg[1], data, data_len); | ||||
| -	sg_set_buf(&sg[2], mic, mic_len); | ||||
| +		b_0[14] = (j >> 8) & 0xff; | ||||
| +		b_0[15] = j & 0xff; | ||||
| +		crypto_cipher_encrypt_one(tfm, e, b_0); | ||||
| @@ -110,41 +135,26 @@ | ||||
| +			*cpos++ = *pos++ ^ e[i]; | ||||
| +	} | ||||
|   | ||||
| -	crypto_aead_encrypt(aead_req); | ||||
| +	for (i = 0; i < mic_len; i++) | ||||
| +		mic[i] = b[i] ^ s_0[i]; | ||||
|  } | ||||
|   | ||||
| -int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			      u8 *data, size_t data_len, u8 *mic, | ||||
|  			      size_t mic_len) | ||||
|  { | ||||
| -	struct scatterlist sg[3]; | ||||
| -	char aead_req_data[sizeof(struct aead_request) + | ||||
| -			   crypto_aead_reqsize(tfm)] | ||||
| -		__aligned(__alignof__(struct aead_request)); | ||||
| -	struct aead_request *aead_req = (void *) aead_req_data; | ||||
| - | ||||
| -	if (data_len == 0) | ||||
| -		return -EINVAL; | ||||
| - | ||||
| -	memset(aead_req, 0, sizeof(aead_req_data)); | ||||
| - | ||||
| -	sg_init_table(sg, 3); | ||||
| -	sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad)); | ||||
| -	sg_set_buf(&sg[1], data, data_len); | ||||
| -	sg_set_buf(&sg[2], mic, mic_len); | ||||
| - | ||||
| -	aead_request_set_tfm(aead_req, tfm); | ||||
| -	aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0); | ||||
| -	aead_request_set_ad(aead_req, sg[0].length); | ||||
| +	for (i = 0; i < mic_len; i++) | ||||
| +		mic[i] = b[i] ^ s_0[i]; | ||||
| +} | ||||
|   | ||||
| -	err = crypto_aead_decrypt(aead_req); | ||||
| -	kzfree(aead_req); | ||||
| +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
| +			      u8 *data, size_t data_len, u8 *mic, | ||||
| +			      size_t mic_len) | ||||
| +{ | ||||
| +	int i, j, last_len, num_blocks; | ||||
| +	u8 *pos, *cpos; | ||||
| +	u8 a[AES_BLOCK_SIZE]; | ||||
| +	u8 b[AES_BLOCK_SIZE]; | ||||
| +	u8 s_0[AES_BLOCK_SIZE]; | ||||
| + | ||||
|   | ||||
| -	return err; | ||||
| +	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | ||||
| +	last_len = data_len % AES_BLOCK_SIZE; | ||||
| +	aes_ccm_prepare(tfm, b_0, aad, s_0, a, b); | ||||
| @@ -171,8 +181,7 @@ | ||||
| +		if ((mic[i] ^ s_0[i]) != a[i]) | ||||
| +			return -1; | ||||
| +	} | ||||
|   | ||||
| -	return crypto_aead_decrypt(aead_req); | ||||
| + | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -185,12 +194,12 @@ | ||||
|  { | ||||
| -	struct crypto_aead *tfm; | ||||
| -	int err; | ||||
| +	struct crypto_cipher *tfm; | ||||
|   | ||||
| - | ||||
| -	tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); | ||||
| -	if (IS_ERR(tfm)) | ||||
| -		return tfm; | ||||
| - | ||||
| +	struct crypto_cipher *tfm; | ||||
|   | ||||
| -	err = crypto_aead_setkey(tfm, key, key_len); | ||||
| -	if (err) | ||||
| -		goto free_aead; | ||||
| @@ -215,73 +224,11 @@ | ||||
| -	crypto_free_aead(tfm); | ||||
| +	crypto_free_cipher(tfm); | ||||
|  } | ||||
| --- a/net/mac80211/aes_ccm.h | ||||
| +++ b/net/mac80211/aes_ccm.h | ||||
| @@ -12,15 +12,15 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| -						    size_t key_len, | ||||
| -						    size_t mic_len); | ||||
| -void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| +						      size_t key_len, | ||||
| +						      size_t mic_len); | ||||
| +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			       u8 *data, size_t data_len, u8 *mic, | ||||
|  			       size_t mic_len); | ||||
| -int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			      u8 *data, size_t data_len, u8 *mic, | ||||
|  			      size_t mic_len); | ||||
| -void ieee80211_aes_key_free(struct crypto_aead *tfm); | ||||
| +void ieee80211_aes_key_free(struct crypto_cipher *tfm); | ||||
|   | ||||
|  #endif /* AES_CCM_H */ | ||||
| --- a/net/mac80211/aes_gcm.h | ||||
| +++ b/net/mac80211/aes_gcm.h | ||||
| @@ -11,12 +11,28 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| -			       u8 *data, size_t data_len, u8 *mic); | ||||
| -int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic); | ||||
| -struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], | ||||
| -							size_t key_len); | ||||
| -void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm); | ||||
| +static inline void | ||||
| +ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| +			  u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +static inline int | ||||
| +ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| +			  u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +    return -EOPNOTSUPP; | ||||
| +} | ||||
| + | ||||
| +static inline struct crypto_aead * | ||||
| +ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len) | ||||
| +{ | ||||
| +    return NULL; | ||||
| +} | ||||
| + | ||||
| +static inline void | ||||
| +ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) | ||||
| +{ | ||||
| +} | ||||
|   | ||||
|  #endif /* AES_GCM_H */ | ||||
| --- a/net/mac80211/aes_gmac.h | ||||
| +++ b/net/mac80211/aes_gmac.h | ||||
| @@ -11,10 +11,22 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
| @@ -15,10 +15,22 @@ | ||||
|  #define GMAC_MIC_LEN	16 | ||||
|  #define GMAC_NONCE_LEN	12 | ||||
|   | ||||
| -struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], | ||||
| -						 size_t key_len); | ||||
| @@ -320,7 +267,7 @@ | ||||
|  		struct { | ||||
| --- a/net/mac80211/wpa.c | ||||
| +++ b/net/mac80211/wpa.c | ||||
| @@ -304,7 +304,8 @@ ieee80211_crypto_tkip_decrypt(struct iee | ||||
| @@ -305,7 +305,8 @@ ieee80211_crypto_tkip_decrypt(struct iee | ||||
|  } | ||||
|   | ||||
|   | ||||
| @@ -330,7 +277,7 @@ | ||||
|  { | ||||
|  	__le16 mask_fc; | ||||
|  	int a4_included, mgmt; | ||||
| @@ -334,14 +335,8 @@ static void ccmp_special_blocks(struct s | ||||
| @@ -335,14 +336,8 @@ static void ccmp_special_blocks(struct s | ||||
|  	else | ||||
|  		qos_tid = 0; | ||||
|   | ||||
| @@ -347,7 +294,7 @@ | ||||
|   | ||||
|  	/* Nonce: Nonce Flags | A2 | PN | ||||
|  	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) | ||||
| @@ -349,6 +344,8 @@ static void ccmp_special_blocks(struct s | ||||
| @@ -350,6 +345,8 @@ static void ccmp_special_blocks(struct s | ||||
|  	b_0[1] = qos_tid | (mgmt << 4); | ||||
|  	memcpy(&b_0[2], hdr->addr2, ETH_ALEN); | ||||
|  	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); | ||||
| @@ -356,16 +303,31 @@ | ||||
|   | ||||
|  	/* AAD (extra authenticate-only data) / masked 802.11 header | ||||
|  	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ | ||||
| @@ -460,7 +457,7 @@ static int ccmp_encrypt_skb(struct ieee8 | ||||
| @@ -406,7 +403,7 @@ static int ccmp_encrypt_skb(struct ieee8 | ||||
|  	u8 *pos; | ||||
|  	u8 pn[6]; | ||||
|  	u64 pn64; | ||||
| -	u8 aad[CCM_AAD_LEN]; | ||||
| +	u8 aad[2 * AES_BLOCK_SIZE]; | ||||
|  	u8 b_0[AES_BLOCK_SIZE]; | ||||
|   | ||||
|  	if (info->control.hw_key && | ||||
| @@ -461,9 +458,11 @@ static int ccmp_encrypt_skb(struct ieee8 | ||||
|  		return 0; | ||||
|   | ||||
|  	pos += IEEE80211_CCMP_HDR_LEN; | ||||
| -	ccmp_special_blocks(skb, pn, b_0, aad); | ||||
| -	return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, | ||||
| -					 skb_put(skb, mic_len), mic_len); | ||||
| +	ccmp_special_blocks(skb, pn, b_0, aad, len); | ||||
|  	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, | ||||
|  				  skb_put(skb, mic_len), mic_len); | ||||
| +	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, | ||||
| +				  skb_put(skb, mic_len), mic_len); | ||||
| + | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -537,7 +534,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee | ||||
|   | ||||
| @@ -536,7 +535,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee | ||||
|  			u8 aad[2 * AES_BLOCK_SIZE]; | ||||
|  			u8 b_0[AES_BLOCK_SIZE]; | ||||
|  			/* hardware didn't decrypt/verify MIC */ | ||||
| @@ -374,3 +336,113 @@ | ||||
|   | ||||
|  			if (ieee80211_aes_ccm_decrypt( | ||||
|  				    key->u.ccmp.tfm, b_0, aad, | ||||
| @@ -638,7 +637,7 @@ static int gcmp_encrypt_skb(struct ieee8 | ||||
|  	u8 *pos; | ||||
|  	u8 pn[6]; | ||||
|  	u64 pn64; | ||||
| -	u8 aad[GCM_AAD_LEN]; | ||||
| +	u8 aad[2 * AES_BLOCK_SIZE]; | ||||
|  	u8 j_0[AES_BLOCK_SIZE]; | ||||
|   | ||||
|  	if (info->control.hw_key && | ||||
| @@ -695,8 +694,10 @@ static int gcmp_encrypt_skb(struct ieee8 | ||||
|   | ||||
|  	pos += IEEE80211_GCMP_HDR_LEN; | ||||
|  	gcmp_special_blocks(skb, pn, j_0, aad); | ||||
| -	return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, | ||||
| -					 skb_put(skb, IEEE80211_GCMP_MIC_LEN)); | ||||
| +	ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, | ||||
| +				  skb_put(skb, IEEE80211_GCMP_MIC_LEN)); | ||||
| + | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
|  ieee80211_tx_result | ||||
| @@ -1120,9 +1121,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct | ||||
|  	struct ieee80211_key *key = tx->key; | ||||
|  	struct ieee80211_mmie_16 *mmie; | ||||
|  	struct ieee80211_hdr *hdr; | ||||
| -	u8 aad[GMAC_AAD_LEN]; | ||||
| +	u8 aad[20]; | ||||
|  	u64 pn64; | ||||
| -	u8 nonce[GMAC_NONCE_LEN]; | ||||
| +	u8 nonce[12]; | ||||
|   | ||||
|  	if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) | ||||
|  		return TX_DROP; | ||||
| @@ -1168,7 +1169,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct | ||||
|  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); | ||||
|  	struct ieee80211_key *key = rx->key; | ||||
|  	struct ieee80211_mmie_16 *mmie; | ||||
| -	u8 aad[GMAC_AAD_LEN], mic[GMAC_MIC_LEN], ipn[6], nonce[GMAC_NONCE_LEN]; | ||||
| +	u8 aad[20], mic[16], ipn[6], nonce[12]; | ||||
|  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||
|   | ||||
|  	if (!ieee80211_is_mgmt(hdr->frame_control)) | ||||
| --- a/net/mac80211/aes_ccm.h | ||||
| +++ b/net/mac80211/aes_ccm.h | ||||
| @@ -12,17 +12,15 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -#define CCM_AAD_LEN	32 | ||||
| - | ||||
| -struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| -						    size_t key_len, | ||||
| -						    size_t mic_len); | ||||
| -int ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic, | ||||
| -			      size_t mic_len); | ||||
| -int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| +						      size_t key_len, | ||||
| +						      size_t mic_len); | ||||
| +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
| +			       u8 *data, size_t data_len, u8 *mic, | ||||
| +			       size_t mic_len); | ||||
| +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			      u8 *data, size_t data_len, u8 *mic, | ||||
|  			      size_t mic_len); | ||||
| -void ieee80211_aes_key_free(struct crypto_aead *tfm); | ||||
| +void ieee80211_aes_key_free(struct crypto_cipher *tfm); | ||||
|   | ||||
|  #endif /* AES_CCM_H */ | ||||
| --- a/net/mac80211/aes_gcm.h | ||||
| +++ b/net/mac80211/aes_gcm.h | ||||
| @@ -11,14 +11,28 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -#define GCM_AAD_LEN	32 | ||||
| +static inline void | ||||
| +ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| +			  u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +} | ||||
|   | ||||
| -int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic); | ||||
| -int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic); | ||||
| -struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], | ||||
| -							size_t key_len); | ||||
| -void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm); | ||||
| +static inline int | ||||
| +ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| +			  u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +    return -EOPNOTSUPP; | ||||
| +} | ||||
| + | ||||
| +static inline struct crypto_aead * | ||||
| +ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len) | ||||
| +{ | ||||
| +    return NULL; | ||||
| +} | ||||
| + | ||||
| +static inline void | ||||
| +ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) | ||||
| +{ | ||||
| +} | ||||
|   | ||||
|  #endif /* AES_GCM_H */ | ||||
|   | ||||
| @@ -2,7 +2,7 @@ Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnects | ||||
|  | ||||
| --- a/net/mac80211/cfg.c | ||||
| +++ b/net/mac80211/cfg.c | ||||
| @@ -1016,7 +1016,6 @@ static int ieee80211_stop_ap(struct wiph | ||||
| @@ -1014,7 +1014,6 @@ static int ieee80211_stop_ap(struct wiph | ||||
|  	sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF; | ||||
|   | ||||
|  	__sta_info_flush(sdata, true); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/drivers/net/wireless/mac80211_hwsim.c | ||||
| +++ b/drivers/net/wireless/mac80211_hwsim.c | ||||
| @@ -2662,7 +2662,7 @@ static int mac80211_hwsim_new_radio(stru | ||||
| @@ -2678,7 +2678,7 @@ static int mac80211_hwsim_new_radio(stru | ||||
|   | ||||
|  	tasklet_hrtimer_init(&data->beacon_timer, | ||||
|  			     mac80211_hwsim_beacon, | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|  static int ieee80211_ifa6_changed(struct notifier_block *nb, | ||||
|  				  unsigned long data, void *arg) | ||||
|  { | ||||
| @@ -1101,14 +1101,14 @@ int ieee80211_register_hw(struct ieee802 | ||||
| @@ -1111,14 +1111,14 @@ int ieee80211_register_hw(struct ieee802 | ||||
|  	if (result) | ||||
|  		goto fail_flows; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ | ||||
|  	local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; | ||||
|  	result = register_inet6addr_notifier(&local->ifa6_notifier); | ||||
|  	if (result) | ||||
| @@ -1117,13 +1117,13 @@ int ieee80211_register_hw(struct ieee802 | ||||
| @@ -1127,13 +1127,13 @@ int ieee80211_register_hw(struct ieee802 | ||||
|   | ||||
|  	return 0; | ||||
|   | ||||
| @@ -52,7 +52,7 @@ | ||||
|   fail_ifa: | ||||
|  #endif | ||||
|  	ieee80211_txq_teardown_flows(local); | ||||
| @@ -1153,10 +1153,10 @@ void ieee80211_unregister_hw(struct ieee | ||||
| @@ -1163,10 +1163,10 @@ void ieee80211_unregister_hw(struct ieee | ||||
|  	tasklet_kill(&local->tx_pending_tasklet); | ||||
|  	tasklet_kill(&local->tasklet); | ||||
|   | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/net/mac80211/cfg.c | ||||
| +++ b/net/mac80211/cfg.c | ||||
| @@ -2175,7 +2175,7 @@ static int ieee80211_scan(struct wiphy * | ||||
| @@ -2165,7 +2165,7 @@ static int ieee80211_scan(struct wiphy * | ||||
|  		 * the  frames sent while scanning on other channel will be | ||||
|  		 * lost) | ||||
|  		 */ | ||||
|   | ||||
| @@ -1,953 +0,0 @@ | ||||
| From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk> | ||||
| Date: Fri, 2 Sep 2016 16:00:30 +0200 | ||||
| Subject: [PATCH] ath9k: Switch to using mac80211 intermediate software | ||||
|  queues. | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This switches ath9k over to using the mac80211 intermediate software | ||||
| queueing mechanism for data packets. It removes the queueing inside the | ||||
| driver, except for the retry queue, and instead pulls from mac80211 when | ||||
| a packet is needed. The retry queue is used to store a packet that was | ||||
| pulled but can't be sent immediately. | ||||
|  | ||||
| The old code path in ath_tx_start that would queue packets has been | ||||
| removed completely, as has the qlen limit tunables (since there's no | ||||
| longer a queue in the driver to limit). | ||||
|  | ||||
| Based on Tim's original patch set, but reworked quite thoroughly. | ||||
|  | ||||
| Cc: Tim Shepard <shep@alum.mit.edu> | ||||
| Cc: Felix Fietkau <nbd@nbd.name> | ||||
| Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ath9k.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | ||||
| @@ -91,7 +91,6 @@ int ath_descdma_setup(struct ath_softc * | ||||
|  #define ATH_RXBUF               512 | ||||
|  #define ATH_TXBUF               512 | ||||
|  #define ATH_TXBUF_RESERVE       5 | ||||
| -#define ATH_MAX_QDEPTH          (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE) | ||||
|  #define ATH_TXMAXTRY            13 | ||||
|  #define ATH_MAX_SW_RETRIES      30 | ||||
|   | ||||
| @@ -145,7 +144,7 @@ int ath_descdma_setup(struct ath_softc * | ||||
|  #define BAW_WITHIN(_start, _bawsz, _seqno) \ | ||||
|  	((((_seqno) - (_start)) & 4095) < (_bawsz)) | ||||
|   | ||||
| -#define ATH_AN_2_TID(_an, _tidno)  (&(_an)->tid[(_tidno)]) | ||||
| +#define ATH_AN_2_TID(_an, _tidno) ath_node_to_tid(_an, _tidno) | ||||
|   | ||||
|  #define IS_HT_RATE(rate)   (rate & 0x80) | ||||
|  #define IS_CCK_RATE(rate)  ((rate >= 0x18) && (rate <= 0x1e)) | ||||
| @@ -164,7 +163,6 @@ struct ath_txq { | ||||
|  	spinlock_t axq_lock; | ||||
|  	u32 axq_depth; | ||||
|  	u32 axq_ampdu_depth; | ||||
| -	bool stopped; | ||||
|  	bool axq_tx_inprogress; | ||||
|  	struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; | ||||
|  	u8 txq_headidx; | ||||
| @@ -232,7 +230,6 @@ struct ath_buf { | ||||
|   | ||||
|  struct ath_atx_tid { | ||||
|  	struct list_head list; | ||||
| -	struct sk_buff_head buf_q; | ||||
|  	struct sk_buff_head retry_q; | ||||
|  	struct ath_node *an; | ||||
|  	struct ath_txq *txq; | ||||
| @@ -247,13 +244,13 @@ struct ath_atx_tid { | ||||
|  	s8 bar_index; | ||||
|  	bool active; | ||||
|  	bool clear_ps_filter; | ||||
| +	bool has_queued; | ||||
|  }; | ||||
|   | ||||
|  struct ath_node { | ||||
|  	struct ath_softc *sc; | ||||
|  	struct ieee80211_sta *sta; /* station struct we're part of */ | ||||
|  	struct ieee80211_vif *vif; /* interface with which we're associated */ | ||||
| -	struct ath_atx_tid tid[IEEE80211_NUM_TIDS]; | ||||
|   | ||||
|  	u16 maxampdu; | ||||
|  	u8 mpdudensity; | ||||
| @@ -276,7 +273,6 @@ struct ath_tx_control { | ||||
|  	struct ath_node *an; | ||||
|  	struct ieee80211_sta *sta; | ||||
|  	u8 paprd; | ||||
| -	bool force_channel; | ||||
|  }; | ||||
|   | ||||
|   | ||||
| @@ -293,7 +289,6 @@ struct ath_tx { | ||||
|  	struct ath_descdma txdma; | ||||
|  	struct ath_txq *txq_map[IEEE80211_NUM_ACS]; | ||||
|  	struct ath_txq *uapsdq; | ||||
| -	u32 txq_max_pending[IEEE80211_NUM_ACS]; | ||||
|  	u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32]; | ||||
|  }; | ||||
|   | ||||
| @@ -421,6 +416,22 @@ struct ath_offchannel { | ||||
|  	int duration; | ||||
|  }; | ||||
|   | ||||
| +static inline struct ath_atx_tid * | ||||
| +ath_node_to_tid(struct ath_node *an, u8 tidno) | ||||
| +{ | ||||
| +	struct ieee80211_sta *sta = an->sta; | ||||
| +	struct ieee80211_vif *vif = an->vif; | ||||
| +	struct ieee80211_txq *txq; | ||||
| + | ||||
| +	BUG_ON(!vif); | ||||
| +	if (sta) | ||||
| +		txq = sta->txq[tidno % ARRAY_SIZE(sta->txq)]; | ||||
| +	else | ||||
| +		txq = vif->txq; | ||||
| + | ||||
| +	return (struct ath_atx_tid *) txq->drv_priv; | ||||
| +} | ||||
| + | ||||
|  #define case_rtn_string(val) case val: return #val | ||||
|   | ||||
|  #define ath_for_each_chanctx(_sc, _ctx)                             \ | ||||
| @@ -575,7 +586,6 @@ void ath_tx_edma_tasklet(struct ath_soft | ||||
|  int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, | ||||
|  		      u16 tid, u16 *ssn); | ||||
|  void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); | ||||
| -void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); | ||||
|   | ||||
|  void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); | ||||
|  void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, | ||||
| @@ -585,6 +595,7 @@ void ath9k_release_buffered_frames(struc | ||||
|  				   u16 tids, int nframes, | ||||
|  				   enum ieee80211_frame_release_type reason, | ||||
|  				   bool more_data); | ||||
| +void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue); | ||||
|   | ||||
|  /********/ | ||||
|  /* VIFs */ | ||||
| --- a/drivers/net/wireless/ath/ath9k/channel.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/channel.c | ||||
| @@ -1010,7 +1010,6 @@ static void ath_scan_send_probe(struct a | ||||
|  		goto error; | ||||
|   | ||||
|  	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; | ||||
| -	txctl.force_channel = true; | ||||
|  	if (ath_tx_start(sc->hw, skb, &txctl)) | ||||
|  		goto error; | ||||
|   | ||||
| @@ -1133,7 +1132,6 @@ ath_chanctx_send_vif_ps_frame(struct ath | ||||
|  	memset(&txctl, 0, sizeof(txctl)); | ||||
|  	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; | ||||
|  	txctl.sta = sta; | ||||
| -	txctl.force_channel = true; | ||||
|  	if (ath_tx_start(sc->hw, skb, &txctl)) { | ||||
|  		ieee80211_free_txskb(sc->hw, skb); | ||||
|  		return false; | ||||
| --- a/drivers/net/wireless/ath/ath9k/debug.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/debug.c | ||||
| @@ -600,7 +600,6 @@ static int read_file_xmit(struct seq_fil | ||||
|  	PR("MPDUs XRetried:  ", xretries); | ||||
|  	PR("Aggregates:      ", a_aggr); | ||||
|  	PR("AMPDUs Queued HW:", a_queued_hw); | ||||
| -	PR("AMPDUs Queued SW:", a_queued_sw); | ||||
|  	PR("AMPDUs Completed:", a_completed); | ||||
|  	PR("AMPDUs Retried:  ", a_retries); | ||||
|  	PR("AMPDUs XRetried: ", a_xretries); | ||||
| @@ -629,8 +628,7 @@ static void print_queue(struct ath_softc | ||||
|  	seq_printf(file, "%s: %d ", "qnum", txq->axq_qnum); | ||||
|  	seq_printf(file, "%s: %2d ", "qdepth", txq->axq_depth); | ||||
|  	seq_printf(file, "%s: %2d ", "ampdu-depth", txq->axq_ampdu_depth); | ||||
| -	seq_printf(file, "%s: %3d ", "pending", txq->pending_frames); | ||||
| -	seq_printf(file, "%s: %d\n", "stopped", txq->stopped); | ||||
| +	seq_printf(file, "%s: %3d\n", "pending", txq->pending_frames); | ||||
|   | ||||
|  	ath_txq_unlock(sc, txq); | ||||
|  } | ||||
| @@ -1208,7 +1206,6 @@ static const char ath9k_gstrings_stats[] | ||||
|  	AMKSTR(d_tx_mpdu_xretries), | ||||
|  	AMKSTR(d_tx_aggregates), | ||||
|  	AMKSTR(d_tx_ampdus_queued_hw), | ||||
| -	AMKSTR(d_tx_ampdus_queued_sw), | ||||
|  	AMKSTR(d_tx_ampdus_completed), | ||||
|  	AMKSTR(d_tx_ampdu_retries), | ||||
|  	AMKSTR(d_tx_ampdu_xretries), | ||||
| @@ -1288,7 +1285,6 @@ void ath9k_get_et_stats(struct ieee80211 | ||||
|  	AWDATA(xretries); | ||||
|  	AWDATA(a_aggr); | ||||
|  	AWDATA(a_queued_hw); | ||||
| -	AWDATA(a_queued_sw); | ||||
|  	AWDATA(a_completed); | ||||
|  	AWDATA(a_retries); | ||||
|  	AWDATA(a_xretries); | ||||
| @@ -1346,14 +1342,6 @@ int ath9k_init_debug(struct ath_hw *ah) | ||||
|  				    read_file_xmit); | ||||
|  	debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy, | ||||
|  				    read_file_queues); | ||||
| -	debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, | ||||
| -			   &sc->tx.txq_max_pending[IEEE80211_AC_BK]); | ||||
| -	debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, | ||||
| -			   &sc->tx.txq_max_pending[IEEE80211_AC_BE]); | ||||
| -	debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, | ||||
| -			   &sc->tx.txq_max_pending[IEEE80211_AC_VI]); | ||||
| -	debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, | ||||
| -			   &sc->tx.txq_max_pending[IEEE80211_AC_VO]); | ||||
|  	debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy, | ||||
|  				    read_file_misc); | ||||
|  	debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy, | ||||
| --- a/drivers/net/wireless/ath/ath9k/debug.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/debug.h | ||||
| @@ -147,7 +147,6 @@ struct ath_interrupt_stats { | ||||
|   * @completed: Total MPDUs (non-aggr) completed | ||||
|   * @a_aggr: Total no. of aggregates queued | ||||
|   * @a_queued_hw: Total AMPDUs queued to hardware | ||||
| - * @a_queued_sw: Total AMPDUs queued to software queues | ||||
|   * @a_completed: Total AMPDUs completed | ||||
|   * @a_retries: No. of AMPDUs retried (SW) | ||||
|   * @a_xretries: No. of AMPDUs dropped due to xretries | ||||
| @@ -174,7 +173,6 @@ struct ath_tx_stats { | ||||
|  	u32 xretries; | ||||
|  	u32 a_aggr; | ||||
|  	u32 a_queued_hw; | ||||
| -	u32 a_queued_sw; | ||||
|  	u32 a_completed; | ||||
|  	u32 a_retries; | ||||
|  	u32 a_xretries; | ||||
| --- a/drivers/net/wireless/ath/ath9k/debug_sta.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c | ||||
| @@ -52,8 +52,8 @@ static ssize_t read_file_node_aggr(struc | ||||
|  			 "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", | ||||
|  			 "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); | ||||
|   | ||||
| -	for (tidno = 0, tid = &an->tid[tidno]; | ||||
| -	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { | ||||
| +	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { | ||||
| +		tid = ath_node_to_tid(an, tidno); | ||||
|  		txq = tid->txq; | ||||
|  		ath_txq_lock(sc, txq); | ||||
|  		if (tid->active) { | ||||
| --- a/drivers/net/wireless/ath/ath9k/init.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/init.c | ||||
| @@ -358,7 +358,6 @@ static int ath9k_init_queues(struct ath_ | ||||
|  	for (i = 0; i < IEEE80211_NUM_ACS; i++) { | ||||
|  		sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); | ||||
|  		sc->tx.txq_map[i]->mac80211_qnum = i; | ||||
| -		sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH; | ||||
|  	} | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -877,6 +876,7 @@ static void ath9k_set_hw_capab(struct at | ||||
|  	hw->max_rate_tries = 10; | ||||
|  	hw->sta_data_size = sizeof(struct ath_node); | ||||
|  	hw->vif_data_size = sizeof(struct ath_vif); | ||||
| +	hw->txq_data_size = sizeof(struct ath_atx_tid); | ||||
|  	hw->extra_tx_headroom = 4; | ||||
|   | ||||
|  	hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; | ||||
| --- a/drivers/net/wireless/ath/ath9k/main.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/main.c | ||||
| @@ -1902,9 +1902,11 @@ static int ath9k_ampdu_action(struct iee | ||||
|  	bool flush = false; | ||||
|  	int ret = 0; | ||||
|  	struct ieee80211_sta *sta = params->sta; | ||||
| +	struct ath_node *an = (struct ath_node *)sta->drv_priv; | ||||
|  	enum ieee80211_ampdu_mlme_action action = params->action; | ||||
|  	u16 tid = params->tid; | ||||
|  	u16 *ssn = ¶ms->ssn; | ||||
| +	struct ath_atx_tid *atid; | ||||
|   | ||||
|  	mutex_lock(&sc->mutex); | ||||
|   | ||||
| @@ -1937,9 +1939,9 @@ static int ath9k_ampdu_action(struct iee | ||||
|  		ath9k_ps_restore(sc); | ||||
|  		break; | ||||
|  	case IEEE80211_AMPDU_TX_OPERATIONAL: | ||||
| -		ath9k_ps_wakeup(sc); | ||||
| -		ath_tx_aggr_resume(sc, sta, tid); | ||||
| -		ath9k_ps_restore(sc); | ||||
| +		atid = ath_node_to_tid(an, tid); | ||||
| +		atid->baw_size = IEEE80211_MIN_AMPDU_BUF << | ||||
| +			        sta->ht_cap.ampdu_factor; | ||||
|  		break; | ||||
|  	default: | ||||
|  		ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); | ||||
| @@ -2701,4 +2703,5 @@ struct ieee80211_ops ath9k_ops = { | ||||
|  	.sw_scan_start	    = ath9k_sw_scan_start, | ||||
|  	.sw_scan_complete   = ath9k_sw_scan_complete, | ||||
|  	.get_txpower        = ath9k_get_txpower, | ||||
| +	.wake_tx_queue      = ath9k_wake_tx_queue, | ||||
|  }; | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| @@ -67,6 +67,8 @@ static struct ath_buf *ath_tx_setup_buff | ||||
|  					   struct ath_txq *txq, | ||||
|  					   struct ath_atx_tid *tid, | ||||
|  					   struct sk_buff *skb); | ||||
| +static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, | ||||
| +			  struct ath_tx_control *txctl); | ||||
|   | ||||
|  enum { | ||||
|  	MCS_HT20, | ||||
| @@ -137,6 +139,26 @@ static void ath_tx_queue_tid(struct ath_ | ||||
|  		list_add_tail(&tid->list, list); | ||||
|  } | ||||
|   | ||||
| +void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue) | ||||
| +{ | ||||
| +	struct ath_softc *sc = hw->priv; | ||||
| +	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||||
| +	struct ath_atx_tid *tid = (struct ath_atx_tid *) queue->drv_priv; | ||||
| +	struct ath_txq *txq = tid->txq; | ||||
| + | ||||
| +	ath_dbg(common, QUEUE, "Waking TX queue: %pM (%d)\n", | ||||
| +		queue->sta ? queue->sta->addr : queue->vif->addr, | ||||
| +		tid->tidno); | ||||
| + | ||||
| +	ath_txq_lock(sc, txq); | ||||
| + | ||||
| +	tid->has_queued = true; | ||||
| +	ath_tx_queue_tid(sc, txq, tid); | ||||
| +	ath_txq_schedule(sc, txq); | ||||
| + | ||||
| +	ath_txq_unlock(sc, txq); | ||||
| +} | ||||
| + | ||||
|  static struct ath_frame_info *get_frame_info(struct sk_buff *skb) | ||||
|  { | ||||
|  	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| @@ -164,7 +186,6 @@ static void ath_set_rates(struct ieee802 | ||||
|  static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, | ||||
|  			     struct sk_buff *skb) | ||||
|  { | ||||
| -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	struct ath_frame_info *fi = get_frame_info(skb); | ||||
|  	int q = fi->txq; | ||||
|   | ||||
| @@ -175,14 +196,6 @@ static void ath_txq_skb_done(struct ath_ | ||||
|  	if (WARN_ON(--txq->pending_frames < 0)) | ||||
|  		txq->pending_frames = 0; | ||||
|   | ||||
| -	if (txq->stopped && | ||||
| -	    txq->pending_frames < sc->tx.txq_max_pending[q]) { | ||||
| -		if (ath9k_is_chanctx_enabled()) | ||||
| -			ieee80211_wake_queue(sc->hw, info->hw_queue); | ||||
| -		else | ||||
| -			ieee80211_wake_queue(sc->hw, q); | ||||
| -		txq->stopped = false; | ||||
| -	} | ||||
|  } | ||||
|   | ||||
|  static struct ath_atx_tid * | ||||
| @@ -192,9 +205,48 @@ ath_get_skb_tid(struct ath_softc *sc, st | ||||
|  	return ATH_AN_2_TID(an, tidno); | ||||
|  } | ||||
|   | ||||
| +static struct sk_buff * | ||||
| +ath_tid_pull(struct ath_atx_tid *tid) | ||||
| +{ | ||||
| +	struct ieee80211_txq *txq = container_of((void*)tid, struct ieee80211_txq, drv_priv); | ||||
| +	struct ath_softc *sc = tid->an->sc; | ||||
| +	struct ieee80211_hw *hw = sc->hw; | ||||
| +	struct ath_tx_control txctl = { | ||||
| +		.txq = tid->txq, | ||||
| +		.sta = tid->an->sta, | ||||
| +	}; | ||||
| +	struct sk_buff *skb; | ||||
| +	struct ath_frame_info *fi; | ||||
| +	int q; | ||||
| + | ||||
| +	if (!tid->has_queued) | ||||
| +		return NULL; | ||||
| + | ||||
| +	skb = ieee80211_tx_dequeue(hw, txq); | ||||
| +	if (!skb) { | ||||
| +		tid->has_queued = false; | ||||
| +		return NULL; | ||||
| +	} | ||||
| + | ||||
| +	if (ath_tx_prepare(hw, skb, &txctl)) { | ||||
| +		ieee80211_free_txskb(hw, skb); | ||||
| +		return NULL; | ||||
| +	} | ||||
| + | ||||
| +	q = skb_get_queue_mapping(skb); | ||||
| +	if (tid->txq == sc->tx.txq_map[q]) { | ||||
| +		fi = get_frame_info(skb); | ||||
| +		fi->txq = q; | ||||
| +		++tid->txq->pending_frames; | ||||
| +	} | ||||
| + | ||||
| +	return skb; | ||||
| + } | ||||
| + | ||||
| + | ||||
|  static bool ath_tid_has_buffered(struct ath_atx_tid *tid) | ||||
|  { | ||||
| -	return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q); | ||||
| +	return !skb_queue_empty(&tid->retry_q) || tid->has_queued; | ||||
|  } | ||||
|   | ||||
|  static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid) | ||||
| @@ -203,46 +255,11 @@ static struct sk_buff *ath_tid_dequeue(s | ||||
|   | ||||
|  	skb = __skb_dequeue(&tid->retry_q); | ||||
|  	if (!skb) | ||||
| -		skb = __skb_dequeue(&tid->buf_q); | ||||
| +		skb = ath_tid_pull(tid); | ||||
|   | ||||
|  	return skb; | ||||
|  } | ||||
|   | ||||
| -/* | ||||
| - * ath_tx_tid_change_state: | ||||
| - * - clears a-mpdu flag of previous session | ||||
| - * - force sequence number allocation to fix next BlockAck Window | ||||
| - */ | ||||
| -static void | ||||
| -ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid) | ||||
| -{ | ||||
| -	struct ath_txq *txq = tid->txq; | ||||
| -	struct ieee80211_tx_info *tx_info; | ||||
| -	struct sk_buff *skb, *tskb; | ||||
| -	struct ath_buf *bf; | ||||
| -	struct ath_frame_info *fi; | ||||
| - | ||||
| -	skb_queue_walk_safe(&tid->buf_q, skb, tskb) { | ||||
| -		fi = get_frame_info(skb); | ||||
| -		bf = fi->bf; | ||||
| - | ||||
| -		tx_info = IEEE80211_SKB_CB(skb); | ||||
| -		tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU; | ||||
| - | ||||
| -		if (bf) | ||||
| -			continue; | ||||
| - | ||||
| -		bf = ath_tx_setup_buffer(sc, txq, tid, skb); | ||||
| -		if (!bf) { | ||||
| -			__skb_unlink(skb, &tid->buf_q); | ||||
| -			ath_txq_skb_done(sc, txq, skb); | ||||
| -			ieee80211_free_txskb(sc->hw, skb); | ||||
| -			continue; | ||||
| -		} | ||||
| -	} | ||||
| - | ||||
| -} | ||||
| - | ||||
|  static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) | ||||
|  { | ||||
|  	struct ath_txq *txq = tid->txq; | ||||
| @@ -883,20 +900,16 @@ static int ath_compute_num_delims(struct | ||||
|   | ||||
|  static struct ath_buf * | ||||
|  ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, | ||||
| -			struct ath_atx_tid *tid, struct sk_buff_head **q) | ||||
| +			struct ath_atx_tid *tid) | ||||
|  { | ||||
|  	struct ieee80211_tx_info *tx_info; | ||||
|  	struct ath_frame_info *fi; | ||||
| -	struct sk_buff *skb; | ||||
| +	struct sk_buff *skb, *first_skb = NULL; | ||||
|  	struct ath_buf *bf; | ||||
|  	u16 seqno; | ||||
|   | ||||
|  	while (1) { | ||||
| -		*q = &tid->retry_q; | ||||
| -		if (skb_queue_empty(*q)) | ||||
| -			*q = &tid->buf_q; | ||||
| - | ||||
| -		skb = skb_peek(*q); | ||||
| +		skb = ath_tid_dequeue(tid); | ||||
|  		if (!skb) | ||||
|  			break; | ||||
|   | ||||
| @@ -908,7 +921,6 @@ ath_tx_get_tid_subframe(struct ath_softc | ||||
|  			bf->bf_state.stale = false; | ||||
|   | ||||
|  		if (!bf) { | ||||
| -			__skb_unlink(skb, *q); | ||||
|  			ath_txq_skb_done(sc, txq, skb); | ||||
|  			ieee80211_free_txskb(sc->hw, skb); | ||||
|  			continue; | ||||
| @@ -937,8 +949,20 @@ ath_tx_get_tid_subframe(struct ath_softc | ||||
|  		seqno = bf->bf_state.seqno; | ||||
|   | ||||
|  		/* do not step over block-ack window */ | ||||
| -		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) | ||||
| +		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { | ||||
| +			__skb_queue_tail(&tid->retry_q, skb); | ||||
| + | ||||
| +			/* If there are other skbs in the retry q, they are | ||||
| +			 * probably within the BAW, so loop immediately to get | ||||
| +			 * one of them. Otherwise the queue can get stuck. */ | ||||
| +			if (!skb_queue_is_first(&tid->retry_q, skb) && | ||||
| +			    !WARN_ON(skb == first_skb)) { | ||||
| +				if(!first_skb) /* infinite loop prevention */ | ||||
| +					first_skb = skb; | ||||
| +				continue; | ||||
| +			} | ||||
|  			break; | ||||
| +		} | ||||
|   | ||||
|  		if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) { | ||||
|  			struct ath_tx_status ts = {}; | ||||
| @@ -946,7 +970,6 @@ ath_tx_get_tid_subframe(struct ath_softc | ||||
|   | ||||
|  			INIT_LIST_HEAD(&bf_head); | ||||
|  			list_add(&bf->list, &bf_head); | ||||
| -			__skb_unlink(skb, *q); | ||||
|  			ath_tx_update_baw(sc, tid, seqno); | ||||
|  			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0); | ||||
|  			continue; | ||||
| @@ -958,11 +981,10 @@ ath_tx_get_tid_subframe(struct ath_softc | ||||
|  	return NULL; | ||||
|  } | ||||
|   | ||||
| -static bool | ||||
| +static int | ||||
|  ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, | ||||
|  		 struct ath_atx_tid *tid, struct list_head *bf_q, | ||||
| -		 struct ath_buf *bf_first, struct sk_buff_head *tid_q, | ||||
| -		 int *aggr_len) | ||||
| +		 struct ath_buf *bf_first) | ||||
|  { | ||||
|  #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) | ||||
|  	struct ath_buf *bf = bf_first, *bf_prev = NULL; | ||||
| @@ -972,12 +994,13 @@ ath_tx_form_aggr(struct ath_softc *sc, s | ||||
|  	struct ieee80211_tx_info *tx_info; | ||||
|  	struct ath_frame_info *fi; | ||||
|  	struct sk_buff *skb; | ||||
| -	bool closed = false; | ||||
| + | ||||
|   | ||||
|  	bf = bf_first; | ||||
|  	aggr_limit = ath_lookup_rate(sc, bf, tid); | ||||
|   | ||||
| -	do { | ||||
| +	while (bf) | ||||
| +	{ | ||||
|  		skb = bf->bf_mpdu; | ||||
|  		fi = get_frame_info(skb); | ||||
|   | ||||
| @@ -986,12 +1009,12 @@ ath_tx_form_aggr(struct ath_softc *sc, s | ||||
|  		if (nframes) { | ||||
|  			if (aggr_limit < al + bpad + al_delta || | ||||
|  			    ath_lookup_legacy(bf) || nframes >= h_baw) | ||||
| -				break; | ||||
| +				goto stop; | ||||
|   | ||||
|  			tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); | ||||
|  			if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) || | ||||
|  			    !(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) | ||||
| -				break; | ||||
| +				goto stop; | ||||
|  		} | ||||
|   | ||||
|  		/* add padding for previous frame to aggregation length */ | ||||
| @@ -1013,20 +1036,18 @@ ath_tx_form_aggr(struct ath_softc *sc, s | ||||
|  			ath_tx_addto_baw(sc, tid, bf); | ||||
|  		bf->bf_state.ndelim = ndelim; | ||||
|   | ||||
| -		__skb_unlink(skb, tid_q); | ||||
|  		list_add_tail(&bf->list, bf_q); | ||||
|  		if (bf_prev) | ||||
|  			bf_prev->bf_next = bf; | ||||
|   | ||||
|  		bf_prev = bf; | ||||
|   | ||||
| -		bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); | ||||
| -		if (!bf) { | ||||
| -			closed = true; | ||||
| -			break; | ||||
| -		} | ||||
| -	} while (ath_tid_has_buffered(tid)); | ||||
| - | ||||
| +		bf = ath_tx_get_tid_subframe(sc, txq, tid); | ||||
| +	} | ||||
| +	goto finish; | ||||
| +stop: | ||||
| +	__skb_queue_tail(&tid->retry_q, bf->bf_mpdu); | ||||
| +finish: | ||||
|  	bf = bf_first; | ||||
|  	bf->bf_lastbf = bf_prev; | ||||
|   | ||||
| @@ -1037,9 +1058,7 @@ ath_tx_form_aggr(struct ath_softc *sc, s | ||||
|  		TX_STAT_INC(txq->axq_qnum, a_aggr); | ||||
|  	} | ||||
|   | ||||
| -	*aggr_len = al; | ||||
| - | ||||
| -	return closed; | ||||
| +	return al; | ||||
|  #undef PADBYTES | ||||
|  } | ||||
|   | ||||
| @@ -1416,18 +1435,15 @@ static void ath_tx_fill_desc(struct ath_ | ||||
|  static void | ||||
|  ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq, | ||||
|  		  struct ath_atx_tid *tid, struct list_head *bf_q, | ||||
| -		  struct ath_buf *bf_first, struct sk_buff_head *tid_q) | ||||
| +		  struct ath_buf *bf_first) | ||||
|  { | ||||
|  	struct ath_buf *bf = bf_first, *bf_prev = NULL; | ||||
| -	struct sk_buff *skb; | ||||
|  	int nframes = 0; | ||||
|   | ||||
|  	do { | ||||
|  		struct ieee80211_tx_info *tx_info; | ||||
| -		skb = bf->bf_mpdu; | ||||
|   | ||||
|  		nframes++; | ||||
| -		__skb_unlink(skb, tid_q); | ||||
|  		list_add_tail(&bf->list, bf_q); | ||||
|  		if (bf_prev) | ||||
|  			bf_prev->bf_next = bf; | ||||
| @@ -1436,13 +1452,15 @@ ath_tx_form_burst(struct ath_softc *sc, | ||||
|  		if (nframes >= 2) | ||||
|  			break; | ||||
|   | ||||
| -		bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); | ||||
| +		bf = ath_tx_get_tid_subframe(sc, txq, tid); | ||||
|  		if (!bf) | ||||
|  			break; | ||||
|   | ||||
|  		tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); | ||||
| -		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) | ||||
| +		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { | ||||
| +			__skb_queue_tail(&tid->retry_q, bf->bf_mpdu); | ||||
|  			break; | ||||
| +		} | ||||
|   | ||||
|  		ath_set_rates(tid->an->vif, tid->an->sta, bf); | ||||
|  	} while (1); | ||||
| @@ -1453,34 +1471,33 @@ static bool ath_tx_sched_aggr(struct ath | ||||
|  { | ||||
|  	struct ath_buf *bf; | ||||
|  	struct ieee80211_tx_info *tx_info; | ||||
| -	struct sk_buff_head *tid_q; | ||||
|  	struct list_head bf_q; | ||||
|  	int aggr_len = 0; | ||||
| -	bool aggr, last = true; | ||||
| +	bool aggr; | ||||
|   | ||||
|  	if (!ath_tid_has_buffered(tid)) | ||||
|  		return false; | ||||
|   | ||||
|  	INIT_LIST_HEAD(&bf_q); | ||||
|   | ||||
| -	bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); | ||||
| +	bf = ath_tx_get_tid_subframe(sc, txq, tid); | ||||
|  	if (!bf) | ||||
|  		return false; | ||||
|   | ||||
|  	tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); | ||||
|  	aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU); | ||||
|  	if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) || | ||||
| -		(!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { | ||||
| +	    (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { | ||||
| +		__skb_queue_tail(&tid->retry_q, bf->bf_mpdu); | ||||
|  		*stop = true; | ||||
|  		return false; | ||||
|  	} | ||||
|   | ||||
|  	ath_set_rates(tid->an->vif, tid->an->sta, bf); | ||||
|  	if (aggr) | ||||
| -		last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf, | ||||
| -					tid_q, &aggr_len); | ||||
| +		aggr_len = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf); | ||||
|  	else | ||||
| -		ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q); | ||||
| +		ath_tx_form_burst(sc, txq, tid, &bf_q, bf); | ||||
|   | ||||
|  	if (list_empty(&bf_q)) | ||||
|  		return false; | ||||
| @@ -1523,9 +1540,6 @@ int ath_tx_aggr_start(struct ath_softc * | ||||
|  		an->mpdudensity = density; | ||||
|  	} | ||||
|   | ||||
| -	/* force sequence number allocation for pending frames */ | ||||
| -	ath_tx_tid_change_state(sc, txtid); | ||||
| - | ||||
|  	txtid->active = true; | ||||
|  	*ssn = txtid->seq_start = txtid->seq_next; | ||||
|  	txtid->bar_index = -1; | ||||
| @@ -1550,7 +1564,6 @@ void ath_tx_aggr_stop(struct ath_softc * | ||||
|  	ath_txq_lock(sc, txq); | ||||
|  	txtid->active = false; | ||||
|  	ath_tx_flush_tid(sc, txtid); | ||||
| -	ath_tx_tid_change_state(sc, txtid); | ||||
|  	ath_txq_unlock_complete(sc, txq); | ||||
|  } | ||||
|   | ||||
| @@ -1560,14 +1573,12 @@ void ath_tx_aggr_sleep(struct ieee80211_ | ||||
|  	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||||
|  	struct ath_atx_tid *tid; | ||||
|  	struct ath_txq *txq; | ||||
| -	bool buffered; | ||||
|  	int tidno; | ||||
|   | ||||
|  	ath_dbg(common, XMIT, "%s called\n", __func__); | ||||
|   | ||||
| -	for (tidno = 0, tid = &an->tid[tidno]; | ||||
| -	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { | ||||
| - | ||||
| +	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { | ||||
| +		tid = ath_node_to_tid(an, tidno); | ||||
|  		txq = tid->txq; | ||||
|   | ||||
|  		ath_txq_lock(sc, txq); | ||||
| @@ -1577,13 +1588,12 @@ void ath_tx_aggr_sleep(struct ieee80211_ | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| -		buffered = ath_tid_has_buffered(tid); | ||||
| +		if (!skb_queue_empty(&tid->retry_q)) | ||||
| +			ieee80211_sta_set_buffered(sta, tid->tidno, true); | ||||
|   | ||||
|  		list_del_init(&tid->list); | ||||
|   | ||||
|  		ath_txq_unlock(sc, txq); | ||||
| - | ||||
| -		ieee80211_sta_set_buffered(sta, tidno, buffered); | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -1596,49 +1606,20 @@ void ath_tx_aggr_wakeup(struct ath_softc | ||||
|   | ||||
|  	ath_dbg(common, XMIT, "%s called\n", __func__); | ||||
|   | ||||
| -	for (tidno = 0, tid = &an->tid[tidno]; | ||||
| -	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { | ||||
| - | ||||
| +	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { | ||||
| +		tid = ath_node_to_tid(an, tidno); | ||||
|  		txq = tid->txq; | ||||
|   | ||||
|  		ath_txq_lock(sc, txq); | ||||
|  		tid->clear_ps_filter = true; | ||||
| - | ||||
|  		if (ath_tid_has_buffered(tid)) { | ||||
|  			ath_tx_queue_tid(sc, txq, tid); | ||||
|  			ath_txq_schedule(sc, txq); | ||||
|  		} | ||||
| - | ||||
|  		ath_txq_unlock_complete(sc, txq); | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, | ||||
| -			u16 tidno) | ||||
| -{ | ||||
| -	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||||
| -	struct ath_atx_tid *tid; | ||||
| -	struct ath_node *an; | ||||
| -	struct ath_txq *txq; | ||||
| - | ||||
| -	ath_dbg(common, XMIT, "%s called\n", __func__); | ||||
| - | ||||
| -	an = (struct ath_node *)sta->drv_priv; | ||||
| -	tid = ATH_AN_2_TID(an, tidno); | ||||
| -	txq = tid->txq; | ||||
| - | ||||
| -	ath_txq_lock(sc, txq); | ||||
| - | ||||
| -	tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; | ||||
| - | ||||
| -	if (ath_tid_has_buffered(tid)) { | ||||
| -		ath_tx_queue_tid(sc, txq, tid); | ||||
| -		ath_txq_schedule(sc, txq); | ||||
| -	} | ||||
| - | ||||
| -	ath_txq_unlock_complete(sc, txq); | ||||
| -} | ||||
| - | ||||
|  void ath9k_release_buffered_frames(struct ieee80211_hw *hw, | ||||
|  				   struct ieee80211_sta *sta, | ||||
|  				   u16 tids, int nframes, | ||||
| @@ -1651,7 +1632,6 @@ void ath9k_release_buffered_frames(struc | ||||
|  	struct ieee80211_tx_info *info; | ||||
|  	struct list_head bf_q; | ||||
|  	struct ath_buf *bf_tail = NULL, *bf; | ||||
| -	struct sk_buff_head *tid_q; | ||||
|  	int sent = 0; | ||||
|  	int i; | ||||
|   | ||||
| @@ -1666,11 +1646,10 @@ void ath9k_release_buffered_frames(struc | ||||
|   | ||||
|  		ath_txq_lock(sc, tid->txq); | ||||
|  		while (nframes > 0) { | ||||
| -			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q); | ||||
| +			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid); | ||||
|  			if (!bf) | ||||
|  				break; | ||||
|   | ||||
| -			__skb_unlink(bf->bf_mpdu, tid_q); | ||||
|  			list_add_tail(&bf->list, &bf_q); | ||||
|  			ath_set_rates(tid->an->vif, tid->an->sta, bf); | ||||
|  			if (bf_isampdu(bf)) { | ||||
| @@ -1685,7 +1664,7 @@ void ath9k_release_buffered_frames(struc | ||||
|  			sent++; | ||||
|  			TX_STAT_INC(txq->axq_qnum, a_queued_hw); | ||||
|   | ||||
| -			if (an->sta && !ath_tid_has_buffered(tid)) | ||||
| +			if (an->sta && skb_queue_empty(&tid->retry_q)) | ||||
|  				ieee80211_sta_set_buffered(an->sta, i, false); | ||||
|  		} | ||||
|  		ath_txq_unlock_complete(sc, tid->txq); | ||||
| @@ -1914,13 +1893,7 @@ bool ath_drain_all_txq(struct ath_softc | ||||
|  		if (!ATH_TXQ_SETUP(sc, i)) | ||||
|  			continue; | ||||
|   | ||||
| -		/* | ||||
| -		 * The caller will resume queues with ieee80211_wake_queues. | ||||
| -		 * Mark the queue as not stopped to prevent ath_tx_complete | ||||
| -		 * from waking the queue too early. | ||||
| -		 */ | ||||
|  		txq = &sc->tx.txq[i]; | ||||
| -		txq->stopped = false; | ||||
|  		ath_draintxq(sc, txq); | ||||
|  	} | ||||
|   | ||||
| @@ -2319,16 +2292,14 @@ int ath_tx_start(struct ieee80211_hw *hw | ||||
|  	struct ath_softc *sc = hw->priv; | ||||
|  	struct ath_txq *txq = txctl->txq; | ||||
|  	struct ath_atx_tid *tid = NULL; | ||||
| +	struct ath_node *an = NULL; | ||||
|  	struct ath_buf *bf; | ||||
| -	bool queue, skip_uapsd = false, ps_resp; | ||||
| +	bool ps_resp; | ||||
|  	int q, ret; | ||||
|   | ||||
|  	if (vif) | ||||
|  		avp = (void *)vif->drv_priv; | ||||
|   | ||||
| -	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) | ||||
| -		txctl->force_channel = true; | ||||
| - | ||||
|  	ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE); | ||||
|   | ||||
|  	ret = ath_tx_prepare(hw, skb, txctl); | ||||
| @@ -2343,63 +2314,18 @@ int ath_tx_start(struct ieee80211_hw *hw | ||||
|   | ||||
|  	q = skb_get_queue_mapping(skb); | ||||
|   | ||||
| -	ath_txq_lock(sc, txq); | ||||
| -	if (txq == sc->tx.txq_map[q]) { | ||||
| -		fi->txq = q; | ||||
| -		if (++txq->pending_frames > sc->tx.txq_max_pending[q] && | ||||
| -		    !txq->stopped) { | ||||
| -			if (ath9k_is_chanctx_enabled()) | ||||
| -				ieee80211_stop_queue(sc->hw, info->hw_queue); | ||||
| -			else | ||||
| -				ieee80211_stop_queue(sc->hw, q); | ||||
| -			txq->stopped = true; | ||||
| -		} | ||||
| -	} | ||||
| - | ||||
| -	queue = ieee80211_is_data_present(hdr->frame_control); | ||||
| - | ||||
| -	/* If chanctx, queue all null frames while NOA could be there */ | ||||
| -	if (ath9k_is_chanctx_enabled() && | ||||
| -	    ieee80211_is_nullfunc(hdr->frame_control) && | ||||
| -	    !txctl->force_channel) | ||||
| -		queue = true; | ||||
| - | ||||
| -	/* Force queueing of all frames that belong to a virtual interface on | ||||
| -	 * a different channel context, to ensure that they are sent on the | ||||
| -	 * correct channel. | ||||
| -	 */ | ||||
| -	if (((avp && avp->chanctx != sc->cur_chan) || | ||||
| -	     sc->cur_chan->stopped) && !txctl->force_channel) { | ||||
| -		if (!txctl->an) | ||||
| -			txctl->an = &avp->mcast_node; | ||||
| -		queue = true; | ||||
| -		skip_uapsd = true; | ||||
| -	} | ||||
| - | ||||
| -	if (txctl->an && queue) | ||||
| -		tid = ath_get_skb_tid(sc, txctl->an, skb); | ||||
| - | ||||
| -	if (!skip_uapsd && ps_resp) { | ||||
| -		ath_txq_unlock(sc, txq); | ||||
| +	if (ps_resp) | ||||
|  		txq = sc->tx.uapsdq; | ||||
| -		ath_txq_lock(sc, txq); | ||||
| -	} else if (txctl->an && queue) { | ||||
| -		WARN_ON(tid->txq != txctl->txq); | ||||
|   | ||||
| -		if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) | ||||
| -			tid->clear_ps_filter = true; | ||||
| - | ||||
| -		/* | ||||
| -		 * Add this frame to software queue for scheduling later | ||||
| -		 * for aggregation. | ||||
| -		 */ | ||||
| -		TX_STAT_INC(txq->axq_qnum, a_queued_sw); | ||||
| -		__skb_queue_tail(&tid->buf_q, skb); | ||||
| -		if (!txctl->an->sleeping) | ||||
| -			ath_tx_queue_tid(sc, txq, tid); | ||||
| +	if (txctl->sta) { | ||||
| +		an = (struct ath_node *) sta->drv_priv; | ||||
| +		tid = ath_get_skb_tid(sc, an, skb); | ||||
| +	} | ||||
|   | ||||
| -		ath_txq_schedule(sc, txq); | ||||
| -		goto out; | ||||
| +	ath_txq_lock(sc, txq); | ||||
| +	if (txq == sc->tx.txq_map[q]) { | ||||
| +		fi->txq = q; | ||||
| +		++txq->pending_frames; | ||||
|  	} | ||||
|   | ||||
|  	bf = ath_tx_setup_buffer(sc, txq, tid, skb); | ||||
| @@ -2892,9 +2818,8 @@ void ath_tx_node_init(struct ath_softc * | ||||
|  	struct ath_atx_tid *tid; | ||||
|  	int tidno, acno; | ||||
|   | ||||
| -	for (tidno = 0, tid = &an->tid[tidno]; | ||||
| -	     tidno < IEEE80211_NUM_TIDS; | ||||
| -	     tidno++, tid++) { | ||||
| +	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { | ||||
| +		tid = ath_node_to_tid(an, tidno); | ||||
|  		tid->an        = an; | ||||
|  		tid->tidno     = tidno; | ||||
|  		tid->seq_start = tid->seq_next = 0; | ||||
| @@ -2902,11 +2827,14 @@ void ath_tx_node_init(struct ath_softc * | ||||
|  		tid->baw_head  = tid->baw_tail = 0; | ||||
|  		tid->active	   = false; | ||||
|  		tid->clear_ps_filter = true; | ||||
| -		__skb_queue_head_init(&tid->buf_q); | ||||
| +		tid->has_queued  = false; | ||||
|  		__skb_queue_head_init(&tid->retry_q); | ||||
|  		INIT_LIST_HEAD(&tid->list); | ||||
|  		acno = TID_TO_WME_AC(tidno); | ||||
|  		tid->txq = sc->tx.txq_map[acno]; | ||||
| + | ||||
| +		if (!an->sta) | ||||
| +			break; /* just one multicast ath_atx_tid */ | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -2916,9 +2844,8 @@ void ath_tx_node_cleanup(struct ath_soft | ||||
|  	struct ath_txq *txq; | ||||
|  	int tidno; | ||||
|   | ||||
| -	for (tidno = 0, tid = &an->tid[tidno]; | ||||
| -	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { | ||||
| - | ||||
| +	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { | ||||
| +		tid = ath_node_to_tid(an, tidno); | ||||
|  		txq = tid->txq; | ||||
|   | ||||
|  		ath_txq_lock(sc, txq); | ||||
| @@ -2930,6 +2857,9 @@ void ath_tx_node_cleanup(struct ath_soft | ||||
|  		tid->active = false; | ||||
|   | ||||
|  		ath_txq_unlock(sc, txq); | ||||
| + | ||||
| +		if (!an->sta) | ||||
| +			break; /* just one multicast ath_atx_tid */ | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -29,21 +29,6 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| -		npend = ath9k_hw_numtxpending(ah, i);
 | ||||
| -		if (npend)
 | ||||
| -			break;
 | ||||
| -	}
 | ||||
| -
 | ||||
| -	if (ah->external_reset &&
 | ||||
| -	    (npend || type == ATH9K_RESET_COLD)) {
 | ||||
| -		int reset_err = 0;
 | ||||
| -
 | ||||
| -		ath_dbg(ath9k_hw_common(ah), RESET,
 | ||||
| -			"reset MAC via external reset\n");
 | ||||
| -
 | ||||
| -		reset_err = ah->external_reset();
 | ||||
| -		if (reset_err) {
 | ||||
| -			ath_err(ath9k_hw_common(ah),
 | ||||
| -				"External reset failed, err=%d\n",
 | ||||
| -				reset_err);
 | ||||
| -			return false;
 | ||||
| +	if (type == ATH9K_RESET_COLD)
 | ||||
| +		return true;
 | ||||
| +
 | ||||
| @@ -59,35 +44,47 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| +		for (i = 0; i < AR_NUM_QCU; i++) {
 | ||||
| +			if (ath9k_hw_numtxpending(ah, i))
 | ||||
| +				return true;
 | ||||
|  		} | ||||
| +		}
 | ||||
| +
 | ||||
|  	} | ||||
|   | ||||
| -	if (ah->external_reset &&
 | ||||
| -	    (npend || type == ATH9K_RESET_COLD)) {
 | ||||
| -		int reset_err = 0;
 | ||||
| +	return false;
 | ||||
| +}
 | ||||
| +
 | ||||
|   | ||||
| -		ath_dbg(ath9k_hw_common(ah), RESET,
 | ||||
| -			"reset MAC via external reset\n");
 | ||||
| +static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
 | ||||
| +{
 | ||||
| +	int err;
 | ||||
| +
 | ||||
|   | ||||
| -		reset_err = ah->external_reset();
 | ||||
| -		if (reset_err) {
 | ||||
| -			ath_err(ath9k_hw_common(ah),
 | ||||
| -				"External reset failed, err=%d\n",
 | ||||
| -				reset_err);
 | ||||
| -			return false;
 | ||||
| -		}
 | ||||
| +	if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
 | ||||
| +		return true;
 | ||||
| +
 | ||||
| +	ath_dbg(ath9k_hw_common(ah), RESET,
 | ||||
| +		"reset MAC via external reset\n");
 | ||||
|   | ||||
| -		REG_WRITE(ah, AR_RTC_RESET, 1);
 | ||||
| +	ath_dbg(ath9k_hw_common(ah), RESET,
 | ||||
| +		"reset MAC via external reset\n");
 | ||||
| +
 | ||||
| +	err = ah->external_reset();
 | ||||
| +	if (err) {
 | ||||
| +		ath_err(ath9k_hw_common(ah),
 | ||||
| +			"External reset failed, err=%d\n", err);
 | ||||
| +		return false;
 | ||||
|  	} | ||||
|   | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (AR_SREV_9550(ah)) {
 | ||||
| +		REG_WRITE(ah, AR_RTC_RESET, 0);
 | ||||
| +		udelay(10);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	} | ||||
|   | ||||
| +	REG_WRITE(ah, AR_RTC_RESET, 1);
 | ||||
| +	udelay(10);
 | ||||
| +
 | ||||
| @@ -1,6 +1,7 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Tue, 27 Dec 2016 23:16:23 +0100 | ||||
| Subject: [PATCH] ath9k: don't run periodic and nf calibation at the same time | ||||
| Subject: [PATCH] ath9k: don't run periodic and nf calibation at the same | ||||
|  time | ||||
| 
 | ||||
| The checks already prevents periodic cal from being started while noise | ||||
| floor calibration runs. It is missing checks for the other way around. | ||||
| @@ -27,7 +27,7 @@ Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> | ||||
| -	REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
 | ||||
| +	u32 reg = AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT;
 | ||||
| +
 | ||||
| +	if (!config_enabled(CPTCFG_ATH9K_TX99))
 | ||||
| +	if (!IS_ENABLED(CPTCFG_ATH9K_TX99))
 | ||||
| +		reg |= AR_DIAG_FORCE_RX_CLEAR;
 | ||||
| +	REG_SET_BIT(ah, AR_DIAG_SW, reg);
 | ||||
|   | ||||
| @@ -13,7 +13,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| 
 | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| @@ -177,10 +177,25 @@ static void ath_send_bar(struct ath_atx_
 | ||||
| @@ -188,10 +188,25 @@ static void ath_send_bar(struct ath_atx_
 | ||||
|  } | ||||
|   | ||||
|  static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta, | ||||
| @@ -40,7 +40,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  } | ||||
|   | ||||
|  static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, | ||||
| @@ -1462,7 +1477,7 @@ ath_tx_form_burst(struct ath_softc *sc,
 | ||||
| @@ -1522,7 +1537,7 @@ ath_tx_form_burst(struct ath_softc *sc,
 | ||||
|  			break; | ||||
|  		} | ||||
|   | ||||
| @@ -49,7 +49,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  	} while (1); | ||||
|  } | ||||
|   | ||||
| @@ -1493,7 +1508,7 @@ static bool ath_tx_sched_aggr(struct ath
 | ||||
| @@ -1552,7 +1567,7 @@ static bool ath_tx_sched_aggr(struct ath
 | ||||
|  		return false; | ||||
|  	} | ||||
|   | ||||
| @@ -58,7 +58,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  	if (aggr) | ||||
|  		aggr_len = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf); | ||||
|  	else | ||||
| @@ -1651,7 +1666,7 @@ void ath9k_release_buffered_frames(struc
 | ||||
| @@ -1710,7 +1725,7 @@ void ath9k_release_buffered_frames(struc
 | ||||
|  				break; | ||||
|   | ||||
|  			list_add_tail(&bf->list, &bf_q); | ||||
| @@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  			if (bf_isampdu(bf)) { | ||||
|  				ath_tx_addto_baw(sc, tid, bf); | ||||
|  				bf->bf_state.bf_type &= ~BUF_AGGR; | ||||
| @@ -2343,7 +2358,7 @@ int ath_tx_start(struct ieee80211_hw *hw
 | ||||
| @@ -2410,7 +2425,7 @@ int ath_tx_start(struct ieee80211_hw *hw
 | ||||
|  	if (txctl->paprd) | ||||
|  		bf->bf_state.bfs_paprd_timestamp = jiffies; | ||||
|   | ||||
| @@ -76,7 +76,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  	ath_tx_send_normal(sc, txq, tid, skb); | ||||
|   | ||||
|  out: | ||||
| @@ -2382,7 +2397,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw
 | ||||
| @@ -2449,7 +2464,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw
 | ||||
|  			break; | ||||
|   | ||||
|  		bf->bf_lastbf = bf; | ||||
| @@ -85,7 +85,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  		ath_buf_set_rate(sc, bf, &info, fi->framelen, false); | ||||
|  		duration += info.rates[0].PktDuration; | ||||
|  		if (bf_tail) | ||||
| @@ -2898,7 +2913,7 @@ int ath9k_tx99_send(struct ath_softc *sc
 | ||||
| @@ -2968,7 +2983,7 @@ int ath9k_tx99_send(struct ath_softc *sc
 | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| @@ -8,7 +8,7 @@ This reverts commit 71f5137bf010c6faffab50c0ec15374c59c4a411. | ||||
| 
 | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.c
 | ||||
| @@ -2910,7 +2910,8 @@ void ath9k_hw_apply_txpower(struct ath_h
 | ||||
| @@ -2931,7 +2931,8 @@ void ath9k_hw_apply_txpower(struct ath_h
 | ||||
|  { | ||||
|  	struct ath_regulatory *reg = ath9k_hw_regulatory(ah); | ||||
|  	struct ieee80211_channel *channel; | ||||
| @@ -18,7 +18,7 @@ This reverts commit 71f5137bf010c6faffab50c0ec15374c59c4a411. | ||||
|   | ||||
|  	if (!chan) | ||||
|  		return; | ||||
| @@ -2918,10 +2919,15 @@ void ath9k_hw_apply_txpower(struct ath_h
 | ||||
| @@ -2939,10 +2940,15 @@ void ath9k_hw_apply_txpower(struct ath_h
 | ||||
|  	channel = chan->chan; | ||||
|  	chan_pwr = min_t(int, channel->max_power * 2, MAX_RATE_POWER); | ||||
|  	new_pwr = min_t(int, chan_pwr, reg->power_limit); | ||||
| @@ -11,7 +11,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
| 
 | ||||
| --- a/net/mac80211/ieee80211_i.h
 | ||||
| +++ b/net/mac80211/ieee80211_i.h
 | ||||
| @@ -175,6 +175,7 @@ struct ieee80211_tx_data {
 | ||||
| @@ -177,6 +177,7 @@ struct ieee80211_tx_data {
 | ||||
|  	struct ieee80211_tx_rate rate; | ||||
|   | ||||
|  	unsigned int flags; | ||||
| @@ -21,7 +21,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
| --- a/net/mac80211/tx.c
 | ||||
| +++ b/net/mac80211/tx.c
 | ||||
| @@ -955,7 +955,7 @@ ieee80211_tx_h_fragment(struct ieee80211
 | ||||
| @@ -925,7 +925,7 @@ ieee80211_tx_h_fragment(struct ieee80211
 | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	struct ieee80211_hdr *hdr = (void *)skb->data; | ||||
|  	int frag_threshold = tx->local->hw.wiphy->frag_threshold; | ||||
| @@ -30,7 +30,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	int fragnum; | ||||
|   | ||||
|  	/* no matter what happens, tx->skb moves to tx->skbs */ | ||||
| @@ -976,8 +976,6 @@ ieee80211_tx_h_fragment(struct ieee80211
 | ||||
| @@ -946,8 +946,6 @@ ieee80211_tx_h_fragment(struct ieee80211
 | ||||
|  	if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) | ||||
|  		return TX_DROP; | ||||
|   | ||||
| @@ -39,7 +39,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	/* internal error, why isn't DONTFRAG set? */ | ||||
|  	if (WARN_ON(skb->len + FCS_LEN <= frag_threshold)) | ||||
|  		return TX_DROP; | ||||
| @@ -1209,6 +1207,8 @@ ieee80211_tx_prepare(struct ieee80211_su
 | ||||
| @@ -1179,6 +1177,8 @@ ieee80211_tx_prepare(struct ieee80211_su
 | ||||
|   | ||||
|  	hdr = (struct ieee80211_hdr *) skb->data; | ||||
|   | ||||
| @@ -48,7 +48,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	if (likely(sta)) { | ||||
|  		if (!IS_ERR(sta)) | ||||
|  			tx->sta = sta; | ||||
| @@ -3414,6 +3414,7 @@ begin:
 | ||||
| @@ -3437,6 +3437,7 @@ begin:
 | ||||
|  	tx.local = local; | ||||
|  	tx.skb = skb; | ||||
|  	tx.sdata = vif_to_sdata(info->control.vif); | ||||
| @@ -56,7 +56,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
|  	if (txq->sta) | ||||
|  		tx.sta = container_of(txq->sta, struct sta_info, sta); | ||||
| @@ -3584,6 +3585,7 @@ ieee80211_build_data_template(struct iee
 | ||||
| @@ -3731,6 +3732,7 @@ ieee80211_build_data_template(struct iee
 | ||||
|  	hdr = (void *)skb->data; | ||||
|  	tx.sta = sta_info_get(sdata, hdr->addr1); | ||||
|  	tx.skb = skb; | ||||
| @@ -126,7 +126,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	if (!iv) | ||||
|  		return -1; | ||||
|   | ||||
| @@ -306,13 +306,14 @@ static int wep_encrypt_skb(struct ieee80
 | ||||
| @@ -307,13 +307,14 @@ static int wep_encrypt_skb(struct ieee80
 | ||||
|  	struct ieee80211_key_conf *hw_key = info->control.hw_key; | ||||
|   | ||||
|  	if (!hw_key) { | ||||
| @@ -181,7 +181,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	len = skb->len - hdrlen; | ||||
|   | ||||
|  	if (info->control.hw_key) | ||||
| @@ -418,7 +417,7 @@ static int ccmp_encrypt_skb(struct ieee8
 | ||||
| @@ -419,7 +418,7 @@ static int ccmp_encrypt_skb(struct ieee8
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
| @@ -190,7 +190,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	len = skb->len - hdrlen; | ||||
|   | ||||
|  	if (info->control.hw_key) | ||||
| @@ -651,7 +650,7 @@ static int gcmp_encrypt_skb(struct ieee8
 | ||||
| @@ -652,7 +651,7 @@ static int gcmp_encrypt_skb(struct ieee8
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
| @@ -199,7 +199,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	len = skb->len - hdrlen; | ||||
|   | ||||
|  	if (info->control.hw_key) | ||||
| @@ -791,7 +790,6 @@ static ieee80211_tx_result
 | ||||
| @@ -792,7 +791,6 @@ static ieee80211_tx_result
 | ||||
|  ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, | ||||
|  			    struct sk_buff *skb) | ||||
|  { | ||||
| @@ -207,7 +207,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	struct ieee80211_key *key = tx->key; | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	int hdrlen; | ||||
| @@ -807,8 +805,7 @@ ieee80211_crypto_cs_encrypt(struct ieee8
 | ||||
| @@ -808,8 +806,7 @@ ieee80211_crypto_cs_encrypt(struct ieee8
 | ||||
|  		     pskb_expand_head(skb, iv_len, 0, GFP_ATOMIC))) | ||||
|  		return TX_DROP; | ||||
|   | ||||
| @@ -23,9 +23,9 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
| 
 | ||||
| --- a/include/net/mac80211.h
 | ||||
| +++ b/include/net/mac80211.h
 | ||||
| @@ -2025,6 +2025,9 @@ struct ieee80211_txq {
 | ||||
|   *	drivers, mac80211 packet loss mechanism will not be triggered and driver | ||||
|   *	is completely depending on firmware event for station kickout. | ||||
| @@ -2043,6 +2043,9 @@ struct ieee80211_txq {
 | ||||
|   *	The stack will not do fragmentation. | ||||
|   *	The callback for @set_frag_threshold should be set as well. | ||||
|   * | ||||
| + * @IEEE80211_HW_NEEDS_ALIGNED4_SKBS: Driver need aligned skbs to four-byte.
 | ||||
| + *	Padding will be added after ieee80211_hdr, before IV/LLC.
 | ||||
| @@ -33,28 +33,28 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays | ||||
|   */ | ||||
|  enum ieee80211_hw_flags { | ||||
| @@ -2066,6 +2069,7 @@ enum ieee80211_hw_flags {
 | ||||
|  	IEEE80211_HW_TX_AMSDU, | ||||
| @@ -2085,6 +2088,7 @@ enum ieee80211_hw_flags {
 | ||||
|  	IEEE80211_HW_TX_FRAG_LIST, | ||||
|  	IEEE80211_HW_REPORTS_LOW_ACK, | ||||
|  	IEEE80211_HW_SUPPORTS_TX_FRAG, | ||||
| +	IEEE80211_HW_NEEDS_ALIGNED4_SKBS,
 | ||||
|   | ||||
|  	/* keep last, obviously */ | ||||
|  	NUM_IEEE80211_HW_FLAGS | ||||
| --- a/net/mac80211/debugfs.c
 | ||||
| +++ b/net/mac80211/debugfs.c
 | ||||
| @@ -210,6 +210,7 @@ static const char *hw_flag_names[] = {
 | ||||
|  	FLAG(TX_AMSDU), | ||||
| @@ -211,6 +211,7 @@ static const char *hw_flag_names[] = {
 | ||||
|  	FLAG(TX_FRAG_LIST), | ||||
|  	FLAG(REPORTS_LOW_ACK), | ||||
|  	FLAG(SUPPORTS_TX_FRAG), | ||||
| +	FLAG(NEEDS_ALIGNED4_SKBS),
 | ||||
|  #undef FLAG | ||||
|  }; | ||||
|   | ||||
| --- a/net/mac80211/ieee80211_i.h
 | ||||
| +++ b/net/mac80211/ieee80211_i.h
 | ||||
| @@ -1529,6 +1529,29 @@ ieee80211_have_rx_timestamp(struct ieee8
 | ||||
|  	return false; | ||||
| @@ -1553,6 +1553,29 @@ ieee80211_vif_get_num_mcast_if(struct ie
 | ||||
|  	return -1; | ||||
|  } | ||||
|   | ||||
| +static inline unsigned int
 | ||||
| @@ -96,7 +96,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	struct rcu_head rcu_head; | ||||
| --- a/net/mac80211/status.c
 | ||||
| +++ b/net/mac80211/status.c
 | ||||
| @@ -689,9 +689,22 @@ void ieee80211_tx_monitor(struct ieee802
 | ||||
| @@ -693,9 +693,22 @@ void ieee80211_tx_monitor(struct ieee802
 | ||||
|  	struct sk_buff *skb2; | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	struct ieee80211_sub_if_data *sdata; | ||||
| @@ -137,7 +137,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
| --- a/net/mac80211/tx.c
 | ||||
| +++ b/net/mac80211/tx.c
 | ||||
| @@ -1206,8 +1206,7 @@ ieee80211_tx_prepare(struct ieee80211_su
 | ||||
| @@ -1176,8 +1176,7 @@ ieee80211_tx_prepare(struct ieee80211_su
 | ||||
|  	info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING; | ||||
|   | ||||
|  	hdr = (struct ieee80211_hdr *) skb->data; | ||||
| @@ -147,7 +147,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
|  	if (likely(sta)) { | ||||
|  		if (!IS_ERR(sta)) | ||||
| @@ -2158,7 +2157,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
 | ||||
| @@ -2152,7 +2151,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
 | ||||
|  		goto fail; | ||||
|   | ||||
|  	hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); | ||||
| @@ -156,7 +156,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
|  	if (skb->len < len_rthdr + hdrlen) | ||||
|  		goto fail; | ||||
| @@ -2376,7 +2375,7 @@ static struct sk_buff *ieee80211_build_h
 | ||||
| @@ -2370,7 +2369,7 @@ static struct sk_buff *ieee80211_build_h
 | ||||
|  	struct ieee80211_chanctx_conf *chanctx_conf; | ||||
|  	struct ieee80211_sub_if_data *ap_sdata; | ||||
|  	enum nl80211_band band; | ||||
| @@ -165,7 +165,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
|  	if (IS_ERR(sta)) | ||||
|  		sta = NULL; | ||||
| @@ -2596,6 +2595,9 @@ static struct sk_buff *ieee80211_build_h
 | ||||
| @@ -2590,6 +2589,9 @@ static struct sk_buff *ieee80211_build_h
 | ||||
|  		hdrlen += 2; | ||||
|  	} | ||||
|   | ||||
| @@ -175,7 +175,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	/* | ||||
|  	 * Drop unicast frames to unauthorised stations unless they are | ||||
|  	 * EAPOL frames from the local station. | ||||
| @@ -2676,6 +2678,7 @@ static struct sk_buff *ieee80211_build_h
 | ||||
| @@ -2670,6 +2672,7 @@ static struct sk_buff *ieee80211_build_h
 | ||||
|   | ||||
|  	skb_pull(skb, skip_header_bytes); | ||||
|  	head_need = hdrlen + encaps_len + meshhdrlen - skb_headroom(skb); | ||||
| @@ -183,7 +183,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|   | ||||
|  	/* | ||||
|  	 * So we need to modify the skb header and hence need a copy of | ||||
| @@ -2708,6 +2711,9 @@ static struct sk_buff *ieee80211_build_h
 | ||||
| @@ -2702,6 +2705,9 @@ static struct sk_buff *ieee80211_build_h
 | ||||
|  		memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen); | ||||
|  #endif | ||||
|   | ||||
| @@ -193,7 +193,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	if (ieee80211_is_data_qos(fc)) { | ||||
|  		__le16 *qos_control; | ||||
|   | ||||
| @@ -2883,6 +2889,9 @@ void ieee80211_check_fast_xmit(struct st
 | ||||
| @@ -2877,6 +2883,9 @@ void ieee80211_check_fast_xmit(struct st
 | ||||
|  		fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); | ||||
|  	} | ||||
|   | ||||
| @@ -203,7 +203,7 @@ Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> | ||||
|  	/* We store the key here so there's no point in using rcu_dereference() | ||||
|  	 * but that's fine because the code that changes the pointers will call | ||||
|  	 * this function after doing so. For a single CPU that would be enough, | ||||
| @@ -3436,7 +3445,7 @@ begin:
 | ||||
| @@ -3464,7 +3473,7 @@ begin:
 | ||||
|   | ||||
|  		if (tx.key && | ||||
|  		    (tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) | ||||
| @@ -10,7 +10,7 @@ Signed-off-by: Chaitanya TK <chaitanya.mgit@gmail.com> | ||||
| 
 | ||||
| --- a/include/linux/ieee80211.h
 | ||||
| +++ b/include/linux/ieee80211.h
 | ||||
| @@ -1551,6 +1551,7 @@ struct ieee80211_vht_operation {
 | ||||
| @@ -1553,6 +1553,7 @@ struct ieee80211_vht_operation {
 | ||||
|  #define IEEE80211_VHT_CAP_RXSTBC_3				0x00000300 | ||||
|  #define IEEE80211_VHT_CAP_RXSTBC_4				0x00000400 | ||||
|  #define IEEE80211_VHT_CAP_RXSTBC_MASK				0x00000700 | ||||
| @@ -20,15 +20,16 @@ Signed-off-by: Chaitanya TK <chaitanya.mgit@gmail.com> | ||||
|  #define IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT                  13 | ||||
| --- a/net/mac80211/rc80211_minstrel_ht.c
 | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht.c
 | ||||
| @@ -1166,13 +1166,14 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
| @@ -1130,7 +1130,7 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
|  	struct minstrel_ht_sta_priv *msp = priv_sta; | ||||
|  	struct minstrel_ht_sta *mi = &msp->ht; | ||||
|  	struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; | ||||
| -	u16 sta_cap = sta->ht_cap.cap;
 | ||||
| +	u16 ht_cap = sta->ht_cap.cap;
 | ||||
|  	struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; | ||||
|  	struct sta_info *sinfo = container_of(sta, struct sta_info, sta); | ||||
|  	int use_vht; | ||||
|  	int n_supported = 0; | ||||
| @@ -1138,6 +1138,7 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
|  	int ack_dur; | ||||
|  	int stbc; | ||||
|  	int i; | ||||
| @@ -36,7 +37,7 @@ Signed-off-by: Chaitanya TK <chaitanya.mgit@gmail.com> | ||||
|   | ||||
|  	/* fall back to the old minstrel for legacy stations */ | ||||
|  	if (!sta->ht_cap.ht_supported) | ||||
| @@ -1210,16 +1211,24 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
| @@ -1175,16 +1176,24 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
|  	} | ||||
|  	mi->sample_tries = 4; | ||||
|   | ||||
| @@ -66,7 +67,7 @@ Signed-off-by: Chaitanya TK <chaitanya.mgit@gmail.com> | ||||
|  	for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { | ||||
|  		u32 gflags = minstrel_mcs_groups[i].flags; | ||||
|  		int bw, nss; | ||||
| @@ -1232,10 +1241,10 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
| @@ -1197,10 +1206,10 @@ minstrel_ht_update_caps(void *priv, stru
 | ||||
|   | ||||
|  		if (gflags & IEEE80211_TX_RC_SHORT_GI) { | ||||
|  			if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) { | ||||
| @@ -7,7 +7,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| 
 | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| @@ -1635,6 +1635,22 @@ void ath_tx_aggr_wakeup(struct ath_softc
 | ||||
| @@ -1694,6 +1694,22 @@ void ath_tx_aggr_wakeup(struct ath_softc
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  void ath9k_release_buffered_frames(struct ieee80211_hw *hw, | ||||
|  				   struct ieee80211_sta *sta, | ||||
|  				   u16 tids, int nframes, | ||||
| @@ -1665,6 +1681,7 @@ void ath9k_release_buffered_frames(struc
 | ||||
| @@ -1724,6 +1740,7 @@ void ath9k_release_buffered_frames(struc
 | ||||
|  			if (!bf) | ||||
|  				break; | ||||
|   | ||||
| @@ -38,7 +38,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  			list_add_tail(&bf->list, &bf_q); | ||||
|  			ath_set_rates(tid->an->vif, tid->an->sta, bf, true); | ||||
|  			if (bf_isampdu(bf)) { | ||||
| @@ -1688,6 +1705,9 @@ void ath9k_release_buffered_frames(struc
 | ||||
| @@ -1747,6 +1764,9 @@ void ath9k_release_buffered_frames(struc
 | ||||
|  	if (list_empty(&bf_q)) | ||||
|  		return; | ||||
|   | ||||
| @@ -1,285 +0,0 @@ | ||||
| From: Ben Greear <greearb@candelatech.com> | ||||
| Date: Thu, 6 Oct 2016 17:34:14 -0700 | ||||
| Subject: [PATCH] ath10k: Add support for 160Mhz. | ||||
|  | ||||
| This patch was written by Sebastian Gottschall. | ||||
|  | ||||
| Signed-off-by: Ben Greear <greearb@candelatech.com> | ||||
| Signed-off-by: Sebastian Gottschall <s.gottschall@dd-wrt.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath10k/htt_rx.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c | ||||
| @@ -702,6 +702,10 @@ static void ath10k_htt_rx_h_rates(struct | ||||
|  		/* 80MHZ */ | ||||
|  		case 2: | ||||
|  			status->vht_flag |= RX_VHT_FLAG_80MHZ; | ||||
| +			break; | ||||
| +		case 3: | ||||
| +			status->vht_flag |= RX_VHT_FLAG_160MHZ; | ||||
| +			break; | ||||
|  		} | ||||
|   | ||||
|  		status->flag |= RX_FLAG_VHT; | ||||
| @@ -926,7 +930,7 @@ static void ath10k_process_rx(struct ath | ||||
|  	*status = *rx_status; | ||||
|   | ||||
|  	ath10k_dbg(ar, ATH10K_DBG_DATA, | ||||
| -		   "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%llx fcs-err %i mic-err %i amsdu-more %i\n", | ||||
| +		   "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%llx fcs-err %i mic-err %i amsdu-more %i\n", | ||||
|  		   skb, | ||||
|  		   skb->len, | ||||
|  		   ieee80211_get_SA(hdr), | ||||
| @@ -940,6 +944,7 @@ static void ath10k_process_rx(struct ath | ||||
|  		   status->flag & RX_FLAG_VHT ? "vht" : "", | ||||
|  		   status->flag & RX_FLAG_40MHZ ? "40" : "", | ||||
|  		   status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "", | ||||
| +		   status->vht_flag & RX_VHT_FLAG_160MHZ ? "160" : "", | ||||
|  		   status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", | ||||
|  		   status->rate_idx, | ||||
|  		   status->vht_nss, | ||||
| --- a/drivers/net/wireless/ath/ath10k/mac.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/mac.c | ||||
| @@ -568,10 +568,14 @@ chan_to_phymode(const struct cfg80211_ch | ||||
|  		case NL80211_CHAN_WIDTH_80: | ||||
|  			phymode = MODE_11AC_VHT80; | ||||
|  			break; | ||||
| +		case NL80211_CHAN_WIDTH_160: | ||||
| +			phymode = MODE_11AC_VHT160; | ||||
| +			break; | ||||
| +		case NL80211_CHAN_WIDTH_80P80: | ||||
| +			phymode = MODE_11AC_VHT80_80; | ||||
| +			break; | ||||
|  		case NL80211_CHAN_WIDTH_5: | ||||
|  		case NL80211_CHAN_WIDTH_10: | ||||
| -		case NL80211_CHAN_WIDTH_80P80: | ||||
| -		case NL80211_CHAN_WIDTH_160: | ||||
|  			phymode = MODE_UNKNOWN; | ||||
|  			break; | ||||
|  		} | ||||
| @@ -970,6 +974,7 @@ static int ath10k_monitor_vdev_start(str | ||||
|  	arg.vdev_id = vdev_id; | ||||
|  	arg.channel.freq = channel->center_freq; | ||||
|  	arg.channel.band_center_freq1 = chandef->center_freq1; | ||||
| +	arg.channel.band_center_freq2 = chandef->center_freq2; | ||||
|   | ||||
|  	/* TODO setup this dynamically, what in case we | ||||
|  	   don't have any vifs? */ | ||||
| @@ -1381,6 +1386,7 @@ static int ath10k_vdev_start_restart(str | ||||
|   | ||||
|  	arg.channel.freq = chandef->chan->center_freq; | ||||
|  	arg.channel.band_center_freq1 = chandef->center_freq1; | ||||
| +	arg.channel.band_center_freq2 = chandef->center_freq2; | ||||
|  	arg.channel.mode = chan_to_phymode(chandef); | ||||
|   | ||||
|  	arg.channel.min_power = 0; | ||||
| @@ -2444,6 +2450,9 @@ static void ath10k_peer_assoc_h_vht(stru | ||||
|  	if (sta->bandwidth == IEEE80211_STA_RX_BW_80) | ||||
|  		arg->peer_flags |= ar->wmi.peer_flags->bw80; | ||||
|   | ||||
| +	if (sta->bandwidth == IEEE80211_STA_RX_BW_160) | ||||
| +		arg->peer_flags |= ar->wmi.peer_flags->bw160; | ||||
| + | ||||
|  	arg->peer_vht_rates.rx_max_rate = | ||||
|  		__le16_to_cpu(vht_cap->vht_mcs.rx_highest); | ||||
|  	arg->peer_vht_rates.rx_mcs_set = | ||||
| @@ -2545,7 +2554,17 @@ static void ath10k_peer_assoc_h_phymode( | ||||
|  		    !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) { | ||||
|  			if (sta->bandwidth == IEEE80211_STA_RX_BW_80) | ||||
|  				phymode = MODE_11AC_VHT80; | ||||
| -			else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) | ||||
| +			else if (sta->bandwidth == IEEE80211_STA_RX_BW_160) { | ||||
| +				phymode = MODE_11AC_VHT160; | ||||
| +				switch (sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { | ||||
| +				case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: | ||||
| +					phymode = MODE_11AC_VHT160; | ||||
| +				break; | ||||
| +				case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: | ||||
| +					phymode = MODE_11AC_VHT80_80; | ||||
| +				break; | ||||
| +				} | ||||
| +			} else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) | ||||
|  				phymode = MODE_11AC_VHT40; | ||||
|  			else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) | ||||
|  				phymode = MODE_11AC_VHT20; | ||||
| @@ -4277,6 +4296,10 @@ static struct ieee80211_sta_vht_cap ath1 | ||||
|  		vht_cap.cap |= val; | ||||
|  	} | ||||
|   | ||||
| +	if ((ar->vht_cap_info & IEEE80211_VHT_CAP_SHORT_GI_160) && !(ar->vht_cap_info & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { | ||||
| +		vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | ||||
| +	} | ||||
| + | ||||
|  	mcs_map = 0; | ||||
|  	for (i = 0; i < 8; i++) { | ||||
|  		if ((i < ar->num_rf_chains) && (ar->cfg_tx_chainmask & BIT(i))) | ||||
| @@ -6913,6 +6936,9 @@ static void ath10k_sta_rc_update(struct | ||||
|  			bw = WMI_PEER_CHWIDTH_80MHZ; | ||||
|  			break; | ||||
|  		case IEEE80211_STA_RX_BW_160: | ||||
| +			bw = WMI_PEER_CHWIDTH_160MHZ; | ||||
| +			break; | ||||
| +		default: | ||||
|  			ath10k_warn(ar, "Invalid bandwidth %d in rc update for %pM\n", | ||||
|  				    sta->bandwidth, sta->addr); | ||||
|  			bw = WMI_PEER_CHWIDTH_20MHZ; | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c | ||||
| @@ -3560,6 +3560,7 @@ static const struct wmi_peer_flags_map w | ||||
|  	.vht = WMI_TLV_PEER_VHT, | ||||
|  	.bw80 = WMI_TLV_PEER_80MHZ, | ||||
|  	.pmf = WMI_TLV_PEER_PMF, | ||||
| +	.bw160 = WMI_TLV_PEER_160MHZ, | ||||
|  }; | ||||
|   | ||||
|  /************/ | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h | ||||
| @@ -543,6 +543,7 @@ enum wmi_tlv_peer_flags { | ||||
|  	WMI_TLV_PEER_VHT = 0x02000000, | ||||
|  	WMI_TLV_PEER_80MHZ = 0x04000000, | ||||
|  	WMI_TLV_PEER_PMF = 0x08000000, | ||||
| +	WMI_TLV_PEER_160MHZ = 0x20000000, | ||||
|  }; | ||||
|   | ||||
|  enum wmi_tlv_tag { | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi.c | ||||
| @@ -1576,6 +1576,7 @@ static const struct wmi_peer_flags_map w | ||||
|  	.bw80 = WMI_PEER_80MHZ, | ||||
|  	.vht_2g = WMI_PEER_VHT_2G, | ||||
|  	.pmf = WMI_PEER_PMF, | ||||
| +	.bw160 = WMI_PEER_160MHZ, | ||||
|  }; | ||||
|   | ||||
|  static const struct wmi_peer_flags_map wmi_10x_peer_flags_map = { | ||||
| @@ -1593,6 +1594,7 @@ static const struct wmi_peer_flags_map w | ||||
|  	.spatial_mux = WMI_10X_PEER_SPATIAL_MUX, | ||||
|  	.vht = WMI_10X_PEER_VHT, | ||||
|  	.bw80 = WMI_10X_PEER_80MHZ, | ||||
| +	.bw160 = WMI_10X_PEER_160MHZ, | ||||
|  }; | ||||
|   | ||||
|  static const struct wmi_peer_flags_map wmi_10_2_peer_flags_map = { | ||||
| @@ -1612,6 +1614,7 @@ static const struct wmi_peer_flags_map w | ||||
|  	.bw80 = WMI_10_2_PEER_80MHZ, | ||||
|  	.vht_2g = WMI_10_2_PEER_VHT_2G, | ||||
|  	.pmf = WMI_10_2_PEER_PMF, | ||||
| +	.bw160 = WMI_10_2_PEER_160MHZ, | ||||
|  }; | ||||
|   | ||||
|  void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, | ||||
| @@ -1636,7 +1639,10 @@ void ath10k_wmi_put_wmi_channel(struct w | ||||
|   | ||||
|  	ch->mhz = __cpu_to_le32(arg->freq); | ||||
|  	ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1); | ||||
| -	ch->band_center_freq2 = 0; | ||||
| +	if (arg->mode == MODE_11AC_VHT80_80) | ||||
| +		ch->band_center_freq2 = __cpu_to_le32(arg->band_center_freq2); | ||||
| +	else | ||||
| +		ch->band_center_freq2 = 0; | ||||
|  	ch->min_power = arg->min_power; | ||||
|  	ch->max_power = arg->max_power; | ||||
|  	ch->reg_power = arg->max_reg_power; | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi.h | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi.h | ||||
| @@ -1728,8 +1728,10 @@ enum wmi_phy_mode { | ||||
|  	MODE_11AC_VHT20_2G = 11, | ||||
|  	MODE_11AC_VHT40_2G = 12, | ||||
|  	MODE_11AC_VHT80_2G = 13, | ||||
| -	MODE_UNKNOWN    = 14, | ||||
| -	MODE_MAX        = 14 | ||||
| +	MODE_11AC_VHT80_80 = 14, | ||||
| +	MODE_11AC_VHT160 = 15, | ||||
| +	MODE_UNKNOWN    = 16, | ||||
| +	MODE_MAX        = 16 | ||||
|  }; | ||||
|   | ||||
|  static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode) | ||||
| @@ -1757,6 +1759,10 @@ static inline const char *ath10k_wmi_phy | ||||
|  		return "11ac-vht40"; | ||||
|  	case MODE_11AC_VHT80: | ||||
|  		return "11ac-vht80"; | ||||
| +	case MODE_11AC_VHT160: | ||||
| +		return "11ac-vht160"; | ||||
| +	case MODE_11AC_VHT80_80: | ||||
| +		return "11ac-vht80+80"; | ||||
|  	case MODE_11AC_VHT20_2G: | ||||
|  		return "11ac-vht20-2g"; | ||||
|  	case MODE_11AC_VHT40_2G: | ||||
| @@ -1811,6 +1817,7 @@ struct wmi_channel { | ||||
|  struct wmi_channel_arg { | ||||
|  	u32 freq; | ||||
|  	u32 band_center_freq1; | ||||
| +	u32 band_center_freq2; | ||||
|  	bool passive; | ||||
|  	bool allow_ibss; | ||||
|  	bool allow_ht; | ||||
| @@ -1875,9 +1882,18 @@ enum wmi_channel_change_cause { | ||||
|  #define WMI_VHT_CAP_MAX_MPDU_LEN_MASK            0x00000003 | ||||
|  #define WMI_VHT_CAP_RX_LDPC                      0x00000010 | ||||
|  #define WMI_VHT_CAP_SGI_80MHZ                    0x00000020 | ||||
| +#define WMI_VHT_CAP_SGI_160MHZ                   0x00000040 | ||||
|  #define WMI_VHT_CAP_TX_STBC                      0x00000080 | ||||
|  #define WMI_VHT_CAP_RX_STBC_MASK                 0x00000300 | ||||
|  #define WMI_VHT_CAP_RX_STBC_MASK_SHIFT           8 | ||||
| +#define WMI_VHT_CAP_SU_BFER                      0x00000800 | ||||
| +#define WMI_VHT_CAP_SU_BFEE                      0x00001000 | ||||
| +#define WMI_VHT_CAP_MAX_CS_ANT_MASK              0x0000E000 | ||||
| +#define WMI_VHT_CAP_MAX_CS_ANT_MASK_SHIFT        13 | ||||
| +#define WMI_VHT_CAP_MAX_SND_DIM_MASK             0x00070000 | ||||
| +#define WMI_VHT_CAP_MAX_SND_DIM_MASK_SHIFT       16 | ||||
| +#define WMI_VHT_CAP_MU_BFER                      0x00080000 | ||||
| +#define WMI_VHT_CAP_MU_BFEE                      0x00100000 | ||||
|  #define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP            0x03800000 | ||||
|  #define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT      23 | ||||
|  #define WMI_VHT_CAP_RX_FIXED_ANT                 0x10000000 | ||||
| @@ -1926,6 +1942,8 @@ enum { | ||||
|  	REGDMN_MODE_11AC_VHT40PLUS   = 0x40000, /* 5Ghz, VHT40 + channels */ | ||||
|  	REGDMN_MODE_11AC_VHT40MINUS  = 0x80000, /* 5Ghz  VHT40 - channels */ | ||||
|  	REGDMN_MODE_11AC_VHT80       = 0x100000, /* 5Ghz, VHT80 channels */ | ||||
| +	REGDMN_MODE_11AC_VHT160      = 0x200000,     /* 5Ghz, VHT160 channels */ | ||||
| +	REGDMN_MODE_11AC_VHT80_80    = 0x400000,     /* 5Ghz, VHT80+80 channels */ | ||||
|  	REGDMN_MODE_ALL              = 0xffffffff | ||||
|  }; | ||||
|   | ||||
| @@ -5769,6 +5787,7 @@ enum wmi_peer_chwidth { | ||||
|  	WMI_PEER_CHWIDTH_20MHZ = 0, | ||||
|  	WMI_PEER_CHWIDTH_40MHZ = 1, | ||||
|  	WMI_PEER_CHWIDTH_80MHZ = 2, | ||||
| +	WMI_PEER_CHWIDTH_160MHZ = 3, | ||||
|  }; | ||||
|   | ||||
|  enum wmi_peer_param { | ||||
| @@ -5859,6 +5878,7 @@ struct wmi_peer_flags_map { | ||||
|  	u32 bw80; | ||||
|  	u32 vht_2g; | ||||
|  	u32 pmf; | ||||
| +	u32 bw160; | ||||
|  }; | ||||
|   | ||||
|  enum wmi_peer_flags { | ||||
| @@ -5878,6 +5898,7 @@ enum wmi_peer_flags { | ||||
|  	WMI_PEER_80MHZ = 0x04000000, | ||||
|  	WMI_PEER_VHT_2G = 0x08000000, | ||||
|  	WMI_PEER_PMF = 0x10000000, | ||||
| +	WMI_PEER_160MHZ = 0x20000000 | ||||
|  }; | ||||
|   | ||||
|  enum wmi_10x_peer_flags { | ||||
| @@ -5895,6 +5916,7 @@ enum wmi_10x_peer_flags { | ||||
|  	WMI_10X_PEER_SPATIAL_MUX = 0x00200000, | ||||
|  	WMI_10X_PEER_VHT = 0x02000000, | ||||
|  	WMI_10X_PEER_80MHZ = 0x04000000, | ||||
| +	WMI_10X_PEER_160MHZ = 0x20000000 | ||||
|  }; | ||||
|   | ||||
|  enum wmi_10_2_peer_flags { | ||||
| @@ -5914,6 +5936,7 @@ enum wmi_10_2_peer_flags { | ||||
|  	WMI_10_2_PEER_80MHZ = 0x04000000, | ||||
|  	WMI_10_2_PEER_VHT_2G = 0x08000000, | ||||
|  	WMI_10_2_PEER_PMF = 0x10000000, | ||||
| +	WMI_10_2_PEER_160MHZ = 0x20000000 | ||||
|  }; | ||||
|   | ||||
|  /* | ||||
| @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| 
 | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| @@ -945,7 +945,8 @@ ath_tx_get_tid_subframe(struct ath_softc
 | ||||
| @@ -1004,7 +1004,8 @@ ath_tx_get_tid_subframe(struct ath_softc
 | ||||
|  		bf->bf_lastbf = bf; | ||||
|   | ||||
|  		tx_info = IEEE80211_SKB_CB(skb); | ||||
| @@ -1,275 +0,0 @@ | ||||
| From: Herbert Xu <herbert@gondor.apana.org.au> | ||||
| Date: Mon, 19 Sep 2016 19:00:10 +0800 | ||||
| Subject: [PATCH] mac80211: Use rhltable instead of rhashtable | ||||
|  | ||||
| mac80211 currently uses rhashtable with insecure_elasticity set | ||||
| to true.  The latter is because of duplicate objects.  What's | ||||
| more, mac80211 walks the rhashtable chains by hand which is broken | ||||
| as rhashtable may contain multiple tables due to resizing or | ||||
| rehashing. | ||||
|  | ||||
| This patch fixes it by converting it to the newly added rhltable | ||||
| interface which is designed for use with duplicate objects. | ||||
|  | ||||
| With rhltable a lookup returns a list of objects instead of a | ||||
| single one.  This is then fed into the existing for_each_sta_info | ||||
| macro. | ||||
|  | ||||
| This patch also deletes the sta_addr_hash function since rhashtable | ||||
| defaults to jhash. | ||||
|  | ||||
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/ieee80211_i.h | ||||
| +++ b/net/mac80211/ieee80211_i.h | ||||
| @@ -1233,7 +1233,7 @@ struct ieee80211_local { | ||||
|  	spinlock_t tim_lock; | ||||
|  	unsigned long num_sta; | ||||
|  	struct list_head sta_list; | ||||
| -	struct rhashtable sta_hash; | ||||
| +	struct rhltable sta_hash; | ||||
|  	struct timer_list sta_cleanup; | ||||
|  	int sta_generation; | ||||
|   | ||||
| --- a/net/mac80211/rx.c | ||||
| +++ b/net/mac80211/rx.c | ||||
| @@ -4004,7 +4004,7 @@ static void __ieee80211_rx_handle_packet | ||||
|  	__le16 fc; | ||||
|  	struct ieee80211_rx_data rx; | ||||
|  	struct ieee80211_sub_if_data *prev; | ||||
| -	struct rhash_head *tmp; | ||||
| +	struct rhlist_head *tmp; | ||||
|  	int err = 0; | ||||
|   | ||||
|  	fc = ((struct ieee80211_hdr *)skb->data)->frame_control; | ||||
| @@ -4047,13 +4047,10 @@ static void __ieee80211_rx_handle_packet | ||||
|  		goto out; | ||||
|  	} else if (ieee80211_is_data(fc)) { | ||||
|  		struct sta_info *sta, *prev_sta; | ||||
| -		const struct bucket_table *tbl; | ||||
|   | ||||
|  		prev_sta = NULL; | ||||
|   | ||||
| -		tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); | ||||
| - | ||||
| -		for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) { | ||||
| +		for_each_sta_info(local, hdr->addr2, sta, tmp) { | ||||
|  			if (!prev_sta) { | ||||
|  				prev_sta = sta; | ||||
|  				continue; | ||||
| --- a/net/mac80211/sta_info.c | ||||
| +++ b/net/mac80211/sta_info.c | ||||
| @@ -67,12 +67,10 @@ | ||||
|   | ||||
|  static const struct rhashtable_params sta_rht_params = { | ||||
|  	.nelem_hint = 3, /* start small */ | ||||
| -	.insecure_elasticity = true, /* Disable chain-length checks. */ | ||||
|  	.automatic_shrinking = true, | ||||
|  	.head_offset = offsetof(struct sta_info, hash_node), | ||||
|  	.key_offset = offsetof(struct sta_info, addr), | ||||
|  	.key_len = ETH_ALEN, | ||||
| -	.hashfn = sta_addr_hash, | ||||
|  	.max_size = CPTCFG_MAC80211_STA_HASH_MAX_SIZE, | ||||
|  }; | ||||
|   | ||||
| @@ -80,8 +78,8 @@ static const struct rhashtable_params st | ||||
|  static int sta_info_hash_del(struct ieee80211_local *local, | ||||
|  			     struct sta_info *sta) | ||||
|  { | ||||
| -	return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node, | ||||
| -				      sta_rht_params); | ||||
| +	return rhltable_remove(&local->sta_hash, &sta->hash_node, | ||||
| +			       sta_rht_params); | ||||
|  } | ||||
|   | ||||
|  static void __cleanup_single_sta(struct sta_info *sta) | ||||
| @@ -157,19 +155,22 @@ static void cleanup_single_sta(struct st | ||||
|  	sta_info_free(local, sta); | ||||
|  } | ||||
|   | ||||
| +struct rhlist_head *sta_info_hash_lookup(struct ieee80211_local *local, | ||||
| +					 const u8 *addr) | ||||
| +{ | ||||
| +	return rhltable_lookup(&local->sta_hash, addr, sta_rht_params); | ||||
| +} | ||||
| + | ||||
|  /* protected by RCU */ | ||||
|  struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, | ||||
|  			      const u8 *addr) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
| +	struct rhlist_head *tmp; | ||||
|  	struct sta_info *sta; | ||||
| -	struct rhash_head *tmp; | ||||
| -	const struct bucket_table *tbl; | ||||
|   | ||||
|  	rcu_read_lock(); | ||||
| -	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); | ||||
| - | ||||
| -	for_each_sta_info(local, tbl, addr, sta, tmp) { | ||||
| +	for_each_sta_info(local, addr, sta, tmp) { | ||||
|  		if (sta->sdata == sdata) { | ||||
|  			rcu_read_unlock(); | ||||
|  			/* this is safe as the caller must already hold | ||||
| @@ -190,14 +191,11 @@ struct sta_info *sta_info_get_bss(struct | ||||
|  				  const u8 *addr) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
| +	struct rhlist_head *tmp; | ||||
|  	struct sta_info *sta; | ||||
| -	struct rhash_head *tmp; | ||||
| -	const struct bucket_table *tbl; | ||||
|   | ||||
|  	rcu_read_lock(); | ||||
| -	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); | ||||
| - | ||||
| -	for_each_sta_info(local, tbl, addr, sta, tmp) { | ||||
| +	for_each_sta_info(local, addr, sta, tmp) { | ||||
|  		if (sta->sdata == sdata || | ||||
|  		    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) { | ||||
|  			rcu_read_unlock(); | ||||
| @@ -263,8 +261,8 @@ void sta_info_free(struct ieee80211_loca | ||||
|  static int sta_info_hash_add(struct ieee80211_local *local, | ||||
|  			     struct sta_info *sta) | ||||
|  { | ||||
| -	return rhashtable_insert_fast(&local->sta_hash, &sta->hash_node, | ||||
| -				      sta_rht_params); | ||||
| +	return rhltable_insert(&local->sta_hash, &sta->hash_node, | ||||
| +			       sta_rht_params); | ||||
|  } | ||||
|   | ||||
|  static void sta_deliver_ps_frames(struct work_struct *wk) | ||||
| @@ -453,9 +451,9 @@ static int sta_info_insert_check(struct | ||||
|  		    is_multicast_ether_addr(sta->sta.addr))) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	/* Strictly speaking this isn't necessary as we hold the mutex, but | ||||
| -	 * the rhashtable code can't really deal with that distinction. We | ||||
| -	 * do require the mutex for correctness though. | ||||
| +	/* The RCU read lock is required by rhashtable due to | ||||
| +	 * asynchronous resize/rehash.  We also require the mutex | ||||
| +	 * for correctness. | ||||
|  	 */ | ||||
|  	rcu_read_lock(); | ||||
|  	lockdep_assert_held(&sdata->local->sta_mtx); | ||||
| @@ -1043,16 +1041,11 @@ static void sta_info_cleanup(unsigned lo | ||||
|  		  round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); | ||||
|  } | ||||
|   | ||||
| -u32 sta_addr_hash(const void *key, u32 length, u32 seed) | ||||
| -{ | ||||
| -	return jhash(key, ETH_ALEN, seed); | ||||
| -} | ||||
| - | ||||
|  int sta_info_init(struct ieee80211_local *local) | ||||
|  { | ||||
|  	int err; | ||||
|   | ||||
| -	err = rhashtable_init(&local->sta_hash, &sta_rht_params); | ||||
| +	err = rhltable_init(&local->sta_hash, &sta_rht_params); | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| @@ -1068,7 +1061,7 @@ int sta_info_init(struct ieee80211_local | ||||
|  void sta_info_stop(struct ieee80211_local *local) | ||||
|  { | ||||
|  	del_timer_sync(&local->sta_cleanup); | ||||
| -	rhashtable_destroy(&local->sta_hash); | ||||
| +	rhltable_destroy(&local->sta_hash); | ||||
|  } | ||||
|   | ||||
|   | ||||
| @@ -1138,17 +1131,14 @@ struct ieee80211_sta *ieee80211_find_sta | ||||
|  						   const u8 *localaddr) | ||||
|  { | ||||
|  	struct ieee80211_local *local = hw_to_local(hw); | ||||
| +	struct rhlist_head *tmp; | ||||
|  	struct sta_info *sta; | ||||
| -	struct rhash_head *tmp; | ||||
| -	const struct bucket_table *tbl; | ||||
| - | ||||
| -	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); | ||||
|   | ||||
|  	/* | ||||
|  	 * Just return a random station if localaddr is NULL | ||||
|  	 * ... first in list. | ||||
|  	 */ | ||||
| -	for_each_sta_info(local, tbl, addr, sta, tmp) { | ||||
| +	for_each_sta_info(local, addr, sta, tmp) { | ||||
|  		if (localaddr && | ||||
|  		    !ether_addr_equal(sta->sdata->vif.addr, localaddr)) | ||||
|  			continue; | ||||
| --- a/net/mac80211/sta_info.h | ||||
| +++ b/net/mac80211/sta_info.h | ||||
| @@ -455,7 +455,7 @@ struct sta_info { | ||||
|  	/* General information, mostly static */ | ||||
|  	struct list_head list, free_list; | ||||
|  	struct rcu_head rcu_head; | ||||
| -	struct rhash_head hash_node; | ||||
| +	struct rhlist_head hash_node; | ||||
|  	u8 addr[ETH_ALEN]; | ||||
|  	struct ieee80211_local *local; | ||||
|  	struct ieee80211_sub_if_data *sdata; | ||||
| @@ -638,6 +638,9 @@ rcu_dereference_protected_tid_tx(struct | ||||
|   */ | ||||
|  #define STA_INFO_CLEANUP_INTERVAL (10 * HZ) | ||||
|   | ||||
| +struct rhlist_head *sta_info_hash_lookup(struct ieee80211_local *local, | ||||
| +					 const u8 *addr); | ||||
| + | ||||
|  /* | ||||
|   * Get a STA info, must be under RCU read lock. | ||||
|   */ | ||||
| @@ -647,17 +650,9 @@ struct sta_info *sta_info_get(struct iee | ||||
|  struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, | ||||
|  				  const u8 *addr); | ||||
|   | ||||
| -u32 sta_addr_hash(const void *key, u32 length, u32 seed); | ||||
| - | ||||
| -#define _sta_bucket_idx(_tbl, _a)					\ | ||||
| -	rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd)) | ||||
| - | ||||
| -#define for_each_sta_info(local, tbl, _addr, _sta, _tmp)		\ | ||||
| -	rht_for_each_entry_rcu(_sta, _tmp, tbl, 			\ | ||||
| -			       _sta_bucket_idx(tbl, _addr),		\ | ||||
| -			       hash_node)				\ | ||||
| -	/* compare address and run code only if it matches */		\ | ||||
| -	if (ether_addr_equal(_sta->addr, (_addr))) | ||||
| +#define for_each_sta_info(local, _addr, _sta, _tmp)			\ | ||||
| +	rhl_for_each_entry_rcu(_sta, _tmp,				\ | ||||
| +			       sta_info_hash_lookup(local, _addr), hash_node) | ||||
|   | ||||
|  /* | ||||
|   * Get STA info by index, BROKEN! | ||||
| --- a/net/mac80211/status.c | ||||
| +++ b/net/mac80211/status.c | ||||
| @@ -759,8 +759,8 @@ void ieee80211_tx_status(struct ieee8021 | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	__le16 fc; | ||||
|  	struct ieee80211_supported_band *sband; | ||||
| +	struct rhlist_head *tmp; | ||||
|  	struct sta_info *sta; | ||||
| -	struct rhash_head *tmp; | ||||
|  	int retry_count; | ||||
|  	int rates_idx; | ||||
|  	bool send_to_cooked; | ||||
| @@ -768,7 +768,6 @@ void ieee80211_tx_status(struct ieee8021 | ||||
|  	struct ieee80211_bar *bar; | ||||
|  	int shift = 0; | ||||
|  	int tid = IEEE80211_NUM_TIDS; | ||||
| -	const struct bucket_table *tbl; | ||||
|   | ||||
|  	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); | ||||
|   | ||||
| @@ -777,9 +776,7 @@ void ieee80211_tx_status(struct ieee8021 | ||||
|  	sband = local->hw.wiphy->bands[info->band]; | ||||
|  	fc = hdr->frame_control; | ||||
|   | ||||
| -	tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); | ||||
| - | ||||
| -	for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) { | ||||
| +	for_each_sta_info(local, hdr->addr1, sta, tmp) { | ||||
|  		/* skip wrong virtual interface */ | ||||
|  		if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) | ||||
|  			continue; | ||||
| @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, | ||||
|  					   struct ath_txq *txq, | ||||
|  					   struct ath_atx_tid *tid, | ||||
| @@ -300,7 +300,7 @@ static void ath_tx_flush_tid(struct ath_
 | ||||
| @@ -311,7 +311,7 @@ static void ath_tx_flush_tid(struct ath_
 | ||||
|  		} | ||||
|   | ||||
|  		if (fi->baw_tracked) { | ||||
| @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  			sendbar = true; | ||||
|  		} | ||||
|   | ||||
| @@ -316,10 +316,15 @@ static void ath_tx_flush_tid(struct ath_
 | ||||
| @@ -327,10 +327,15 @@ static void ath_tx_flush_tid(struct ath_
 | ||||
|  } | ||||
|   | ||||
|  static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, | ||||
| @@ -47,7 +47,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  	index  = ATH_BA_INDEX(tid->seq_start, seqno); | ||||
|  	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); | ||||
|   | ||||
| @@ -340,6 +345,9 @@ static void ath_tx_addto_baw(struct ath_
 | ||||
| @@ -351,6 +356,9 @@ static void ath_tx_addto_baw(struct ath_
 | ||||
|  	u16 seqno = bf->bf_state.seqno; | ||||
|  	int index, cindex; | ||||
|   | ||||
| @@ -57,7 +57,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  	index  = ATH_BA_INDEX(tid->seq_start, seqno); | ||||
|  	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); | ||||
|  	__set_bit(cindex, tid->tx_buf); | ||||
| @@ -616,7 +624,7 @@ static void ath_tx_complete_aggr(struct
 | ||||
| @@ -627,7 +635,7 @@ static void ath_tx_complete_aggr(struct
 | ||||
|  			 * complete the acked-ones/xretried ones; update | ||||
|  			 * block-ack window | ||||
|  			 */ | ||||
| @@ -66,7 +66,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|   | ||||
|  			if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { | ||||
|  				memcpy(tx_info->control.rates, rates, sizeof(rates)); | ||||
| @@ -646,7 +654,7 @@ static void ath_tx_complete_aggr(struct
 | ||||
| @@ -657,7 +665,7 @@ static void ath_tx_complete_aggr(struct
 | ||||
|  				 * run out of tx buf. | ||||
|  				 */ | ||||
|  				if (!tbf) { | ||||
| @@ -75,7 +75,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|   | ||||
|  					ath_tx_complete_buf(sc, bf, txq, | ||||
|  							    &bf_head, NULL, ts, | ||||
| @@ -987,11 +995,14 @@ ath_tx_get_tid_subframe(struct ath_softc
 | ||||
| @@ -1046,11 +1054,14 @@ ath_tx_get_tid_subframe(struct ath_softc
 | ||||
|   | ||||
|  			INIT_LIST_HEAD(&bf_head); | ||||
|  			list_add(&bf->list, &bf_head); | ||||
| @@ -91,7 +91,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  		return bf; | ||||
|  	} | ||||
|   | ||||
| @@ -1049,8 +1060,6 @@ ath_tx_form_aggr(struct ath_softc *sc, s
 | ||||
| @@ -1108,8 +1119,6 @@ ath_tx_form_aggr(struct ath_softc *sc, s
 | ||||
|  		bf->bf_next = NULL; | ||||
|   | ||||
|  		/* link buffers of this frame to the aggregate */ | ||||
| @@ -100,7 +100,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  		bf->bf_state.ndelim = ndelim; | ||||
|   | ||||
|  		list_add_tail(&bf->list, bf_q); | ||||
| @@ -1686,10 +1695,8 @@ void ath9k_release_buffered_frames(struc
 | ||||
| @@ -1745,10 +1754,8 @@ void ath9k_release_buffered_frames(struc
 | ||||
|  			ath9k_set_moredata(sc, bf, true); | ||||
|  			list_add_tail(&bf->list, &bf_q); | ||||
|  			ath_set_rates(tid->an->vif, tid->an->sta, bf, true); | ||||
| @@ -1,37 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Tue, 11 Oct 2016 11:24:07 +0200 | ||||
| Subject: [PATCH] mac80211: fix sequence number allocation regression | ||||
|  | ||||
| The recent commit that moved around TX handlers dropped the sequence | ||||
| number allocation at the end of ieee80211_tx_dequeue and calls | ||||
| ieee80211_tx_h_sequence instead (for the non-fast-xmit case). | ||||
| However, it did not change the fast-xmit sequence allocation condition | ||||
| in ieee80211_xmit_fast_finish, which skipped seqno alloc if intermediate | ||||
| tx queues are being used. | ||||
|  | ||||
| Drop the now obsolete condition. | ||||
|  | ||||
| Fixes: bb42f2d13ffc ("mac80211: Move reorder-sensitive TX handlers to after TXQ dequeue") | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3212,7 +3212,6 @@ static void ieee80211_xmit_fast_finish(s | ||||
|  				       struct sk_buff *skb) | ||||
|  { | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| -	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_hdr *hdr = (void *)skb->data; | ||||
|  	u8 tid = IEEE80211_NUM_TIDS; | ||||
|   | ||||
| @@ -3224,8 +3223,7 @@ static void ieee80211_xmit_fast_finish(s | ||||
|  	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { | ||||
|  		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; | ||||
|  		*ieee80211_get_qos_ctl(hdr) = tid; | ||||
| -		if (!ieee80211_get_txq(local, &sdata->vif, &sta->sta, skb)) | ||||
| -			hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid); | ||||
| +		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid); | ||||
|  	} else { | ||||
|  		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; | ||||
|  		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number); | ||||
| @@ -164,7 +164,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| @@ -2915,8 +2915,6 @@ int ath_tx_init(struct ath_softc *sc, in
 | ||||
| @@ -2916,8 +2916,6 @@ int ath_tx_init(struct ath_softc *sc, in
 | ||||
|  		return error; | ||||
|  	} | ||||
|   | ||||
| @@ -1,101 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Tue, 11 Oct 2016 19:45:41 +0200 | ||||
| Subject: [PATCH] Revert "ath9k_hw: implement temperature compensation support | ||||
|  for AR9003+" | ||||
|  | ||||
| This reverts commit 171f6402e4aa5cd3b8407f82501f7ea21fa54ccc. | ||||
| Some users report that this commit causes a regression in performance | ||||
| under some conditions. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c | ||||
| @@ -33,7 +33,6 @@ struct coeff { | ||||
|   | ||||
|  enum ar9003_cal_types { | ||||
|  	IQ_MISMATCH_CAL = BIT(0), | ||||
| -	TEMP_COMP_CAL = BIT(1), | ||||
|  }; | ||||
|   | ||||
|  static void ar9003_hw_setup_calibration(struct ath_hw *ah, | ||||
| @@ -59,12 +58,6 @@ static void ar9003_hw_setup_calibration( | ||||
|  		/* Kick-off cal */ | ||||
|  		REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); | ||||
|  		break; | ||||
| -	case TEMP_COMP_CAL: | ||||
| -		ath_dbg(common, CALIBRATE, | ||||
| -			"starting Temperature Compensation Calibration\n"); | ||||
| -		REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_LOCAL); | ||||
| -		REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_START); | ||||
| -		break; | ||||
|  	default: | ||||
|  		ath_err(common, "Invalid calibration type\n"); | ||||
|  		break; | ||||
| @@ -93,8 +86,7 @@ static bool ar9003_hw_per_calibration(st | ||||
|  		/* | ||||
|  		* Accumulate cal measures for active chains | ||||
|  		*/ | ||||
| -		if (cur_caldata->calCollect) | ||||
| -			cur_caldata->calCollect(ah); | ||||
| +		cur_caldata->calCollect(ah); | ||||
|  		ah->cal_samples++; | ||||
|   | ||||
|  		if (ah->cal_samples >= cur_caldata->calNumSamples) { | ||||
| @@ -107,8 +99,7 @@ static bool ar9003_hw_per_calibration(st | ||||
|  			/* | ||||
|  			* Process accumulated data | ||||
|  			*/ | ||||
| -			if (cur_caldata->calPostProc) | ||||
| -				cur_caldata->calPostProc(ah, numChains); | ||||
| +			cur_caldata->calPostProc(ah, numChains); | ||||
|   | ||||
|  			/* Calibration has finished. */ | ||||
|  			caldata->CalValid |= cur_caldata->calType; | ||||
| @@ -323,16 +314,9 @@ static const struct ath9k_percal_data iq | ||||
|  	ar9003_hw_iqcalibrate | ||||
|  }; | ||||
|   | ||||
| -static const struct ath9k_percal_data temp_cal_single_sample = { | ||||
| -	TEMP_COMP_CAL, | ||||
| -	MIN_CAL_SAMPLES, | ||||
| -	PER_MAX_LOG_COUNT, | ||||
| -}; | ||||
| - | ||||
|  static void ar9003_hw_init_cal_settings(struct ath_hw *ah) | ||||
|  { | ||||
|  	ah->iq_caldata.calData = &iq_cal_single_sample; | ||||
| -	ah->temp_caldata.calData = &temp_cal_single_sample; | ||||
|   | ||||
|  	if (AR_SREV_9300_20_OR_LATER(ah)) { | ||||
|  		ah->enabled_cals |= TX_IQ_CAL; | ||||
| @@ -340,7 +324,7 @@ static void ar9003_hw_init_cal_settings( | ||||
|  			ah->enabled_cals |= TX_IQ_ON_AGC_CAL; | ||||
|  	} | ||||
|   | ||||
| -	ah->supp_cals = IQ_MISMATCH_CAL | TEMP_COMP_CAL; | ||||
| +	ah->supp_cals = IQ_MISMATCH_CAL; | ||||
|  } | ||||
|   | ||||
|  #define OFF_UPPER_LT 24 | ||||
| @@ -1399,9 +1383,6 @@ static void ar9003_hw_init_cal_common(st | ||||
|  	INIT_CAL(&ah->iq_caldata); | ||||
|  	INSERT_CAL(ah, &ah->iq_caldata); | ||||
|   | ||||
| -	INIT_CAL(&ah->temp_caldata); | ||||
| -	INSERT_CAL(ah, &ah->temp_caldata); | ||||
| - | ||||
|  	/* Initialize current pointer to first element in list */ | ||||
|  	ah->cal_list_curr = ah->cal_list; | ||||
|   | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.h | ||||
| @@ -830,7 +830,6 @@ struct ath_hw { | ||||
|  	/* Calibration */ | ||||
|  	u32 supp_cals; | ||||
|  	struct ath9k_cal_list iq_caldata; | ||||
| -	struct ath9k_cal_list temp_caldata; | ||||
|  	struct ath9k_cal_list adcgain_caldata; | ||||
|  	struct ath9k_cal_list adcdc_caldata; | ||||
|  	struct ath9k_cal_list *cal_list; | ||||
| @@ -0,0 +1,73 @@ | ||||
| From: Stanislaw Gruszka <sgruszka@redhat.com> | ||||
| Date: Thu, 2 Feb 2017 10:57:40 +0100 | ||||
| Subject: [PATCH] rt2x00: avoid introducing a USB dependency in the | ||||
|  rt2x00lib module | ||||
|  | ||||
| As reported by Felix: | ||||
|  | ||||
| Though protected by an ifdef, introducing an usb symbol dependency in | ||||
| the rt2x00lib module is a major inconvenience for distributions that | ||||
| package kernel modules split into individual packages. | ||||
|  | ||||
| Get rid of this unnecessary dependency by calling the usb related | ||||
| function from a more suitable place. | ||||
|  | ||||
| Cc: Vishal Thanki <vishalthanki@gmail.com> | ||||
| Reported-by: Felix Fietkau <nbd@nbd.name> | ||||
| Fixes: 8b4c0009313f ("rt2x00usb: Use usb anchor to manage URB") | ||||
| Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | ||||
| +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | ||||
| @@ -1436,21 +1436,6 @@ void rt2x00lib_remove_dev(struct rt2x00_ | ||||
|  	cancel_work_sync(&rt2x00dev->intf_work); | ||||
|  	cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); | ||||
|  	cancel_work_sync(&rt2x00dev->sleep_work); | ||||
| -#if IS_ENABLED(CPTCFG_RT2X00_LIB_USB) | ||||
| -	if (rt2x00_is_usb(rt2x00dev)) { | ||||
| -		usb_kill_anchored_urbs(rt2x00dev->anchor); | ||||
| -		hrtimer_cancel(&rt2x00dev->txstatus_timer); | ||||
| -		cancel_work_sync(&rt2x00dev->rxdone_work); | ||||
| -		cancel_work_sync(&rt2x00dev->txdone_work); | ||||
| -	} | ||||
| -#endif | ||||
| -	if (rt2x00dev->workqueue) | ||||
| -		destroy_workqueue(rt2x00dev->workqueue); | ||||
| - | ||||
| -	/* | ||||
| -	 * Free the tx status fifo. | ||||
| -	 */ | ||||
| -	kfifo_free(&rt2x00dev->txstatus_fifo); | ||||
|   | ||||
|  	/* | ||||
|  	 * Kill the tx status tasklet. | ||||
| @@ -1466,6 +1451,14 @@ void rt2x00lib_remove_dev(struct rt2x00_ | ||||
|  	 */ | ||||
|  	rt2x00lib_uninitialize(rt2x00dev); | ||||
|   | ||||
| +	if (rt2x00dev->workqueue) | ||||
| +		destroy_workqueue(rt2x00dev->workqueue); | ||||
| + | ||||
| +	/* | ||||
| +	 * Free the tx status fifo. | ||||
| +	 */ | ||||
| +	kfifo_free(&rt2x00dev->txstatus_fifo); | ||||
| + | ||||
|  	/* | ||||
|  	 * Free extra components | ||||
|  	 */ | ||||
| --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c | ||||
| +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c | ||||
| @@ -744,6 +744,11 @@ void rt2x00usb_uninitialize(struct rt2x0 | ||||
|  { | ||||
|  	struct data_queue *queue; | ||||
|   | ||||
| +	usb_kill_anchored_urbs(rt2x00dev->anchor); | ||||
| +	hrtimer_cancel(&rt2x00dev->txstatus_timer); | ||||
| +	cancel_work_sync(&rt2x00dev->rxdone_work); | ||||
| +	cancel_work_sync(&rt2x00dev->txdone_work); | ||||
| + | ||||
|  	queue_for_each(rt2x00dev, queue) | ||||
|  		rt2x00usb_free_entries(queue); | ||||
|  } | ||||
| @@ -1,4 +1,3 @@ | ||||
| From f4737a62033d7f3e0db740c449fc62119da7ab8a Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Mon, 30 Jan 2017 16:09:51 +0100 | ||||
| Subject: [PATCH] brcmfmac: check brcmf_bus_get_memdump result for error | ||||
| @@ -15,8 +14,6 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| ---
 | ||||
|  .../wireless/broadcom/brcm80211/brcmfmac/debug.c   | 23 +++++++++++++++------- | ||||
|  1 file changed, 16 insertions(+), 7 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
 | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
 | ||||
| @@ -1,35 +0,0 @@ | ||||
| From: Bob Copeland <me@bobcopeland.com> | ||||
| Date: Wed, 12 Oct 2016 08:24:54 -0400 | ||||
| Subject: [PATCH] mac80211: fix up mismerge of ieee80211_tx_dequeue | ||||
|  | ||||
| Looks like this spinlock wound up on the wrong side of the | ||||
| linearize, and I also lost the part that re-enters the loop. | ||||
| Fix up to match mac80211-next. | ||||
|  | ||||
| Signed-off-by: Bob Copeland <me@bobcopeland.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3457,17 +3457,17 @@ begin: | ||||
|  			skb_queue_splice_tail(&tx.skbs, &txqi->frags); | ||||
|  	} | ||||
|   | ||||
| -out: | ||||
| -	spin_unlock_bh(&fq->lock); | ||||
| - | ||||
|  	if (skb && skb_has_frag_list(skb) && | ||||
|  	    !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) { | ||||
|  		if (skb_linearize(skb)) { | ||||
|  			ieee80211_free_txskb(&local->hw, skb); | ||||
| -			return NULL; | ||||
| +			goto begin; | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +out: | ||||
| +	spin_unlock_bh(&fq->lock); | ||||
| + | ||||
|  	return skb; | ||||
|  } | ||||
|  EXPORT_SYMBOL(ieee80211_tx_dequeue); | ||||
| @@ -1,4 +1,3 @@ | ||||
| From 36401cb7ffae731295a6dd1ce2b40d7ad74245f4 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Mon, 30 Jan 2017 16:09:52 +0100 | ||||
| Subject: [PATCH] brcmfmac: be more verbose when PSM's watchdog fires | ||||
| @@ -13,8 +12,6 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| ---
 | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c | 12 ++++++++++-- | ||||
|  1 file changed, 10 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
 | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
 | ||||
| @@ -1,56 +0,0 @@ | ||||
| From: Michael Braun <michael-dev@fami-braun.de> | ||||
| Date: Sat, 15 Oct 2016 13:28:18 +0200 | ||||
| Subject: [PATCH] mac80211: avoid extra memcpy in A-MSDU head creation | ||||
|  | ||||
| Signed-off-by: Michael Braun <michael-dev@fami-braun.de> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3069,11 +3069,11 @@ static bool ieee80211_amsdu_prepare_head | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	struct ieee80211_hdr *hdr; | ||||
| -	struct ethhdr amsdu_hdr; | ||||
| +	struct ethhdr *amsdu_hdr; | ||||
|  	int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header); | ||||
|  	int subframe_len = skb->len - hdr_len; | ||||
|  	void *data; | ||||
| -	u8 *qc; | ||||
| +	u8 *qc, *h_80211_src, *h_80211_dst; | ||||
|   | ||||
|  	if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) | ||||
|  		return false; | ||||
| @@ -3081,19 +3081,22 @@ static bool ieee80211_amsdu_prepare_head | ||||
|  	if (info->control.flags & IEEE80211_TX_CTRL_AMSDU) | ||||
|  		return true; | ||||
|   | ||||
| -	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr), | ||||
| +	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr), | ||||
|  					 &subframe_len)) | ||||
|  		return false; | ||||
|   | ||||
| -	amsdu_hdr.h_proto = cpu_to_be16(subframe_len); | ||||
| -	memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN); | ||||
| -	memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN); | ||||
| - | ||||
| -	data = skb_push(skb, sizeof(amsdu_hdr)); | ||||
| -	memmove(data, data + sizeof(amsdu_hdr), hdr_len); | ||||
| -	memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr)); | ||||
| - | ||||
| +	data = skb_push(skb, sizeof(*amsdu_hdr)); | ||||
| +	memmove(data, data + sizeof(*amsdu_hdr), hdr_len); | ||||
|  	hdr = data; | ||||
| +	amsdu_hdr = data + hdr_len; | ||||
| +	/* h_80211_src/dst is addr* field within hdr */ | ||||
| +	h_80211_src = data + fast_tx->sa_offs; | ||||
| +	h_80211_dst = data + fast_tx->da_offs; | ||||
| + | ||||
| +	amsdu_hdr->h_proto = cpu_to_be16(subframe_len); | ||||
| +	ether_addr_copy(amsdu_hdr->h_source, h_80211_src); | ||||
| +	ether_addr_copy(amsdu_hdr->h_dest, h_80211_dst); | ||||
| + | ||||
|  	qc = ieee80211_get_qos_ctl(hdr); | ||||
|  	*qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; | ||||
|   | ||||
| @@ -1,73 +0,0 @@ | ||||
| From: Michael Braun <michael-dev@fami-braun.de> | ||||
| Date: Sat, 15 Oct 2016 13:28:19 +0200 | ||||
| Subject: [PATCH] mac80211: fix A-MSDU outer SA/DA | ||||
|  | ||||
| According to IEEE 802.11-2012 section 8.3.2 table 8-19, the outer SA/DA | ||||
| of A-MSDU frames need to be changed depending on FromDS/ToDS values. | ||||
|  | ||||
| Signed-off-by: Michael Braun <michael-dev@fami-braun.de> | ||||
| [use ether_addr_copy and add alignment annotations] | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/include/net/mac80211.h | ||||
| +++ b/include/net/mac80211.h | ||||
| @@ -1438,7 +1438,7 @@ enum ieee80211_vif_flags { | ||||
|  struct ieee80211_vif { | ||||
|  	enum nl80211_iftype type; | ||||
|  	struct ieee80211_bss_conf bss_conf; | ||||
| -	u8 addr[ETH_ALEN]; | ||||
| +	u8 addr[ETH_ALEN] __aligned(2); | ||||
|  	bool p2p; | ||||
|  	bool csa_active; | ||||
|  	bool mu_mimo_owner; | ||||
| --- a/net/mac80211/ieee80211_i.h | ||||
| +++ b/net/mac80211/ieee80211_i.h | ||||
| @@ -443,7 +443,7 @@ struct ieee80211_if_managed { | ||||
|  	struct ieee80211_mgd_auth_data *auth_data; | ||||
|  	struct ieee80211_mgd_assoc_data *assoc_data; | ||||
|   | ||||
| -	u8 bssid[ETH_ALEN]; | ||||
| +	u8 bssid[ETH_ALEN] __aligned(2); | ||||
|   | ||||
|  	u16 aid; | ||||
|   | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3074,6 +3074,7 @@ static bool ieee80211_amsdu_prepare_head | ||||
|  	int subframe_len = skb->len - hdr_len; | ||||
|  	void *data; | ||||
|  	u8 *qc, *h_80211_src, *h_80211_dst; | ||||
| +	const u8 *bssid; | ||||
|   | ||||
|  	if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) | ||||
|  		return false; | ||||
| @@ -3097,6 +3098,28 @@ static bool ieee80211_amsdu_prepare_head | ||||
|  	ether_addr_copy(amsdu_hdr->h_source, h_80211_src); | ||||
|  	ether_addr_copy(amsdu_hdr->h_dest, h_80211_dst); | ||||
|   | ||||
| +	/* according to IEEE 802.11-2012 8.3.2 table 8-19, the outer SA/DA | ||||
| +	 * fields needs to be changed to BSSID for A-MSDU frames depending | ||||
| +	 * on FromDS/ToDS values. | ||||
| +	 */ | ||||
| +	switch (sdata->vif.type) { | ||||
| +	case NL80211_IFTYPE_STATION: | ||||
| +		bssid = sdata->u.mgd.bssid; | ||||
| +		break; | ||||
| +	case NL80211_IFTYPE_AP: | ||||
| +	case NL80211_IFTYPE_AP_VLAN: | ||||
| +		bssid = sdata->vif.addr; | ||||
| +		break; | ||||
| +	default: | ||||
| +		bssid = NULL; | ||||
| +	} | ||||
| + | ||||
| +	if (bssid && ieee80211_has_fromds(hdr->frame_control)) | ||||
| +		ether_addr_copy(h_80211_src, bssid); | ||||
| + | ||||
| +	if (bssid && ieee80211_has_tods(hdr->frame_control)) | ||||
| +		ether_addr_copy(h_80211_dst, bssid); | ||||
| + | ||||
|  	qc = ieee80211_get_qos_ctl(hdr); | ||||
|  	*qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; | ||||
|   | ||||
| @@ -1,28 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 3 Nov 2016 12:10:34 +0100 | ||||
| Subject: [PATCH] Revert "mac80211: allow using AP_LINK_PS with | ||||
|  mac80211-generated TIM IE" | ||||
|  | ||||
| This reverts commit c68df2e7be0c1238ea3c281fd744a204ef3b15a0. | ||||
|  | ||||
| __sta_info_recalc_tim turns into a no-op if local->ops->set_tim is not | ||||
| set. This prevents the beacon TIM bit from being set for all drivers | ||||
| that do not implement this op (almost all of them), thus thoroughly | ||||
| essential AP mode powersave functionality. | ||||
|  | ||||
| Cc: Emmanuel Grumbach <emmanuel.grumbach@intel.com> | ||||
| Fixes: c68df2e7be0c ("mac80211: allow using AP_LINK_PS with mac80211-generated TIM IE") | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/sta_info.c | ||||
| +++ b/net/mac80211/sta_info.c | ||||
| @@ -688,7 +688,7 @@ static void __sta_info_recalc_tim(struct | ||||
|  	} | ||||
|   | ||||
|  	/* No need to do anything if the driver does all */ | ||||
| -	if (!local->ops->set_tim) | ||||
| +	if (ieee80211_hw_check(&local->hw, AP_LINK_PS)) | ||||
|  		return; | ||||
|   | ||||
|  	if (sta->dead) | ||||
| @@ -1,30 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Fri, 4 Nov 2016 10:13:34 +0100 | ||||
| Subject: [PATCH] mac80211: update A-MPDU flag on tx dequeue | ||||
|  | ||||
| The sequence number counter is used to derive the starting sequence | ||||
| number. Since that counter is updated on tx dequeue, the A-MPDU flag | ||||
| needs to be up to date at the tme of dequeue as well. | ||||
|  | ||||
| This patch prevents sending more A-MPDU frames after the session has | ||||
| been terminated and also ensures that aggregation starts right after the | ||||
| session has been established | ||||
|  | ||||
| Fixes: bb42f2d13ffc ("mac80211: Move reorder-sensitive TX handlers to after TXQ dequeue") | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3462,6 +3462,11 @@ begin: | ||||
|  		goto begin; | ||||
|  	} | ||||
|   | ||||
| +	if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags)) | ||||
| +		info->flags |= IEEE80211_TX_CTL_AMPDU; | ||||
| +	else | ||||
| +		info->flags &= ~IEEE80211_TX_CTL_AMPDU; | ||||
| + | ||||
|  	if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) { | ||||
|  		struct sta_info *sta = container_of(txq->sta, struct sta_info, | ||||
|  						    sta); | ||||
| @@ -1,29 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Fri, 4 Nov 2016 10:17:38 +0100 | ||||
| Subject: [PATCH] mac80211: remove bogus skb vif assignment | ||||
|  | ||||
| The call to ieee80211_txq_enqueue overwrites the vif pointer with the | ||||
| codel enqueue time, so setting it just before that call makes no sense. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -1500,7 +1500,6 @@ static bool ieee80211_queue_skb(struct i | ||||
|  				struct sta_info *sta, | ||||
|  				struct sk_buff *skb) | ||||
|  { | ||||
| -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
|  	struct fq *fq = &local->fq; | ||||
|  	struct ieee80211_vif *vif; | ||||
|  	struct txq_info *txqi; | ||||
| @@ -1525,8 +1524,6 @@ static bool ieee80211_queue_skb(struct i | ||||
|  	if (!txqi) | ||||
|  		return false; | ||||
|   | ||||
| -	info->control.vif = vif; | ||||
| - | ||||
|  	spin_lock_bh(&fq->lock); | ||||
|  	ieee80211_txq_enqueue(local, txqi, skb); | ||||
|  	spin_unlock_bh(&fq->lock); | ||||
| @@ -1,34 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Fri, 4 Nov 2016 10:18:51 +0100 | ||||
| Subject: [PATCH] mac80211: fix A-MSDU aggregation with fast-xmit + txq | ||||
|  | ||||
| A-MSDU aggregation alters the QoS header after a frame has been | ||||
| enqueued, so it needs to be ready before enqueue and not overwritten | ||||
| again afterwards | ||||
|  | ||||
| Fixes: bb42f2d13ffc ("mac80211: Move reorder-sensitive TX handlers to after TXQ dequeue") | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3245,7 +3245,6 @@ static void ieee80211_xmit_fast_finish(s | ||||
|   | ||||
|  	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { | ||||
|  		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; | ||||
| -		*ieee80211_get_qos_ctl(hdr) = tid; | ||||
|  		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid); | ||||
|  	} else { | ||||
|  		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; | ||||
| @@ -3370,6 +3369,11 @@ static bool ieee80211_xmit_fast(struct i | ||||
|  		      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0); | ||||
|  	info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT; | ||||
|   | ||||
| +	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { | ||||
| +		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; | ||||
| +		*ieee80211_get_qos_ctl(hdr) = tid; | ||||
| +	} | ||||
| + | ||||
|  	__skb_queue_head_init(&tx.skbs); | ||||
|   | ||||
|  	tx.flags = IEEE80211_TX_UNICAST; | ||||
| @@ -1,29 +0,0 @@ | ||||
| From: Matthias Schiffer <mschiffer@universe-factory.net> | ||||
| Date: Tue, 15 Nov 2016 16:08:29 +0100 | ||||
| Subject: [PATCH] ath9k: fix ath9k_hw_gpio_get() to return 0 or 1 on success | ||||
|  | ||||
| Commit b2d70d4944c1 ("ath9k: make GPIO API to support both of WMAC and | ||||
| SOC") refactored ath9k_hw_gpio_get() to support both WMAC and SOC GPIOs, | ||||
| changing the return on success from 1 to BIT(gpio). This broke some callers | ||||
| like ath_is_rfkill_set(). | ||||
|  | ||||
| Instead of fixing all callers, change ath9k_hw_gpio_get() back to only | ||||
| return 0 or 1. | ||||
|  | ||||
| Fixes: b2d70d4944c1 ("ath9k: make GPIO API to support both of WMAC and SOC") | ||||
| Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net> | ||||
| --- | ||||
|  drivers/net/wireless/ath/ath9k/hw.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.c | ||||
| @@ -2813,7 +2813,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, | ||||
|  		WARN_ON(1); | ||||
|  	} | ||||
|   | ||||
| -	return val; | ||||
| +	return !!val; | ||||
|  } | ||||
|  EXPORT_SYMBOL(ath9k_hw_gpio_get); | ||||
|   | ||||
| @@ -1,67 +0,0 @@ | ||||
| From b263e0bb9d4585ca3ec04d7257ca5308d21333bb Mon Sep 17 00:00:00 2001 | ||||
| From: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| Date: Sun, 16 Oct 2016 22:59:05 +0200 | ||||
| Subject: [PATCH 1/3] Documentation: dt: net: add ath9k wireless device binding | ||||
|  | ||||
| Add documentation how devicetree can be used to configure ath9k based | ||||
| devices. | ||||
|  | ||||
| Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| Acked-by: Rob Herring <robh@kernel.org> | ||||
| Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> | ||||
| --- | ||||
|  .../devicetree/bindings/net/wireless/qca,ath9k.txt | 48 ++++++++++++++++++++++ | ||||
|  1 file changed, 48 insertions(+) | ||||
|  create mode 100644 Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt | ||||
|  | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt | ||||
| @@ -0,0 +1,48 @@ | ||||
| +* Qualcomm Atheros ath9k wireless devices | ||||
| + | ||||
| +This node provides properties for configuring the ath9k wireless device. The | ||||
| +node is expected to be specified as a child node of the PCI controller to | ||||
| +which the wireless chip is connected. | ||||
| + | ||||
| +Required properties: | ||||
| +- compatible: For PCI and PCIe devices this should be an identifier following | ||||
| +		the format as defined in "PCI Bus Binding to Open Firmware" | ||||
| +		Revision 2.1. One of the possible formats is "pciVVVV,DDDD" | ||||
| +		where VVVV is the PCI vendor ID and DDDD is PCI device ID. | ||||
| +		Typically QCA's PCI vendor ID 168c is used while the PCI device | ||||
| +		ID depends on the chipset - see the following (possibly | ||||
| +		incomplete) list: | ||||
| +			- 0023 for AR5416 | ||||
| +			- 0024 for AR5418 | ||||
| +			- 0027 for AR9160 | ||||
| +			- 0029 for AR9220 and AR9223 | ||||
| +			- 002a for AR9280 and AR9283 | ||||
| +			- 002b for AR9285 | ||||
| +			- 002c for AR2427 | ||||
| +			- 002d for AR9227 | ||||
| +			- 002e for AR9287 | ||||
| +			- 0030 for AR9380, AR9381 and AR9382 | ||||
| +			- 0032 for AR9485 | ||||
| +			- 0033 for AR9580 and AR9590 | ||||
| +			- 0034 for AR9462 | ||||
| +			- 0036 for AR9565 | ||||
| +			- 0037 for AR9485 | ||||
| +- reg: Address and length of the register set for the device. | ||||
| + | ||||
| +Optional properties: | ||||
| +- qca,no-eeprom: Indicates that there is no physical EEPROM connected to the | ||||
| +			ath9k wireless chip (in this case the calibration / | ||||
| +			EEPROM data will be loaded from userspace using the | ||||
| +			kernel firmware loader). | ||||
| +- mac-address: See ethernet.txt in the parent directory | ||||
| +- local-mac-address: See ethernet.txt in the parent directory | ||||
| + | ||||
| + | ||||
| +In this example, the node is defined as child node of the PCI controller: | ||||
| +&pci0 { | ||||
| +	wifi@168c,002d { | ||||
| +		compatible = "pci168c,002d"; | ||||
| +		reg = <0x7000 0 0 0 0x1000>; | ||||
| +		qca,no-eeprom; | ||||
| +	}; | ||||
| +}; | ||||
| @@ -1,42 +0,0 @@ | ||||
| From 25b8b2d57def4854558c135228a52326a7d346ad Mon Sep 17 00:00:00 2001 | ||||
| From: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| Date: Sun, 16 Oct 2016 22:59:06 +0200 | ||||
| Subject: [PATCH 2/3] ath9k: add a helper to get the string representation of | ||||
|  ath_bus_type | ||||
|  | ||||
| This can be used when the ath_bus_type has to be presented in a log | ||||
| message or firmware filename. | ||||
|  | ||||
| Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> | ||||
| --- | ||||
|  drivers/net/wireless/ath/ath.h  | 6 ++++++ | ||||
|  drivers/net/wireless/ath/main.c | 7 +++++++ | ||||
|  2 files changed, 13 insertions(+) | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath.h | ||||
| +++ b/drivers/net/wireless/ath/ath.h | ||||
| @@ -327,4 +327,10 @@ static inline const char *ath_opmode_to_ | ||||
|  } | ||||
|  #endif | ||||
|   | ||||
| +extern const char *ath_bus_type_strings[]; | ||||
| +static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype) | ||||
| +{ | ||||
| +	return ath_bus_type_strings[bustype]; | ||||
| +} | ||||
| + | ||||
|  #endif /* ATH_H */ | ||||
| --- a/drivers/net/wireless/ath/main.c | ||||
| +++ b/drivers/net/wireless/ath/main.c | ||||
| @@ -90,3 +90,10 @@ void ath_printk(const char *level, const | ||||
|  	va_end(args); | ||||
|  } | ||||
|  EXPORT_SYMBOL(ath_printk); | ||||
| + | ||||
| +const char *ath_bus_type_strings[] = { | ||||
| +	[ATH_PCI] = "pci", | ||||
| +	[ATH_AHB] = "ahb", | ||||
| +	[ATH_USB] = "usb", | ||||
| +}; | ||||
| +EXPORT_SYMBOL(ath_bus_type_strings); | ||||
| @@ -1,85 +0,0 @@ | ||||
| From cea03be5a848823cb8052e2e7b93cb2249d5f60c Mon Sep 17 00:00:00 2001 | ||||
| From: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| Date: Sun, 16 Oct 2016 22:59:07 +0200 | ||||
| Subject: [PATCH 3/3] ath9k: parse the device configuration from an OF node | ||||
|  | ||||
| This allows setting the MAC address and specifying that the firmware | ||||
| will be requested from userspace (because there might not be a hardware | ||||
| EEPROM connected to the chip) for ath9k based PCI devices using | ||||
| the device tree. | ||||
|  | ||||
| There is some out-of-tree code to "convert devicetree to | ||||
| ath9k_platform_data" (for example in OpenWrt and LEDE) which becomes | ||||
| obsolete with this patch. | ||||
|  | ||||
| Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> | ||||
| --- | ||||
|  drivers/net/wireless/ath/ath9k/init.c | 42 +++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 42 insertions(+) | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/init.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/init.c | ||||
| @@ -20,6 +20,8 @@ | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/ath9k_platform.h> | ||||
|  #include <linux/module.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/of_net.h> | ||||
|  #include <linux/relay.h> | ||||
|  #include <net/ieee80211_radiotap.h> | ||||
|   | ||||
| @@ -554,6 +556,42 @@ static int ath9k_init_platform(struct at | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int ath9k_of_init(struct ath_softc *sc) | ||||
| +{ | ||||
| +	struct device_node *np = sc->dev->of_node; | ||||
| +	struct ath_hw *ah = sc->sc_ah; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	enum ath_bus_type bus_type = common->bus_ops->ath_bus_type; | ||||
| +	const char *mac; | ||||
| +	char eeprom_name[100]; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (!of_device_is_available(np)) | ||||
| +		return 0; | ||||
| + | ||||
| +	ath_dbg(common, CONFIG, "parsing configuration from OF node\n"); | ||||
| + | ||||
| +	if (of_property_read_bool(np, "qca,no-eeprom")) { | ||||
| +		/* ath9k-eeprom-<bus>-<id>.bin */ | ||||
| +		scnprintf(eeprom_name, sizeof(eeprom_name), | ||||
| +			  "ath9k-eeprom-%s-%s.bin", | ||||
| +			  ath_bus_type_to_string(bus_type), dev_name(ah->dev)); | ||||
| + | ||||
| +		ret = ath9k_eeprom_request(sc, eeprom_name); | ||||
| +		if (ret) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
| +	mac = of_get_mac_address(np); | ||||
| +	if (mac) | ||||
| +		ether_addr_copy(common->macaddr, mac); | ||||
| + | ||||
| +	ah->ah_flags &= ~AH_USE_EEPROM; | ||||
| +	ah->ah_flags |= AH_NO_EEP_SWAP; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int ath9k_init_softc(u16 devid, struct ath_softc *sc, | ||||
|  			    const struct ath_bus_ops *bus_ops) | ||||
|  { | ||||
| @@ -610,6 +648,10 @@ static int ath9k_init_softc(u16 devid, s | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| +	ret = ath9k_of_init(sc); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
|  	if (ath9k_led_active_high != -1) | ||||
|  		ah->config.led_active_high = ath9k_led_active_high == 1; | ||||
|   | ||||
| @@ -1,65 +0,0 @@ | ||||
| From: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de> | ||||
| Date: Mon, 12 Dec 2016 19:50:01 +0100 | ||||
| Subject: [PATCH] ath9k: unlock rcu read when returning early | ||||
|  | ||||
| Starting with ath9k: use ieee80211_tx_status_noskb where possible | ||||
| [d94a461d7a7df68991fb9663531173f60ef89c68] the driver uses rcu_read_lock() && | ||||
| rcu_read_unlock() yet on returning early in ath_tx_edma_tasklet() the unlock is | ||||
| missing leading to stalls and suspicious RCU usage: | ||||
|  | ||||
|  =============================== | ||||
|  [ INFO: suspicious RCU usage. ] | ||||
|  4.9.0-rc8 #11 Not tainted | ||||
|  ------------------------------- | ||||
|  kernel/rcu/tree.c:705 Illegal idle entry in RCU read-side critical section.! | ||||
|  | ||||
|  other info that might help us debug this: | ||||
|  | ||||
|  RCU used illegally from idle CPU! | ||||
|  rcu_scheduler_active = 1, debug_locks = 0 | ||||
|  RCU used illegally from extended quiescent state! | ||||
|  1 lock held by swapper/7/0: | ||||
|  #0: | ||||
|   ( | ||||
|  rcu_read_lock | ||||
|  ){......} | ||||
|  , at: | ||||
|  [<ffffffffa06ed110>] ath_tx_edma_tasklet+0x0/0x450 [ath9k] | ||||
|  | ||||
|  stack backtrace: | ||||
|  CPU: 7 PID: 0 Comm: swapper/7 Not tainted 4.9.0-rc8 #11 | ||||
|  Hardware name: Acer Aspire V3-571G/VA50_HC_CR, BIOS V2.21 12/16/2013 | ||||
|   ffff88025efc3f38 ffffffff8132b1e5 ffff88017ede4540 0000000000000001 | ||||
|   ffff88025efc3f68 ffffffff810a25f7 ffff88025efcee60 ffff88017edebdd8 | ||||
|   ffff88025eeb5400 0000000000000091 ffff88025efc3f88 ffffffff810c3cd4 | ||||
|  Call Trace: | ||||
|   <IRQ> | ||||
|   [<ffffffff8132b1e5>] dump_stack+0x68/0x93 | ||||
|   [<ffffffff810a25f7>] lockdep_rcu_suspicious+0xd7/0x110 | ||||
|   [<ffffffff810c3cd4>] rcu_eqs_enter_common.constprop.85+0x154/0x200 | ||||
|   [<ffffffff810c5a54>] rcu_irq_exit+0x44/0xa0 | ||||
|   [<ffffffff81058631>] irq_exit+0x61/0xd0 | ||||
|   [<ffffffff81018d25>] do_IRQ+0x65/0x110 | ||||
|   [<ffffffff81672189>] common_interrupt+0x89/0x89 | ||||
|   <EOI> | ||||
|   [<ffffffff814ffe11>] ? cpuidle_enter_state+0x151/0x200 | ||||
|   [<ffffffff814ffee2>] cpuidle_enter+0x12/0x20 | ||||
|   [<ffffffff8109a6ae>] call_cpuidle+0x1e/0x40 | ||||
|   [<ffffffff8109a8f6>] cpu_startup_entry+0x146/0x220 | ||||
|   [<ffffffff810336f8>] start_secondary+0x148/0x170 | ||||
|  | ||||
| Signed-off-by: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de> | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| @@ -2757,7 +2757,7 @@ void ath_tx_edma_tasklet(struct ath_soft | ||||
|  		fifo_list = &txq->txq_fifo[txq->txq_tailidx]; | ||||
|  		if (list_empty(fifo_list)) { | ||||
|  			ath_txq_unlock(sc, txq); | ||||
| -			return; | ||||
| +			break; | ||||
|  		} | ||||
|   | ||||
|  		bf = list_first_entry(fifo_list, struct ath_buf, list); | ||||
| @@ -1,27 +0,0 @@ | ||||
| From: Ben Greear <greearb@candelatech.com> | ||||
| Date: Tue, 29 Nov 2016 14:00:28 -0800 | ||||
| Subject: [PATCH] ath10k: wmi-alloc-chunk should use DMA_BIDIRECTIONAL. | ||||
|  | ||||
| These memory chunks are often used as 'swap' by the NIC, | ||||
| so it will be both reading and writing to these areas. | ||||
|  | ||||
| This seems to fix errors like this on my x86-64 machine: | ||||
|  | ||||
| kernel: DMAR: DMAR:[DMA Write] Request device [05:00.0] fault addr ff5de000 | ||||
|         DMAR:[fault reason 05] PTE Write access is not set | ||||
|  | ||||
| Tested-by: Marek Behun <kabel@blackhole.sk> | ||||
| Signed-off-by: Ben Greear <greearb@candelatech.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi.c | ||||
| @@ -4495,7 +4495,7 @@ static int ath10k_wmi_alloc_chunk(struct | ||||
|  	if (!num_units) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| -	paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE); | ||||
| +	paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_BIDIRECTIONAL); | ||||
|  	if (dma_mapping_error(ar->dev, paddr)) { | ||||
|  		kfree(vaddr); | ||||
|  		return -ENOMEM; | ||||
| @@ -1,81 +0,0 @@ | ||||
| From: Ben Greear <greearb@candelatech.com> | ||||
| Date: Mon, 5 Dec 2016 10:28:39 -0800 | ||||
| Subject: [PATCH] ath10k: free host-mem with DMA_BIRECTIONAL flag. | ||||
|  | ||||
| Hopefully this fixes the problem reported by Kalle: | ||||
|  | ||||
| Noticed this in my log, but I don't have time to investigate this in | ||||
| detail right now: | ||||
|  | ||||
| [  413.795346] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready | ||||
| [  414.158755] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready | ||||
| [  477.439659] ath10k_pci 0000:02:00.0: could not get mac80211 beacon | ||||
| [  481.666630] ------------[ cut here ]------------ | ||||
| [  481.666669] WARNING: CPU: 0 PID: 1978 at lib/dma-debug.c:1155 check_unmap+0x320/0x8e0 | ||||
| [  481.666688] ath10k_pci 0000:02:00.0: DMA-API: device driver frees DMA memory with different direction [device address=0x000000002d130000] [size=63800 bytes] [mapped with DMA_BIDIRECTIONAL] [unmapped with DMA_TO_DEVICE] | ||||
| [  481.666703] Modules linked in: ctr ccm ath10k_pci(E-) ath10k_core(E) ath(E) mac80211(E) cfg80211(E) snd_hda_codec_hdmi snd_hda_codec_idt snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_pcm snd_seq_midi arc4 snd_rawmidi snd_seq_midi_event snd_seq btusb btintel snd_seq_device joydev coret | ||||
| [  481.671468] CPU: 0 PID: 1978 Comm: rmmod Tainted: G            E   4.9.0-rc7-wt+ #54 | ||||
| [  481.671478] Hardware name: Hewlett-Packard HP ProBook 6540b/1722, BIOS 68CDD Ver. F.04 01/27/2010 | ||||
| [  481.671489]  ef49dcec c842ee92 c8b5830e ef49dd34 ef49dd20 c80850f5 c8b5a13c ef49dd50 | ||||
| [  481.671560]  000007ba c8b5830e 00000483 c8461830 c8461830 00000483 ef49ddcc f34e64b8 | ||||
| [  481.671641]  c8b58360 ef49dd3c c80851bb 00000009 00000000 ef49dd34 c8b5a13c ef49dd50 | ||||
| [  481.671716] Call Trace: | ||||
| [  481.671731]  [<c842ee92>] dump_stack+0x76/0xb4 | ||||
| [  481.671745]  [<c80850f5>] __warn+0xe5/0x100 | ||||
| [  481.671757]  [<c8461830>] ? check_unmap+0x320/0x8e0 | ||||
| [  481.671769]  [<c8461830>] ? check_unmap+0x320/0x8e0 | ||||
| [  481.671780]  [<c80851bb>] warn_slowpath_fmt+0x3b/0x40 | ||||
| [  481.671791]  [<c8461830>] check_unmap+0x320/0x8e0 | ||||
| [  481.671804]  [<c8462054>] debug_dma_unmap_page+0x84/0xa0 | ||||
| [  481.671835]  [<f937cd7a>] ath10k_wmi_free_host_mem+0x9a/0xe0 [ath10k_core] | ||||
| [  481.671861]  [<f9363400>] ath10k_core_destroy+0x50/0x60 [ath10k_core] | ||||
| [  481.671875]  [<f8e13969>] ath10k_pci_remove+0x79/0xa0 [ath10k_pci] | ||||
| [  481.671889]  [<c848d8d8>] pci_device_remove+0x38/0xb0 | ||||
| [  481.671901]  [<c859fe4b>] __device_release_driver+0x7b/0x110 | ||||
| [  481.671913]  [<c85a00e7>] driver_detach+0x97/0xa0 | ||||
| [  481.671923]  [<c859ef8b>] bus_remove_driver+0x4b/0xb0 | ||||
| [  481.671934]  [<c85a0cda>] driver_unregister+0x2a/0x60 | ||||
| [  481.671949]  [<c848c888>] pci_unregister_driver+0x18/0x70 | ||||
| [  481.671965]  [<f8e14dae>] ath10k_pci_exit+0xd/0x25f [ath10k_pci] | ||||
| [  481.671979]  [<c812bb84>] SyS_delete_module+0xf4/0x180 | ||||
| [  481.671995]  [<c81f801b>] ? __might_fault+0x8b/0xa0 | ||||
| [  481.672009]  [<c80037d0>] do_fast_syscall_32+0xa0/0x1e0 | ||||
| [  481.672025]  [<c88d4c88>] sysenter_past_esp+0x45/0x74 | ||||
| [  481.672037] ---[ end trace 3fd23759e17e1622 ]--- | ||||
| [  481.672049] Mapped at: | ||||
| [  481.672060]  [  481.672072] [<c846062c>] debug_dma_map_page.part.25+0x1c/0xf0 | ||||
| [  481.672083]  [  481.672095] [<c8460799>] debug_dma_map_page+0x99/0xc0 | ||||
| [  481.672106]  [  481.672132] [<f93745ec>] ath10k_wmi_alloc_chunk+0x12c/0x1f0 [ath10k_core] | ||||
| [  481.672142]  [  481.672168] [<f937d0c4>] ath10k_wmi_event_service_ready_work+0x304/0x540 [ath10k_core] | ||||
| [  481.672178]  [  481.672190] [<c80a3643>] process_one_work+0x1c3/0x670 | ||||
| [  482.137134] ath10k_pci 0000:02:00.0: pci irq msi oper_irq_mode 2 irq_mode 0 reset_mode 0 | ||||
| [  482.313144] ath10k_pci 0000:02:00.0: Direct firmware load for ath10k/pre-cal-pci-0000:02:00.0.bin failed with error -2 | ||||
| [  482.313274] ath10k_pci 0000:02:00.0: Direct firmware load for ath10k/cal-pci-0000:02:00.0.bin failed with error -2 | ||||
| [  482.313768] ath10k_pci 0000:02:00.0: qca988x hw2.0 target 0x4100016c chip_id 0x043202ff sub 0000:0000 | ||||
| [  482.313777] ath10k_pci 0000:02:00.0: kconfig debug 1 debugfs 1 tracing 1 dfs 0 testmode 1 | ||||
| [  482.313974] ath10k_pci 0000:02:00.0: firmware ver 10.2.4.70.59-2 api 5 features no-p2p,raw-mode,mfp,allows-mesh-bcast crc32 4159f498 | ||||
| [  482.369858] ath10k_pci 0000:02:00.0: Direct firmware load for ath10k/QCA988X/hw2.0/board-2.bin failed with error -2 | ||||
| [  482.370011] ath10k_pci 0000:02:00.0: board_file api 1 bmi_id N/A crc32 bebc7c08 | ||||
| [  483.596770] ath10k_pci 0000:02:00.0: htt-ver 2.1 wmi-op 5 htt-op 2 cal otp max-sta 128 raw 0 hwcrypto 1 | ||||
| [  483.701686] ath: EEPROM regdomain: 0x0 | ||||
| [  483.701706] ath: EEPROM indicates default country code should be used | ||||
| [  483.701713] ath: doing EEPROM country->regdmn map search | ||||
| [  483.701721] ath: country maps to regdmn code: 0x3a | ||||
| [  483.701730] ath: Country alpha2 being used: US | ||||
| [  483.701737] ath: Regpair used: 0x3a | ||||
|  | ||||
| Reported-by: Kalle Valo <kvalo@qca.qualcomm.com> | ||||
| Signed-off-by: Ben Greear <greearb@candelatech.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath10k/wmi.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/wmi.c | ||||
| @@ -8227,7 +8227,7 @@ void ath10k_wmi_free_host_mem(struct ath | ||||
|  		dma_unmap_single(ar->dev, | ||||
|  				 ar->wmi.mem_chunks[i].paddr, | ||||
|  				 ar->wmi.mem_chunks[i].len, | ||||
| -				 DMA_TO_DEVICE); | ||||
| +				 DMA_BIDIRECTIONAL); | ||||
|  		kfree(ar->wmi.mem_chunks[i].vaddr); | ||||
|  	} | ||||
|   | ||||
| @@ -1,42 +0,0 @@ | ||||
| From: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> | ||||
| Date: Wed, 12 Oct 2016 12:59:02 +0530 | ||||
| Subject: [PATCH] ath10k: Fix failure to send NULL func frame for 10.4 | ||||
|  | ||||
| This partially reverts 'commit 2cdce425aa33 | ||||
| ("ath10k: Fix broken NULL func data frame status for 10.4")' | ||||
| Unfortunately this breaks sending NULL func and the existing | ||||
| issue of obtaining proper tx status for NULL function will be | ||||
| fixed. Also update the comments for feature flag added to be | ||||
| useless and not working | ||||
|  | ||||
| Fixes: 2cdce425aa33 "ath10k: Fix broken NULL func data frame status for | ||||
| 10.4" | ||||
| Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath10k/core.h | ||||
| +++ b/drivers/net/wireless/ath/ath10k/core.h | ||||
| @@ -556,10 +556,8 @@ enum ath10k_fw_features { | ||||
|  	 */ | ||||
|  	ATH10K_FW_FEATURE_BTCOEX_PARAM = 14, | ||||
|   | ||||
| -	/* Older firmware with HTT delivers incorrect tx status for null func | ||||
| -	 * frames to driver, but this fixed in 10.2 and 10.4 firmware versions. | ||||
| -	 * Also this workaround results in reporting of incorrect null func | ||||
| -	 * status for 10.4. This flag is used to skip the workaround. | ||||
| +	/* Unused flag and proven to be not working, enable this if you want | ||||
| +	 * to experiment sending NULL func data frames in HTT TX | ||||
|  	 */ | ||||
|  	ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR = 15, | ||||
|   | ||||
| --- a/drivers/net/wireless/ath/ath10k/mac.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/mac.c | ||||
| @@ -3274,8 +3274,6 @@ ath10k_mac_tx_h_get_txmode(struct ath10k | ||||
|  	if (ar->htt.target_version_major < 3 && | ||||
|  	    (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) && | ||||
|  	    !test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, | ||||
| -		      ar->running_fw->fw_file.fw_features) && | ||||
| -	    !test_bit(ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR, | ||||
|  		      ar->running_fw->fw_file.fw_features)) | ||||
|  		return ATH10K_HW_TXRX_MGMT; | ||||
|   | ||||
| @@ -1,49 +0,0 @@ | ||||
| From: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> | ||||
| Date: Wed, 30 Nov 2016 10:59:29 +0530 | ||||
| Subject: [PATCH] ath10k: Fix soft lockup during firmware crash/hw-restart | ||||
|  | ||||
| During firmware crash (or) user requested manual restart | ||||
| the system gets into a soft lock up state because of the | ||||
| below root cause. | ||||
|  | ||||
| During user requested hardware restart / firmware crash | ||||
| the system goes into a soft lockup state as 'napi_synchronize' | ||||
| is called after 'napi_disable' (which sets 'NAPI_STATE_SCHED' | ||||
| bit) and it sleeps into infinite loop as it waits for | ||||
| 'NAPI_STATE_SCHED' to be cleared. This condition is hit because | ||||
| 'ath10k_hif_stop' is called twice as below (resulting in calling | ||||
| 'napi_synchronize' after 'napi_disable') | ||||
|  | ||||
| 'ath10k_core_restart' -> 'ath10k_hif_stop' (ATH10K_STATE_ON) -> | ||||
| -> 'ieee80211_restart_hw' -> 'ath10k_start' -> 'ath10k_halt' -> | ||||
| 'ath10k_core_stop' -> 'ath10k_hif_stop' (ATH10K_STATE_RESTARTING) | ||||
|  | ||||
| Fix this by calling 'ath10k_halt' in ath10k_core_restart itself | ||||
| as it makes more sense before informing mac80211 to restart h/w | ||||
| Also remove 'ath10k_halt' in ath10k_start for the state of 'restarting' | ||||
|  | ||||
| Fixes: 3c97f5de1f28 ("ath10k: implement NAPI support") | ||||
| Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath10k/core.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/core.c | ||||
| @@ -1534,7 +1534,7 @@ static void ath10k_core_restart(struct w | ||||
|  	switch (ar->state) { | ||||
|  	case ATH10K_STATE_ON: | ||||
|  		ar->state = ATH10K_STATE_RESTARTING; | ||||
| -		ath10k_hif_stop(ar); | ||||
| +		ath10k_halt(ar); | ||||
|  		ath10k_scan_finish(ar); | ||||
|  		ieee80211_restart_hw(ar->hw); | ||||
|  		break; | ||||
| --- a/drivers/net/wireless/ath/ath10k/mac.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/mac.c | ||||
| @@ -4470,7 +4470,6 @@ static int ath10k_start(struct ieee80211 | ||||
|  		ar->state = ATH10K_STATE_ON; | ||||
|  		break; | ||||
|  	case ATH10K_STATE_RESTARTING: | ||||
| -		ath10k_halt(ar); | ||||
|  		ar->state = ATH10K_STATE_RESTARTED; | ||||
|  		break; | ||||
|  	case ATH10K_STATE_ON: | ||||
| @@ -1,196 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 19:33:23 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel_ht: move supported bitrate mask out of | ||||
|  group data | ||||
|  | ||||
| Improves dcache footprint by ensuring that fewer cache lines need to be | ||||
| touched. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel_ht.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht.c | ||||
| @@ -301,7 +301,7 @@ minstrel_ht_get_stats(struct minstrel_pr | ||||
|  				break; | ||||
|   | ||||
|  		/* short preamble */ | ||||
| -		if (!(mi->groups[group].supported & BIT(idx))) | ||||
| +		if (!(mi->supported[group] & BIT(idx))) | ||||
|  			idx += 4; | ||||
|  	} | ||||
|  	return &mi->groups[group].rates[idx]; | ||||
| @@ -486,7 +486,7 @@ minstrel_ht_prob_rate_reduce_streams(str | ||||
|  			  MCS_GROUP_RATES].streams; | ||||
|  	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { | ||||
|  		mg = &mi->groups[group]; | ||||
| -		if (!mg->supported || group == MINSTREL_CCK_GROUP) | ||||
| +		if (!mi->supported[group] || group == MINSTREL_CCK_GROUP) | ||||
|  			continue; | ||||
|   | ||||
|  		tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; | ||||
| @@ -540,7 +540,7 @@ minstrel_ht_update_stats(struct minstrel | ||||
|  	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { | ||||
|   | ||||
|  		mg = &mi->groups[group]; | ||||
| -		if (!mg->supported) | ||||
| +		if (!mi->supported[group]) | ||||
|  			continue; | ||||
|   | ||||
|  		mi->sample_count++; | ||||
| @@ -550,7 +550,7 @@ minstrel_ht_update_stats(struct minstrel | ||||
|  			tmp_group_tp_rate[j] = group; | ||||
|   | ||||
|  		for (i = 0; i < MCS_GROUP_RATES; i++) { | ||||
| -			if (!(mg->supported & BIT(i))) | ||||
| +			if (!(mi->supported[group] & BIT(i))) | ||||
|  				continue; | ||||
|   | ||||
|  			index = MCS_GROUP_RATES * group + i; | ||||
| @@ -636,7 +636,7 @@ minstrel_set_next_sample_idx(struct mins | ||||
|  		mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); | ||||
|  		mg = &mi->groups[mi->sample_group]; | ||||
|   | ||||
| -		if (!mg->supported) | ||||
| +		if (!mi->supported[mi->sample_group]) | ||||
|  			continue; | ||||
|   | ||||
|  		if (++mg->index >= MCS_GROUP_RATES) { | ||||
| @@ -657,7 +657,7 @@ minstrel_downgrade_rate(struct minstrel_ | ||||
|  	while (group > 0) { | ||||
|  		group--; | ||||
|   | ||||
| -		if (!mi->groups[group].supported) | ||||
| +		if (!mi->supported[group]) | ||||
|  			continue; | ||||
|   | ||||
|  		if (minstrel_mcs_groups[group].streams > | ||||
| @@ -994,7 +994,7 @@ minstrel_get_sample_rate(struct minstrel | ||||
|  	sample_idx = sample_table[mg->column][mg->index]; | ||||
|  	minstrel_set_next_sample_idx(mi); | ||||
|   | ||||
| -	if (!(mg->supported & BIT(sample_idx))) | ||||
| +	if (!(mi->supported[sample_group] & BIT(sample_idx))) | ||||
|  		return -1; | ||||
|   | ||||
|  	mrs = &mg->rates[sample_idx]; | ||||
| @@ -1052,7 +1052,7 @@ static void | ||||
|  minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp, | ||||
|  				    struct minstrel_ht_sta *mi, bool val) | ||||
|  { | ||||
| -	u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported; | ||||
| +	u8 supported = mi->supported[MINSTREL_CCK_GROUP]; | ||||
|   | ||||
|  	if (!supported || !mi->cck_supported_short) | ||||
|  		return; | ||||
| @@ -1061,7 +1061,7 @@ minstrel_ht_check_cck_shortpreamble(stru | ||||
|  		return; | ||||
|   | ||||
|  	supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4); | ||||
| -	mi->groups[MINSTREL_CCK_GROUP].supported = supported; | ||||
| +	mi->supported[MINSTREL_CCK_GROUP] = supported; | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -1154,7 +1154,7 @@ minstrel_ht_update_cck(struct minstrel_p | ||||
|  			mi->cck_supported_short |= BIT(i); | ||||
|  	} | ||||
|   | ||||
| -	mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported; | ||||
| +	mi->supported[MINSTREL_CCK_GROUP] = mi->cck_supported; | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -1233,7 +1233,7 @@ minstrel_ht_update_caps(void *priv, stru | ||||
|  		u32 gflags = minstrel_mcs_groups[i].flags; | ||||
|  		int bw, nss; | ||||
|   | ||||
| -		mi->groups[i].supported = 0; | ||||
| +		mi->supported[i] = 0; | ||||
|  		if (i == MINSTREL_CCK_GROUP) { | ||||
|  			minstrel_ht_update_cck(mp, mi, sband, sta); | ||||
|  			continue; | ||||
| @@ -1265,8 +1265,8 @@ minstrel_ht_update_caps(void *priv, stru | ||||
|  			if (use_vht && minstrel_vht_only) | ||||
|  				continue; | ||||
|  #endif | ||||
| -			mi->groups[i].supported = mcs->rx_mask[nss - 1]; | ||||
| -			if (mi->groups[i].supported) | ||||
| +			mi->supported[i] = mcs->rx_mask[nss - 1]; | ||||
| +			if (mi->supported[i]) | ||||
|  				n_supported++; | ||||
|  			continue; | ||||
|  		} | ||||
| @@ -1292,10 +1292,10 @@ minstrel_ht_update_caps(void *priv, stru | ||||
|  		else | ||||
|  			bw = BW_20; | ||||
|   | ||||
| -		mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss, | ||||
| +		mi->supported[i] = minstrel_get_valid_vht_rates(bw, nss, | ||||
|  				vht_cap->vht_mcs.tx_mcs_map); | ||||
|   | ||||
| -		if (mi->groups[i].supported) | ||||
| +		if (mi->supported[i]) | ||||
|  			n_supported++; | ||||
|  	} | ||||
|   | ||||
| --- a/net/mac80211/rc80211_minstrel_ht.h | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht.h | ||||
| @@ -52,9 +52,6 @@ struct minstrel_mcs_group_data { | ||||
|  	u8 index; | ||||
|  	u8 column; | ||||
|   | ||||
| -	/* bitfield of supported MCS rates of this group */ | ||||
| -	u16 supported; | ||||
| - | ||||
|  	/* sorted rate set within a MCS group*/ | ||||
|  	u16 max_group_tp_rate[MAX_THR_RATES]; | ||||
|  	u16 max_group_prob_rate; | ||||
| @@ -101,6 +98,9 @@ struct minstrel_ht_sta { | ||||
|  	u8 cck_supported; | ||||
|  	u8 cck_supported_short; | ||||
|   | ||||
| +	/* Bitfield of supported MCS rates of all groups */ | ||||
| +	u16 supported[MINSTREL_GROUPS_NB]; | ||||
| + | ||||
|  	/* MCS rate group info and statistics */ | ||||
|  	struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; | ||||
|  }; | ||||
| --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c | ||||
| @@ -24,7 +24,7 @@ minstrel_ht_stats_dump(struct minstrel_h | ||||
|  	char gimode = 'L'; | ||||
|  	u32 gflags; | ||||
|   | ||||
| -	if (!mi->groups[i].supported) | ||||
| +	if (!mi->supported[i]) | ||||
|  		return p; | ||||
|   | ||||
|  	mg = &minstrel_mcs_groups[i]; | ||||
| @@ -42,7 +42,7 @@ minstrel_ht_stats_dump(struct minstrel_h | ||||
|  		static const int bitrates[4] = { 10, 20, 55, 110 }; | ||||
|  		int idx = i * MCS_GROUP_RATES + j; | ||||
|   | ||||
| -		if (!(mi->groups[i].supported & BIT(j))) | ||||
| +		if (!(mi->supported[i] & BIT(j))) | ||||
|  			continue; | ||||
|   | ||||
|  		if (gflags & IEEE80211_TX_RC_MCS) { | ||||
| @@ -170,7 +170,7 @@ minstrel_ht_stats_csv_dump(struct minstr | ||||
|  	char gimode = 'L'; | ||||
|  	u32 gflags; | ||||
|   | ||||
| -	if (!mi->groups[i].supported) | ||||
| +	if (!mi->supported[i]) | ||||
|  		return p; | ||||
|   | ||||
|  	mg = &minstrel_mcs_groups[i]; | ||||
| @@ -188,7 +188,7 @@ minstrel_ht_stats_csv_dump(struct minstr | ||||
|  		static const int bitrates[4] = { 10, 20, 55, 110 }; | ||||
|  		int idx = i * MCS_GROUP_RATES + j; | ||||
|   | ||||
| -		if (!(mi->groups[i].supported & BIT(j))) | ||||
| +		if (!(mi->supported[i] & BIT(j))) | ||||
|  			continue; | ||||
|   | ||||
|  		if (gflags & IEEE80211_TX_RC_MCS) { | ||||
| @@ -1,70 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:06:08 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel_ht: move short preamble check out of | ||||
|  get_rate | ||||
|  | ||||
| Test short preamble support in minstrel_ht_update_caps instead of | ||||
| looking at the per-packet flag. Makes the code more efficient. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel_ht.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht.c | ||||
| @@ -14,6 +14,7 @@ | ||||
|  #include <linux/ieee80211.h> | ||||
|  #include <net/mac80211.h> | ||||
|  #include "rate.h" | ||||
| +#include "sta_info.h" | ||||
|  #include "rc80211_minstrel.h" | ||||
|  #include "rc80211_minstrel_ht.h" | ||||
|   | ||||
| @@ -1049,22 +1050,6 @@ minstrel_get_sample_rate(struct minstrel | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| -minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp, | ||||
| -				    struct minstrel_ht_sta *mi, bool val) | ||||
| -{ | ||||
| -	u8 supported = mi->supported[MINSTREL_CCK_GROUP]; | ||||
| - | ||||
| -	if (!supported || !mi->cck_supported_short) | ||||
| -		return; | ||||
| - | ||||
| -	if (supported & (mi->cck_supported_short << (val * 4))) | ||||
| -		return; | ||||
| - | ||||
| -	supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4); | ||||
| -	mi->supported[MINSTREL_CCK_GROUP] = supported; | ||||
| -} | ||||
| - | ||||
| -static void | ||||
|  minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, | ||||
|                       struct ieee80211_tx_rate_control *txrc) | ||||
|  { | ||||
| @@ -1087,7 +1072,6 @@ minstrel_ht_get_rate(void *priv, struct | ||||
|  		minstrel_aggr_check(sta, txrc->skb); | ||||
|   | ||||
|  	info->flags |= mi->tx_flags; | ||||
| -	minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); | ||||
|   | ||||
|  #ifdef CPTCFG_MAC80211_DEBUGFS | ||||
|  	if (mp->fixed_rate_idx != -1) | ||||
| @@ -1168,6 +1152,7 @@ minstrel_ht_update_caps(void *priv, stru | ||||
|  	struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; | ||||
|  	u16 ht_cap = sta->ht_cap.cap; | ||||
|  	struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; | ||||
| +	struct sta_info *sinfo = container_of(sta, struct sta_info, sta); | ||||
|  	int use_vht; | ||||
|  	int n_supported = 0; | ||||
|  	int ack_dur; | ||||
| @@ -1302,6 +1287,9 @@ minstrel_ht_update_caps(void *priv, stru | ||||
|  	if (!n_supported) | ||||
|  		goto use_legacy; | ||||
|   | ||||
| +	if (test_sta_flag(sinfo, WLAN_STA_SHORT_PREAMBLE)) | ||||
| +		mi->cck_supported_short |= mi->cck_supported_short << 4; | ||||
| + | ||||
|  	/* create an initial rate table with the lowest supported rates */ | ||||
|  	minstrel_ht_update_stats(mp, mi); | ||||
|  	minstrel_ht_update_rates(mp, mi); | ||||
| @@ -1,22 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:09:14 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel_ht: make att_hist and succ_hist u32 | ||||
|  instead of u64 | ||||
|  | ||||
| They are only used for debugging purposes and take a very long time to | ||||
| overflow. Visibly reduces the size of the per-sta rate control data. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel.h | ||||
| +++ b/net/mac80211/rc80211_minstrel.h | ||||
| @@ -59,7 +59,7 @@ struct minstrel_rate_stats { | ||||
|  	u16 success, last_success; | ||||
|   | ||||
|  	/* total attempts/success counters */ | ||||
| -	u64 att_hist, succ_hist; | ||||
| +	u32 att_hist, succ_hist; | ||||
|   | ||||
|  	/* statistis of packet delivery probability | ||||
|  	 *  cur_prob  - current prob within last update intervall | ||||
| @@ -1,34 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:12:25 +0100 | ||||
| Subject: [PATCH] mac80211: check for MCS in ieee80211_duration before fetching | ||||
|  chanctx | ||||
|  | ||||
| Makes the code a bit more efficient | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -63,6 +63,10 @@ static __le16 ieee80211_duration(struct | ||||
|  	struct ieee80211_chanctx_conf *chanctx_conf; | ||||
|  	u32 rate_flags = 0; | ||||
|   | ||||
| +	/* assume HW handles this */ | ||||
| +	if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)) | ||||
| +		return 0; | ||||
| + | ||||
|  	rcu_read_lock(); | ||||
|  	chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf); | ||||
|  	if (chanctx_conf) { | ||||
| @@ -71,10 +75,6 @@ static __le16 ieee80211_duration(struct | ||||
|  	} | ||||
|  	rcu_read_unlock(); | ||||
|   | ||||
| -	/* assume HW handles this */ | ||||
| -	if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)) | ||||
| -		return 0; | ||||
| - | ||||
|  	/* uh huh? */ | ||||
|  	if (WARN_ON_ONCE(tx->rate.idx < 0)) | ||||
|  		return 0; | ||||
| @@ -1,192 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:13:58 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel: remove cur_prob from debugfs | ||||
|  | ||||
| This field is redundant, because it is simply last success divided by | ||||
| last attempt count. Removing it from the rate stats struct saves about | ||||
| 1.2 KiB per HT station. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel.c | ||||
| +++ b/net/mac80211/rc80211_minstrel.c | ||||
| @@ -159,21 +159,23 @@ minstrel_update_rates(struct minstrel_pr | ||||
|  void | ||||
|  minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) | ||||
|  { | ||||
| +	unsigned int cur_prob; | ||||
| + | ||||
|  	if (unlikely(mrs->attempts > 0)) { | ||||
|  		mrs->sample_skipped = 0; | ||||
| -		mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); | ||||
| +		cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); | ||||
|  		if (unlikely(!mrs->att_hist)) { | ||||
| -			mrs->prob_ewma = mrs->cur_prob; | ||||
| +			mrs->prob_ewma = cur_prob; | ||||
|  		} else { | ||||
|  			/* update exponential weighted moving variance */ | ||||
|  			mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd, | ||||
| -							 mrs->cur_prob, | ||||
| +							 cur_prob, | ||||
|  							 mrs->prob_ewma, | ||||
|  							 EWMA_LEVEL); | ||||
|   | ||||
|  			/*update exponential weighted moving avarage */ | ||||
|  			mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma, | ||||
| -						       mrs->cur_prob, | ||||
| +						       cur_prob, | ||||
|  						       EWMA_LEVEL); | ||||
|  		} | ||||
|  		mrs->att_hist += mrs->attempts; | ||||
| --- a/net/mac80211/rc80211_minstrel.h | ||||
| +++ b/net/mac80211/rc80211_minstrel.h | ||||
| @@ -62,10 +62,8 @@ struct minstrel_rate_stats { | ||||
|  	u32 att_hist, succ_hist; | ||||
|   | ||||
|  	/* statistis of packet delivery probability | ||||
| -	 *  cur_prob  - current prob within last update intervall | ||||
|  	 *  prob_ewma - exponential weighted moving average of prob | ||||
|  	 *  prob_ewmsd - exp. weighted moving standard deviation of prob */ | ||||
| -	unsigned int cur_prob; | ||||
|  	unsigned int prob_ewma; | ||||
|  	u16 prob_ewmsd; | ||||
|   | ||||
| --- a/net/mac80211/rc80211_minstrel_debugfs.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_debugfs.c | ||||
| @@ -75,7 +75,7 @@ minstrel_stats_open(struct inode *inode, | ||||
|  { | ||||
|  	struct minstrel_sta_info *mi = inode->i_private; | ||||
|  	struct minstrel_debugfs_info *ms; | ||||
| -	unsigned int i, tp_max, tp_avg, prob, eprob; | ||||
| +	unsigned int i, tp_max, tp_avg, eprob; | ||||
|  	char *p; | ||||
|   | ||||
|  	ms = kmalloc(2048, GFP_KERNEL); | ||||
| @@ -86,9 +86,9 @@ minstrel_stats_open(struct inode *inode, | ||||
|  	p = ms->buf; | ||||
|  	p += sprintf(p, "\n"); | ||||
|  	p += sprintf(p, | ||||
| -		     "best   __________rate_________    ________statistics________    ________last_______    ______sum-of________\n"); | ||||
| +		     "best   __________rate_________    ________statistics________    ____last_____    ______sum-of________\n"); | ||||
|  	p += sprintf(p, | ||||
| -		     "rate  [name idx airtime max_tp]  [avg(tp) avg(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | #attempts]\n"); | ||||
| +		     "rate  [name idx airtime max_tp]  [avg(tp) avg(prob) sd(prob)]  [retry|suc|att]  [#success | #attempts]\n"); | ||||
|   | ||||
|  	for (i = 0; i < mi->n_rates; i++) { | ||||
|  		struct minstrel_rate *mr = &mi->r[i]; | ||||
| @@ -107,17 +107,15 @@ minstrel_stats_open(struct inode *inode, | ||||
|   | ||||
|  		tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); | ||||
|  		tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); | ||||
| -		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
|   | ||||
|  		p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u" | ||||
| -				"     %3u.%1u %3u   %3u %-3u   " | ||||
| +				"     %3u   %3u %-3u   " | ||||
|  				"%9llu   %-9llu\n", | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
|  				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| -				prob / 10, prob % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| @@ -148,7 +146,7 @@ minstrel_stats_csv_open(struct inode *in | ||||
|  { | ||||
|  	struct minstrel_sta_info *mi = inode->i_private; | ||||
|  	struct minstrel_debugfs_info *ms; | ||||
| -	unsigned int i, tp_max, tp_avg, prob, eprob; | ||||
| +	unsigned int i, tp_max, tp_avg, eprob; | ||||
|  	char *p; | ||||
|   | ||||
|  	ms = kmalloc(2048, GFP_KERNEL); | ||||
| @@ -175,16 +173,14 @@ minstrel_stats_csv_open(struct inode *in | ||||
|   | ||||
|  		tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); | ||||
|  		tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); | ||||
| -		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
|   | ||||
| -		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," | ||||
| +		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," | ||||
|  				"%llu,%llu,%d,%d\n", | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
|  				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| -				prob / 10, prob % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c | ||||
| @@ -19,7 +19,7 @@ static char * | ||||
|  minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) | ||||
|  { | ||||
|  	const struct mcs_group *mg; | ||||
| -	unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; | ||||
| +	unsigned int j, tp_max, tp_avg, eprob, tx_time; | ||||
|  	char htmode = '2'; | ||||
|  	char gimode = 'L'; | ||||
|  	u32 gflags; | ||||
| @@ -83,17 +83,15 @@ minstrel_ht_stats_dump(struct minstrel_h | ||||
|   | ||||
|  		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); | ||||
|  		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); | ||||
| -		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
|   | ||||
|  		p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u" | ||||
| -				"     %3u.%1u %3u   %3u %-3u   " | ||||
| +				"     %3u   %3u %-3u   " | ||||
|  				"%9llu   %-9llu\n", | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
|  				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| -				prob / 10, prob % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| @@ -130,9 +128,9 @@ minstrel_ht_stats_open(struct inode *ino | ||||
|   | ||||
|  	p += sprintf(p, "\n"); | ||||
|  	p += sprintf(p, | ||||
| -		     "              best   ____________rate__________    ________statistics________    ________last_______    ______sum-of________\n"); | ||||
| +		     "              best   ____________rate__________    ________statistics________    _____last____    ______sum-of________\n"); | ||||
|  	p += sprintf(p, | ||||
| -		     "mode guard #  rate  [name   idx airtime  max_tp]  [avg(tp) avg(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | #attempts]\n"); | ||||
| +		     "mode guard #  rate  [name   idx airtime  max_tp]  [avg(tp) avg(prob) sd(prob)]  [retry|suc|att]  [#success | #attempts]\n"); | ||||
|   | ||||
|  	p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); | ||||
|  	for (i = 0; i < MINSTREL_CCK_GROUP; i++) | ||||
| @@ -165,7 +163,7 @@ static char * | ||||
|  minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) | ||||
|  { | ||||
|  	const struct mcs_group *mg; | ||||
| -	unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; | ||||
| +	unsigned int j, tp_max, tp_avg, eprob, tx_time; | ||||
|  	char htmode = '2'; | ||||
|  	char gimode = 'L'; | ||||
|  	u32 gflags; | ||||
| @@ -226,16 +224,14 @@ minstrel_ht_stats_csv_dump(struct minstr | ||||
|   | ||||
|  		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); | ||||
|  		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); | ||||
| -		prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
|   | ||||
| -		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u," | ||||
| +		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u," | ||||
|  				"%u,%llu,%llu,", | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
|  				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| -				prob / 10, prob % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| @@ -1,22 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:15:33 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel: reduce MINSTREL_SCALE | ||||
|  | ||||
| The loss of a bit of extra precision does not hurt the calculation, 12 | ||||
| bits is still enough to calculate probabilities well. Reducing the scale | ||||
| makes it easier to avoid overflows | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel.h | ||||
| +++ b/net/mac80211/rc80211_minstrel.h | ||||
| @@ -14,7 +14,7 @@ | ||||
|  #define SAMPLE_COLUMNS	10	/* number of columns in sample table */ | ||||
|   | ||||
|  /* scaled fraction values */ | ||||
| -#define MINSTREL_SCALE  16 | ||||
| +#define MINSTREL_SCALE  12 | ||||
|  #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) | ||||
|  #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) | ||||
|   | ||||
| @@ -1,186 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:17:06 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel: store probability variance instead of | ||||
|  standard deviation | ||||
|  | ||||
| This avoids the costly int_sqrt calls in the statistics update and moves | ||||
| it to the debugfs code instead. | ||||
| This also fixes an overflow in the previous standard deviation | ||||
| calculation. | ||||
|  | ||||
| Signed-off-by: Thomas Huehn <thomas.huehn@evernet-eg.de> | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel.c | ||||
| +++ b/net/mac80211/rc80211_minstrel.c | ||||
| @@ -168,10 +168,10 @@ minstrel_calc_rate_stats(struct minstrel | ||||
|  			mrs->prob_ewma = cur_prob; | ||||
|  		} else { | ||||
|  			/* update exponential weighted moving variance */ | ||||
| -			mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd, | ||||
| -							 cur_prob, | ||||
| -							 mrs->prob_ewma, | ||||
| -							 EWMA_LEVEL); | ||||
| +			mrs->prob_ewmv = minstrel_ewmv(mrs->prob_ewmv, | ||||
| +							cur_prob, | ||||
| +							mrs->prob_ewma, | ||||
| +							EWMA_LEVEL); | ||||
|   | ||||
|  			/*update exponential weighted moving avarage */ | ||||
|  			mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma, | ||||
| --- a/net/mac80211/rc80211_minstrel.h | ||||
| +++ b/net/mac80211/rc80211_minstrel.h | ||||
| @@ -36,21 +36,16 @@ minstrel_ewma(int old, int new, int weig | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| - * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation | ||||
| + * Perform EWMV (Exponentially Weighted Moving Variance) calculation | ||||
|   */ | ||||
|  static inline int | ||||
| -minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight) | ||||
| +minstrel_ewmv(int old_ewmv, int cur_prob, int prob_ewma, int weight) | ||||
|  { | ||||
| -	int diff, incr, tmp_var; | ||||
| +	int diff, incr; | ||||
|   | ||||
| -	/* calculate exponential weighted moving variance */ | ||||
| -	diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000); | ||||
| +	diff = cur_prob - prob_ewma; | ||||
|  	incr = (EWMA_DIV - weight) * diff / EWMA_DIV; | ||||
| -	tmp_var = old_ewmsd * old_ewmsd; | ||||
| -	tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV; | ||||
| - | ||||
| -	/* return standard deviation */ | ||||
| -	return (u16) int_sqrt(tmp_var); | ||||
| +	return weight * (old_ewmv + MINSTREL_TRUNC(diff * incr)) / EWMA_DIV; | ||||
|  } | ||||
|   | ||||
|  struct minstrel_rate_stats { | ||||
| @@ -65,7 +60,7 @@ struct minstrel_rate_stats { | ||||
|  	 *  prob_ewma - exponential weighted moving average of prob | ||||
|  	 *  prob_ewmsd - exp. weighted moving standard deviation of prob */ | ||||
|  	unsigned int prob_ewma; | ||||
| -	u16 prob_ewmsd; | ||||
| +	u16 prob_ewmv; | ||||
|   | ||||
|  	/* maximum retry counts */ | ||||
|  	u8 retry_count; | ||||
| @@ -151,6 +146,14 @@ struct minstrel_debugfs_info { | ||||
|  	char buf[]; | ||||
|  }; | ||||
|   | ||||
| +/* Get EWMSD (Exponentially Weighted Moving Standard Deviation) * 10 */ | ||||
| +static inline int | ||||
| +minstrel_get_ewmsd10(struct minstrel_rate_stats *mrs) | ||||
| +{ | ||||
| +	unsigned int ewmv = mrs->prob_ewmv; | ||||
| +	return int_sqrt(MINSTREL_TRUNC(ewmv * 1000 * 1000)); | ||||
| +} | ||||
| + | ||||
|  extern const struct rate_control_ops mac80211_minstrel; | ||||
|  void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); | ||||
|  void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); | ||||
| --- a/net/mac80211/rc80211_minstrel_debugfs.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_debugfs.c | ||||
| @@ -93,6 +93,7 @@ minstrel_stats_open(struct inode *inode, | ||||
|  	for (i = 0; i < mi->n_rates; i++) { | ||||
|  		struct minstrel_rate *mr = &mi->r[i]; | ||||
|  		struct minstrel_rate_stats *mrs = &mi->r[i].stats; | ||||
| +		unsigned int prob_ewmsd; | ||||
|   | ||||
|  		*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; | ||||
|  		*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; | ||||
| @@ -108,6 +109,7 @@ minstrel_stats_open(struct inode *inode, | ||||
|  		tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); | ||||
|  		tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
| +		prob_ewmsd = minstrel_get_ewmsd10(mrs); | ||||
|   | ||||
|  		p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u" | ||||
|  				"     %3u   %3u %-3u   " | ||||
| @@ -115,7 +117,7 @@ minstrel_stats_open(struct inode *inode, | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
| -				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| +				prob_ewmsd / 10, prob_ewmsd % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| @@ -159,6 +161,7 @@ minstrel_stats_csv_open(struct inode *in | ||||
|  	for (i = 0; i < mi->n_rates; i++) { | ||||
|  		struct minstrel_rate *mr = &mi->r[i]; | ||||
|  		struct minstrel_rate_stats *mrs = &mi->r[i].stats; | ||||
| +		unsigned int prob_ewmsd; | ||||
|   | ||||
|  		p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : "")); | ||||
|  		p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : "")); | ||||
| @@ -174,13 +177,14 @@ minstrel_stats_csv_open(struct inode *in | ||||
|  		tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); | ||||
|  		tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
| +		prob_ewmsd = minstrel_get_ewmsd10(mrs); | ||||
|   | ||||
|  		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," | ||||
|  				"%llu,%llu,%d,%d\n", | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
| -				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| +				prob_ewmsd / 10, prob_ewmsd % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c | ||||
| @@ -41,6 +41,7 @@ minstrel_ht_stats_dump(struct minstrel_h | ||||
|  		struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; | ||||
|  		static const int bitrates[4] = { 10, 20, 55, 110 }; | ||||
|  		int idx = i * MCS_GROUP_RATES + j; | ||||
| +		unsigned int prob_ewmsd; | ||||
|   | ||||
|  		if (!(mi->supported[i] & BIT(j))) | ||||
|  			continue; | ||||
| @@ -84,6 +85,7 @@ minstrel_ht_stats_dump(struct minstrel_h | ||||
|  		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); | ||||
|  		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
| +		prob_ewmsd = minstrel_get_ewmsd10(mrs); | ||||
|   | ||||
|  		p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u    %3u.%1u" | ||||
|  				"     %3u   %3u %-3u   " | ||||
| @@ -91,7 +93,7 @@ minstrel_ht_stats_dump(struct minstrel_h | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
| -				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| +				prob_ewmsd / 10, prob_ewmsd % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| @@ -185,6 +187,7 @@ minstrel_ht_stats_csv_dump(struct minstr | ||||
|  		struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; | ||||
|  		static const int bitrates[4] = { 10, 20, 55, 110 }; | ||||
|  		int idx = i * MCS_GROUP_RATES + j; | ||||
| +		unsigned int prob_ewmsd; | ||||
|   | ||||
|  		if (!(mi->supported[i] & BIT(j))) | ||||
|  			continue; | ||||
| @@ -225,13 +228,14 @@ minstrel_ht_stats_csv_dump(struct minstr | ||||
|  		tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); | ||||
|  		tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); | ||||
|  		eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||||
| +		prob_ewmsd = minstrel_get_ewmsd10(mrs); | ||||
|   | ||||
|  		p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u," | ||||
|  				"%u,%llu,%llu,", | ||||
|  				tp_max / 10, tp_max % 10, | ||||
|  				tp_avg / 10, tp_avg % 10, | ||||
|  				eprob / 10, eprob % 10, | ||||
| -				mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||||
| +				prob_ewmsd / 10, prob_ewmsd % 10, | ||||
|  				mrs->retry_count, | ||||
|  				mrs->last_success, | ||||
|  				mrs->last_attempts, | ||||
| @@ -1,20 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:19:56 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel: make prob_ewma u16 instead of u32 | ||||
|  | ||||
| Saves about 1.2 KiB memory per station | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel.h | ||||
| +++ b/net/mac80211/rc80211_minstrel.h | ||||
| @@ -59,7 +59,7 @@ struct minstrel_rate_stats { | ||||
|  	/* statistis of packet delivery probability | ||||
|  	 *  prob_ewma - exponential weighted moving average of prob | ||||
|  	 *  prob_ewmsd - exp. weighted moving standard deviation of prob */ | ||||
| -	unsigned int prob_ewma; | ||||
| +	u16 prob_ewma; | ||||
|  	u16 prob_ewmv; | ||||
|   | ||||
|  	/* maximum retry counts */ | ||||
| @@ -1,80 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 14 Dec 2016 20:23:29 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel_ht: remove obsolete #if for >= 3 streams | ||||
|  | ||||
| This was added during early development when 3x3 hardware was not very | ||||
| common yet. This is completely unnecessary now. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel_ht.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht.c | ||||
| @@ -155,67 +155,47 @@ MODULE_PARM_DESC(minstrel_vht_only, | ||||
|  const struct mcs_group minstrel_mcs_groups[] = { | ||||
|  	MCS_GROUP(1, 0, BW_20), | ||||
|  	MCS_GROUP(2, 0, BW_20), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	MCS_GROUP(3, 0, BW_20), | ||||
| -#endif | ||||
|   | ||||
|  	MCS_GROUP(1, 1, BW_20), | ||||
|  	MCS_GROUP(2, 1, BW_20), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	MCS_GROUP(3, 1, BW_20), | ||||
| -#endif | ||||
|   | ||||
|  	MCS_GROUP(1, 0, BW_40), | ||||
|  	MCS_GROUP(2, 0, BW_40), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	MCS_GROUP(3, 0, BW_40), | ||||
| -#endif | ||||
|   | ||||
|  	MCS_GROUP(1, 1, BW_40), | ||||
|  	MCS_GROUP(2, 1, BW_40), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	MCS_GROUP(3, 1, BW_40), | ||||
| -#endif | ||||
|   | ||||
|  	CCK_GROUP, | ||||
|   | ||||
|  #ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT | ||||
|  	VHT_GROUP(1, 0, BW_20), | ||||
|  	VHT_GROUP(2, 0, BW_20), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	VHT_GROUP(3, 0, BW_20), | ||||
| -#endif | ||||
|   | ||||
|  	VHT_GROUP(1, 1, BW_20), | ||||
|  	VHT_GROUP(2, 1, BW_20), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	VHT_GROUP(3, 1, BW_20), | ||||
| -#endif | ||||
|   | ||||
|  	VHT_GROUP(1, 0, BW_40), | ||||
|  	VHT_GROUP(2, 0, BW_40), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	VHT_GROUP(3, 0, BW_40), | ||||
| -#endif | ||||
|   | ||||
|  	VHT_GROUP(1, 1, BW_40), | ||||
|  	VHT_GROUP(2, 1, BW_40), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	VHT_GROUP(3, 1, BW_40), | ||||
| -#endif | ||||
|   | ||||
|  	VHT_GROUP(1, 0, BW_80), | ||||
|  	VHT_GROUP(2, 0, BW_80), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	VHT_GROUP(3, 0, BW_80), | ||||
| -#endif | ||||
|   | ||||
|  	VHT_GROUP(1, 1, BW_80), | ||||
|  	VHT_GROUP(2, 1, BW_80), | ||||
| -#if MINSTREL_MAX_STREAMS >= 3 | ||||
|  	VHT_GROUP(3, 1, BW_80), | ||||
|  #endif | ||||
| -#endif | ||||
|  }; | ||||
|   | ||||
|  static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; | ||||
| @@ -1,150 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Tue, 15 Nov 2016 12:05:11 +0100 | ||||
| Subject: [PATCH] cfg80211: limit scan results cache size | ||||
|  | ||||
| It's possible to make scanning consume almost arbitrary amounts | ||||
| of memory, e.g. by sending beacon frames with random BSSIDs at | ||||
| high rates while somebody is scanning. | ||||
|  | ||||
| Limit the number of BSS table entries we're willing to cache to | ||||
| 1000, limiting maximum memory usage to maybe 4-5MB, but lower | ||||
| in practice - that would be the case for having both full-sized | ||||
| beacon and probe response frames for each entry; this seems not | ||||
| possible in practice, so a limit of 1000 entries will likely be | ||||
| closer to 0.5 MB. | ||||
|  | ||||
| Cc: stable@vger.kernel.org | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/wireless/core.h | ||||
| +++ b/net/wireless/core.h | ||||
| @@ -71,6 +71,7 @@ struct cfg80211_registered_device { | ||||
|  	struct list_head bss_list; | ||||
|  	struct rb_root bss_tree; | ||||
|  	u32 bss_generation; | ||||
| +	u32 bss_entries; | ||||
|  	struct cfg80211_scan_request *scan_req; /* protected by RTNL */ | ||||
|  	struct sk_buff *scan_msg; | ||||
|  	struct cfg80211_sched_scan_request __rcu *sched_scan_req; | ||||
| --- a/net/wireless/scan.c | ||||
| +++ b/net/wireless/scan.c | ||||
| @@ -57,6 +57,19 @@ | ||||
|   * also linked into the probe response struct. | ||||
|   */ | ||||
|   | ||||
| +/* | ||||
| + * Limit the number of BSS entries stored in mac80211. Each one is | ||||
| + * a bit over 4k at most, so this limits to roughly 4-5M of memory. | ||||
| + * If somebody wants to really attack this though, they'd likely | ||||
| + * use small beacons, and only one type of frame, limiting each of | ||||
| + * the entries to a much smaller size (in order to generate more | ||||
| + * entries in total, so overhead is bigger.) | ||||
| + */ | ||||
| +static int bss_entries_limit = 1000; | ||||
| +module_param(bss_entries_limit, int, 0644); | ||||
| +MODULE_PARM_DESC(bss_entries_limit, | ||||
| +                 "limit to number of scan BSS entries (per wiphy, default 1000)"); | ||||
| + | ||||
|  #define IEEE80211_SCAN_RESULT_EXPIRE	(30 * HZ) | ||||
|   | ||||
|  static void bss_free(struct cfg80211_internal_bss *bss) | ||||
| @@ -137,6 +150,10 @@ static bool __cfg80211_unlink_bss(struct | ||||
|   | ||||
|  	list_del_init(&bss->list); | ||||
|  	rb_erase(&bss->rbn, &rdev->bss_tree); | ||||
| +	rdev->bss_entries--; | ||||
| +	WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list), | ||||
| +		  "rdev bss entries[%d]/list[empty:%d] corruption\n", | ||||
| +		  rdev->bss_entries, list_empty(&rdev->bss_list)); | ||||
|  	bss_ref_put(rdev, bss); | ||||
|  	return true; | ||||
|  } | ||||
| @@ -163,6 +180,40 @@ static void __cfg80211_bss_expire(struct | ||||
|  		rdev->bss_generation++; | ||||
|  } | ||||
|   | ||||
| +static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev) | ||||
| +{ | ||||
| +	struct cfg80211_internal_bss *bss, *oldest = NULL; | ||||
| +	bool ret; | ||||
| + | ||||
| +	lockdep_assert_held(&rdev->bss_lock); | ||||
| + | ||||
| +	list_for_each_entry(bss, &rdev->bss_list, list) { | ||||
| +		if (atomic_read(&bss->hold)) | ||||
| +			continue; | ||||
| + | ||||
| +		if (!list_empty(&bss->hidden_list) && | ||||
| +		    !bss->pub.hidden_beacon_bss) | ||||
| +			continue; | ||||
| + | ||||
| +		if (oldest && time_before(oldest->ts, bss->ts)) | ||||
| +			continue; | ||||
| +		oldest = bss; | ||||
| +	} | ||||
| + | ||||
| +	if (WARN_ON(!oldest)) | ||||
| +		return false; | ||||
| + | ||||
| +	/* | ||||
| +	 * The callers make sure to increase rdev->bss_generation if anything | ||||
| +	 * gets removed (and a new entry added), so there's no need to also do | ||||
| +	 * it here. | ||||
| +	 */ | ||||
| + | ||||
| +	ret = __cfg80211_unlink_bss(rdev, oldest); | ||||
| +	WARN_ON(!ret); | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, | ||||
|  			   bool send_message) | ||||
|  { | ||||
| @@ -689,6 +740,7 @@ static bool cfg80211_combine_bsses(struc | ||||
|  	const u8 *ie; | ||||
|  	int i, ssidlen; | ||||
|  	u8 fold = 0; | ||||
| +	u32 n_entries = 0; | ||||
|   | ||||
|  	ies = rcu_access_pointer(new->pub.beacon_ies); | ||||
|  	if (WARN_ON(!ies)) | ||||
| @@ -712,6 +764,12 @@ static bool cfg80211_combine_bsses(struc | ||||
|  	/* This is the bad part ... */ | ||||
|   | ||||
|  	list_for_each_entry(bss, &rdev->bss_list, list) { | ||||
| +		/* | ||||
| +		 * we're iterating all the entries anyway, so take the | ||||
| +		 * opportunity to validate the list length accounting | ||||
| +		 */ | ||||
| +		n_entries++; | ||||
| + | ||||
|  		if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) | ||||
|  			continue; | ||||
|  		if (bss->pub.channel != new->pub.channel) | ||||
| @@ -740,6 +798,10 @@ static bool cfg80211_combine_bsses(struc | ||||
|  				   new->pub.beacon_ies); | ||||
|  	} | ||||
|   | ||||
| +	WARN_ONCE(n_entries != rdev->bss_entries, | ||||
| +		  "rdev bss entries[%d]/list[len:%d] corruption\n", | ||||
| +		  rdev->bss_entries, n_entries); | ||||
| + | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| @@ -894,7 +956,14 @@ cfg80211_bss_update(struct cfg80211_regi | ||||
|  			} | ||||
|  		} | ||||
|   | ||||
| +		if (rdev->bss_entries >= bss_entries_limit && | ||||
| +		    !cfg80211_bss_expire_oldest(rdev)) { | ||||
| +			kfree(new); | ||||
| +			goto drop; | ||||
| +		} | ||||
| + | ||||
|  		list_add_tail(&new->list, &rdev->bss_list); | ||||
| +		rdev->bss_entries++; | ||||
|  		rb_insert_bss(rdev, new); | ||||
|  		found = new; | ||||
|  	} | ||||
| @@ -1,651 +0,0 @@ | ||||
| From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk> | ||||
| Date: Mon, 5 Dec 2016 13:27:37 +0200 | ||||
| Subject: [PATCH] ath9k: Introduce airtime fairness scheduling between stations | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This reworks the ath9k driver to schedule transmissions to connected | ||||
| stations in a way that enforces airtime fairness between them. It | ||||
| accomplishes this by measuring the time spent transmitting to or | ||||
| receiving from a station at TX and RX completion, and accounting this to | ||||
| a per-station, per-QoS level airtime deficit. Then, an FQ-CoDel based | ||||
| deficit scheduler is employed at packet dequeue time, to control which | ||||
| station gets the next transmission opportunity. | ||||
|  | ||||
| Airtime fairness can significantly improve the efficiency of the network | ||||
| when station rates vary. The following throughput values are from a | ||||
| simple three-station test scenario, where two stations operate at the | ||||
| highest HT20 rate, and one station at the lowest, and the scheduler is | ||||
| employed at the access point: | ||||
|  | ||||
|                   Before   /   After | ||||
| Fast station 1:    19.17   /   25.09 Mbps | ||||
| Fast station 2:    19.83   /   25.21 Mbps | ||||
| Slow station:       2.58   /    1.77 Mbps | ||||
| Total:             41.58   /   52.07 Mbps | ||||
|  | ||||
| The benefit of airtime fairness goes up the more stations are present. | ||||
| In a 30-station test with one station artificially limited to 1 Mbps, | ||||
| we have seen aggregate throughput go from 2.14 to 17.76 Mbps. | ||||
|  | ||||
| Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> | ||||
| Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ath9k.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | ||||
| @@ -112,6 +112,8 @@ int ath_descdma_setup(struct ath_softc * | ||||
|  #define ATH_TXFIFO_DEPTH           8 | ||||
|  #define ATH_TX_ERROR               0x01 | ||||
|   | ||||
| +#define ATH_AIRTIME_QUANTUM        300 /* usec */ | ||||
| + | ||||
|  /* Stop tx traffic 1ms before the GO goes away */ | ||||
|  #define ATH_P2P_PS_STOP_TIME       1000 | ||||
|   | ||||
| @@ -247,6 +249,9 @@ struct ath_atx_tid { | ||||
|  	bool has_queued; | ||||
|  }; | ||||
|   | ||||
| +void __ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid); | ||||
| +void ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid); | ||||
| + | ||||
|  struct ath_node { | ||||
|  	struct ath_softc *sc; | ||||
|  	struct ieee80211_sta *sta; /* station struct we're part of */ | ||||
| @@ -258,9 +263,12 @@ struct ath_node { | ||||
|   | ||||
|  	bool sleeping; | ||||
|  	bool no_ps_filter; | ||||
| +	s64 airtime_deficit[IEEE80211_NUM_ACS]; | ||||
| +	u32 airtime_rx_start; | ||||
|   | ||||
|  #ifdef CPTCFG_ATH9K_STATION_STATISTICS | ||||
|  	struct ath_rx_rate_stats rx_rate_stats; | ||||
| +	struct ath_airtime_stats airtime_stats; | ||||
|  #endif | ||||
|  	u8 key_idx[4]; | ||||
|   | ||||
| @@ -317,10 +325,16 @@ struct ath_rx { | ||||
|  /* Channel Context */ | ||||
|  /*******************/ | ||||
|   | ||||
| +struct ath_acq { | ||||
| +	struct list_head acq_new; | ||||
| +	struct list_head acq_old; | ||||
| +	spinlock_t lock; | ||||
| +}; | ||||
| + | ||||
|  struct ath_chanctx { | ||||
|  	struct cfg80211_chan_def chandef; | ||||
|  	struct list_head vifs; | ||||
| -	struct list_head acq[IEEE80211_NUM_ACS]; | ||||
| +	struct ath_acq acq[IEEE80211_NUM_ACS]; | ||||
|  	int hw_queue_base; | ||||
|   | ||||
|  	/* do not dereference, use for comparison only */ | ||||
| @@ -575,6 +589,8 @@ void ath_txq_schedule_all(struct ath_sof | ||||
|  int ath_tx_init(struct ath_softc *sc, int nbufs); | ||||
|  int ath_txq_update(struct ath_softc *sc, int qnum, | ||||
|  		   struct ath9k_tx_queue_info *q); | ||||
| +u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, | ||||
| +		     int width, int half_gi, bool shortPreamble); | ||||
|  void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); | ||||
|  void ath_assign_seq(struct ath_common *common, struct sk_buff *skb); | ||||
|  int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, | ||||
| @@ -963,6 +979,11 @@ void ath_ant_comb_scan(struct ath_softc | ||||
|   | ||||
|  #define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */ | ||||
|   | ||||
| +#define AIRTIME_USE_TX		BIT(0) | ||||
| +#define AIRTIME_USE_RX		BIT(1) | ||||
| +#define AIRTIME_USE_NEW_QUEUES	BIT(2) | ||||
| +#define AIRTIME_ACTIVE(flags) (!!(flags & (AIRTIME_USE_TX|AIRTIME_USE_RX))) | ||||
| + | ||||
|  struct ath_softc { | ||||
|  	struct ieee80211_hw *hw; | ||||
|  	struct device *dev; | ||||
| @@ -1005,6 +1026,8 @@ struct ath_softc { | ||||
|  	short nbcnvifs; | ||||
|  	unsigned long ps_usecount; | ||||
|   | ||||
| +	u16 airtime_flags; /* AIRTIME_* */ | ||||
| + | ||||
|  	struct ath_rx rx; | ||||
|  	struct ath_tx tx; | ||||
|  	struct ath_beacon beacon; | ||||
| --- a/drivers/net/wireless/ath/ath9k/channel.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/channel.c | ||||
| @@ -118,8 +118,11 @@ void ath_chanctx_init(struct ath_softc * | ||||
|  		INIT_LIST_HEAD(&ctx->vifs); | ||||
|  		ctx->txpower = ATH_TXPOWER_MAX; | ||||
|  		ctx->flush_timeout = HZ / 5; /* 200ms */ | ||||
| -		for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) | ||||
| -			INIT_LIST_HEAD(&ctx->acq[j]); | ||||
| +		for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) { | ||||
| +			INIT_LIST_HEAD(&ctx->acq[j].acq_new); | ||||
| +			INIT_LIST_HEAD(&ctx->acq[j].acq_old); | ||||
| +			spin_lock_init(&ctx->acq[j].lock); | ||||
| +		} | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -1345,8 +1348,11 @@ void ath9k_offchannel_init(struct ath_so | ||||
|  	ctx->txpower = ATH_TXPOWER_MAX; | ||||
|  	cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); | ||||
|   | ||||
| -	for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) | ||||
| -		INIT_LIST_HEAD(&ctx->acq[i]); | ||||
| +	for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) { | ||||
| +		INIT_LIST_HEAD(&ctx->acq[i].acq_new); | ||||
| +		INIT_LIST_HEAD(&ctx->acq[i].acq_old); | ||||
| +		spin_lock_init(&ctx->acq[i].lock); | ||||
| +	} | ||||
|   | ||||
|  	sc->offchannel.chan.offchannel = true; | ||||
|  } | ||||
| --- a/drivers/net/wireless/ath/ath9k/debug.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/debug.c | ||||
| @@ -1399,5 +1399,8 @@ int ath9k_init_debug(struct ath_hw *ah) | ||||
|  	debugfs_create_file("tpc", S_IRUSR | S_IWUSR, | ||||
|  			    sc->debug.debugfs_phy, sc, &fops_tpc); | ||||
|   | ||||
| +	debugfs_create_u16("airtime_flags", S_IRUSR | S_IWUSR, | ||||
| +			   sc->debug.debugfs_phy, &sc->airtime_flags); | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
| --- a/drivers/net/wireless/ath/ath9k/debug.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/debug.h | ||||
| @@ -221,6 +221,11 @@ struct ath_rx_rate_stats { | ||||
|  	} cck_stats[4]; | ||||
|  }; | ||||
|   | ||||
| +struct ath_airtime_stats { | ||||
| +	u32 rx_airtime; | ||||
| +	u32 tx_airtime; | ||||
| +}; | ||||
| + | ||||
|  #define ANT_MAIN 0 | ||||
|  #define ANT_ALT  1 | ||||
|   | ||||
| @@ -314,12 +319,20 @@ ath9k_debug_sync_cause(struct ath_softc | ||||
|  void ath_debug_rate_stats(struct ath_softc *sc, | ||||
|  			  struct ath_rx_status *rs, | ||||
|  			  struct sk_buff *skb); | ||||
| +void ath_debug_airtime(struct ath_softc *sc, | ||||
| +		       struct ath_node *an, | ||||
| +		       u32 rx, u32 tx); | ||||
|  #else | ||||
|  static inline void ath_debug_rate_stats(struct ath_softc *sc, | ||||
|  					struct ath_rx_status *rs, | ||||
|  					struct sk_buff *skb) | ||||
|  { | ||||
|  } | ||||
| +static inline void ath_debug_airtime(struct ath_softc *sc, | ||||
| +			      struct ath_node *an, | ||||
| +			      u32 rx, u32 tx) | ||||
| +{ | ||||
| +} | ||||
|  #endif /* CPTCFG_ATH9K_STATION_STATISTICS */ | ||||
|   | ||||
|  #endif /* DEBUG_H */ | ||||
| --- a/drivers/net/wireless/ath/ath9k/debug_sta.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c | ||||
| @@ -242,6 +242,59 @@ static const struct file_operations fops | ||||
|  	.llseek = default_llseek, | ||||
|  }; | ||||
|   | ||||
| +void ath_debug_airtime(struct ath_softc *sc, | ||||
| +		struct ath_node *an, | ||||
| +		u32 rx, | ||||
| +		u32 tx) | ||||
| +{ | ||||
| +	struct ath_airtime_stats *astats = &an->airtime_stats; | ||||
| + | ||||
| +	astats->rx_airtime += rx; | ||||
| +	astats->tx_airtime += tx; | ||||
| +} | ||||
| + | ||||
| +static ssize_t read_airtime(struct file *file, char __user *user_buf, | ||||
| +			size_t count, loff_t *ppos) | ||||
| +{ | ||||
| +	struct ath_node *an = file->private_data; | ||||
| +	struct ath_airtime_stats *astats; | ||||
| +	static const char *qname[4] = { | ||||
| +		"VO", "VI", "BE", "BK" | ||||
| +	}; | ||||
| +	u32 len = 0, size = 256; | ||||
| +	char *buf; | ||||
| +	size_t retval; | ||||
| +	int i; | ||||
| + | ||||
| +	buf = kzalloc(size, GFP_KERNEL); | ||||
| +	if (buf == NULL) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	astats = &an->airtime_stats; | ||||
| + | ||||
| +	len += scnprintf(buf + len, size - len, "RX: %u us\n", astats->rx_airtime); | ||||
| +	len += scnprintf(buf + len, size - len, "TX: %u us\n", astats->tx_airtime); | ||||
| +	len += scnprintf(buf + len, size - len, "Deficit: "); | ||||
| +	for (i = 0; i < 4; i++) | ||||
| +		len += scnprintf(buf+len, size - len, "%s: %lld us ", qname[i], an->airtime_deficit[i]); | ||||
| +	if (len < size) | ||||
| +		buf[len++] = '\n'; | ||||
| + | ||||
| +	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||||
| +	kfree(buf); | ||||
| + | ||||
| +	return retval; | ||||
| +} | ||||
| + | ||||
| + | ||||
| +static const struct file_operations fops_airtime = { | ||||
| +	.read = read_airtime, | ||||
| +	.open = simple_open, | ||||
| +	.owner = THIS_MODULE, | ||||
| +	.llseek = default_llseek, | ||||
| +}; | ||||
| + | ||||
| + | ||||
|  void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, | ||||
|  			   struct ieee80211_vif *vif, | ||||
|  			   struct ieee80211_sta *sta, | ||||
| @@ -251,4 +304,5 @@ void ath9k_sta_add_debugfs(struct ieee80 | ||||
|   | ||||
|  	debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr); | ||||
|  	debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv); | ||||
| +	debugfs_create_file("airtime", S_IRUGO, dir, an, &fops_airtime); | ||||
|  } | ||||
| --- a/drivers/net/wireless/ath/ath9k/init.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/init.c | ||||
| @@ -620,6 +620,8 @@ static int ath9k_init_softc(u16 devid, s | ||||
|   | ||||
|  	/* Will be cleared in ath9k_start() */ | ||||
|  	set_bit(ATH_OP_INVALID, &common->op_flags); | ||||
| +	sc->airtime_flags = (AIRTIME_USE_TX | AIRTIME_USE_RX | | ||||
| +			     AIRTIME_USE_NEW_QUEUES); | ||||
|   | ||||
|  	sc->sc_ah = ah; | ||||
|  	sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); | ||||
| --- a/drivers/net/wireless/ath/ath9k/main.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/main.c | ||||
| @@ -70,10 +70,10 @@ static bool ath9k_has_pending_frames(str | ||||
|  		goto out; | ||||
|   | ||||
|  	if (txq->mac80211_qnum >= 0) { | ||||
| -		struct list_head *list; | ||||
| +		struct ath_acq *acq; | ||||
|   | ||||
| -		list = &sc->cur_chan->acq[txq->mac80211_qnum]; | ||||
| -		if (!list_empty(list)) | ||||
| +		acq = &sc->cur_chan->acq[txq->mac80211_qnum]; | ||||
| +		if (!list_empty(&acq->acq_new) || !list_empty(&acq->acq_old)) | ||||
|  			pending = true; | ||||
|  	} | ||||
|  out: | ||||
| --- a/drivers/net/wireless/ath/ath9k/recv.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/recv.c | ||||
| @@ -991,6 +991,70 @@ static void ath9k_apply_ampdu_details(st | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +static void ath_rx_count_airtime(struct ath_softc *sc, | ||||
| +				 struct ath_rx_status *rs, | ||||
| +				 struct sk_buff *skb) | ||||
| +{ | ||||
| +	struct ath_node *an; | ||||
| +	struct ath_acq *acq; | ||||
| +	struct ath_vif *avp; | ||||
| +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||||
| +	struct ath_hw *ah = sc->sc_ah; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	struct ieee80211_sta *sta; | ||||
| +	struct ieee80211_rx_status *rxs; | ||||
| +	const struct ieee80211_rate *rate; | ||||
| +	bool is_sgi, is_40, is_sp; | ||||
| +	int phy; | ||||
| +	u16 len = rs->rs_datalen; | ||||
| +	u32 airtime = 0; | ||||
| +	u8 tidno, acno; | ||||
| + | ||||
| +	if (!ieee80211_is_data(hdr->frame_control)) | ||||
| +		return; | ||||
| + | ||||
| +	rcu_read_lock(); | ||||
| + | ||||
| +	sta = ieee80211_find_sta_by_ifaddr(sc->hw, hdr->addr2, NULL); | ||||
| +	if (!sta) | ||||
| +		goto exit; | ||||
| +	an = (struct ath_node *) sta->drv_priv; | ||||
| +	avp = (struct ath_vif *) an->vif->drv_priv; | ||||
| +	tidno = skb->priority & IEEE80211_QOS_CTL_TID_MASK; | ||||
| +	acno = TID_TO_WME_AC(tidno); | ||||
| +	acq = &avp->chanctx->acq[acno]; | ||||
| + | ||||
| +	rxs = IEEE80211_SKB_RXCB(skb); | ||||
| + | ||||
| +	is_sgi = !!(rxs->flag & RX_FLAG_SHORT_GI); | ||||
| +	is_40 = !!(rxs->flag & RX_FLAG_40MHZ); | ||||
| +	is_sp = !!(rxs->flag & RX_FLAG_SHORTPRE); | ||||
| + | ||||
| +	if (!!(rxs->flag & RX_FLAG_HT)) { | ||||
| +		/* MCS rates */ | ||||
| + | ||||
| +		airtime += ath_pkt_duration(sc, rxs->rate_idx, len, | ||||
| +					is_40, is_sgi, is_sp); | ||||
| +	} else { | ||||
| + | ||||
| +		phy = IS_CCK_RATE(rs->rs_rate) ? WLAN_RC_PHY_CCK : WLAN_RC_PHY_OFDM; | ||||
| +		rate = &common->sbands[rxs->band].bitrates[rxs->rate_idx]; | ||||
| +		airtime += ath9k_hw_computetxtime(ah, phy, rate->bitrate * 100, | ||||
| +						len, rxs->rate_idx, is_sp); | ||||
| +	} | ||||
| + | ||||
| + 	if (!!(sc->airtime_flags & AIRTIME_USE_RX)) { | ||||
| +		spin_lock_bh(&acq->lock); | ||||
| +		an->airtime_deficit[acno] -= airtime; | ||||
| +		if (an->airtime_deficit[acno] <= 0) | ||||
| +			__ath_tx_queue_tid(sc, ATH_AN_2_TID(an, tidno)); | ||||
| +		spin_unlock_bh(&acq->lock); | ||||
| +	} | ||||
| +	ath_debug_airtime(sc, an, airtime, 0); | ||||
| +exit: | ||||
| +	rcu_read_unlock(); | ||||
| +} | ||||
| + | ||||
|  int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) | ||||
|  { | ||||
|  	struct ath_rxbuf *bf; | ||||
| @@ -1137,6 +1201,7 @@ int ath_rx_tasklet(struct ath_softc *sc, | ||||
|  		ath9k_antenna_check(sc, &rs); | ||||
|  		ath9k_apply_ampdu_details(sc, &rs, rxs); | ||||
|  		ath_debug_rate_stats(sc, &rs, skb); | ||||
| +		ath_rx_count_airtime(sc, &rs, skb); | ||||
|   | ||||
|  		hdr = (struct ieee80211_hdr *)skb->data; | ||||
|  		if (ieee80211_is_ack(hdr->frame_control)) | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| @@ -125,21 +125,44 @@ void ath_txq_unlock_complete(struct ath_ | ||||
|  		ath_tx_status(hw, skb); | ||||
|  } | ||||
|   | ||||
| -static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq, | ||||
| -			     struct ath_atx_tid *tid) | ||||
| +void __ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid) | ||||
|  { | ||||
| -	struct list_head *list; | ||||
|  	struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv; | ||||
|  	struct ath_chanctx *ctx = avp->chanctx; | ||||
| +	struct ath_acq *acq; | ||||
| +	struct list_head *tid_list; | ||||
| +	u8 acno = TID_TO_WME_AC(tid->tidno); | ||||
|   | ||||
| -	if (!ctx) | ||||
| +	if (!ctx || !list_empty(&tid->list)) | ||||
|  		return; | ||||
|   | ||||
| -	list = &ctx->acq[TID_TO_WME_AC(tid->tidno)]; | ||||
| -	if (list_empty(&tid->list)) | ||||
| -		list_add_tail(&tid->list, list); | ||||
| + | ||||
| +	acq = &ctx->acq[acno]; | ||||
| +	if ((sc->airtime_flags & AIRTIME_USE_NEW_QUEUES) && | ||||
| +	    tid->an->airtime_deficit[acno] > 0) | ||||
| +		tid_list = &acq->acq_new; | ||||
| +	else | ||||
| +		tid_list = &acq->acq_old; | ||||
| + | ||||
| +	list_add_tail(&tid->list, tid_list); | ||||
|  } | ||||
|   | ||||
| +void ath_tx_queue_tid(struct ath_softc *sc, struct ath_atx_tid *tid) | ||||
| +{ | ||||
| +	struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv; | ||||
| +	struct ath_chanctx *ctx = avp->chanctx; | ||||
| +	struct ath_acq *acq; | ||||
| + | ||||
| +	if (!ctx || !list_empty(&tid->list)) | ||||
| +		return; | ||||
| + | ||||
| +	acq = &ctx->acq[TID_TO_WME_AC(tid->tidno)]; | ||||
| +	spin_lock_bh(&acq->lock); | ||||
| +	__ath_tx_queue_tid(sc, tid); | ||||
| +	spin_unlock_bh(&acq->lock); | ||||
| +} | ||||
| + | ||||
| + | ||||
|  void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue) | ||||
|  { | ||||
|  	struct ath_softc *sc = hw->priv; | ||||
| @@ -154,7 +177,7 @@ void ath9k_wake_tx_queue(struct ieee8021 | ||||
|  	ath_txq_lock(sc, txq); | ||||
|   | ||||
|  	tid->has_queued = true; | ||||
| -	ath_tx_queue_tid(sc, txq, tid); | ||||
| +	ath_tx_queue_tid(sc, tid); | ||||
|  	ath_txq_schedule(sc, txq); | ||||
|   | ||||
|  	ath_txq_unlock(sc, txq); | ||||
| @@ -684,7 +707,7 @@ static void ath_tx_complete_aggr(struct | ||||
|   | ||||
|  		skb_queue_splice_tail(&bf_pending, &tid->retry_q); | ||||
|  		if (!an->sleeping) { | ||||
| -			ath_tx_queue_tid(sc, txq, tid); | ||||
| +			ath_tx_queue_tid(sc, tid); | ||||
|   | ||||
|  			if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) | ||||
|  				tid->clear_ps_filter = true; | ||||
| @@ -712,6 +735,53 @@ static bool bf_is_ampdu_not_probing(stru | ||||
|      return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); | ||||
|  } | ||||
|   | ||||
| +static void ath_tx_count_airtime(struct ath_softc *sc, struct ath_txq *txq, | ||||
| +				 struct ath_buf *bf, struct ath_tx_status *ts) | ||||
| +{ | ||||
| +	struct ath_node *an; | ||||
| +	struct ath_acq *acq = &sc->cur_chan->acq[txq->mac80211_qnum]; | ||||
| +	struct sk_buff *skb; | ||||
| +	struct ieee80211_hdr *hdr; | ||||
| +	struct ieee80211_hw *hw = sc->hw; | ||||
| +	struct ieee80211_tx_rate rates[4]; | ||||
| +	struct ieee80211_sta *sta; | ||||
| +	int i; | ||||
| +	u32 airtime = 0; | ||||
| + | ||||
| +	skb = bf->bf_mpdu; | ||||
| +	if(!skb) | ||||
| +		return; | ||||
| + | ||||
| +	hdr = (struct ieee80211_hdr *)skb->data; | ||||
| +	memcpy(rates, bf->rates, sizeof(rates)); | ||||
| + | ||||
| +	rcu_read_lock(); | ||||
| + | ||||
| +	sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); | ||||
| +	if(!sta) | ||||
| +		goto exit; | ||||
| + | ||||
| + | ||||
| +	an = (struct ath_node *) sta->drv_priv; | ||||
| + | ||||
| +	airtime += ts->duration * (ts->ts_longretry + 1); | ||||
| + | ||||
| +	for(i=0; i < ts->ts_rateindex; i++) | ||||
| +		airtime += ath9k_hw_get_duration(sc->sc_ah, bf->bf_desc, i) * rates[i].count; | ||||
| + | ||||
| +	if (!!(sc->airtime_flags & AIRTIME_USE_TX)) { | ||||
| +		spin_lock_bh(&acq->lock); | ||||
| +		an->airtime_deficit[txq->mac80211_qnum] -= airtime; | ||||
| +		if (an->airtime_deficit[txq->mac80211_qnum] <= 0) | ||||
| +			__ath_tx_queue_tid(sc, ath_get_skb_tid(sc, an, skb)); | ||||
| +		spin_unlock_bh(&acq->lock); | ||||
| +	} | ||||
| +	ath_debug_airtime(sc, an, 0, airtime); | ||||
| + | ||||
| +exit: | ||||
| +	rcu_read_unlock(); | ||||
| +} | ||||
| + | ||||
|  static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, | ||||
|  				  struct ath_tx_status *ts, struct ath_buf *bf, | ||||
|  				  struct list_head *bf_head) | ||||
| @@ -733,6 +803,7 @@ static void ath_tx_process_buffer(struct | ||||
|   | ||||
|  	ts->duration = ath9k_hw_get_duration(sc->sc_ah, bf->bf_desc, | ||||
|  					     ts->ts_rateindex); | ||||
| +	ath_tx_count_airtime(sc, txq, bf, ts); | ||||
|   | ||||
|  	hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data; | ||||
|  	sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2); | ||||
| @@ -1094,8 +1165,8 @@ finish: | ||||
|   * width  - 0 for 20 MHz, 1 for 40 MHz | ||||
|   * half_gi - to use 4us v/s 3.6 us for symbol time | ||||
|   */ | ||||
| -static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, | ||||
| -			    int width, int half_gi, bool shortPreamble) | ||||
| +u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, | ||||
| +		     int width, int half_gi, bool shortPreamble) | ||||
|  { | ||||
|  	u32 nbits, nsymbits, duration, nsymbols; | ||||
|  	int streams; | ||||
| @@ -1493,7 +1564,7 @@ ath_tx_form_burst(struct ath_softc *sc, | ||||
|  } | ||||
|   | ||||
|  static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, | ||||
| -			      struct ath_atx_tid *tid, bool *stop) | ||||
| +			      struct ath_atx_tid *tid) | ||||
|  { | ||||
|  	struct ath_buf *bf; | ||||
|  	struct ieee80211_tx_info *tx_info; | ||||
| @@ -1515,7 +1586,6 @@ static bool ath_tx_sched_aggr(struct ath | ||||
|  	if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) || | ||||
|  	    (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { | ||||
|  		__skb_queue_tail(&tid->retry_q, bf->bf_mpdu); | ||||
| -		*stop = true; | ||||
|  		return false; | ||||
|  	} | ||||
|   | ||||
| @@ -1639,7 +1709,7 @@ void ath_tx_aggr_wakeup(struct ath_softc | ||||
|  		ath_txq_lock(sc, txq); | ||||
|  		tid->clear_ps_filter = true; | ||||
|  		if (ath_tid_has_buffered(tid)) { | ||||
| -			ath_tx_queue_tid(sc, txq, tid); | ||||
| +			ath_tx_queue_tid(sc, tid); | ||||
|  			ath_txq_schedule(sc, txq); | ||||
|  		} | ||||
|  		ath_txq_unlock_complete(sc, txq); | ||||
| @@ -1956,9 +2026,10 @@ void ath_tx_cleanupq(struct ath_softc *s | ||||
|  void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) | ||||
|  { | ||||
|  	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||||
| -	struct ath_atx_tid *tid, *last_tid; | ||||
| +	struct ath_atx_tid *tid; | ||||
|  	struct list_head *tid_list; | ||||
| -	bool sent = false; | ||||
| +	struct ath_acq *acq; | ||||
| +	bool active = AIRTIME_ACTIVE(sc->airtime_flags); | ||||
|   | ||||
|  	if (txq->mac80211_qnum < 0) | ||||
|  		return; | ||||
| @@ -1967,48 +2038,55 @@ void ath_txq_schedule(struct ath_softc * | ||||
|  		return; | ||||
|   | ||||
|  	spin_lock_bh(&sc->chan_lock); | ||||
| -	tid_list = &sc->cur_chan->acq[txq->mac80211_qnum]; | ||||
| - | ||||
| -	if (list_empty(tid_list)) { | ||||
| -		spin_unlock_bh(&sc->chan_lock); | ||||
| -		return; | ||||
| -	} | ||||
| - | ||||
|  	rcu_read_lock(); | ||||
| +	acq = &sc->cur_chan->acq[txq->mac80211_qnum]; | ||||
|   | ||||
| -	last_tid = list_entry(tid_list->prev, struct ath_atx_tid, list); | ||||
| -	while (!list_empty(tid_list)) { | ||||
| -		bool stop = false; | ||||
| - | ||||
| -		if (sc->cur_chan->stopped) | ||||
| -			break; | ||||
| - | ||||
| -		tid = list_first_entry(tid_list, struct ath_atx_tid, list); | ||||
| -		list_del_init(&tid->list); | ||||
| +	if (sc->cur_chan->stopped) | ||||
| +		goto out; | ||||
|   | ||||
| -		if (ath_tx_sched_aggr(sc, txq, tid, &stop)) | ||||
| -			sent = true; | ||||
| +begin: | ||||
| +	tid_list = &acq->acq_new; | ||||
| +	if (list_empty(tid_list)) { | ||||
| +		tid_list = &acq->acq_old; | ||||
| +		if (list_empty(tid_list)) | ||||
| +			goto out; | ||||
| +	} | ||||
| +	tid = list_first_entry(tid_list, struct ath_atx_tid, list); | ||||
|   | ||||
| -		/* | ||||
| -		 * add tid to round-robin queue if more frames | ||||
| -		 * are pending for the tid | ||||
| -		 */ | ||||
| -		if (ath_tid_has_buffered(tid)) | ||||
| -			ath_tx_queue_tid(sc, txq, tid); | ||||
| +	if (active && tid->an->airtime_deficit[txq->mac80211_qnum] <= 0) { | ||||
| +		spin_lock_bh(&acq->lock); | ||||
| +		tid->an->airtime_deficit[txq->mac80211_qnum] += ATH_AIRTIME_QUANTUM; | ||||
| +		list_move_tail(&tid->list, &acq->acq_old); | ||||
| +		spin_unlock_bh(&acq->lock); | ||||
| +		goto begin; | ||||
| +	} | ||||
|   | ||||
| -		if (stop) | ||||
| -			break; | ||||
| +	if (!ath_tid_has_buffered(tid)) { | ||||
| +		spin_lock_bh(&acq->lock); | ||||
| +		if ((tid_list == &acq->acq_new) && !list_empty(&acq->acq_old)) | ||||
| +			list_move_tail(&tid->list, &acq->acq_old); | ||||
| +		else { | ||||
| +			list_del_init(&tid->list); | ||||
| +		} | ||||
| +		spin_unlock_bh(&acq->lock); | ||||
| +		goto begin; | ||||
| +	} | ||||
|   | ||||
| -		if (tid == last_tid) { | ||||
| -			if (!sent) | ||||
| -				break; | ||||
|   | ||||
| -			sent = false; | ||||
| -			last_tid = list_entry(tid_list->prev, | ||||
| -					      struct ath_atx_tid, list); | ||||
| +	/* | ||||
| +	 * If we succeed in scheduling something, immediately restart to make | ||||
| +	 * sure we keep the HW busy. | ||||
| +	 */ | ||||
| +	if(ath_tx_sched_aggr(sc, txq, tid)) { | ||||
| +		if (!active) { | ||||
| +			spin_lock_bh(&acq->lock); | ||||
| +			list_move_tail(&tid->list, &acq->acq_old); | ||||
| +			spin_unlock_bh(&acq->lock); | ||||
|  		} | ||||
| +		goto begin; | ||||
|  	} | ||||
|   | ||||
| +out: | ||||
|  	rcu_read_unlock(); | ||||
|  	spin_unlock_bh(&sc->chan_lock); | ||||
|  } | ||||
| @@ -2862,6 +2940,9 @@ void ath_tx_node_init(struct ath_softc * | ||||
|  	struct ath_atx_tid *tid; | ||||
|  	int tidno, acno; | ||||
|   | ||||
| +	for (acno = 0; acno < IEEE80211_NUM_ACS; acno++) | ||||
| +		an->airtime_deficit[acno] = ATH_AIRTIME_QUANTUM; | ||||
| + | ||||
|  	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { | ||||
|  		tid = ath_node_to_tid(an, tidno); | ||||
|  		tid->an        = an; | ||||
| @@ -1,62 +0,0 @@ | ||||
| From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk> | ||||
| Date: Mon, 5 Dec 2016 13:27:38 +0200 | ||||
| Subject: [PATCH] ath9k: Turn ath_txq_lock/unlock() into static inlines. | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| These are one-line functions that just call spin_lock/unlock_bh(); turn | ||||
| them into static inlines to avoid the function call overhead. | ||||
|  | ||||
| Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> | ||||
| Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ath9k.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | ||||
| @@ -569,6 +569,15 @@ static inline void ath_chanctx_check_act | ||||
|   | ||||
|  #endif /* CPTCFG_ATH9K_CHANNEL_CONTEXT */ | ||||
|   | ||||
| +static inline void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq) | ||||
| +{ | ||||
| +	spin_lock_bh(&txq->axq_lock); | ||||
| +} | ||||
| +static inline void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq) | ||||
| +{ | ||||
| +	spin_unlock_bh(&txq->axq_lock); | ||||
| +} | ||||
| + | ||||
|  void ath_startrecv(struct ath_softc *sc); | ||||
|  bool ath_stoprecv(struct ath_softc *sc); | ||||
|  u32 ath_calcrxfilter(struct ath_softc *sc); | ||||
| @@ -576,8 +585,6 @@ int ath_rx_init(struct ath_softc *sc, in | ||||
|  void ath_rx_cleanup(struct ath_softc *sc); | ||||
|  int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp); | ||||
|  struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype); | ||||
| -void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq); | ||||
| -void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq); | ||||
|  void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq); | ||||
|  void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); | ||||
|  bool ath_drain_all_txq(struct ath_softc *sc); | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| @@ -98,18 +98,6 @@ static void ath_tx_status(struct ieee802 | ||||
|  	dev_kfree_skb(skb); | ||||
|  } | ||||
|   | ||||
| -void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq) | ||||
| -	__acquires(&txq->axq_lock) | ||||
| -{ | ||||
| -	spin_lock_bh(&txq->axq_lock); | ||||
| -} | ||||
| - | ||||
| -void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq) | ||||
| -	__releases(&txq->axq_lock) | ||||
| -{ | ||||
| -	spin_unlock_bh(&txq->axq_lock); | ||||
| -} | ||||
| - | ||||
|  void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) | ||||
|  	__releases(&txq->axq_lock) | ||||
|  { | ||||
| @@ -1,23 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Tue, 27 Dec 2016 12:15:14 +0100 | ||||
| Subject: [PATCH] ath5k: drop bogus warning on drv_set_key with unsupported | ||||
|  cipher | ||||
|  | ||||
| Simply return -EOPNOTSUPP instead. | ||||
|  | ||||
| Cc: stable@vger.kernel.org | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c | ||||
| +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c | ||||
| @@ -502,8 +502,7 @@ ath5k_set_key(struct ieee80211_hw *hw, e | ||||
|  			break; | ||||
|  		return -EOPNOTSUPP; | ||||
|  	default: | ||||
| -		WARN_ON(1); | ||||
| -		return -EINVAL; | ||||
| +		return -EOPNOTSUPP; | ||||
|  	} | ||||
|   | ||||
|  	mutex_lock(&ah->lock); | ||||
| @@ -1,101 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Wed, 4 Jan 2017 18:58:30 +0100 | ||||
| Subject: [PATCH] cfg80211: move function checking range fit to util.c | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| It is needed for another cfg80211 helper that will be out of reg.c so | ||||
| move it to common util.c file and make it non-static. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/wireless/core.h | ||||
| +++ b/net/wireless/core.h | ||||
| @@ -429,6 +429,9 @@ int cfg80211_change_iface(struct cfg8021 | ||||
|  void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | ||||
|  void cfg80211_process_wdev_events(struct wireless_dev *wdev); | ||||
|   | ||||
| +bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range, | ||||
| +				u32 center_freq_khz, u32 bw_khz); | ||||
| + | ||||
|  /** | ||||
|   * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable | ||||
|   * @wiphy: the wiphy to validate against | ||||
| --- a/net/wireless/reg.c | ||||
| +++ b/net/wireless/reg.c | ||||
| @@ -748,21 +748,6 @@ static bool is_valid_rd(const struct iee | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| -static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, | ||||
| -			    u32 center_freq_khz, u32 bw_khz) | ||||
| -{ | ||||
| -	u32 start_freq_khz, end_freq_khz; | ||||
| - | ||||
| -	start_freq_khz = center_freq_khz - (bw_khz/2); | ||||
| -	end_freq_khz = center_freq_khz + (bw_khz/2); | ||||
| - | ||||
| -	if (start_freq_khz >= freq_range->start_freq_khz && | ||||
| -	    end_freq_khz <= freq_range->end_freq_khz) | ||||
| -		return true; | ||||
| - | ||||
| -	return false; | ||||
| -} | ||||
| - | ||||
|  /** | ||||
|   * freq_in_rule_band - tells us if a frequency is in a frequency band | ||||
|   * @freq_range: frequency rule we want to query | ||||
| @@ -1070,7 +1055,7 @@ freq_reg_info_regd(u32 center_freq, | ||||
|  		if (!band_rule_found) | ||||
|  			band_rule_found = freq_in_rule_band(fr, center_freq); | ||||
|   | ||||
| -		bw_fits = reg_does_bw_fit(fr, center_freq, bw); | ||||
| +		bw_fits = cfg80211_does_bw_fit_range(fr, center_freq, bw); | ||||
|   | ||||
|  		if (band_rule_found && bw_fits) | ||||
|  			return rr; | ||||
| @@ -1138,11 +1123,13 @@ static uint32_t reg_rule_to_chan_bw_flag | ||||
|  		max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); | ||||
|   | ||||
|  	/* If we get a reg_rule we can assume that at least 5Mhz fit */ | ||||
| -	if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq), | ||||
| -			     MHZ_TO_KHZ(10))) | ||||
| +	if (!cfg80211_does_bw_fit_range(freq_range, | ||||
| +					MHZ_TO_KHZ(chan->center_freq), | ||||
| +					MHZ_TO_KHZ(10))) | ||||
|  		bw_flags |= IEEE80211_CHAN_NO_10MHZ; | ||||
| -	if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq), | ||||
| -			     MHZ_TO_KHZ(20))) | ||||
| +	if (!cfg80211_does_bw_fit_range(freq_range, | ||||
| +					MHZ_TO_KHZ(chan->center_freq), | ||||
| +					MHZ_TO_KHZ(20))) | ||||
|  		bw_flags |= IEEE80211_CHAN_NO_20MHZ; | ||||
|   | ||||
|  	if (max_bandwidth_khz < MHZ_TO_KHZ(10)) | ||||
| --- a/net/wireless/util.c | ||||
| +++ b/net/wireless/util.c | ||||
| @@ -1788,6 +1788,21 @@ void cfg80211_free_nan_func(struct cfg80 | ||||
|  } | ||||
|  EXPORT_SYMBOL(cfg80211_free_nan_func); | ||||
|   | ||||
| +bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range, | ||||
| +				u32 center_freq_khz, u32 bw_khz) | ||||
| +{ | ||||
| +	u32 start_freq_khz, end_freq_khz; | ||||
| + | ||||
| +	start_freq_khz = center_freq_khz - (bw_khz / 2); | ||||
| +	end_freq_khz = center_freq_khz + (bw_khz / 2); | ||||
| + | ||||
| +	if (start_freq_khz >= freq_range->start_freq_khz && | ||||
| +	    end_freq_khz <= freq_range->end_freq_khz) | ||||
| +		return true; | ||||
| + | ||||
| +	return false; | ||||
| +} | ||||
| + | ||||
|  /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ | ||||
|  /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ | ||||
|  const unsigned char rfc1042_header[] __aligned(2) = | ||||
| @@ -1,211 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Wed, 4 Jan 2017 18:58:31 +0100 | ||||
| Subject: [PATCH] cfg80211: support ieee80211-freq-limit DT property | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This patch adds a helper for reading that new property and applying | ||||
| limitations of supported channels specified this way. | ||||
| It is used with devices that normally support a wide wireless band but | ||||
| in a given config are limited to some part of it (usually due to board | ||||
| design). For example a dual-band chipset may be able to support one band | ||||
| only because of used antennas. | ||||
| It's also common that tri-band routers have separated radios for lower | ||||
| and higher part of 5 GHz band and it may be impossible to say which is | ||||
| which without a DT info. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| [add new function to documentation, fix link] | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  create mode 100644 net/wireless/of.c | ||||
|  | ||||
| --- a/include/net/cfg80211.h | ||||
| +++ b/include/net/cfg80211.h | ||||
| @@ -311,6 +311,34 @@ struct ieee80211_supported_band { | ||||
|  	struct ieee80211_sta_vht_cap vht_cap; | ||||
|  }; | ||||
|   | ||||
| +/** | ||||
| + * wiphy_read_of_freq_limits - read frequency limits from device tree | ||||
| + * | ||||
| + * @wiphy: the wireless device to get extra limits for | ||||
| + * | ||||
| + * Some devices may have extra limitations specified in DT. This may be useful | ||||
| + * for chipsets that normally support more bands but are limited due to board | ||||
| + * design (e.g. by antennas or external power amplifier). | ||||
| + * | ||||
| + * This function reads info from DT and uses it to *modify* channels (disable | ||||
| + * unavailable ones). It's usually a *bad* idea to use it in drivers with | ||||
| + * shared channel data as DT limitations are device specific. You should make | ||||
| + * sure to call it only if channels in wiphy are copied and can be modified | ||||
| + * without affecting other devices. | ||||
| + * | ||||
| + * As this function access device node it has to be called after set_wiphy_dev. | ||||
| + * It also modifies channels so they have to be set first. | ||||
| + * If using this helper, call it before wiphy_register(). | ||||
| + */ | ||||
| +#ifdef CONFIG_OF | ||||
| +void wiphy_read_of_freq_limits(struct wiphy *wiphy); | ||||
| +#else /* CONFIG_OF */ | ||||
| +static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) | ||||
| +{ | ||||
| +} | ||||
| +#endif /* !CONFIG_OF */ | ||||
| + | ||||
| + | ||||
|  /* | ||||
|   * Wireless hardware/device configuration structures and methods | ||||
|   */ | ||||
| --- a/net/wireless/Makefile | ||||
| +++ b/net/wireless/Makefile | ||||
| @@ -11,6 +11,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o | ||||
|   | ||||
|  cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o | ||||
|  cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o | ||||
| +cfg80211-$(CONFIG_OF) += of.o | ||||
|  cfg80211-$(CPTCFG_CFG80211_DEBUGFS) += debugfs.o | ||||
|  cfg80211-$(CPTCFG_CFG80211_WEXT) += wext-compat.o wext-sme.o | ||||
|  cfg80211-$(CPTCFG_CFG80211_INTERNAL_REGDB) += regdb.o | ||||
| --- /dev/null | ||||
| +++ b/net/wireless/of.c | ||||
| @@ -0,0 +1,138 @@ | ||||
| +/* | ||||
| + * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl> | ||||
| + * | ||||
| + * Permission to use, copy, modify, and/or distribute this software for any | ||||
| + * purpose with or without fee is hereby granted, provided that the above | ||||
| + * copyright notice and this permission notice appear in all copies. | ||||
| + * | ||||
| + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
| + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
| + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
| + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
| + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
| + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/of.h> | ||||
| +#include <net/cfg80211.h> | ||||
| +#include "core.h" | ||||
| + | ||||
| +static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy, | ||||
| +					 struct ieee80211_freq_range *freq_limits, | ||||
| +					 unsigned int n_freq_limits, | ||||
| +					 struct ieee80211_channel *chan) | ||||
| +{ | ||||
| +	u32 bw = MHZ_TO_KHZ(20); | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 0; i < n_freq_limits; i++) { | ||||
| +		struct ieee80211_freq_range *limit = &freq_limits[i]; | ||||
| + | ||||
| +		if (cfg80211_does_bw_fit_range(limit, | ||||
| +					       MHZ_TO_KHZ(chan->center_freq), | ||||
| +					       bw)) | ||||
| +			return true; | ||||
| +	} | ||||
| + | ||||
| +	return false; | ||||
| +} | ||||
| + | ||||
| +static void wiphy_freq_limits_apply(struct wiphy *wiphy, | ||||
| +				    struct ieee80211_freq_range *freq_limits, | ||||
| +				    unsigned int n_freq_limits) | ||||
| +{ | ||||
| +	enum nl80211_band band; | ||||
| +	int i; | ||||
| + | ||||
| +	if (WARN_ON(!n_freq_limits)) | ||||
| +		return; | ||||
| + | ||||
| +	for (band = 0; band < NUM_NL80211_BANDS; band++) { | ||||
| +		struct ieee80211_supported_band *sband = wiphy->bands[band]; | ||||
| + | ||||
| +		if (!sband) | ||||
| +			continue; | ||||
| + | ||||
| +		for (i = 0; i < sband->n_channels; i++) { | ||||
| +			struct ieee80211_channel *chan = &sband->channels[i]; | ||||
| + | ||||
| +			if (chan->flags & IEEE80211_CHAN_DISABLED) | ||||
| +				continue; | ||||
| + | ||||
| +			if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits, | ||||
| +							  n_freq_limits, | ||||
| +							  chan)) { | ||||
| +				pr_debug("Disabling freq %d MHz as it's out of OF limits\n", | ||||
| +					 chan->center_freq); | ||||
| +				chan->flags |= IEEE80211_CHAN_DISABLED; | ||||
| +			} | ||||
| +		} | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +void wiphy_read_of_freq_limits(struct wiphy *wiphy) | ||||
| +{ | ||||
| +	struct device *dev = wiphy_dev(wiphy); | ||||
| +	struct device_node *np; | ||||
| +	struct property *prop; | ||||
| +	struct ieee80211_freq_range *freq_limits; | ||||
| +	unsigned int n_freq_limits; | ||||
| +	const __be32 *p; | ||||
| +	int len, i; | ||||
| +	int err = 0; | ||||
| + | ||||
| +	if (!dev) | ||||
| +		return; | ||||
| +	np = dev_of_node(dev); | ||||
| +	if (!np) | ||||
| +		return; | ||||
| + | ||||
| +	prop = of_find_property(np, "ieee80211-freq-limit", &len); | ||||
| +	if (!prop) | ||||
| +		return; | ||||
| + | ||||
| +	if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) { | ||||
| +		dev_err(dev, "ieee80211-freq-limit wrong format"); | ||||
| +		return; | ||||
| +	} | ||||
| +	n_freq_limits = len / sizeof(u32) / 2; | ||||
| + | ||||
| +	freq_limits = kcalloc(n_freq_limits, sizeof(*freq_limits), GFP_KERNEL); | ||||
| +	if (!freq_limits) { | ||||
| +		err = -ENOMEM; | ||||
| +		goto out_kfree; | ||||
| +	} | ||||
| + | ||||
| +	p = NULL; | ||||
| +	for (i = 0; i < n_freq_limits; i++) { | ||||
| +		struct ieee80211_freq_range *limit = &freq_limits[i]; | ||||
| + | ||||
| +		p = of_prop_next_u32(prop, p, &limit->start_freq_khz); | ||||
| +		if (!p) { | ||||
| +			err = -EINVAL; | ||||
| +			goto out_kfree; | ||||
| +		} | ||||
| + | ||||
| +		p = of_prop_next_u32(prop, p, &limit->end_freq_khz); | ||||
| +		if (!p) { | ||||
| +			err = -EINVAL; | ||||
| +			goto out_kfree; | ||||
| +		} | ||||
| + | ||||
| +		if (!limit->start_freq_khz || | ||||
| +		    !limit->end_freq_khz || | ||||
| +		    limit->start_freq_khz >= limit->end_freq_khz) { | ||||
| +			err = -EINVAL; | ||||
| +			goto out_kfree; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits); | ||||
| + | ||||
| +out_kfree: | ||||
| +	kfree(freq_limits); | ||||
| +	if (err) | ||||
| +		dev_err(dev, "Failed to get limits: %d\n", err); | ||||
| +} | ||||
| +EXPORT_SYMBOL(wiphy_read_of_freq_limits); | ||||
| @@ -1,41 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Wed, 11 Jan 2017 23:30:20 +0100 | ||||
| Subject: [PATCH] mac80211: initialize SMPS field in HT capabilities | ||||
|  | ||||
| ibss and mesh modes copy the ht capabilites from the band without | ||||
| overriding the SMPS state. Unfortunately the default value 0 for the | ||||
| SMPS field means static SMPS instead of disabled. | ||||
|  | ||||
| This results in HT ibss and mesh setups using only single-stream rates, | ||||
| even though SMPS is not supposed to be active. | ||||
|  | ||||
| Initialize SMPS to disabled for all bands on ieee80211_hw_register to | ||||
| ensure that the value is sane where it is not overriden with the real | ||||
| SMPS state. | ||||
|  | ||||
| Reported-by: Elektra Wagenrad <onelektra@gmx.net> | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/main.c | ||||
| +++ b/net/mac80211/main.c | ||||
| @@ -908,10 +908,15 @@ int ieee80211_register_hw(struct ieee802 | ||||
|  		supp_ht = supp_ht || sband->ht_cap.ht_supported; | ||||
|  		supp_vht = supp_vht || sband->vht_cap.vht_supported; | ||||
|   | ||||
| -		if (sband->ht_cap.ht_supported) | ||||
| -			local->rx_chains = | ||||
| -				max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), | ||||
| -				    local->rx_chains); | ||||
| +		if (!sband->ht_cap.ht_supported) | ||||
| +			continue; | ||||
| + | ||||
| +		local->rx_chains = | ||||
| +			max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), | ||||
| +			    local->rx_chains); | ||||
| + | ||||
| +		sband->ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED << | ||||
| +			             IEEE80211_HT_CAP_SM_PS_SHIFT; | ||||
|   | ||||
|  		/* TODO: consider VHT for RX chains, hopefully it's the same */ | ||||
|  	} | ||||
| @@ -1,60 +0,0 @@ | ||||
| From a17d93ff3a950fefaea40e4a4bf3669b9137c533 Mon Sep 17 00:00:00 2001 | ||||
| From: Ben Greear <greearb@candelatech.com> | ||||
| Date: Wed, 14 Dec 2016 11:30:38 -0800 | ||||
| Subject: [PATCH] mac80211: fix legacy and invalid rx-rate report | ||||
|  | ||||
| This fixes obtaining the rate info via sta_set_sinfo | ||||
| when the rx rate is invalid (for instance, on IBSS | ||||
| interface that has received no frames from one of its | ||||
| peers). | ||||
|  | ||||
| Also initialize rinfo->flags for legacy rates, to not | ||||
| rely on the whole sinfo being initialized to zero. | ||||
|  | ||||
| Signed-off-by: Ben Greear <greearb@candelatech.com> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  net/mac80211/sta_info.c | 14 ++++++++------ | ||||
|  1 file changed, 8 insertions(+), 6 deletions(-) | ||||
|  | ||||
| --- a/net/mac80211/sta_info.c | ||||
| +++ b/net/mac80211/sta_info.c | ||||
| @@ -1975,6 +1975,7 @@ static void sta_stats_decode_rate(struct | ||||
|  		u16 brate; | ||||
|  		unsigned int shift; | ||||
|   | ||||
| +		rinfo->flags = 0; | ||||
|  		sband = local->hw.wiphy->bands[(rate >> 4) & 0xf]; | ||||
|  		brate = sband->bitrates[rate & 0xf].bitrate; | ||||
|  		if (rinfo->bw == RATE_INFO_BW_5) | ||||
| @@ -1990,14 +1991,15 @@ static void sta_stats_decode_rate(struct | ||||
|  		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; | ||||
|  } | ||||
|   | ||||
| -static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) | ||||
| +static int sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) | ||||
|  { | ||||
|  	u16 rate = ACCESS_ONCE(sta_get_last_rx_stats(sta)->last_rate); | ||||
|   | ||||
|  	if (rate == STA_STATS_RATE_INVALID) | ||||
| -		rinfo->flags = 0; | ||||
| -	else | ||||
| -		sta_stats_decode_rate(sta->local, rate, rinfo); | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	sta_stats_decode_rate(sta->local, rate, rinfo); | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
|  static void sta_set_tidstats(struct sta_info *sta, | ||||
| @@ -2202,8 +2204,8 @@ void sta_set_sinfo(struct sta_info *sta, | ||||
|  	} | ||||
|   | ||||
|  	if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE))) { | ||||
| -		sta_set_rate_info_rx(sta, &sinfo->rxrate); | ||||
| -		sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); | ||||
| +		if (sta_set_rate_info_rx(sta, &sinfo->rxrate) == 0) | ||||
| +			sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); | ||||
|  	} | ||||
|   | ||||
|  	sinfo->filled |= BIT(NL80211_STA_INFO_TID_STATS); | ||||
| @@ -1,41 +0,0 @@ | ||||
| From 35f432a03e41d3bf08c51ede917f94e2288fbe8c Mon Sep 17 00:00:00 2001 | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Mon, 2 Jan 2017 11:19:29 +0100 | ||||
| Subject: [PATCH] mac80211: initialize fast-xmit 'info' later | ||||
|  | ||||
| In ieee80211_xmit_fast(), 'info' is initialized to point to the skb | ||||
| that's passed in, but that skb may later be replaced by a clone (if | ||||
| it was shared), leading to an invalid pointer. | ||||
|  | ||||
| This can lead to use-after-free and also later crashes since the | ||||
| real SKB's info->hw_queue doesn't get initialized properly. | ||||
|  | ||||
| Fix this by assigning info only later, when it's needed, after the | ||||
| skb replacement (may have) happened. | ||||
|  | ||||
| Cc: stable@vger.kernel.org | ||||
| Reported-by: Ben Greear <greearb@candelatech.com> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  net/mac80211/tx.c | 3 ++- | ||||
|  1 file changed, 2 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -3297,7 +3297,7 @@ static bool ieee80211_xmit_fast(struct i | ||||
|  	int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); | ||||
|  	int hw_headroom = sdata->local->hw.extra_tx_headroom; | ||||
|  	struct ethhdr eth; | ||||
| -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| +	struct ieee80211_tx_info *info; | ||||
|  	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; | ||||
|  	struct ieee80211_tx_data tx; | ||||
|  	ieee80211_tx_result r; | ||||
| @@ -3361,6 +3361,7 @@ static bool ieee80211_xmit_fast(struct i | ||||
|  	memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); | ||||
|  	memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); | ||||
|   | ||||
| +	info = IEEE80211_SKB_CB(skb); | ||||
|  	memset(info, 0, sizeof(*info)); | ||||
|  	info->band = fast_tx->band; | ||||
|  	info->control.vif = &sdata->vif; | ||||
| @@ -1,99 +0,0 @@ | ||||
| From 1c3d185a9a0b136a58e73b02912d593d0303d1da Mon Sep 17 00:00:00 2001 | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Tue, 18 Oct 2016 23:12:08 +0300 | ||||
| Subject: [PATCH] mac80211: fix tid_agg_rx NULL dereference | ||||
|  | ||||
| On drivers setting the SUPPORTS_REORDERING_BUFFER hardware flag, | ||||
| we crash when the peer sends an AddBA request while we already | ||||
| have a session open on the seame TID; this is because on those | ||||
| drivers, the tid_agg_rx is left NULL even though the session is | ||||
| valid, and the agg_session_valid bit is set. | ||||
|  | ||||
| To fix this, store the dialog tokens outside the tid_agg_rx to | ||||
| be able to compare them to the received AddBA request. | ||||
|  | ||||
| Fixes: f89e07d4cf26 ("mac80211: agg-rx: refuse ADDBA Request with timeout update") | ||||
| Reported-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  net/mac80211/agg-rx.c      | 8 ++------ | ||||
|  net/mac80211/debugfs_sta.c | 2 +- | ||||
|  net/mac80211/sta_info.h    | 4 ++-- | ||||
|  3 files changed, 5 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/net/mac80211/agg-rx.c | ||||
| +++ b/net/mac80211/agg-rx.c | ||||
| @@ -315,11 +315,7 @@ void __ieee80211_start_rx_ba_session(str | ||||
|  	mutex_lock(&sta->ampdu_mlme.mtx); | ||||
|   | ||||
|  	if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { | ||||
| -		tid_agg_rx = rcu_dereference_protected( | ||||
| -				sta->ampdu_mlme.tid_rx[tid], | ||||
| -				lockdep_is_held(&sta->ampdu_mlme.mtx)); | ||||
| - | ||||
| -		if (tid_agg_rx->dialog_token == dialog_token) { | ||||
| +		if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) { | ||||
|  			ht_dbg_ratelimited(sta->sdata, | ||||
|  					   "updated AddBA Req from %pM on tid %u\n", | ||||
|  					   sta->sta.addr, tid); | ||||
| @@ -396,7 +392,6 @@ void __ieee80211_start_rx_ba_session(str | ||||
|  	} | ||||
|   | ||||
|  	/* update data */ | ||||
| -	tid_agg_rx->dialog_token = dialog_token; | ||||
|  	tid_agg_rx->ssn = start_seq_num; | ||||
|  	tid_agg_rx->head_seq_num = start_seq_num; | ||||
|  	tid_agg_rx->buf_size = buf_size; | ||||
| @@ -418,6 +413,7 @@ end: | ||||
|  	if (status == WLAN_STATUS_SUCCESS) { | ||||
|  		__set_bit(tid, sta->ampdu_mlme.agg_session_valid); | ||||
|  		__clear_bit(tid, sta->ampdu_mlme.unexpected_agg); | ||||
| +		sta->ampdu_mlme.tid_rx_token[tid] = dialog_token; | ||||
|  	} | ||||
|  	mutex_unlock(&sta->ampdu_mlme.mtx); | ||||
|   | ||||
| --- a/net/mac80211/debugfs_sta.c | ||||
| +++ b/net/mac80211/debugfs_sta.c | ||||
| @@ -205,7 +205,7 @@ static ssize_t sta_agg_status_read(struc | ||||
|  		p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); | ||||
|  		p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx); | ||||
|  		p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", | ||||
| -				tid_rx ? tid_rx->dialog_token : 0); | ||||
| +				tid_rx ? sta->ampdu_mlme.tid_rx_token[i] : 0); | ||||
|  		p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", | ||||
|  				tid_rx ? tid_rx->ssn : 0); | ||||
|   | ||||
| --- a/net/mac80211/sta_info.h | ||||
| +++ b/net/mac80211/sta_info.h | ||||
| @@ -184,7 +184,6 @@ struct tid_ampdu_tx { | ||||
|   * @ssn: Starting Sequence Number expected to be aggregated. | ||||
|   * @buf_size: buffer size for incoming A-MPDUs | ||||
|   * @timeout: reset timer value (in TUs). | ||||
| - * @dialog_token: dialog token for aggregation session | ||||
|   * @rcu_head: RCU head used for freeing this struct | ||||
|   * @reorder_lock: serializes access to reorder buffer, see below. | ||||
|   * @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and | ||||
| @@ -213,7 +212,6 @@ struct tid_ampdu_rx { | ||||
|  	u16 ssn; | ||||
|  	u16 buf_size; | ||||
|  	u16 timeout; | ||||
| -	u8 dialog_token; | ||||
|  	bool auto_seq; | ||||
|  	bool removed; | ||||
|  }; | ||||
| @@ -225,6 +223,7 @@ struct tid_ampdu_rx { | ||||
|   *	to tid_tx[idx], which are protected by the sta spinlock) | ||||
|   *	tid_start_tx is also protected by sta->lock. | ||||
|   * @tid_rx: aggregation info for Rx per TID -- RCU protected | ||||
| + * @tid_rx_token: dialog tokens for valid aggregation sessions | ||||
|   * @tid_rx_timer_expired: bitmap indicating on which TIDs the | ||||
|   *	RX timer expired until the work for it runs | ||||
|   * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the | ||||
| @@ -243,6 +242,7 @@ struct sta_ampdu_mlme { | ||||
|  	struct mutex mtx; | ||||
|  	/* rx */ | ||||
|  	struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; | ||||
| +	u8 tid_rx_token[IEEE80211_NUM_TIDS]; | ||||
|  	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; | ||||
|  	unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; | ||||
|  	unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; | ||||
| @@ -1,107 +0,0 @@ | ||||
| From: Michal Kazior <michal.kazior@tieto.com> | ||||
| Date: Fri, 13 Jan 2017 13:32:51 +0100 | ||||
| Subject: [PATCH] mac80211: prevent skb/txq mismatch | ||||
|  | ||||
| Station structure is considered as not uploaded | ||||
| (to driver) until drv_sta_state() finishes. This | ||||
| call is however done after the structure is | ||||
| attached to mac80211 internal lists and hashes. | ||||
| This means mac80211 can lookup (and use) station | ||||
| structure before it is uploaded to a driver. | ||||
|  | ||||
| If this happens (structure exists, but | ||||
| sta->uploaded is false) fast_tx path can still be | ||||
| taken. Deep in the fastpath call the sta->uploaded | ||||
| is checked against to derive "pubsta" argument for | ||||
| ieee80211_get_txq(). If sta->uploaded is false | ||||
| (and sta is actually non-NULL) ieee80211_get_txq() | ||||
| effectively downgraded to vif->txq. | ||||
|  | ||||
| At first glance this may look innocent but coerces | ||||
| mac80211 into a state that is almost guaranteed | ||||
| (codel may drop offending skb) to crash because a | ||||
| station-oriented skb gets queued up on | ||||
| vif-oriented txq. The ieee80211_tx_dequeue() ends | ||||
| up looking at info->control.flags and tries to use | ||||
| txq->sta which in the fail case is NULL. | ||||
|  | ||||
| It's probably pointless to pretend one can | ||||
| downgrade skb from sta-txq to vif-txq. | ||||
|  | ||||
| Since downgrading unicast traffic to vif->txq must | ||||
| not be done there's no txq to put a frame on if | ||||
| sta->uploaded is false. Therefore the code is made | ||||
| to fall back to regular tx() op path if the | ||||
| described condition is hit. | ||||
|  | ||||
| Only drivers using wake_tx_queue were affected. | ||||
|  | ||||
| Example crash dump before fix: | ||||
|  | ||||
|  Unable to handle kernel paging request at virtual address ffffe26c | ||||
|  PC is at ieee80211_tx_dequeue+0x204/0x690 [mac80211] | ||||
|  [<bf4252a4>] (ieee80211_tx_dequeue [mac80211]) from | ||||
|  [<bf4b1388>] (ath10k_mac_tx_push_txq+0x54/0x1c0 [ath10k_core]) | ||||
|  [<bf4b1388>] (ath10k_mac_tx_push_txq [ath10k_core]) from | ||||
|  [<bf4bdfbc>] (ath10k_htt_txrx_compl_task+0xd78/0x11d0 [ath10k_core]) | ||||
|  [<bf4bdfbc>] (ath10k_htt_txrx_compl_task [ath10k_core]) | ||||
|  [<bf51c5a4>] (ath10k_pci_napi_poll+0x54/0xe8 [ath10k_pci]) | ||||
|  [<bf51c5a4>] (ath10k_pci_napi_poll [ath10k_pci]) from | ||||
|  [<c0572e90>] (net_rx_action+0xac/0x160) | ||||
|  | ||||
| Reported-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> | ||||
| Signed-off-by: Michal Kazior <michal.kazior@tieto.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -798,7 +798,7 @@ static __le16 ieee80211_tx_next_seq(stru | ||||
|   | ||||
|  static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, | ||||
|  					  struct ieee80211_vif *vif, | ||||
| -					  struct ieee80211_sta *pubsta, | ||||
| +					  struct sta_info *sta, | ||||
|  					  struct sk_buff *skb) | ||||
|  { | ||||
|  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||||
| @@ -812,10 +812,13 @@ static struct txq_info *ieee80211_get_tx | ||||
|  	if (!ieee80211_is_data(hdr->frame_control)) | ||||
|  		return NULL; | ||||
|   | ||||
| -	if (pubsta) { | ||||
| +	if (sta) { | ||||
|  		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; | ||||
|   | ||||
| -		txq = pubsta->txq[tid]; | ||||
| +		if (!sta->uploaded) | ||||
| +			return NULL; | ||||
| + | ||||
| +		txq = sta->sta.txq[tid]; | ||||
|  	} else if (vif) { | ||||
|  		txq = vif->txq; | ||||
|  	} | ||||
| @@ -1503,23 +1506,17 @@ static bool ieee80211_queue_skb(struct i | ||||
|  	struct fq *fq = &local->fq; | ||||
|  	struct ieee80211_vif *vif; | ||||
|  	struct txq_info *txqi; | ||||
| -	struct ieee80211_sta *pubsta; | ||||
|   | ||||
|  	if (!local->ops->wake_tx_queue || | ||||
|  	    sdata->vif.type == NL80211_IFTYPE_MONITOR) | ||||
|  		return false; | ||||
|   | ||||
| -	if (sta && sta->uploaded) | ||||
| -		pubsta = &sta->sta; | ||||
| -	else | ||||
| -		pubsta = NULL; | ||||
| - | ||||
|  	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||||
|  		sdata = container_of(sdata->bss, | ||||
|  				     struct ieee80211_sub_if_data, u.ap); | ||||
|   | ||||
|  	vif = &sdata->vif; | ||||
| -	txqi = ieee80211_get_txq(local, vif, pubsta, skb); | ||||
| +	txqi = ieee80211_get_txq(local, vif, sta, skb); | ||||
|   | ||||
|  	if (!txqi) | ||||
|  		return false; | ||||
| @@ -1,43 +0,0 @@ | ||||
| From d3532ea6ce4ea501e421d130555e59edc2945f99 Mon Sep 17 00:00:00 2001 | ||||
| From: Arnd Bergmann <arnd@arndb.de> | ||||
| Date: Tue, 18 Oct 2016 00:13:40 +0200 | ||||
| Subject: [PATCH] brcmfmac: avoid maybe-uninitialized warning in | ||||
|  brcmf_cfg80211_start_ap | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| A bugfix added a sanity check around the assignment and use of the | ||||
| 'is_11d' variable, which looks correct to me, but as the function is | ||||
| rather complex already, this confuses the compiler to the point where | ||||
| it can no longer figure out if the variable is always initialized | ||||
| correctly: | ||||
|  | ||||
| brcm80211/brcmfmac/cfg80211.c: In function ‘brcmf_cfg80211_start_ap’: | ||||
| brcm80211/brcmfmac/cfg80211.c:4586:10: error: ‘is_11d’ may be used uninitialized in this function [-Werror=maybe-uninitialized] | ||||
|  | ||||
| This adds an initialization for the newly introduced case in which | ||||
| the variable should not really be used, in order to make the warning | ||||
| go away. | ||||
|  | ||||
| Fixes: b3589dfe0212 ("brcmfmac: ignore 11d configuration errors") | ||||
| Cc: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Cc: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Cc: Kalle Valo <kvalo@codeaurora.org> | ||||
| Signed-off-by: Arnd Bergmann <arnd@arndb.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -4516,7 +4516,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wi | ||||
|  	/* store current 11d setting */ | ||||
|  	if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, | ||||
|  				  &ifp->vif->is_11d)) { | ||||
| -		supports_11d = false; | ||||
| +		is_11d = supports_11d = false; | ||||
|  	} else { | ||||
|  		country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, | ||||
|  					      settings->beacon.tail_len, | ||||
| @@ -1,174 +0,0 @@ | ||||
| From b073ac1fcf42376018f6db6acc885dfd2cc9ff02 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Mon, 26 Sep 2016 23:51:44 +0200 | ||||
| Subject: [PATCH] brcmfmac: proto: add callback for queuing TX data | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| So far our core code was calling brcmf_fws_process_skb which wasn't | ||||
| a proper thing to do. If case of devices using msgbuf protocol fwsignal | ||||
| shouldn't be used. It was an unnecessary extra layer simply calling | ||||
| a protocol specifix txdata function. | ||||
|  | ||||
| Please note we already have txdata callback, but it's used for calls | ||||
| between bcdc and fwsignal so it couldn't be simply used there. | ||||
|  | ||||
| This makes core code more generic (instead of bcdc/fwsignal specific). | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c   | 12 ++++++++++++ | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c   |  8 +++++++- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c   | 15 +++++---------- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h   |  1 + | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c |  6 +++--- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c  |  2 +- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h  |  9 +++++++++ | ||||
|  7 files changed, 38 insertions(+), 15 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | ||||
| @@ -326,6 +326,17 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx, | ||||
| +					  struct sk_buff *skb) | ||||
| +{ | ||||
| +	struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx); | ||||
| + | ||||
| +	if (!brcmf_fws_queue_skbs(drvr->fws)) | ||||
| +		return brcmf_proto_txdata(drvr, ifidx, 0, skb); | ||||
| + | ||||
| +	return brcmf_fws_process_skb(ifp, skb); | ||||
| +} | ||||
| + | ||||
|  static int | ||||
|  brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, | ||||
|  			struct sk_buff *pktbuf) | ||||
| @@ -375,6 +386,7 @@ int brcmf_proto_bcdc_attach(struct brcmf | ||||
|  	drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull; | ||||
|  	drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; | ||||
|  	drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; | ||||
| +	drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data; | ||||
|  	drvr->proto->txdata = brcmf_proto_bcdc_txdata; | ||||
|  	drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; | ||||
|  	drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | ||||
| @@ -239,7 +239,13 @@ static netdev_tx_t brcmf_netdev_start_xm | ||||
|  	if (eh->h_proto == htons(ETH_P_PAE)) | ||||
|  		atomic_inc(&ifp->pend_8021x_cnt); | ||||
|   | ||||
| -	ret = brcmf_fws_process_skb(ifp, skb); | ||||
| +	/* determine the priority */ | ||||
| +	if ((skb->priority == 0) || (skb->priority > 7)) | ||||
| +		skb->priority = cfg80211_classify8021d(skb, NULL); | ||||
| + | ||||
| +	ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb); | ||||
| +	if (ret < 0) | ||||
| +		brcmf_txfinalize(ifp, skb, false); | ||||
|   | ||||
|  done: | ||||
|  	if (ret) { | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | ||||
| @@ -2100,16 +2100,6 @@ int brcmf_fws_process_skb(struct brcmf_i | ||||
|  	int rc = 0; | ||||
|   | ||||
|  	brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); | ||||
| -	/* determine the priority */ | ||||
| -	if ((skb->priority == 0) || (skb->priority > 7)) | ||||
| -		skb->priority = cfg80211_classify8021d(skb, NULL); | ||||
| - | ||||
| -	if (fws->avoid_queueing) { | ||||
| -		rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); | ||||
| -		if (rc < 0) | ||||
| -			brcmf_txfinalize(ifp, skb, false); | ||||
| -		return rc; | ||||
| -	} | ||||
|   | ||||
|  	/* set control buffer information */ | ||||
|  	skcb->if_flags = 0; | ||||
| @@ -2442,6 +2432,11 @@ void brcmf_fws_deinit(struct brcmf_pub * | ||||
|  	kfree(fws); | ||||
|  } | ||||
|   | ||||
| +bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws) | ||||
| +{ | ||||
| +	return !fws->avoid_queueing; | ||||
| +} | ||||
| + | ||||
|  bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) | ||||
|  { | ||||
|  	if (!fws->creditmap_received) | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h | ||||
| @@ -20,6 +20,7 @@ | ||||
|   | ||||
|  int brcmf_fws_init(struct brcmf_pub *drvr); | ||||
|  void brcmf_fws_deinit(struct brcmf_pub *drvr); | ||||
| +bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws); | ||||
|  bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); | ||||
|  void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); | ||||
|  int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -782,8 +782,8 @@ static int brcmf_msgbuf_schedule_txdata( | ||||
|  } | ||||
|   | ||||
|   | ||||
| -static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, | ||||
| -			       u8 offset, struct sk_buff *skb) | ||||
| +static int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx, | ||||
| +				      struct sk_buff *skb) | ||||
|  { | ||||
|  	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; | ||||
|  	struct brcmf_flowring *flow = msgbuf->flow; | ||||
| @@ -1467,7 +1467,7 @@ int brcmf_proto_msgbuf_attach(struct brc | ||||
|  	drvr->proto->hdrpull = brcmf_msgbuf_hdrpull; | ||||
|  	drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd; | ||||
|  	drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd; | ||||
| -	drvr->proto->txdata = brcmf_msgbuf_txdata; | ||||
| +	drvr->proto->tx_queue_data = brcmf_msgbuf_tx_queue_data; | ||||
|  	drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; | ||||
|  	drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; | ||||
|  	drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer; | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c | ||||
| @@ -51,7 +51,7 @@ int brcmf_proto_attach(struct brcmf_pub | ||||
|  			  drvr->bus_if->proto_type); | ||||
|  		goto fail; | ||||
|  	} | ||||
| -	if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || | ||||
| +	if (!proto->tx_queue_data || (proto->hdrpull == NULL) || | ||||
|  	    (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || | ||||
|  	    (proto->configure_addr_mode == NULL) || | ||||
|  	    (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) { | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | ||||
| @@ -33,6 +33,8 @@ struct brcmf_proto { | ||||
|  			  void *buf, uint len); | ||||
|  	int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, | ||||
|  			uint len); | ||||
| +	int (*tx_queue_data)(struct brcmf_pub *drvr, int ifidx, | ||||
| +			     struct sk_buff *skb); | ||||
|  	int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, | ||||
|  		      struct sk_buff *skb); | ||||
|  	void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx, | ||||
| @@ -74,6 +76,13 @@ static inline int brcmf_proto_set_dcmd(s | ||||
|  { | ||||
|  	return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); | ||||
|  } | ||||
| + | ||||
| +static inline int brcmf_proto_tx_queue_data(struct brcmf_pub *drvr, int ifidx, | ||||
| +					    struct sk_buff *skb) | ||||
| +{ | ||||
| +	return drvr->proto->tx_queue_data(drvr, ifidx, skb); | ||||
| +} | ||||
| + | ||||
|  static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, | ||||
|  				     u8 offset, struct sk_buff *skb) | ||||
|  { | ||||
| @@ -1,62 +0,0 @@ | ||||
| From e1c122d55f9ec5608ca98a9a846fd39cdf3ed7d7 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Fri, 14 Oct 2016 09:45:59 +0200 | ||||
| Subject: [PATCH] brcmfmac: print name of connect status event | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This simplifies debugging. Format %s (%u) comes from similar debugging | ||||
| message in brcmf_fweh_event_worker. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 3 ++- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c     | 4 ++-- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h     | 2 ++ | ||||
|  3 files changed, 6 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -5506,7 +5506,8 @@ brcmf_notify_connect_status_ap(struct br | ||||
|  	u32 reason = e->reason; | ||||
|  	struct station_info sinfo; | ||||
|   | ||||
| -	brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); | ||||
| +	brcmf_dbg(CONN, "event %s (%u), reason %d\n", | ||||
| +		  brcmf_fweh_event_name(event), event, reason); | ||||
|  	if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && | ||||
|  	    ndev != cfg_to_ndev(cfg)) { | ||||
|  		brcmf_dbg(CONN, "AP mode link down\n"); | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | ||||
| @@ -69,7 +69,7 @@ static struct brcmf_fweh_event_name fweh | ||||
|   * | ||||
|   * @code: code to lookup. | ||||
|   */ | ||||
| -static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) | ||||
| +const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) | ||||
|  { | ||||
|  	int i; | ||||
|  	for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) { | ||||
| @@ -79,7 +79,7 @@ static const char *brcmf_fweh_event_name | ||||
|  	return "unknown"; | ||||
|  } | ||||
|  #else | ||||
| -static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) | ||||
| +const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) | ||||
|  { | ||||
|  	return "nodebug"; | ||||
|  } | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h | ||||
| @@ -287,6 +287,8 @@ struct brcmf_fweh_info { | ||||
|  					 void *data); | ||||
|  }; | ||||
|   | ||||
| +const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code); | ||||
| + | ||||
|  void brcmf_fweh_attach(struct brcmf_pub *drvr); | ||||
|  void brcmf_fweh_detach(struct brcmf_pub *drvr); | ||||
|  int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, | ||||
| @@ -1,45 +0,0 @@ | ||||
| From f25ba69c638b24097840a96bd3caf5599f9a3616 Mon Sep 17 00:00:00 2001 | ||||
| From: Wright Feng <wefe@cypress.com> | ||||
| Date: Fri, 18 Nov 2016 09:59:52 +0800 | ||||
| Subject: [PATCH] brcmfmac: update beacon IE after bss up and clear when AP | ||||
|  stopped | ||||
|  | ||||
| Firmware doesn't update beacon/Probe Response vendor IEs correctly when | ||||
| bss is down, so we move brcmf_config_ap_mgmt_ie after BSS up. And host | ||||
| driver should clear IEs when AP stopped so that the IEs in host side will | ||||
| be synced with in firmware side. | ||||
|  | ||||
| Signed-off-by: Wright Feng <wright.feng@cypress.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 5 +++-- | ||||
|  1 file changed, 3 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -4578,8 +4578,6 @@ brcmf_cfg80211_start_ap(struct wiphy *wi | ||||
|  		brcmf_configure_opensecurity(ifp); | ||||
|  	} | ||||
|   | ||||
| -	brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); | ||||
| - | ||||
|  	/* Parameters shared by all radio interfaces */ | ||||
|  	if (!mbss) { | ||||
|  		if ((supports_11d) && (is_11d != ifp->vif->is_11d)) { | ||||
| @@ -4708,6 +4706,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wi | ||||
|  		WARN_ON(1); | ||||
|  	} | ||||
|   | ||||
| +	brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); | ||||
|  	set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); | ||||
|  	brcmf_net_setcarrier(ifp, true); | ||||
|   | ||||
| @@ -4764,6 +4763,8 @@ static int brcmf_cfg80211_stop_ap(struct | ||||
|  		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); | ||||
|  		if (err < 0) | ||||
|  			brcmf_err("BRCMF_C_UP error %d\n", err); | ||||
| + | ||||
| +		brcmf_vif_clear_mgmt_ies(ifp->vif); | ||||
|  	} else { | ||||
|  		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); | ||||
|  		bss_enable.enable = cpu_to_le32(0); | ||||
| @@ -1,490 +0,0 @@ | ||||
| From be4b092cab84b2ecc01ee7f4da6a044279430b6f Mon Sep 17 00:00:00 2001 | ||||
| From: Franky Lin <franky.lin@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:20 +0000 | ||||
| Subject: [PATCH] brcmfmac: add pcie host dongle interface rev6 support | ||||
|  | ||||
| In rev6 of pcie host dongle interface protocol, host needs to maximum | ||||
| supported ring number from dongle shared memory and set up ring buffer | ||||
| and ring indices offset accordingly. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h |  10 +- | ||||
|  .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c  |  38 +++-- | ||||
|  .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.h  |   4 + | ||||
|  .../wireless/broadcom/brcm80211/brcmfmac/pcie.c    | 171 ++++++++++++--------- | ||||
|  4 files changed, 132 insertions(+), 91 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | ||||
| @@ -22,10 +22,12 @@ | ||||
|  /* IDs of the 6 default common rings of msgbuf protocol */ | ||||
|  #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT	0 | ||||
|  #define BRCMF_H2D_MSGRING_RXPOST_SUBMIT		1 | ||||
| +#define BRCMF_H2D_MSGRING_FLOWRING_IDSTART	2 | ||||
|  #define BRCMF_D2H_MSGRING_CONTROL_COMPLETE	2 | ||||
|  #define BRCMF_D2H_MSGRING_TX_COMPLETE		3 | ||||
|  #define BRCMF_D2H_MSGRING_RX_COMPLETE		4 | ||||
|   | ||||
| + | ||||
|  #define BRCMF_NROF_H2D_COMMON_MSGRINGS		2 | ||||
|  #define BRCMF_NROF_D2H_COMMON_MSGRINGS		3 | ||||
|  #define BRCMF_NROF_COMMON_MSGRINGS	(BRCMF_NROF_H2D_COMMON_MSGRINGS + \ | ||||
| @@ -95,14 +97,18 @@ struct brcmf_bus_ops { | ||||
|   * @flowrings: commonrings which are dynamically created and destroyed for data. | ||||
|   * @rx_dataoffset: if set then all rx data has this this offset. | ||||
|   * @max_rxbufpost: maximum number of buffers to post for rx. | ||||
| - * @nrof_flowrings: number of flowrings. | ||||
| + * @max_flowrings: maximum number of tx flow rings supported. | ||||
| + * @max_submissionrings: maximum number of submission rings(h2d) supported. | ||||
| + * @max_completionrings: maximum number of completion rings(d2h) supported. | ||||
|   */ | ||||
|  struct brcmf_bus_msgbuf { | ||||
|  	struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; | ||||
|  	struct brcmf_commonring **flowrings; | ||||
|  	u32 rx_dataoffset; | ||||
|  	u32 max_rxbufpost; | ||||
| -	u32 nrof_flowrings; | ||||
| +	u16 max_flowrings; | ||||
| +	u16 max_submissionrings; | ||||
| +	u16 max_completionrings; | ||||
|  }; | ||||
|   | ||||
|   | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -87,11 +87,6 @@ struct msgbuf_common_hdr { | ||||
|  	__le32				request_id; | ||||
|  }; | ||||
|   | ||||
| -struct msgbuf_buf_addr { | ||||
| -	__le32				low_addr; | ||||
| -	__le32				high_addr; | ||||
| -}; | ||||
| - | ||||
|  struct msgbuf_ioctl_req_hdr { | ||||
|  	struct msgbuf_common_hdr	msg; | ||||
|  	__le32				cmd; | ||||
| @@ -227,7 +222,10 @@ struct brcmf_msgbuf { | ||||
|  	struct brcmf_commonring **commonrings; | ||||
|  	struct brcmf_commonring **flowrings; | ||||
|  	dma_addr_t *flowring_dma_handle; | ||||
| -	u16 nrof_flowrings; | ||||
| + | ||||
| +	u16 max_flowrings; | ||||
| +	u16 max_submissionrings; | ||||
| +	u16 max_completionrings; | ||||
|   | ||||
|  	u16 rx_dataoffset; | ||||
|  	u32 max_rxbufpost; | ||||
| @@ -610,7 +608,7 @@ brcmf_msgbuf_flowring_create_worker(stru | ||||
|  	create->msg.request_id = 0; | ||||
|  	create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); | ||||
|  	create->flow_ring_id = cpu_to_le16(flowid + | ||||
| -					   BRCMF_NROF_H2D_COMMON_MSGRINGS); | ||||
| +					   BRCMF_H2D_MSGRING_FLOWRING_IDSTART); | ||||
|  	memcpy(create->sa, work->sa, ETH_ALEN); | ||||
|  	memcpy(create->da, work->da, ETH_ALEN); | ||||
|  	address = (u64)msgbuf->flowring_dma_handle[flowid]; | ||||
| @@ -760,7 +758,7 @@ static void brcmf_msgbuf_txflow_worker(s | ||||
|  	u32 flowid; | ||||
|   | ||||
|  	msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work); | ||||
| -	for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) { | ||||
| +	for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->max_flowrings) { | ||||
|  		clear_bit(flowid, msgbuf->flow_map); | ||||
|  		brcmf_msgbuf_txflow(msgbuf, flowid); | ||||
|  	} | ||||
| @@ -866,7 +864,7 @@ brcmf_msgbuf_process_txstatus(struct brc | ||||
|  	tx_status = (struct msgbuf_tx_status *)buf; | ||||
|  	idx = le32_to_cpu(tx_status->msg.request_id); | ||||
|  	flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); | ||||
| -	flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
| +	flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; | ||||
|  	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, | ||||
|  				     msgbuf->tx_pktids, idx); | ||||
|  	if (!skb) | ||||
| @@ -1174,7 +1172,7 @@ brcmf_msgbuf_process_flow_ring_create_re | ||||
|  	flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; | ||||
|   | ||||
|  	flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); | ||||
| -	flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
| +	flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; | ||||
|  	status =  le16_to_cpu(flowring_create_resp->compl_hdr.status); | ||||
|   | ||||
|  	if (status) { | ||||
| @@ -1202,7 +1200,7 @@ brcmf_msgbuf_process_flow_ring_delete_re | ||||
|  	flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; | ||||
|   | ||||
|  	flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); | ||||
| -	flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
| +	flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; | ||||
|  	status =  le16_to_cpu(flowring_delete_resp->compl_hdr.status); | ||||
|   | ||||
|  	if (status) { | ||||
| @@ -1307,7 +1305,7 @@ int brcmf_proto_msgbuf_rx_trigger(struct | ||||
|  	brcmf_msgbuf_process_rx(msgbuf, buf); | ||||
|   | ||||
|  	for_each_set_bit(flowid, msgbuf->txstatus_done_map, | ||||
| -			 msgbuf->nrof_flowrings) { | ||||
| +			 msgbuf->max_flowrings) { | ||||
|  		clear_bit(flowid, msgbuf->txstatus_done_map); | ||||
|  		commonring = msgbuf->flowrings[flowid]; | ||||
|  		qlen = brcmf_flowring_qlen(msgbuf->flow, flowid); | ||||
| @@ -1349,7 +1347,7 @@ void brcmf_msgbuf_delete_flowring(struct | ||||
|  	delete->msg.request_id = 0; | ||||
|   | ||||
|  	delete->flow_ring_id = cpu_to_le16(flowid + | ||||
| -					   BRCMF_NROF_H2D_COMMON_MSGRINGS); | ||||
| +					   BRCMF_H2D_MSGRING_FLOWRING_IDSTART); | ||||
|  	delete->reason = 0; | ||||
|   | ||||
|  	brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n", | ||||
| @@ -1427,10 +1425,10 @@ int brcmf_proto_msgbuf_attach(struct brc | ||||
|   | ||||
|  	if_msgbuf = drvr->bus_if->msgbuf; | ||||
|   | ||||
| -	if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) { | ||||
| +	if (if_msgbuf->max_flowrings >= BRCMF_FLOWRING_HASHSIZE) { | ||||
|  		brcmf_err("driver not configured for this many flowrings %d\n", | ||||
| -			  if_msgbuf->nrof_flowrings); | ||||
| -		if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1; | ||||
| +			  if_msgbuf->max_flowrings); | ||||
| +		if_msgbuf->max_flowrings = BRCMF_FLOWRING_HASHSIZE - 1; | ||||
|  	} | ||||
|   | ||||
|  	msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); | ||||
| @@ -1443,7 +1441,7 @@ int brcmf_proto_msgbuf_attach(struct brc | ||||
|  		goto fail; | ||||
|  	} | ||||
|  	INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); | ||||
| -	count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); | ||||
| +	count = BITS_TO_LONGS(if_msgbuf->max_flowrings); | ||||
|  	count = count * sizeof(unsigned long); | ||||
|  	msgbuf->flow_map = kzalloc(count, GFP_KERNEL); | ||||
|  	if (!msgbuf->flow_map) | ||||
| @@ -1479,8 +1477,8 @@ int brcmf_proto_msgbuf_attach(struct brc | ||||
|  	msgbuf->commonrings = | ||||
|  		(struct brcmf_commonring **)if_msgbuf->commonrings; | ||||
|  	msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; | ||||
| -	msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; | ||||
| -	msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * | ||||
| +	msgbuf->max_flowrings = if_msgbuf->max_flowrings; | ||||
| +	msgbuf->flowring_dma_handle = kzalloc(msgbuf->max_flowrings * | ||||
|  		sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); | ||||
|  	if (!msgbuf->flowring_dma_handle) | ||||
|  		goto fail; | ||||
| @@ -1501,7 +1499,7 @@ int brcmf_proto_msgbuf_attach(struct brc | ||||
|  		goto fail; | ||||
|   | ||||
|  	msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev, | ||||
| -					     if_msgbuf->nrof_flowrings); | ||||
| +					     if_msgbuf->max_flowrings); | ||||
|  	if (!msgbuf->flow) | ||||
|  		goto fail; | ||||
|   | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h | ||||
| @@ -31,6 +31,10 @@ | ||||
|  #define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE		32 | ||||
|  #define BRCMF_H2D_TXFLOWRING_ITEMSIZE			48 | ||||
|   | ||||
| +struct msgbuf_buf_addr { | ||||
| +	__le32		low_addr; | ||||
| +	__le32		high_addr; | ||||
| +}; | ||||
|   | ||||
|  int brcmf_proto_msgbuf_rx_trigger(struct device *dev); | ||||
|  void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid); | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||
| @@ -135,7 +135,7 @@ static struct brcmf_firmware_mapping brc | ||||
|  						 BRCMF_PCIE_MB_INT_D2H3_DB1) | ||||
|   | ||||
|  #define BRCMF_PCIE_MIN_SHARED_VERSION		5 | ||||
| -#define BRCMF_PCIE_MAX_SHARED_VERSION		5 | ||||
| +#define BRCMF_PCIE_MAX_SHARED_VERSION		6 | ||||
|  #define BRCMF_PCIE_SHARED_VERSION_MASK		0x00FF | ||||
|  #define BRCMF_PCIE_SHARED_DMA_INDEX		0x10000 | ||||
|  #define BRCMF_PCIE_SHARED_DMA_2B_IDX		0x100000 | ||||
| @@ -166,17 +166,6 @@ static struct brcmf_firmware_mapping brc | ||||
|  #define BRCMF_RING_MEM_SZ			16 | ||||
|  #define BRCMF_RING_STATE_SZ			8 | ||||
|   | ||||
| -#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET	4 | ||||
| -#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET	8 | ||||
| -#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET	12 | ||||
| -#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET	16 | ||||
| -#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET	20 | ||||
| -#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET	28 | ||||
| -#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET	36 | ||||
| -#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET	44 | ||||
| -#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET	0 | ||||
| -#define BRCMF_SHARED_RING_MAX_SUB_QUEUES	52 | ||||
| - | ||||
|  #define BRCMF_DEF_MAX_RXBUFPOST			255 | ||||
|   | ||||
|  #define BRCMF_CONSOLE_BUFADDR_OFFSET		8 | ||||
| @@ -231,7 +220,9 @@ struct brcmf_pcie_shared_info { | ||||
|  	struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; | ||||
|  	struct brcmf_pcie_ringbuf *flowrings; | ||||
|  	u16 max_rxbufpost; | ||||
| -	u32 nrof_flowrings; | ||||
| +	u16 max_flowrings; | ||||
| +	u16 max_submissionrings; | ||||
| +	u16 max_completionrings; | ||||
|  	u32 rx_dataoffset; | ||||
|  	u32 htod_mb_data_addr; | ||||
|  	u32 dtoh_mb_data_addr; | ||||
| @@ -241,6 +232,7 @@ struct brcmf_pcie_shared_info { | ||||
|  	dma_addr_t scratch_dmahandle; | ||||
|  	void *ringupd; | ||||
|  	dma_addr_t ringupd_dmahandle; | ||||
| +	u8 version; | ||||
|  }; | ||||
|   | ||||
|  struct brcmf_pcie_core_info { | ||||
| @@ -284,6 +276,36 @@ struct brcmf_pcie_ringbuf { | ||||
|  	u8 id; | ||||
|  }; | ||||
|   | ||||
| +/** | ||||
| + * struct brcmf_pcie_dhi_ringinfo - dongle/host interface shared ring info | ||||
| + * | ||||
| + * @ringmem: dongle memory pointer to ring memory location | ||||
| + * @h2d_w_idx_ptr: h2d ring write indices dongle memory pointers | ||||
| + * @h2d_r_idx_ptr: h2d ring read indices dongle memory pointers | ||||
| + * @d2h_w_idx_ptr: d2h ring write indices dongle memory pointers | ||||
| + * @d2h_r_idx_ptr: d2h ring read indices dongle memory pointers | ||||
| + * @h2d_w_idx_hostaddr: h2d ring write indices host memory pointers | ||||
| + * @h2d_r_idx_hostaddr: h2d ring read indices host memory pointers | ||||
| + * @d2h_w_idx_hostaddr: d2h ring write indices host memory pointers | ||||
| + * @d2h_r_idx_hostaddr: d2h ring reaD indices host memory pointers | ||||
| + * @max_flowrings: maximum number of tx flow rings supported. | ||||
| + * @max_submissionrings: maximum number of submission rings(h2d) supported. | ||||
| + * @max_completionrings: maximum number of completion rings(d2h) supported. | ||||
| + */ | ||||
| +struct brcmf_pcie_dhi_ringinfo { | ||||
| +	__le32			ringmem; | ||||
| +	__le32			h2d_w_idx_ptr; | ||||
| +	__le32			h2d_r_idx_ptr; | ||||
| +	__le32			d2h_w_idx_ptr; | ||||
| +	__le32			d2h_r_idx_ptr; | ||||
| +	struct msgbuf_buf_addr	h2d_w_idx_hostaddr; | ||||
| +	struct msgbuf_buf_addr	h2d_r_idx_hostaddr; | ||||
| +	struct msgbuf_buf_addr	d2h_w_idx_hostaddr; | ||||
| +	struct msgbuf_buf_addr	d2h_r_idx_hostaddr; | ||||
| +	__le16			max_flowrings; | ||||
| +	__le16			max_submissionrings; | ||||
| +	__le16			max_completionrings; | ||||
| +}; | ||||
|   | ||||
|  static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = { | ||||
|  	BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM, | ||||
| @@ -1054,26 +1076,35 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  { | ||||
|  	struct brcmf_pcie_ringbuf *ring; | ||||
|  	struct brcmf_pcie_ringbuf *rings; | ||||
| -	u32 ring_addr; | ||||
|  	u32 d2h_w_idx_ptr; | ||||
|  	u32 d2h_r_idx_ptr; | ||||
|  	u32 h2d_w_idx_ptr; | ||||
|  	u32 h2d_r_idx_ptr; | ||||
| -	u32 addr; | ||||
|  	u32 ring_mem_ptr; | ||||
|  	u32 i; | ||||
|  	u64 address; | ||||
|  	u32 bufsz; | ||||
| -	u16 max_sub_queues; | ||||
|  	u8 idx_offset; | ||||
| - | ||||
| -	ring_addr = devinfo->shared.ring_info_addr; | ||||
| -	brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; | ||||
| -	max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); | ||||
| +	struct brcmf_pcie_dhi_ringinfo ringinfo; | ||||
| +	u16 max_flowrings; | ||||
| +	u16 max_submissionrings; | ||||
| +	u16 max_completionrings; | ||||
| + | ||||
| +	memcpy_fromio(&ringinfo, devinfo->tcm + devinfo->shared.ring_info_addr, | ||||
| +		      sizeof(ringinfo)); | ||||
| +	if (devinfo->shared.version >= 6) { | ||||
| +		max_submissionrings = le16_to_cpu(ringinfo.max_submissionrings); | ||||
| +		max_flowrings = le16_to_cpu(ringinfo.max_flowrings); | ||||
| +		max_completionrings = le16_to_cpu(ringinfo.max_completionrings); | ||||
| +	} else { | ||||
| +		max_submissionrings = le16_to_cpu(ringinfo.max_flowrings); | ||||
| +		max_flowrings = max_submissionrings - | ||||
| +				BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
| +		max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS; | ||||
| +	} | ||||
|   | ||||
|  	if (devinfo->dma_idx_sz != 0) { | ||||
| -		bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * | ||||
| +		bufsz = (max_submissionrings + max_completionrings) * | ||||
|  			devinfo->dma_idx_sz * 2; | ||||
|  		devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, | ||||
|  						     &devinfo->idxbuf_dmahandle, | ||||
| @@ -1083,14 +1114,10 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  	} | ||||
|   | ||||
|  	if (devinfo->dma_idx_sz == 0) { | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; | ||||
| -		d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; | ||||
| -		d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; | ||||
| -		h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; | ||||
| -		h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +		d2h_w_idx_ptr = le32_to_cpu(ringinfo.d2h_w_idx_ptr); | ||||
| +		d2h_r_idx_ptr = le32_to_cpu(ringinfo.d2h_r_idx_ptr); | ||||
| +		h2d_w_idx_ptr = le32_to_cpu(ringinfo.h2d_w_idx_ptr); | ||||
| +		h2d_r_idx_ptr = le32_to_cpu(ringinfo.h2d_r_idx_ptr); | ||||
|  		idx_offset = sizeof(u32); | ||||
|  		devinfo->write_ptr = brcmf_pcie_write_tcm16; | ||||
|  		devinfo->read_ptr = brcmf_pcie_read_tcm16; | ||||
| @@ -1103,34 +1130,42 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  		devinfo->read_ptr = brcmf_pcie_read_idx; | ||||
|   | ||||
|  		h2d_w_idx_ptr = 0; | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; | ||||
|  		address = (u64)devinfo->idxbuf_dmahandle; | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| - | ||||
| -		h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; | ||||
| -		address += max_sub_queues * idx_offset; | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| - | ||||
| -		d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; | ||||
| -		address += max_sub_queues * idx_offset; | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| +		ringinfo.h2d_w_idx_hostaddr.low_addr = | ||||
| +			cpu_to_le32(address & 0xffffffff); | ||||
| +		ringinfo.h2d_w_idx_hostaddr.high_addr = | ||||
| +			cpu_to_le32(address >> 32); | ||||
| + | ||||
| +		h2d_r_idx_ptr = h2d_w_idx_ptr + | ||||
| +				max_submissionrings * idx_offset; | ||||
| +		address += max_submissionrings * idx_offset; | ||||
| +		ringinfo.h2d_r_idx_hostaddr.low_addr = | ||||
| +			cpu_to_le32(address & 0xffffffff); | ||||
| +		ringinfo.h2d_r_idx_hostaddr.high_addr = | ||||
| +			cpu_to_le32(address >> 32); | ||||
| + | ||||
| +		d2h_w_idx_ptr = h2d_r_idx_ptr + | ||||
| +				max_submissionrings * idx_offset; | ||||
| +		address += max_submissionrings * idx_offset; | ||||
| +		ringinfo.d2h_w_idx_hostaddr.low_addr = | ||||
| +			cpu_to_le32(address & 0xffffffff); | ||||
| +		ringinfo.d2h_w_idx_hostaddr.high_addr = | ||||
| +			cpu_to_le32(address >> 32); | ||||
|   | ||||
|  		d2h_r_idx_ptr = d2h_w_idx_ptr + | ||||
| -				BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; | ||||
| -		addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; | ||||
| -		address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| -		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| +				max_completionrings * idx_offset; | ||||
| +		address += max_completionrings * idx_offset; | ||||
| +		ringinfo.d2h_r_idx_hostaddr.low_addr = | ||||
| +			cpu_to_le32(address & 0xffffffff); | ||||
| +		ringinfo.d2h_r_idx_hostaddr.high_addr = | ||||
| +			cpu_to_le32(address >> 32); | ||||
| + | ||||
| +		memcpy_toio(devinfo->tcm + devinfo->shared.ring_info_addr, | ||||
| +			    &ringinfo, sizeof(ringinfo)); | ||||
|  		brcmf_dbg(PCIE, "Using host memory indices\n"); | ||||
|  	} | ||||
|   | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; | ||||
| -	ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +	ring_mem_ptr = le32_to_cpu(ringinfo.ringmem); | ||||
|   | ||||
|  	for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) { | ||||
|  		ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); | ||||
| @@ -1161,20 +1196,19 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  		ring_mem_ptr += BRCMF_RING_MEM_SZ; | ||||
|  	} | ||||
|   | ||||
| -	devinfo->shared.nrof_flowrings = | ||||
| -			max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
| -	rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), | ||||
| -			GFP_KERNEL); | ||||
| +	devinfo->shared.max_flowrings = max_flowrings; | ||||
| +	devinfo->shared.max_submissionrings = max_submissionrings; | ||||
| +	devinfo->shared.max_completionrings = max_completionrings; | ||||
| +	rings = kcalloc(max_flowrings, sizeof(*ring), GFP_KERNEL); | ||||
|  	if (!rings) | ||||
|  		goto fail; | ||||
|   | ||||
| -	brcmf_dbg(PCIE, "Nr of flowrings is %d\n", | ||||
| -		  devinfo->shared.nrof_flowrings); | ||||
| +	brcmf_dbg(PCIE, "Nr of flowrings is %d\n", max_flowrings); | ||||
|   | ||||
| -	for (i = 0; i < devinfo->shared.nrof_flowrings; i++) { | ||||
| +	for (i = 0; i < max_flowrings; i++) { | ||||
|  		ring = &rings[i]; | ||||
|  		ring->devinfo = devinfo; | ||||
| -		ring->id = i + BRCMF_NROF_COMMON_MSGRINGS; | ||||
| +		ring->id = i + BRCMF_H2D_MSGRING_FLOWRING_IDSTART; | ||||
|  		brcmf_commonring_register_cb(&ring->commonring, | ||||
|  					     brcmf_pcie_ring_mb_ring_bell, | ||||
|  					     brcmf_pcie_ring_mb_update_rptr, | ||||
| @@ -1357,17 +1391,16 @@ brcmf_pcie_init_share_ram_info(struct br | ||||
|  { | ||||
|  	struct brcmf_pcie_shared_info *shared; | ||||
|  	u32 addr; | ||||
| -	u32 version; | ||||
|   | ||||
|  	shared = &devinfo->shared; | ||||
|  	shared->tcm_base_address = sharedram_addr; | ||||
|   | ||||
|  	shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr); | ||||
| -	version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK; | ||||
| -	brcmf_dbg(PCIE, "PCIe protocol version %d\n", version); | ||||
| -	if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) || | ||||
| -	    (version < BRCMF_PCIE_MIN_SHARED_VERSION)) { | ||||
| -		brcmf_err("Unsupported PCIE version %d\n", version); | ||||
| +	shared->version = (u8)(shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK); | ||||
| +	brcmf_dbg(PCIE, "PCIe protocol version %d\n", shared->version); | ||||
| +	if ((shared->version > BRCMF_PCIE_MAX_SHARED_VERSION) || | ||||
| +	    (shared->version < BRCMF_PCIE_MIN_SHARED_VERSION)) { | ||||
| +		brcmf_err("Unsupported PCIE version %d\n", shared->version); | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| @@ -1661,18 +1694,18 @@ static void brcmf_pcie_setup(struct devi | ||||
|  		bus->msgbuf->commonrings[i] = | ||||
|  				&devinfo->shared.commonrings[i]->commonring; | ||||
|   | ||||
| -	flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), | ||||
| +	flowrings = kcalloc(devinfo->shared.max_flowrings, sizeof(*flowrings), | ||||
|  			    GFP_KERNEL); | ||||
|  	if (!flowrings) | ||||
|  		goto fail; | ||||
|   | ||||
| -	for (i = 0; i < devinfo->shared.nrof_flowrings; i++) | ||||
| +	for (i = 0; i < devinfo->shared.max_flowrings; i++) | ||||
|  		flowrings[i] = &devinfo->shared.flowrings[i].commonring; | ||||
|  	bus->msgbuf->flowrings = flowrings; | ||||
|   | ||||
|  	bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset; | ||||
|  	bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost; | ||||
| -	bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings; | ||||
| +	bus->msgbuf->max_flowrings = devinfo->shared.max_flowrings; | ||||
|   | ||||
|  	init_waitqueue_head(&devinfo->mbdata_resp_wait); | ||||
|   | ||||
| @@ -1,39 +0,0 @@ | ||||
| From dc630dc5c753ccba97ce174f9c2894f802f9bd93 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:21 +0000 | ||||
| Subject: [PATCH] brcmfmac: add support for 43341 chip | ||||
|  | ||||
| This chip was already supported, but seems a device came up giving | ||||
| a different chip identifier. So adding that effectively mapping to | ||||
| the same firmware file as for 43340 chip. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c       | 1 + | ||||
|  drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 1 + | ||||
|  2 files changed, 2 insertions(+) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | ||||
| @@ -621,6 +621,7 @@ static struct brcmf_firmware_mapping brc | ||||
|  	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330), | ||||
|  	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334), | ||||
|  	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340), | ||||
| +	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340), | ||||
|  	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335), | ||||
|  	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362), | ||||
|  	BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339), | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | ||||
| @@ -36,6 +36,7 @@ | ||||
|  #define BRCM_CC_4330_CHIP_ID		0x4330 | ||||
|  #define BRCM_CC_4334_CHIP_ID		0x4334 | ||||
|  #define BRCM_CC_43340_CHIP_ID		43340 | ||||
| +#define BRCM_CC_43341_CHIP_ID		43341 | ||||
|  #define BRCM_CC_43362_CHIP_ID		43362 | ||||
|  #define BRCM_CC_4335_CHIP_ID		0x4335 | ||||
|  #define BRCM_CC_4339_CHIP_ID		0x4339 | ||||
| @@ -1,319 +0,0 @@ | ||||
| From ac55136f43d3336c7b40238b779c404008229929 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:22 +0000 | ||||
| Subject: [PATCH] brcmfmac: move pno helper functions in separate source file | ||||
|  | ||||
| Introducing new source file for pno related functionality. Moving | ||||
| existing pno functions. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../wireless/broadcom/brcm80211/brcmfmac/Makefile  |   3 +- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.c         |  93 ++----------------- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 100 +++++++++++++++++++++ | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h |  40 +++++++++ | ||||
|  4 files changed, 150 insertions(+), 86 deletions(-) | ||||
|  create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
|  create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | ||||
| @@ -35,7 +35,8 @@ brcmfmac-objs += \ | ||||
|  		firmware.o \ | ||||
|  		feature.o \ | ||||
|  		btcoex.o \ | ||||
| -		vendor.o | ||||
| +		vendor.o \ | ||||
| +		pno.o | ||||
|  brcmfmac-$(CPTCFG_BRCMFMAC_PROTO_BCDC) += \ | ||||
|  		bcdc.o | ||||
|  brcmfmac-$(CPTCFG_BRCMFMAC_PROTO_MSGBUF) += \ | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -32,6 +32,7 @@ | ||||
|  #include "fwil_types.h" | ||||
|  #include "p2p.h" | ||||
|  #include "btcoex.h" | ||||
| +#include "pno.h" | ||||
|  #include "cfg80211.h" | ||||
|  #include "feature.h" | ||||
|  #include "fwil.h" | ||||
| @@ -41,16 +42,6 @@ | ||||
|  #include "common.h" | ||||
|   | ||||
|  #define BRCMF_SCAN_IE_LEN_MAX		2048 | ||||
| -#define BRCMF_PNO_VERSION		2 | ||||
| -#define BRCMF_PNO_TIME			30 | ||||
| -#define BRCMF_PNO_REPEAT		4 | ||||
| -#define BRCMF_PNO_FREQ_EXPO_MAX		3 | ||||
| -#define BRCMF_PNO_MAX_PFN_COUNT		16 | ||||
| -#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6 | ||||
| -#define BRCMF_PNO_HIDDEN_BIT		2 | ||||
| -#define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF | ||||
| -#define BRCMF_PNO_SCAN_COMPLETE		1 | ||||
| -#define BRCMF_PNO_SCAN_INCOMPLETE	0 | ||||
|   | ||||
|  #define WPA_OUI				"\x00\x50\xF2"	/* WPA OUI */ | ||||
|  #define WPA_OUI_TYPE			1 | ||||
| @@ -3323,76 +3314,6 @@ out_err: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -static int brcmf_dev_pno_clean(struct net_device *ndev) | ||||
| -{ | ||||
| -	int ret; | ||||
| - | ||||
| -	/* Disable pfn */ | ||||
| -	ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); | ||||
| -	if (ret == 0) { | ||||
| -		/* clear pfn */ | ||||
| -		ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", | ||||
| -					       NULL, 0); | ||||
| -	} | ||||
| -	if (ret < 0) | ||||
| -		brcmf_err("failed code %d\n", ret); | ||||
| - | ||||
| -	return ret; | ||||
| -} | ||||
| - | ||||
| -static int brcmf_dev_pno_config(struct brcmf_if *ifp, | ||||
| -				struct cfg80211_sched_scan_request *request) | ||||
| -{ | ||||
| -	struct brcmf_pno_param_le pfn_param; | ||||
| -	struct brcmf_pno_macaddr_le pfn_mac; | ||||
| -	s32 err; | ||||
| -	u8 *mac_mask; | ||||
| -	int i; | ||||
| - | ||||
| -	memset(&pfn_param, 0, sizeof(pfn_param)); | ||||
| -	pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); | ||||
| - | ||||
| -	/* set extra pno params */ | ||||
| -	pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); | ||||
| -	pfn_param.repeat = BRCMF_PNO_REPEAT; | ||||
| -	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; | ||||
| - | ||||
| -	/* set up pno scan fr */ | ||||
| -	pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); | ||||
| - | ||||
| -	err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, | ||||
| -				       sizeof(pfn_param)); | ||||
| -	if (err) { | ||||
| -		brcmf_err("pfn_set failed, err=%d\n", err); | ||||
| -		return err; | ||||
| -	} | ||||
| - | ||||
| -	/* Find out if mac randomization should be turned on */ | ||||
| -	if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) | ||||
| -		return 0; | ||||
| - | ||||
| -	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; | ||||
| -	pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; | ||||
| - | ||||
| -	memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); | ||||
| -	mac_mask = request->mac_addr_mask; | ||||
| -	for (i = 0; i < ETH_ALEN; i++) { | ||||
| -		pfn_mac.mac[i] &= mac_mask[i]; | ||||
| -		pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); | ||||
| -	} | ||||
| -	/* Clear multi bit */ | ||||
| -	pfn_mac.mac[0] &= 0xFE; | ||||
| -	/* Set locally administered */ | ||||
| -	pfn_mac.mac[0] |= 0x02; | ||||
| - | ||||
| -	err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, | ||||
| -				       sizeof(pfn_mac)); | ||||
| -	if (err) | ||||
| -		brcmf_err("pfn_macaddr failed, err=%d\n", err); | ||||
| - | ||||
| -	return err; | ||||
| -} | ||||
| - | ||||
|  static int | ||||
|  brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, | ||||
|  				struct net_device *ndev, | ||||
| @@ -3436,15 +3357,16 @@ brcmf_cfg80211_sched_scan_start(struct w | ||||
|   | ||||
|  	if (request->n_match_sets > 0) { | ||||
|  		/* clean up everything */ | ||||
| -		ret = brcmf_dev_pno_clean(ndev); | ||||
| +		ret = brcmf_pno_clean(ifp); | ||||
|  		if  (ret < 0) { | ||||
|  			brcmf_err("failed error=%d\n", ret); | ||||
|  			return ret; | ||||
|  		} | ||||
|   | ||||
|  		/* configure pno */ | ||||
| -		if (brcmf_dev_pno_config(ifp, request)) | ||||
| -			return -EINVAL; | ||||
| +		ret = brcmf_pno_config(ifp, request); | ||||
| +		if (ret < 0) | ||||
| +			return ret; | ||||
|   | ||||
|  		/* configure each match set */ | ||||
|  		for (i = 0; i < request->n_match_sets; i++) { | ||||
| @@ -3486,11 +3408,12 @@ static int brcmf_cfg80211_sched_scan_sto | ||||
|  					  struct net_device *ndev) | ||||
|  { | ||||
|  	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); | ||||
| +	struct brcmf_if *ifp = netdev_priv(ndev); | ||||
|   | ||||
|  	brcmf_dbg(SCAN, "enter\n"); | ||||
| -	brcmf_dev_pno_clean(ndev); | ||||
| +	brcmf_pno_clean(ifp); | ||||
|  	if (cfg->sched_escan) | ||||
| -		brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); | ||||
| +		brcmf_notify_escan_complete(cfg, ifp, true, true); | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -0,0 +1,100 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2016 Broadcom | ||||
| + * | ||||
| + * Permission to use, copy, modify, and/or distribute this software for any | ||||
| + * purpose with or without fee is hereby granted, provided that the above | ||||
| + * copyright notice and this permission notice appear in all copies. | ||||
| + * | ||||
| + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
| + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
| + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
| + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
| + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
| + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
| + */ | ||||
| +#include <linux/netdevice.h> | ||||
| +#include <net/cfg80211.h> | ||||
| + | ||||
| +#include "core.h" | ||||
| +#include "debug.h" | ||||
| +#include "pno.h" | ||||
| +#include "fwil.h" | ||||
| +#include "fwil_types.h" | ||||
| + | ||||
| +#define BRCMF_PNO_VERSION		2 | ||||
| +#define BRCMF_PNO_TIME			30 | ||||
| +#define BRCMF_PNO_REPEAT		4 | ||||
| +#define BRCMF_PNO_FREQ_EXPO_MAX		3 | ||||
| +#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6 | ||||
| +#define BRCMF_PNO_SCAN_INCOMPLETE	0 | ||||
| + | ||||
| +int brcmf_pno_clean(struct brcmf_if *ifp) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	/* Disable pfn */ | ||||
| +	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); | ||||
| +	if (ret == 0) { | ||||
| +		/* clear pfn */ | ||||
| +		ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); | ||||
| +	} | ||||
| +	if (ret < 0) | ||||
| +		brcmf_err("failed code %d\n", ret); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +int brcmf_pno_config(struct brcmf_if *ifp, | ||||
| +		     struct cfg80211_sched_scan_request *request) | ||||
| +{ | ||||
| +	struct brcmf_pno_param_le pfn_param; | ||||
| +	struct brcmf_pno_macaddr_le pfn_mac; | ||||
| +	s32 err; | ||||
| +	u8 *mac_mask; | ||||
| +	int i; | ||||
| + | ||||
| +	memset(&pfn_param, 0, sizeof(pfn_param)); | ||||
| +	pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); | ||||
| + | ||||
| +	/* set extra pno params */ | ||||
| +	pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); | ||||
| +	pfn_param.repeat = BRCMF_PNO_REPEAT; | ||||
| +	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; | ||||
| + | ||||
| +	/* set up pno scan fr */ | ||||
| +	pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); | ||||
| + | ||||
| +	err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, | ||||
| +				       sizeof(pfn_param)); | ||||
| +	if (err) { | ||||
| +		brcmf_err("pfn_set failed, err=%d\n", err); | ||||
| +		return err; | ||||
| +	} | ||||
| + | ||||
| +	/* Find out if mac randomization should be turned on */ | ||||
| +	if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) | ||||
| +		return 0; | ||||
| + | ||||
| +	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; | ||||
| +	pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; | ||||
| + | ||||
| +	memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); | ||||
| +	mac_mask = request->mac_addr_mask; | ||||
| +	for (i = 0; i < ETH_ALEN; i++) { | ||||
| +		pfn_mac.mac[i] &= mac_mask[i]; | ||||
| +		pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); | ||||
| +	} | ||||
| +	/* Clear multi bit */ | ||||
| +	pfn_mac.mac[0] &= 0xFE; | ||||
| +	/* Set locally administered */ | ||||
| +	pfn_mac.mac[0] |= 0x02; | ||||
| + | ||||
| +	err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, | ||||
| +				       sizeof(pfn_mac)); | ||||
| +	if (err) | ||||
| +		brcmf_err("pfn_macaddr failed, err=%d\n", err); | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| @@ -0,0 +1,40 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2016 Broadcom | ||||
| + * | ||||
| + * Permission to use, copy, modify, and/or distribute this software for any | ||||
| + * purpose with or without fee is hereby granted, provided that the above | ||||
| + * copyright notice and this permission notice appear in all copies. | ||||
| + * | ||||
| + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
| + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
| + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
| + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
| + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
| + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
| + */ | ||||
| +#ifndef _BRCMF_PNO_H | ||||
| +#define _BRCMF_PNO_H | ||||
| + | ||||
| +#define BRCMF_PNO_SCAN_COMPLETE		1 | ||||
| +#define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF | ||||
| +#define BRCMF_PNO_HIDDEN_BIT		2 | ||||
| +#define BRCMF_PNO_MAX_PFN_COUNT		16 | ||||
| + | ||||
| +/** | ||||
| + * brcmf_pno_clean - disable and clear pno in firmware. | ||||
| + * | ||||
| + * @ifp: interface object used. | ||||
| + */ | ||||
| +int brcmf_pno_clean(struct brcmf_if *ifp); | ||||
| + | ||||
| +/** | ||||
| + * brcmf_pno_config - configure pno parameters. | ||||
| + * | ||||
| + * @ifp: interface object used. | ||||
| + * @request: scheduled scan parameters. | ||||
| + */ | ||||
| +int brcmf_pno_config(struct brcmf_if *ifp, | ||||
| +		     struct cfg80211_sched_scan_request *request); | ||||
| + | ||||
| +#endif /* _BRCMF_PNO_H */ | ||||
| @@ -1,221 +0,0 @@ | ||||
| From 3e2e86ab19c2a43953de30089c5411c580ddb5f7 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:23 +0000 | ||||
| Subject: [PATCH] brcmfmac: fix handling ssids in .sched_scan_start() callback | ||||
|  | ||||
| The ssids list in the scheduled scan request were not properly taken | ||||
| into account when configuring in firmware. The hidden bit was set for | ||||
| any ssid resulting in active scanning for all. Only set it for ssids | ||||
| that are in the ssids list. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.c         | 103 ++++++++++----------- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c |  18 ++++ | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h |  12 ++- | ||||
|  3 files changed, 76 insertions(+), 57 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -3314,19 +3314,37 @@ out_err: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, | ||||
| +				 struct cfg80211_sched_scan_request *req) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	if (!ssid || !req->ssids || !req->n_ssids) | ||||
| +		return false; | ||||
| + | ||||
| +	for (i = 0; i < req->n_ssids; i++) { | ||||
| +		if (ssid->ssid_len == req->ssids[i].ssid_len) { | ||||
| +			if (!strncmp(ssid->ssid, req->ssids[i].ssid, | ||||
| +				     ssid->ssid_len)) | ||||
| +				return true; | ||||
| +		} | ||||
| +	} | ||||
| +	return false; | ||||
| +} | ||||
| + | ||||
|  static int | ||||
|  brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, | ||||
|  				struct net_device *ndev, | ||||
| -				struct cfg80211_sched_scan_request *request) | ||||
| +				struct cfg80211_sched_scan_request *req) | ||||
|  { | ||||
|  	struct brcmf_if *ifp = netdev_priv(ndev); | ||||
|  	struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); | ||||
| -	struct brcmf_pno_net_param_le pfn; | ||||
| +	struct cfg80211_ssid *ssid; | ||||
|  	int i; | ||||
|  	int ret = 0; | ||||
|   | ||||
|  	brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", | ||||
| -		  request->n_match_sets, request->n_ssids); | ||||
| +		  req->n_match_sets, req->n_ssids); | ||||
|  	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { | ||||
|  		brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); | ||||
|  		return -EAGAIN; | ||||
| @@ -3337,71 +3355,46 @@ brcmf_cfg80211_sched_scan_start(struct w | ||||
|  		return -EAGAIN; | ||||
|  	} | ||||
|   | ||||
| -	if (!request->n_ssids || !request->n_match_sets) { | ||||
| -		brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", | ||||
| -			  request->n_ssids); | ||||
| +	if (req->n_match_sets <= 0) { | ||||
| +		brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n", | ||||
| +			  req->n_match_sets); | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	if (request->n_ssids > 0) { | ||||
| -		for (i = 0; i < request->n_ssids; i++) { | ||||
| -			/* Active scan req for ssids */ | ||||
| -			brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", | ||||
| -				  request->ssids[i].ssid); | ||||
| - | ||||
| -			/* match_set ssids is a supert set of n_ssid list, | ||||
| -			 * so we need not add these set separately. | ||||
| -			 */ | ||||
| -		} | ||||
| +	/* clean up everything */ | ||||
| +	ret = brcmf_pno_clean(ifp); | ||||
| +	if  (ret < 0) { | ||||
| +		brcmf_err("failed error=%d\n", ret); | ||||
| +		return ret; | ||||
|  	} | ||||
|   | ||||
| -	if (request->n_match_sets > 0) { | ||||
| -		/* clean up everything */ | ||||
| -		ret = brcmf_pno_clean(ifp); | ||||
| -		if  (ret < 0) { | ||||
| -			brcmf_err("failed error=%d\n", ret); | ||||
| -			return ret; | ||||
| +	/* configure pno */ | ||||
| +	ret = brcmf_pno_config(ifp, req); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* configure each match set */ | ||||
| +	for (i = 0; i < req->n_match_sets; i++) { | ||||
| + | ||||
| +		ssid = &req->match_sets[i].ssid; | ||||
| + | ||||
| +		if (!ssid->ssid_len) { | ||||
| +			brcmf_err("skip broadcast ssid\n"); | ||||
| +			continue; | ||||
|  		} | ||||
|   | ||||
| -		/* configure pno */ | ||||
| -		ret = brcmf_pno_config(ifp, request); | ||||
| +		ret = brcmf_pno_add_ssid(ifp, ssid, | ||||
| +					 brcmf_is_ssid_active(ssid, req)); | ||||
|  		if (ret < 0) | ||||
| -			return ret; | ||||
| - | ||||
| -		/* configure each match set */ | ||||
| -		for (i = 0; i < request->n_match_sets; i++) { | ||||
| -			struct cfg80211_ssid *ssid; | ||||
| -			u32 ssid_len; | ||||
| - | ||||
| -			ssid = &request->match_sets[i].ssid; | ||||
| -			ssid_len = ssid->ssid_len; | ||||
| - | ||||
| -			if (!ssid_len) { | ||||
| -				brcmf_err("skip broadcast ssid\n"); | ||||
| -				continue; | ||||
| -			} | ||||
| -			pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); | ||||
| -			pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); | ||||
| -			pfn.wsec = cpu_to_le32(0); | ||||
| -			pfn.infra = cpu_to_le32(1); | ||||
| -			pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); | ||||
| -			pfn.ssid.SSID_len = cpu_to_le32(ssid_len); | ||||
| -			memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); | ||||
| -			ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, | ||||
| -						       sizeof(pfn)); | ||||
|  			brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", | ||||
|  				  ret == 0 ? "set" : "failed", ssid->ssid); | ||||
| -		} | ||||
| -		/* Enable the PNO */ | ||||
| -		if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { | ||||
| -			brcmf_err("PNO enable failed!! ret=%d\n", ret); | ||||
| -			return -EINVAL; | ||||
| -		} | ||||
| -	} else { | ||||
| -		return -EINVAL; | ||||
|  	} | ||||
| +	/* Enable the PNO */ | ||||
| +	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); | ||||
| +	if (ret < 0) | ||||
| +		brcmf_err("PNO enable failed!! ret=%d\n", ret); | ||||
|   | ||||
| -	return 0; | ||||
| +	return ret; | ||||
|  } | ||||
|   | ||||
|  static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -28,6 +28,8 @@ | ||||
|  #define BRCMF_PNO_FREQ_EXPO_MAX		3 | ||||
|  #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6 | ||||
|  #define BRCMF_PNO_SCAN_INCOMPLETE	0 | ||||
| +#define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF | ||||
| +#define BRCMF_PNO_HIDDEN_BIT		2 | ||||
|   | ||||
|  int brcmf_pno_clean(struct brcmf_if *ifp) | ||||
|  { | ||||
| @@ -98,3 +100,19 @@ int brcmf_pno_config(struct brcmf_if *if | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, | ||||
| +		       bool active) | ||||
| +{ | ||||
| +	struct brcmf_pno_net_param_le pfn; | ||||
| + | ||||
| +	pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); | ||||
| +	pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); | ||||
| +	pfn.wsec = cpu_to_le32(0); | ||||
| +	pfn.infra = cpu_to_le32(1); | ||||
| +	if (active) | ||||
| +		pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); | ||||
| +	pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len); | ||||
| +	memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len); | ||||
| +	return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn)); | ||||
| +} | ||||
| + | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| @@ -17,8 +17,6 @@ | ||||
|  #define _BRCMF_PNO_H | ||||
|   | ||||
|  #define BRCMF_PNO_SCAN_COMPLETE		1 | ||||
| -#define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF | ||||
| -#define BRCMF_PNO_HIDDEN_BIT		2 | ||||
|  #define BRCMF_PNO_MAX_PFN_COUNT		16 | ||||
|   | ||||
|  /** | ||||
| @@ -37,4 +35,14 @@ int brcmf_pno_clean(struct brcmf_if *ifp | ||||
|  int brcmf_pno_config(struct brcmf_if *ifp, | ||||
|  		     struct cfg80211_sched_scan_request *request); | ||||
|   | ||||
| +/** | ||||
| + * brcmf_pno_add_ssid - add ssid for pno in firmware. | ||||
| + * | ||||
| + * @ifp: interface object used. | ||||
| + * @ssid: ssid information. | ||||
| + * @active: indicate this ssid needs to be actively probed. | ||||
| + */ | ||||
| +int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, | ||||
| +		       bool active); | ||||
| + | ||||
|  #endif /* _BRCMF_PNO_H */ | ||||
| @@ -1,58 +0,0 @@ | ||||
| From ab5981c830339b945ddbedc314567a9e5f506d72 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:24 +0000 | ||||
| Subject: [PATCH] brcmfmac: change prototype for brcmf_do_escan() | ||||
|  | ||||
| Reduce the number of parameters as the removed ones can be obtained | ||||
| through struct brcmf_if parameter. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 10 +++++----- | ||||
|  1 file changed, 5 insertions(+), 5 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -1080,9 +1080,9 @@ exit: | ||||
|  } | ||||
|   | ||||
|  static s32 | ||||
| -brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, | ||||
| -	       struct brcmf_if *ifp, struct cfg80211_scan_request *request) | ||||
| +brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request) | ||||
|  { | ||||
| +	struct brcmf_cfg80211_info *cfg = ifp->drvr->config; | ||||
|  	s32 err; | ||||
|  	u32 passive_scan; | ||||
|  	struct brcmf_scan_results *results; | ||||
| @@ -1090,7 +1090,7 @@ brcmf_do_escan(struct brcmf_cfg80211_inf | ||||
|   | ||||
|  	brcmf_dbg(SCAN, "Enter\n"); | ||||
|  	escan->ifp = ifp; | ||||
| -	escan->wiphy = wiphy; | ||||
| +	escan->wiphy = cfg->wiphy; | ||||
|  	escan->escan_state = WL_ESCAN_STATE_SCANNING; | ||||
|  	passive_scan = cfg->active_scan ? 0 : 1; | ||||
|  	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, | ||||
| @@ -1170,7 +1170,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy | ||||
|  		if (err) | ||||
|  			goto scan_out; | ||||
|   | ||||
| -		err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); | ||||
| +		err = brcmf_do_escan(vif->ifp, request); | ||||
|  		if (err) | ||||
|  			goto scan_out; | ||||
|  	} else { | ||||
| @@ -3289,7 +3289,7 @@ brcmf_notify_sched_scan_results(struct b | ||||
|   | ||||
|  		set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); | ||||
|  		cfg->escan_info.run = brcmf_run_escan; | ||||
| -		err = brcmf_do_escan(cfg, wiphy, ifp, request); | ||||
| +		err = brcmf_do_escan(ifp, request); | ||||
|  		if (err) { | ||||
|  			clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); | ||||
|  			goto out_err; | ||||
| @@ -1,306 +0,0 @@ | ||||
| From fa85b30a908455ff25def3a5f319aad272ef4862 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:25 +0000 | ||||
| Subject: [PATCH] brcmfmac: make internal escan more generic | ||||
|  | ||||
| For scheduled scan we initiate an escan in firmware to obtain more | ||||
| info missing from the scheduled scan notification we get from firmware. | ||||
| For upcoming functionality this is also required so make it a bit | ||||
| more generic. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.c         | 187 ++++++++++++--------- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.h         |   4 +- | ||||
|  2 files changed, 109 insertions(+), 82 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -757,12 +757,12 @@ s32 brcmf_notify_escan_complete(struct b | ||||
|  	brcmf_scan_config_mpc(ifp, 1); | ||||
|   | ||||
|  	/* | ||||
| -	 * e-scan can be initiated by scheduled scan | ||||
| +	 * e-scan can be initiated internally | ||||
|  	 * which takes precedence. | ||||
|  	 */ | ||||
| -	if (cfg->sched_escan) { | ||||
| +	if (cfg->internal_escan) { | ||||
|  		brcmf_dbg(SCAN, "scheduled scan completed\n"); | ||||
| -		cfg->sched_escan = false; | ||||
| +		cfg->internal_escan = false; | ||||
|  		if (!aborted) | ||||
|  			cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); | ||||
|  	} else if (scan_request) { | ||||
| @@ -3013,7 +3013,7 @@ void brcmf_abort_scanning(struct brcmf_c | ||||
|  	struct escan_info *escan = &cfg->escan_info; | ||||
|   | ||||
|  	set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); | ||||
| -	if (cfg->scan_request) { | ||||
| +	if (cfg->internal_escan || cfg->scan_request) { | ||||
|  		escan->escan_state = WL_ESCAN_STATE_IDLE; | ||||
|  		brcmf_notify_escan_complete(cfg, escan->ifp, true, true); | ||||
|  	} | ||||
| @@ -3036,7 +3036,7 @@ static void brcmf_escan_timeout(unsigned | ||||
|  	struct brcmf_cfg80211_info *cfg = | ||||
|  			(struct brcmf_cfg80211_info *)data; | ||||
|   | ||||
| -	if (cfg->scan_request) { | ||||
| +	if (cfg->internal_escan || cfg->scan_request) { | ||||
|  		brcmf_err("timer expired\n"); | ||||
|  		schedule_work(&cfg->escan_timeout_work); | ||||
|  	} | ||||
| @@ -3119,7 +3119,7 @@ brcmf_cfg80211_escan_handler(struct brcm | ||||
|  		if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) | ||||
|  			goto exit; | ||||
|   | ||||
| -		if (!cfg->scan_request) { | ||||
| +		if (!cfg->internal_escan && !cfg->scan_request) { | ||||
|  			brcmf_dbg(SCAN, "result without cfg80211 request\n"); | ||||
|  			goto exit; | ||||
|  		} | ||||
| @@ -3165,7 +3165,7 @@ brcmf_cfg80211_escan_handler(struct brcm | ||||
|  		cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; | ||||
|  		if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) | ||||
|  			goto exit; | ||||
| -		if (cfg->scan_request) { | ||||
| +		if (cfg->internal_escan || cfg->scan_request) { | ||||
|  			brcmf_inform_bss(cfg); | ||||
|  			aborted = status != BRCMF_E_STATUS_SUCCESS; | ||||
|  			brcmf_notify_escan_complete(cfg, ifp, aborted, false); | ||||
| @@ -3190,6 +3190,73 @@ static void brcmf_init_escan(struct brcm | ||||
|  		  brcmf_cfg80211_escan_timeout_worker); | ||||
|  } | ||||
|   | ||||
| +static struct cfg80211_scan_request * | ||||
| +brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) { | ||||
| +	struct cfg80211_scan_request *req; | ||||
| +	size_t req_size; | ||||
| + | ||||
| +	req_size = sizeof(*req) + | ||||
| +		   n_netinfo * sizeof(req->channels[0]) + | ||||
| +		   n_netinfo * sizeof(*req->ssids); | ||||
| + | ||||
| +	req = kzalloc(req_size, GFP_KERNEL); | ||||
| +	if (req) { | ||||
| +		req->wiphy = wiphy; | ||||
| +		req->ssids = (void *)(&req->channels[0]) + | ||||
| +			     n_netinfo * sizeof(req->channels[0]); | ||||
| +	} | ||||
| +	return req; | ||||
| +} | ||||
| + | ||||
| +static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, | ||||
| +					 u8 *ssid, u8 ssid_len, u8 channel) | ||||
| +{ | ||||
| +	struct ieee80211_channel *chan; | ||||
| +	enum nl80211_band band; | ||||
| +	int freq; | ||||
| + | ||||
| +	if (channel <= CH_MAX_2G_CHANNEL) | ||||
| +		band = NL80211_BAND_2GHZ; | ||||
| +	else | ||||
| +		band = NL80211_BAND_5GHZ; | ||||
| + | ||||
| +	freq = ieee80211_channel_to_frequency(channel, band); | ||||
| +	if (!freq) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	chan = ieee80211_get_channel(req->wiphy, freq); | ||||
| +	if (!chan) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	req->channels[req->n_channels++] = chan; | ||||
| +	memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len); | ||||
| +	req->ssids[req->n_ssids++].ssid_len = ssid_len; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int brcmf_start_internal_escan(struct brcmf_if *ifp, | ||||
| +				      struct cfg80211_scan_request *request) | ||||
| +{ | ||||
| +	struct brcmf_cfg80211_info *cfg = ifp->drvr->config; | ||||
| +	int err; | ||||
| + | ||||
| +	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { | ||||
| +		/* Abort any on-going scan */ | ||||
| +		brcmf_abort_scanning(cfg); | ||||
| +	} | ||||
| + | ||||
| +	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); | ||||
| +	cfg->escan_info.run = brcmf_run_escan; | ||||
| +	err = brcmf_do_escan(ifp, request); | ||||
| +	if (err) { | ||||
| +		clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); | ||||
| +		return err; | ||||
| +	} | ||||
| +	cfg->internal_escan = true; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  /* PFN result doesn't have all the info which are required by the supplicant | ||||
|   * (For e.g IEs) Do a target Escan so that sched scan results are reported | ||||
|   * via wl_inform_single_bss in the required format. Escan does require the | ||||
| @@ -3203,12 +3270,8 @@ brcmf_notify_sched_scan_results(struct b | ||||
|  	struct brcmf_cfg80211_info *cfg = ifp->drvr->config; | ||||
|  	struct brcmf_pno_net_info_le *netinfo, *netinfo_start; | ||||
|  	struct cfg80211_scan_request *request = NULL; | ||||
| -	struct cfg80211_ssid *ssid = NULL; | ||||
| -	struct ieee80211_channel *channel = NULL; | ||||
|  	struct wiphy *wiphy = cfg_to_wiphy(cfg); | ||||
| -	int err = 0; | ||||
| -	int channel_req = 0; | ||||
| -	int band = 0; | ||||
| +	int i, err = 0; | ||||
|  	struct brcmf_pno_scanresults_le *pfn_result; | ||||
|  	u32 result_count; | ||||
|  	u32 status; | ||||
| @@ -3234,83 +3297,47 @@ brcmf_notify_sched_scan_results(struct b | ||||
|  	 */ | ||||
|  	WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); | ||||
|  	brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); | ||||
| -	if (result_count > 0) { | ||||
| -		int i; | ||||
| - | ||||
| -		request = kzalloc(sizeof(*request), GFP_KERNEL); | ||||
| -		ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); | ||||
| -		channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); | ||||
| -		if (!request || !ssid || !channel) { | ||||
| -			err = -ENOMEM; | ||||
| -			goto out_err; | ||||
| -		} | ||||
| +	if (!result_count) { | ||||
| +		brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); | ||||
| +		goto out_err; | ||||
| +	} | ||||
| +	request = brcmf_alloc_internal_escan_request(wiphy, | ||||
| +						     result_count); | ||||
| +	if (!request) { | ||||
| +		err = -ENOMEM; | ||||
| +		goto out_err; | ||||
| +	} | ||||
|   | ||||
| -		request->wiphy = wiphy; | ||||
| -		data += sizeof(struct brcmf_pno_scanresults_le); | ||||
| -		netinfo_start = (struct brcmf_pno_net_info_le *)data; | ||||
| - | ||||
| -		for (i = 0; i < result_count; i++) { | ||||
| -			netinfo = &netinfo_start[i]; | ||||
| -			if (!netinfo) { | ||||
| -				brcmf_err("Invalid netinfo ptr. index: %d\n", | ||||
| -					  i); | ||||
| -				err = -EINVAL; | ||||
| -				goto out_err; | ||||
| -			} | ||||
| +	data += sizeof(struct brcmf_pno_scanresults_le); | ||||
| +	netinfo_start = (struct brcmf_pno_net_info_le *)data; | ||||
|   | ||||
| -			brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", | ||||
| -				  netinfo->SSID, netinfo->channel); | ||||
| -			memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); | ||||
| -			ssid[i].ssid_len = netinfo->SSID_len; | ||||
| -			request->n_ssids++; | ||||
| - | ||||
| -			channel_req = netinfo->channel; | ||||
| -			if (channel_req <= CH_MAX_2G_CHANNEL) | ||||
| -				band = NL80211_BAND_2GHZ; | ||||
| -			else | ||||
| -				band = NL80211_BAND_5GHZ; | ||||
| -			channel[i].center_freq = | ||||
| -				ieee80211_channel_to_frequency(channel_req, | ||||
| -							       band); | ||||
| -			channel[i].band = band; | ||||
| -			channel[i].flags |= IEEE80211_CHAN_NO_HT40; | ||||
| -			request->channels[i] = &channel[i]; | ||||
| -			request->n_channels++; | ||||
| -		} | ||||
| - | ||||
| -		/* assign parsed ssid array */ | ||||
| -		if (request->n_ssids) | ||||
| -			request->ssids = &ssid[0]; | ||||
| - | ||||
| -		if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { | ||||
| -			/* Abort any on-going scan */ | ||||
| -			brcmf_abort_scanning(cfg); | ||||
| +	for (i = 0; i < result_count; i++) { | ||||
| +		netinfo = &netinfo_start[i]; | ||||
| +		if (!netinfo) { | ||||
| +			brcmf_err("Invalid netinfo ptr. index: %d\n", | ||||
| +				  i); | ||||
| +			err = -EINVAL; | ||||
| +			goto out_err; | ||||
|  		} | ||||
|   | ||||
| -		set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); | ||||
| -		cfg->escan_info.run = brcmf_run_escan; | ||||
| -		err = brcmf_do_escan(ifp, request); | ||||
| -		if (err) { | ||||
| -			clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); | ||||
| +		brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n", | ||||
| +			  netinfo->SSID, netinfo->channel); | ||||
| +		err = brcmf_internal_escan_add_info(request, | ||||
| +						    netinfo->SSID, | ||||
| +						    netinfo->SSID_len, | ||||
| +						    netinfo->channel); | ||||
| +		if (err) | ||||
|  			goto out_err; | ||||
| -		} | ||||
| -		cfg->sched_escan = true; | ||||
| -		cfg->scan_request = request; | ||||
| -	} else { | ||||
| -		brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); | ||||
| -		goto out_err; | ||||
|  	} | ||||
|   | ||||
| -	kfree(ssid); | ||||
| -	kfree(channel); | ||||
| -	kfree(request); | ||||
| -	return 0; | ||||
| +	err = brcmf_start_internal_escan(ifp, request); | ||||
| +	if (!err) | ||||
| +		goto free_req; | ||||
|   | ||||
|  out_err: | ||||
| -	kfree(ssid); | ||||
| -	kfree(channel); | ||||
| -	kfree(request); | ||||
|  	cfg80211_sched_scan_stopped(wiphy); | ||||
| +free_req: | ||||
| +	kfree(request); | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| @@ -3405,7 +3432,7 @@ static int brcmf_cfg80211_sched_scan_sto | ||||
|   | ||||
|  	brcmf_dbg(SCAN, "enter\n"); | ||||
|  	brcmf_pno_clean(ifp); | ||||
| -	if (cfg->sched_escan) | ||||
| +	if (cfg->internal_escan) | ||||
|  		brcmf_notify_escan_complete(cfg, ifp, true, true); | ||||
|  	return 0; | ||||
|  } | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h | ||||
| @@ -271,7 +271,7 @@ struct brcmf_cfg80211_wowl { | ||||
|   * @pub: common driver information. | ||||
|   * @channel: current channel. | ||||
|   * @active_scan: current scan mode. | ||||
| - * @sched_escan: e-scan for scheduled scan support running. | ||||
| + * @internal_escan: indicates internally initiated e-scan is running. | ||||
|   * @ibss_starter: indicates this sta is ibss starter. | ||||
|   * @pwr_save: indicate whether dongle to support power save mode. | ||||
|   * @dongle_up: indicate whether dongle up or not. | ||||
| @@ -303,7 +303,7 @@ struct brcmf_cfg80211_info { | ||||
|  	struct brcmf_pub *pub; | ||||
|  	u32 channel; | ||||
|  	bool active_scan; | ||||
| -	bool sched_escan; | ||||
| +	bool internal_escan; | ||||
|  	bool ibss_starter; | ||||
|  	bool pwr_save; | ||||
|  	bool dongle_up; | ||||
| @@ -1,176 +0,0 @@ | ||||
| From fca6cb2f059e51dec3fcf3589a5abbbcce5b4043 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:26 +0000 | ||||
| Subject: [PATCH] brcmfmac: split up brcmf_pno_config() function | ||||
|  | ||||
| The brcmf_pno_config() function handles two configurations in | ||||
| firmware. Split it and have caller sort out what is needed. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.c         | 11 +++- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 60 ++++++++++++++++------ | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 17 ++++-- | ||||
|  3 files changed, 68 insertions(+), 20 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -42,6 +42,7 @@ | ||||
|  #include "common.h" | ||||
|   | ||||
|  #define BRCMF_SCAN_IE_LEN_MAX		2048 | ||||
| +#define BRCMF_SCHED_SCAN_PERIOD		30 | ||||
|   | ||||
|  #define WPA_OUI				"\x00\x50\xF2"	/* WPA OUI */ | ||||
|  #define WPA_OUI_TYPE			1 | ||||
| @@ -3396,10 +3397,18 @@ brcmf_cfg80211_sched_scan_start(struct w | ||||
|  	} | ||||
|   | ||||
|  	/* configure pno */ | ||||
| -	ret = brcmf_pno_config(ifp, req); | ||||
| +	ret = brcmf_pno_config(ifp, BRCMF_SCHED_SCAN_PERIOD, 0, 0); | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| +	/* configure random mac */ | ||||
| +	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { | ||||
| +		ret = brcmf_pno_set_random(ifp, req->mac_addr, | ||||
| +					   req->mac_addr_mask); | ||||
| +		if (ret < 0) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
|  	/* configure each match set */ | ||||
|  	for (i = 0; i < req->n_match_sets; i++) { | ||||
|   | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -23,10 +23,12 @@ | ||||
|  #include "fwil_types.h" | ||||
|   | ||||
|  #define BRCMF_PNO_VERSION		2 | ||||
| -#define BRCMF_PNO_TIME			30 | ||||
|  #define BRCMF_PNO_REPEAT		4 | ||||
|  #define BRCMF_PNO_FREQ_EXPO_MAX		3 | ||||
| +#define BRCMF_PNO_IMMEDIATE_SCAN_BIT	3 | ||||
| +#define BRCMF_PNO_ENABLE_BD_SCAN_BIT	5 | ||||
|  #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6 | ||||
| +#define BRCMF_PNO_REPORT_SEPARATELY_BIT	11 | ||||
|  #define BRCMF_PNO_SCAN_INCOMPLETE	0 | ||||
|  #define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF | ||||
|  #define BRCMF_PNO_HIDDEN_BIT		2 | ||||
| @@ -47,42 +49,68 @@ int brcmf_pno_clean(struct brcmf_if *ifp | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -int brcmf_pno_config(struct brcmf_if *ifp, | ||||
| -		     struct cfg80211_sched_scan_request *request) | ||||
| +int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, | ||||
| +		     u32 mscan, u32 bestn) | ||||
|  { | ||||
|  	struct brcmf_pno_param_le pfn_param; | ||||
| -	struct brcmf_pno_macaddr_le pfn_mac; | ||||
| +	u16 flags; | ||||
| +	u32 pfnmem; | ||||
|  	s32 err; | ||||
| -	u8 *mac_mask; | ||||
| -	int i; | ||||
|   | ||||
|  	memset(&pfn_param, 0, sizeof(pfn_param)); | ||||
|  	pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); | ||||
|   | ||||
|  	/* set extra pno params */ | ||||
| -	pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); | ||||
| +	flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) | | ||||
| +		BIT(BRCMF_PNO_REPORT_SEPARATELY_BIT) | | ||||
| +		BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); | ||||
|  	pfn_param.repeat = BRCMF_PNO_REPEAT; | ||||
|  	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; | ||||
|   | ||||
|  	/* set up pno scan fr */ | ||||
| -	pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); | ||||
| +	pfn_param.scan_freq = cpu_to_le32(scan_freq); | ||||
| + | ||||
| +	if (mscan) { | ||||
| +		pfnmem = bestn; | ||||
|   | ||||
| +		/* set bestn in firmware */ | ||||
| +		err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem); | ||||
| +		if (err < 0) { | ||||
| +			brcmf_err("failed to set pfnmem\n"); | ||||
| +			goto exit; | ||||
| +		} | ||||
| +		/* get max mscan which the firmware supports */ | ||||
| +		err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem); | ||||
| +		if (err < 0) { | ||||
| +			brcmf_err("failed to get pfnmem\n"); | ||||
| +			goto exit; | ||||
| +		} | ||||
| +		mscan = min_t(u32, mscan, pfnmem); | ||||
| +		pfn_param.mscan = mscan; | ||||
| +		pfn_param.bestn = bestn; | ||||
| +		flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT); | ||||
| +		brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn); | ||||
| +	} | ||||
| + | ||||
| +	pfn_param.flags = cpu_to_le16(flags); | ||||
|  	err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, | ||||
|  				       sizeof(pfn_param)); | ||||
| -	if (err) { | ||||
| +	if (err) | ||||
|  		brcmf_err("pfn_set failed, err=%d\n", err); | ||||
| -		return err; | ||||
| -	} | ||||
|   | ||||
| -	/* Find out if mac randomization should be turned on */ | ||||
| -	if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) | ||||
| -		return 0; | ||||
| +exit: | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask) | ||||
| +{ | ||||
| +	struct brcmf_pno_macaddr_le pfn_mac; | ||||
| +	int err, i; | ||||
|   | ||||
|  	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; | ||||
|  	pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; | ||||
|   | ||||
| -	memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); | ||||
| -	mac_mask = request->mac_addr_mask; | ||||
| +	memcpy(pfn_mac.mac, mac_addr, ETH_ALEN); | ||||
|  	for (i = 0; i < ETH_ALEN; i++) { | ||||
|  		pfn_mac.mac[i] &= mac_mask[i]; | ||||
|  		pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| @@ -30,10 +30,21 @@ int brcmf_pno_clean(struct brcmf_if *ifp | ||||
|   * brcmf_pno_config - configure pno parameters. | ||||
|   * | ||||
|   * @ifp: interface object used. | ||||
| - * @request: scheduled scan parameters. | ||||
| + * @scan_freq: scan frequency period in seconds. | ||||
| + * @mscan: maximum number of scans stored in firmware. | ||||
| + * @bestn: maximum number of APs per scan stored in firmware. | ||||
|   */ | ||||
| -int brcmf_pno_config(struct brcmf_if *ifp, | ||||
| -		     struct cfg80211_sched_scan_request *request); | ||||
| +int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, | ||||
| +		     u32 mscan, u32 bestn); | ||||
| + | ||||
| +/** | ||||
| + * brcmf_pno_set_random - setup randomisation mac address for pno. | ||||
| + * | ||||
| + * @ifp: interface object used. | ||||
| + * @mac_addr: MAC address used with randomisation. | ||||
| + * @mac_mask: MAC address mask used for randomisation. | ||||
| + */ | ||||
| +int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask); | ||||
|   | ||||
|  /** | ||||
|   * brcmf_pno_add_ssid - add ssid for pno in firmware. | ||||
| @@ -1,292 +0,0 @@ | ||||
| From 3e48611d31dd333be01576902f2dc11adefc9a06 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:27 +0000 | ||||
| Subject: [PATCH] brcmfmac: move scheduled scan activation to pno source file | ||||
|  | ||||
| Rework .sched_scan_start() callback moving actual configuration of | ||||
| the device in pno source file. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.c         |  65 +----------- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 110 +++++++++++++++++---- | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h |  29 +----- | ||||
|  3 files changed, 94 insertions(+), 110 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -42,7 +42,6 @@ | ||||
|  #include "common.h" | ||||
|   | ||||
|  #define BRCMF_SCAN_IE_LEN_MAX		2048 | ||||
| -#define BRCMF_SCHED_SCAN_PERIOD		30 | ||||
|   | ||||
|  #define WPA_OUI				"\x00\x50\xF2"	/* WPA OUI */ | ||||
|  #define WPA_OUI_TYPE			1 | ||||
| @@ -3342,24 +3341,6 @@ free_req: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, | ||||
| -				 struct cfg80211_sched_scan_request *req) | ||||
| -{ | ||||
| -	int i; | ||||
| - | ||||
| -	if (!ssid || !req->ssids || !req->n_ssids) | ||||
| -		return false; | ||||
| - | ||||
| -	for (i = 0; i < req->n_ssids; i++) { | ||||
| -		if (ssid->ssid_len == req->ssids[i].ssid_len) { | ||||
| -			if (!strncmp(ssid->ssid, req->ssids[i].ssid, | ||||
| -				     ssid->ssid_len)) | ||||
| -				return true; | ||||
| -		} | ||||
| -	} | ||||
| -	return false; | ||||
| -} | ||||
| - | ||||
|  static int | ||||
|  brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, | ||||
|  				struct net_device *ndev, | ||||
| @@ -3367,9 +3348,6 @@ brcmf_cfg80211_sched_scan_start(struct w | ||||
|  { | ||||
|  	struct brcmf_if *ifp = netdev_priv(ndev); | ||||
|  	struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); | ||||
| -	struct cfg80211_ssid *ssid; | ||||
| -	int i; | ||||
| -	int ret = 0; | ||||
|   | ||||
|  	brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", | ||||
|  		  req->n_match_sets, req->n_ssids); | ||||
| @@ -3389,48 +3367,7 @@ brcmf_cfg80211_sched_scan_start(struct w | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	/* clean up everything */ | ||||
| -	ret = brcmf_pno_clean(ifp); | ||||
| -	if  (ret < 0) { | ||||
| -		brcmf_err("failed error=%d\n", ret); | ||||
| -		return ret; | ||||
| -	} | ||||
| - | ||||
| -	/* configure pno */ | ||||
| -	ret = brcmf_pno_config(ifp, BRCMF_SCHED_SCAN_PERIOD, 0, 0); | ||||
| -	if (ret < 0) | ||||
| -		return ret; | ||||
| - | ||||
| -	/* configure random mac */ | ||||
| -	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { | ||||
| -		ret = brcmf_pno_set_random(ifp, req->mac_addr, | ||||
| -					   req->mac_addr_mask); | ||||
| -		if (ret < 0) | ||||
| -			return ret; | ||||
| -	} | ||||
| - | ||||
| -	/* configure each match set */ | ||||
| -	for (i = 0; i < req->n_match_sets; i++) { | ||||
| - | ||||
| -		ssid = &req->match_sets[i].ssid; | ||||
| - | ||||
| -		if (!ssid->ssid_len) { | ||||
| -			brcmf_err("skip broadcast ssid\n"); | ||||
| -			continue; | ||||
| -		} | ||||
| - | ||||
| -		ret = brcmf_pno_add_ssid(ifp, ssid, | ||||
| -					 brcmf_is_ssid_active(ssid, req)); | ||||
| -		if (ret < 0) | ||||
| -			brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", | ||||
| -				  ret == 0 ? "set" : "failed", ssid->ssid); | ||||
| -	} | ||||
| -	/* Enable the PNO */ | ||||
| -	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); | ||||
| -	if (ret < 0) | ||||
| -		brcmf_err("PNO enable failed!! ret=%d\n", ret); | ||||
| - | ||||
| -	return ret; | ||||
| +	return brcmf_pno_start_sched_scan(ifp, req); | ||||
|  } | ||||
|   | ||||
|  static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -32,25 +32,10 @@ | ||||
|  #define BRCMF_PNO_SCAN_INCOMPLETE	0 | ||||
|  #define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF | ||||
|  #define BRCMF_PNO_HIDDEN_BIT		2 | ||||
| +#define BRCMF_PNO_SCHED_SCAN_PERIOD	30 | ||||
|   | ||||
| -int brcmf_pno_clean(struct brcmf_if *ifp) | ||||
| -{ | ||||
| -	int ret; | ||||
| - | ||||
| -	/* Disable pfn */ | ||||
| -	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); | ||||
| -	if (ret == 0) { | ||||
| -		/* clear pfn */ | ||||
| -		ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); | ||||
| -	} | ||||
| -	if (ret < 0) | ||||
| -		brcmf_err("failed code %d\n", ret); | ||||
| - | ||||
| -	return ret; | ||||
| -} | ||||
| - | ||||
| -int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, | ||||
| -		     u32 mscan, u32 bestn) | ||||
| +static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, | ||||
| +			    u32 mscan, u32 bestn) | ||||
|  { | ||||
|  	struct brcmf_pno_param_le pfn_param; | ||||
|  	u16 flags; | ||||
| @@ -102,7 +87,8 @@ exit: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask) | ||||
| +static int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, | ||||
| +				u8 *mac_mask) | ||||
|  { | ||||
|  	struct brcmf_pno_macaddr_le pfn_mac; | ||||
|  	int err, i; | ||||
| @@ -128,8 +114,8 @@ int brcmf_pno_set_random(struct brcmf_if | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, | ||||
| -		       bool active) | ||||
| +static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, | ||||
| +			      bool active) | ||||
|  { | ||||
|  	struct brcmf_pno_net_param_le pfn; | ||||
|   | ||||
| @@ -144,3 +130,85 @@ int brcmf_pno_add_ssid(struct brcmf_if * | ||||
|  	return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn)); | ||||
|  } | ||||
|   | ||||
| +static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, | ||||
| +				 struct cfg80211_sched_scan_request *req) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	if (!ssid || !req->ssids || !req->n_ssids) | ||||
| +		return false; | ||||
| + | ||||
| +	for (i = 0; i < req->n_ssids; i++) { | ||||
| +		if (ssid->ssid_len == req->ssids[i].ssid_len) { | ||||
| +			if (!strncmp(ssid->ssid, req->ssids[i].ssid, | ||||
| +				     ssid->ssid_len)) | ||||
| +				return true; | ||||
| +		} | ||||
| +	} | ||||
| +	return false; | ||||
| +} | ||||
| + | ||||
| +int brcmf_pno_clean(struct brcmf_if *ifp) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	/* Disable pfn */ | ||||
| +	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); | ||||
| +	if (ret == 0) { | ||||
| +		/* clear pfn */ | ||||
| +		ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); | ||||
| +	} | ||||
| +	if (ret < 0) | ||||
| +		brcmf_err("failed code %d\n", ret); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, | ||||
| +			       struct cfg80211_sched_scan_request *req) | ||||
| +{ | ||||
| +	struct cfg80211_ssid *ssid; | ||||
| +	int i, ret; | ||||
| + | ||||
| +	/* clean up everything */ | ||||
| +	ret = brcmf_pno_clean(ifp); | ||||
| +	if  (ret < 0) { | ||||
| +		brcmf_err("failed error=%d\n", ret); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* configure pno */ | ||||
| +	ret = brcmf_pno_config(ifp, BRCMF_PNO_SCHED_SCAN_PERIOD, 0, 0); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* configure random mac */ | ||||
| +	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { | ||||
| +		ret = brcmf_pno_set_random(ifp, req->mac_addr, | ||||
| +					   req->mac_addr_mask); | ||||
| +		if (ret < 0) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* configure each match set */ | ||||
| +	for (i = 0; i < req->n_match_sets; i++) { | ||||
| +		ssid = &req->match_sets[i].ssid; | ||||
| +		if (!ssid->ssid_len) { | ||||
| +			brcmf_err("skip broadcast ssid\n"); | ||||
| +			continue; | ||||
| +		} | ||||
| + | ||||
| +		ret = brcmf_pno_add_ssid(ifp, ssid, | ||||
| +					 brcmf_is_ssid_active(ssid, req)); | ||||
| +		if (ret < 0) | ||||
| +			brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", | ||||
| +				  ret == 0 ? "set" : "failed", ssid->ssid); | ||||
| +	} | ||||
| +	/* Enable the PNO */ | ||||
| +	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); | ||||
| +	if (ret < 0) | ||||
| +		brcmf_err("PNO enable failed!! ret=%d\n", ret); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| @@ -27,33 +27,12 @@ | ||||
|  int brcmf_pno_clean(struct brcmf_if *ifp); | ||||
|   | ||||
|  /** | ||||
| - * brcmf_pno_config - configure pno parameters. | ||||
| + * brcmf_pno_start_sched_scan - initiate scheduled scan on device. | ||||
|   * | ||||
|   * @ifp: interface object used. | ||||
| - * @scan_freq: scan frequency period in seconds. | ||||
| - * @mscan: maximum number of scans stored in firmware. | ||||
| - * @bestn: maximum number of APs per scan stored in firmware. | ||||
| + * @req: configuration parameters for scheduled scan. | ||||
|   */ | ||||
| -int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, | ||||
| -		     u32 mscan, u32 bestn); | ||||
| - | ||||
| -/** | ||||
| - * brcmf_pno_set_random - setup randomisation mac address for pno. | ||||
| - * | ||||
| - * @ifp: interface object used. | ||||
| - * @mac_addr: MAC address used with randomisation. | ||||
| - * @mac_mask: MAC address mask used for randomisation. | ||||
| - */ | ||||
| -int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask); | ||||
| - | ||||
| -/** | ||||
| - * brcmf_pno_add_ssid - add ssid for pno in firmware. | ||||
| - * | ||||
| - * @ifp: interface object used. | ||||
| - * @ssid: ssid information. | ||||
| - * @active: indicate this ssid needs to be actively probed. | ||||
| - */ | ||||
| -int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, | ||||
| -		       bool active); | ||||
| +int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, | ||||
| +			       struct cfg80211_sched_scan_request *req); | ||||
|   | ||||
|  #endif /* _BRCMF_PNO_H */ | ||||
| @@ -1,111 +0,0 @@ | ||||
| From 331e789443618ca9cc3ed48ada4e670225cca036 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:28 +0000 | ||||
| Subject: [PATCH] brcmfmac: use provided channels for scheduled scan | ||||
|  | ||||
| User-space can provide list of channels in the schedule scan request. | ||||
| This was ignored so all channels supported and allowed by the device | ||||
| were used. This patch configures the device to use the channels as | ||||
| listed in the request. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/fwil_types.h       | 16 +++++++++++++ | ||||
|  .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 26 +++++++++++++++++++++- | ||||
|  2 files changed, 41 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | ||||
| @@ -131,6 +131,7 @@ | ||||
|  #define BRCMF_TXBF_MU_BFR_CAP		BIT(1) | ||||
|   | ||||
|  #define	BRCMF_MAXPMKID			16	/* max # PMKID cache entries */ | ||||
| +#define BRCMF_NUMCHANNELS		64 | ||||
|   | ||||
|  #define BRCMF_PFN_MACADDR_CFG_VER	1 | ||||
|  #define BRCMF_PFN_MAC_OUI_ONLY		BIT(0) | ||||
| @@ -719,6 +720,21 @@ struct brcmf_pno_param_le { | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| + * struct brcmf_pno_config_le - PNO channel configuration. | ||||
| + * | ||||
| + * @reporttype: determines what is reported. | ||||
| + * @channel_num: number of channels specified in @channel_list. | ||||
| + * @channel_list: channels to use in PNO scan. | ||||
| + * @flags: reserved. | ||||
| + */ | ||||
| +struct brcmf_pno_config_le { | ||||
| +	__le32  reporttype; | ||||
| +	__le32  channel_num; | ||||
| +	__le16  channel_list[BRCMF_NUMCHANNELS]; | ||||
| +	__le32  flags; | ||||
| +}; | ||||
| + | ||||
| +/** | ||||
|   * struct brcmf_pno_net_param_le - scan parameters per preferred network. | ||||
|   * | ||||
|   * @ssid: ssid name and its length. | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -18,9 +18,10 @@ | ||||
|   | ||||
|  #include "core.h" | ||||
|  #include "debug.h" | ||||
| -#include "pno.h" | ||||
|  #include "fwil.h" | ||||
|  #include "fwil_types.h" | ||||
| +#include "cfg80211.h" | ||||
| +#include "pno.h" | ||||
|   | ||||
|  #define BRCMF_PNO_VERSION		2 | ||||
|  #define BRCMF_PNO_REPEAT		4 | ||||
| @@ -34,6 +35,15 @@ | ||||
|  #define BRCMF_PNO_HIDDEN_BIT		2 | ||||
|  #define BRCMF_PNO_SCHED_SCAN_PERIOD	30 | ||||
|   | ||||
| +static int brcmf_pno_channel_config(struct brcmf_if *ifp, | ||||
| +				    struct brcmf_pno_config_le *cfg) | ||||
| +{ | ||||
| +	cfg->reporttype = 0; | ||||
| +	cfg->flags = 0; | ||||
| + | ||||
| +	return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg)); | ||||
| +} | ||||
| + | ||||
|  static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, | ||||
|  			    u32 mscan, u32 bestn) | ||||
|  { | ||||
| @@ -167,7 +177,10 @@ int brcmf_pno_clean(struct brcmf_if *ifp | ||||
|  int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, | ||||
|  			       struct cfg80211_sched_scan_request *req) | ||||
|  { | ||||
| +	struct brcmu_d11inf *d11inf; | ||||
| +	struct brcmf_pno_config_le pno_cfg; | ||||
|  	struct cfg80211_ssid *ssid; | ||||
| +	u16 chan; | ||||
|  	int i, ret; | ||||
|   | ||||
|  	/* clean up everything */ | ||||
| @@ -190,6 +203,17 @@ int brcmf_pno_start_sched_scan(struct br | ||||
|  			return ret; | ||||
|  	} | ||||
|   | ||||
| +	/* configure channels to use */ | ||||
| +	d11inf = &ifp->drvr->config->d11inf; | ||||
| +	for (i = 0; i < req->n_channels; i++) { | ||||
| +		chan = req->channels[i]->hw_value; | ||||
| +		pno_cfg.channel_list[i] = cpu_to_le16(chan); | ||||
| +	} | ||||
| +	if (req->n_channels) { | ||||
| +		pno_cfg.channel_num = cpu_to_le32(req->n_channels); | ||||
| +		brcmf_pno_channel_config(ifp, &pno_cfg); | ||||
| +	} | ||||
| + | ||||
|  	/* configure each match set */ | ||||
|  	for (i = 0; i < req->n_match_sets; i++) { | ||||
|  		ssid = &req->match_sets[i].ssid; | ||||
| @@ -1,34 +0,0 @@ | ||||
| From dfe5b0d52d5880bd9d4b427e1a53c9e9e4c3c820 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:29 +0000 | ||||
| Subject: [PATCH] brcmfmac: remove restriction from .sched_scan_start() | ||||
|  callback | ||||
|  | ||||
| In the .sched_scan_start() callback a condition was checked whether a | ||||
| normal scan was ongoing. However, there is no need for this check as | ||||
| it is ok to start the scheduled scan irrespective whether or not a | ||||
| normal scan is ongoing. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 5 +---- | ||||
|  1 file changed, 1 insertion(+), 4 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -3351,10 +3351,7 @@ brcmf_cfg80211_sched_scan_start(struct w | ||||
|   | ||||
|  	brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", | ||||
|  		  req->n_match_sets, req->n_ssids); | ||||
| -	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { | ||||
| -		brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); | ||||
| -		return -EAGAIN; | ||||
| -	} | ||||
| + | ||||
|  	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { | ||||
|  		brcmf_err("Scanning suppressed: status (%lu)\n", | ||||
|  			  cfg->scan_status); | ||||
| @@ -1,67 +0,0 @@ | ||||
| From c6989fd55ceb633d2f18c12ffae01b9123125c89 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:30 +0000 | ||||
| Subject: [PATCH] brcmfmac: use requested scan interval in scheduled scan | ||||
|  | ||||
| User-space can specify the interval for the scheduled scan. This | ||||
| interval is found in scheduled scan plan. The driver supports only | ||||
| one plan, which is legacy behaviour. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 1 + | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c      | 6 +++++- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h      | 6 ++++-- | ||||
|  3 files changed, 10 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -6312,6 +6312,7 @@ static void brcmf_wiphy_pno_params(struc | ||||
|  	wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; | ||||
|  	wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; | ||||
|  	wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; | ||||
| +	wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD; | ||||
|  	wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -63,6 +63,10 @@ static int brcmf_pno_config(struct brcmf | ||||
|  	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; | ||||
|   | ||||
|  	/* set up pno scan fr */ | ||||
| +	if (scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) { | ||||
| +		brcmf_dbg(SCAN, "scan period too small, using minimum\n"); | ||||
| +		scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD; | ||||
| +	} | ||||
|  	pfn_param.scan_freq = cpu_to_le32(scan_freq); | ||||
|   | ||||
|  	if (mscan) { | ||||
| @@ -191,7 +195,7 @@ int brcmf_pno_start_sched_scan(struct br | ||||
|  	} | ||||
|   | ||||
|  	/* configure pno */ | ||||
| -	ret = brcmf_pno_config(ifp, BRCMF_PNO_SCHED_SCAN_PERIOD, 0, 0); | ||||
| +	ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0); | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | ||||
| @@ -16,8 +16,10 @@ | ||||
|  #ifndef _BRCMF_PNO_H | ||||
|  #define _BRCMF_PNO_H | ||||
|   | ||||
| -#define BRCMF_PNO_SCAN_COMPLETE		1 | ||||
| -#define BRCMF_PNO_MAX_PFN_COUNT		16 | ||||
| +#define BRCMF_PNO_SCAN_COMPLETE			1 | ||||
| +#define BRCMF_PNO_MAX_PFN_COUNT			16 | ||||
| +#define BRCMF_PNO_SCHED_SCAN_MIN_PERIOD	10 | ||||
| +#define BRCMF_PNO_SCHED_SCAN_MAX_PERIOD	508 | ||||
|   | ||||
|  /** | ||||
|   * brcmf_pno_clean - disable and clear pno in firmware. | ||||
| @@ -1,76 +0,0 @@ | ||||
| From 53e3a80d80c80bf50ab64cf6c44fb0fa41aa22d8 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Wed, 23 Nov 2016 10:25:31 +0000 | ||||
| Subject: [PATCH] brcmfmac: fix scheduled scan result handling for newer chips | ||||
|  | ||||
| The scan results for scheduled scan as retrieved from the device | ||||
| have changed. A field has been added which is not needed. However, | ||||
| the appended info is. Luckily they are versioned so check that to | ||||
| find out the location of the appended data. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/cfg80211.c         | 24 +++++++++++++++++++++- | ||||
|  .../broadcom/brcm80211/brcmfmac/fwil_types.h       |  7 +++++++ | ||||
|  2 files changed, 30 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -3257,6 +3257,28 @@ static int brcmf_start_internal_escan(st | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static struct brcmf_pno_net_info_le * | ||||
| +brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1) | ||||
| +{ | ||||
| +	struct brcmf_pno_scanresults_v2_le *pfn_v2; | ||||
| +	struct brcmf_pno_net_info_le *netinfo; | ||||
| + | ||||
| +	switch (pfn_v1->version) { | ||||
| +	default: | ||||
| +		WARN_ON(1); | ||||
| +		/* fall-thru */ | ||||
| +	case cpu_to_le32(1): | ||||
| +		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1); | ||||
| +		break; | ||||
| +	case cpu_to_le32(2): | ||||
| +		pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1; | ||||
| +		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1); | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	return netinfo; | ||||
| +} | ||||
| + | ||||
|  /* PFN result doesn't have all the info which are required by the supplicant | ||||
|   * (For e.g IEs) Do a target Escan so that sched scan results are reported | ||||
|   * via wl_inform_single_bss in the required format. Escan does require the | ||||
| @@ -3309,7 +3331,7 @@ brcmf_notify_sched_scan_results(struct b | ||||
|  	} | ||||
|   | ||||
|  	data += sizeof(struct brcmf_pno_scanresults_le); | ||||
| -	netinfo_start = (struct brcmf_pno_net_info_le *)data; | ||||
| +	netinfo_start = brcmf_get_netinfo_array(pfn_result); | ||||
|   | ||||
|  	for (i = 0; i < result_count; i++) { | ||||
|  		netinfo = &netinfo_start[i]; | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | ||||
| @@ -785,6 +785,13 @@ struct brcmf_pno_scanresults_le { | ||||
|  	__le32 count; | ||||
|  }; | ||||
|   | ||||
| +struct brcmf_pno_scanresults_v2_le { | ||||
| +	__le32 version; | ||||
| +	__le32 status; | ||||
| +	__le32 count; | ||||
| +	__le32 scan_ch_bucket; | ||||
| +}; | ||||
| + | ||||
|  /** | ||||
|   * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization. | ||||
|   * | ||||
| @@ -1,47 +0,0 @@ | ||||
| From cb853da3a368c40300a0e940f86be582037bb082 Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Fri, 9 Dec 2016 11:34:13 +0000 | ||||
| Subject: [PATCH] brcmfmac: fix memory leak in brcmf_cfg80211_attach() | ||||
|  | ||||
| In brcmf_cfg80211_attach() there was one error path not properly | ||||
| handled as it leaked memory allocated in brcmf_btcoex_attach(). | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 7 +++++-- | ||||
|  1 file changed, 5 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -6866,7 +6866,7 @@ struct brcmf_cfg80211_info *brcmf_cfg802 | ||||
|   | ||||
|  	err = brcmf_p2p_attach(cfg, p2pdev_forced); | ||||
|  	if (err) { | ||||
| -		brcmf_err("P2P initilisation failed (%d)\n", err); | ||||
| +		brcmf_err("P2P initialisation failed (%d)\n", err); | ||||
|  		goto wiphy_unreg_out; | ||||
|  	} | ||||
|  	err = brcmf_btcoex_attach(cfg); | ||||
| @@ -6891,7 +6891,7 @@ struct brcmf_cfg80211_info *brcmf_cfg802 | ||||
|  	err = brcmf_fweh_activate_events(ifp); | ||||
|  	if (err) { | ||||
|  		brcmf_err("FWEH activation failed (%d)\n", err); | ||||
| -		goto wiphy_unreg_out; | ||||
| +		goto detach; | ||||
|  	} | ||||
|   | ||||
|  	/* Fill in some of the advertised nl80211 supported features */ | ||||
| @@ -6906,6 +6906,9 @@ struct brcmf_cfg80211_info *brcmf_cfg802 | ||||
|   | ||||
|  	return cfg; | ||||
|   | ||||
| +detach: | ||||
| +	brcmf_btcoex_detach(cfg); | ||||
| +	brcmf_p2p_detach(&cfg->p2p); | ||||
|  wiphy_unreg_out: | ||||
|  	wiphy_unregister(cfg->wiphy); | ||||
|  priv_out: | ||||
| @@ -1,29 +0,0 @@ | ||||
| From 2b66325d5ea7c2a39ac69ed83b6979afe480d81a Mon Sep 17 00:00:00 2001 | ||||
| From: Arend Van Spriel <arend.vanspriel@broadcom.com> | ||||
| Date: Fri, 9 Dec 2016 11:34:14 +0000 | ||||
| Subject: [PATCH] brcmfmac: fix uninitialized field in scheduled scan ssid | ||||
|  configuration | ||||
|  | ||||
| The scheduled scan ssid configuration in firmware has a flags field that | ||||
| was not initialized resulting in unexpected behaviour. | ||||
|  | ||||
| Fixes: e3bdb7cc0300 ("brcmfmac: fix handling ssids in .sched_scan_start() callback") | ||||
| Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com> | ||||
| Reviewed-by: Franky Lin <franky.lin@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | ||||
| @@ -137,6 +137,7 @@ static int brcmf_pno_add_ssid(struct brc | ||||
|  	pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); | ||||
|  	pfn.wsec = cpu_to_le32(0); | ||||
|  	pfn.infra = cpu_to_le32(1); | ||||
| +	pfn.flags = 0; | ||||
|  	if (active) | ||||
|  		pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); | ||||
|  	pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len); | ||||
| @@ -1,35 +0,0 @@ | ||||
| From ad334bbb07b07e2873942571b0c9f3c34571bd47 Mon Sep 17 00:00:00 2001 | ||||
| From: Colin Ian King <colin.king@canonical.com> | ||||
| Date: Fri, 23 Dec 2016 00:43:22 +0000 | ||||
| Subject: [PATCH] brcmfmac: fix spelling mistakes on "Ivalid" | ||||
|  | ||||
| Trivial fixes to spelling mistake "Ivalid" to "Invalid" in | ||||
| brcmf_err error messages. | ||||
|  | ||||
| Signed-off-by: Colin Ian King <colin.king@canonical.com> | ||||
| Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -3969,7 +3969,7 @@ brcmf_configure_wpaie(struct brcmf_if *i | ||||
|  			pval |= AES_ENABLED; | ||||
|  			break; | ||||
|  		default: | ||||
| -			brcmf_err("Ivalid unicast security info\n"); | ||||
| +			brcmf_err("Invalid unicast security info\n"); | ||||
|  		} | ||||
|  		offset++; | ||||
|  	} | ||||
| @@ -4013,7 +4013,7 @@ brcmf_configure_wpaie(struct brcmf_if *i | ||||
|  			wpa_auth |= WPA2_AUTH_1X_SHA256; | ||||
|  			break; | ||||
|  		default: | ||||
| -			brcmf_err("Ivalid key mgmt info\n"); | ||||
| +			brcmf_err("Invalid key mgmt info\n"); | ||||
|  		} | ||||
|  		offset++; | ||||
|  	} | ||||
| @@ -1,101 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Wed, 4 Jan 2017 12:09:41 +0100 | ||||
| Subject: [PATCH] brcmfmac: avoid writing channel out of allocated array | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| Our code was assigning number of channels to the index variable by | ||||
| default. If firmware reported channel we didn't predict this would | ||||
| result in using that initial index value and writing out of array. This | ||||
| never happened so far (we got a complete list of supported channels) but | ||||
| it means possible memory corruption so we should handle it anyway. | ||||
|  | ||||
| This patch simply detects unexpected channel and ignores it. | ||||
|  | ||||
| As we don't try to create new entry now, it's also safe to drop hw_value | ||||
| and center_freq assignment. For known channels we have these set anyway. | ||||
|  | ||||
| I decided to fix this issue by assigning NULL or a target channel to the | ||||
| channel variable. This was one of possible ways, I prefefred this one as | ||||
| it also avoids using channel[index] over and over. | ||||
|  | ||||
| Fixes: 58de92d2f95e ("brcmfmac: use static superset of channels for wiphy bands") | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -5823,7 +5823,6 @@ static int brcmf_construct_chaninfo(stru | ||||
|  	u32 i, j; | ||||
|  	u32 total; | ||||
|  	u32 chaninfo; | ||||
| -	u32 index; | ||||
|   | ||||
|  	pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); | ||||
|   | ||||
| @@ -5871,33 +5870,36 @@ static int brcmf_construct_chaninfo(stru | ||||
|  		    ch.bw == BRCMU_CHAN_BW_80) | ||||
|  			continue; | ||||
|   | ||||
| -		channel = band->channels; | ||||
| -		index = band->n_channels; | ||||
| +		channel = NULL; | ||||
|  		for (j = 0; j < band->n_channels; j++) { | ||||
| -			if (channel[j].hw_value == ch.control_ch_num) { | ||||
| -				index = j; | ||||
| +			if (band->channels[j].hw_value == ch.control_ch_num) { | ||||
| +				channel = &band->channels[j]; | ||||
|  				break; | ||||
|  			} | ||||
|  		} | ||||
| -		channel[index].center_freq = | ||||
| -			ieee80211_channel_to_frequency(ch.control_ch_num, | ||||
| -						       band->band); | ||||
| -		channel[index].hw_value = ch.control_ch_num; | ||||
| +		if (!channel) { | ||||
| +			/* It seems firmware supports some channel we never | ||||
| +			 * considered. Something new in IEEE standard? | ||||
| +			 */ | ||||
| +			brcmf_err("Ignoring unexpected firmware channel %d\n", | ||||
| +				  ch.control_ch_num); | ||||
| +			continue; | ||||
| +		} | ||||
|   | ||||
|  		/* assuming the chanspecs order is HT20, | ||||
|  		 * HT40 upper, HT40 lower, and VHT80. | ||||
|  		 */ | ||||
|  		if (ch.bw == BRCMU_CHAN_BW_80) { | ||||
| -			channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; | ||||
| +			channel->flags &= ~IEEE80211_CHAN_NO_80MHZ; | ||||
|  		} else if (ch.bw == BRCMU_CHAN_BW_40) { | ||||
| -			brcmf_update_bw40_channel_flag(&channel[index], &ch); | ||||
| +			brcmf_update_bw40_channel_flag(channel, &ch); | ||||
|  		} else { | ||||
|  			/* enable the channel and disable other bandwidths | ||||
|  			 * for now as mentioned order assure they are enabled | ||||
|  			 * for subsequent chanspecs. | ||||
|  			 */ | ||||
| -			channel[index].flags = IEEE80211_CHAN_NO_HT40 | | ||||
| -					       IEEE80211_CHAN_NO_80MHZ; | ||||
| +			channel->flags = IEEE80211_CHAN_NO_HT40 | | ||||
| +					 IEEE80211_CHAN_NO_80MHZ; | ||||
|  			ch.bw = BRCMU_CHAN_BW_20; | ||||
|  			cfg->d11inf.encchspec(&ch); | ||||
|  			chaninfo = ch.chspec; | ||||
| @@ -5905,11 +5907,11 @@ static int brcmf_construct_chaninfo(stru | ||||
|  						       &chaninfo); | ||||
|  			if (!err) { | ||||
|  				if (chaninfo & WL_CHAN_RADAR) | ||||
| -					channel[index].flags |= | ||||
| +					channel->flags |= | ||||
|  						(IEEE80211_CHAN_RADAR | | ||||
|  						 IEEE80211_CHAN_NO_IR); | ||||
|  				if (chaninfo & WL_CHAN_PASSIVE) | ||||
| -					channel[index].flags |= | ||||
| +					channel->flags |= | ||||
|  						IEEE80211_CHAN_NO_IR; | ||||
|  			} | ||||
|  		} | ||||
| @@ -1,35 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Sat, 7 Jan 2017 21:36:04 +0100 | ||||
| Subject: [PATCH] brcmfmac: don't preset all channels as disabled | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| During init we take care of regulatory stuff by disabling all | ||||
| unavailable channels (see brcmf_construct_chaninfo) so this predisabling | ||||
| them is not really required (and this patch won't change any behavior). | ||||
| It will on the other hand allow more detailed runtime control over | ||||
| channels which is the main reason for this change. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -138,7 +138,6 @@ static struct ieee80211_rate __wl_rates[ | ||||
|  	.band			= NL80211_BAND_2GHZ,		\ | ||||
|  	.center_freq		= (_freq),			\ | ||||
|  	.hw_value		= (_channel),			\ | ||||
| -	.flags			= IEEE80211_CHAN_DISABLED,	\ | ||||
|  	.max_antenna_gain	= 0,				\ | ||||
|  	.max_power		= 30,				\ | ||||
|  } | ||||
| @@ -147,7 +146,6 @@ static struct ieee80211_rate __wl_rates[ | ||||
|  	.band			= NL80211_BAND_5GHZ,		\ | ||||
|  	.center_freq		= 5000 + (5 * (_channel)),	\ | ||||
|  	.hw_value		= (_channel),			\ | ||||
| -	.flags			= IEEE80211_CHAN_DISABLED,	\ | ||||
|  	.max_antenna_gain	= 0,				\ | ||||
|  	.max_power		= 30,				\ | ||||
|  } | ||||
| @@ -1,50 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Sat, 7 Jan 2017 21:36:05 +0100 | ||||
| Subject: [PATCH] brcmfmac: setup wiphy bands after registering it first | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| During bands setup we disable all channels that firmware doesn't support | ||||
| in the current regulatory setup. If we do this before wiphy_register | ||||
| it will result in copying set flags (including IEEE80211_CHAN_DISABLED) | ||||
| to the orig_flags which is supposed to be persistent. We don't want this | ||||
| as regulatory change may result in enabling some channels. We shouldn't | ||||
| mess with orig_flags then (by changing them or ignoring them) so it's | ||||
| better to just take care of their proper values. | ||||
|  | ||||
| This patch cleanups code a bit (by taking orig_flags more seriously) and | ||||
| allows further improvements like disabling really unavailable channels. | ||||
| We will need that e.g. if some frequencies should be disabled for good | ||||
| due to hardware setup (design). | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -6475,8 +6475,7 @@ static int brcmf_setup_wiphy(struct wiph | ||||
|  			wiphy->bands[NL80211_BAND_5GHZ] = band; | ||||
|  		} | ||||
|  	} | ||||
| -	err = brcmf_setup_wiphybands(wiphy); | ||||
| -	return err; | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
|  static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) | ||||
| @@ -6841,6 +6840,12 @@ struct brcmf_cfg80211_info *brcmf_cfg802 | ||||
|  		goto priv_out; | ||||
|  	} | ||||
|   | ||||
| +	err = brcmf_setup_wiphybands(wiphy); | ||||
| +	if (err) { | ||||
| +		brcmf_err("Setting wiphy bands failed (%d)\n", err); | ||||
| +		goto wiphy_unreg_out; | ||||
| +	} | ||||
| + | ||||
|  	/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), | ||||
|  	 * setup 40MHz in 2GHz band and enable OBSS scanning. | ||||
|  	 */ | ||||
| @@ -1,78 +0,0 @@ | ||||
| From e457a8a01a19277e96830d3d95887e0e3c1e2f26 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Sat, 7 Jan 2017 23:43:45 +0100 | ||||
| Subject: [PATCH] brcmfmac: make brcmf_of_probe more generic | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| We may want to use Open Firmware for other devices than just SDIO ones. | ||||
| In future we may want to support more Broadcom properties so there is | ||||
| really no reason for such limitation. | ||||
|  | ||||
| Call brcmf_of_probe for all kind of devices & move extra conditions to | ||||
| the body of that funcion. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 8 +++----- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c     | 7 +++++-- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h     | 6 ++++-- | ||||
|  3 files changed, 12 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | ||||
| @@ -299,11 +299,9 @@ struct brcmf_mp_device *brcmf_get_module | ||||
|  			} | ||||
|  		} | ||||
|  	} | ||||
| -	if ((bus_type == BRCMF_BUSTYPE_SDIO) && (!found)) { | ||||
| -		/* No platform data for this device. In case of SDIO try OF | ||||
| -		 * (Open Firwmare) Device Tree. | ||||
| -		 */ | ||||
| -		brcmf_of_probe(dev, &settings->bus.sdio); | ||||
| +	if (!found) { | ||||
| +		/* No platform data for this device, try OF (Open Firwmare) */ | ||||
| +		brcmf_of_probe(dev, bus_type, settings); | ||||
|  	} | ||||
|  	return settings; | ||||
|  } | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c | ||||
| @@ -23,14 +23,17 @@ | ||||
|  #include "common.h" | ||||
|  #include "of.h" | ||||
|   | ||||
| -void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio) | ||||
| +void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, | ||||
| +		    struct brcmf_mp_device *settings) | ||||
|  { | ||||
| +	struct brcmfmac_sdio_pd *sdio = &settings->bus.sdio; | ||||
|  	struct device_node *np = dev->of_node; | ||||
|  	int irq; | ||||
|  	u32 irqf; | ||||
|  	u32 val; | ||||
|   | ||||
| -	if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) | ||||
| +	if (!np || bus_type != BRCMF_BUSTYPE_SDIO || | ||||
| +	    !of_device_is_compatible(np, "brcm,bcm4329-fmac")) | ||||
|  		return; | ||||
|   | ||||
|  	if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h | ||||
| @@ -14,9 +14,11 @@ | ||||
|   * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|   */ | ||||
|  #ifdef CONFIG_OF | ||||
| -void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio); | ||||
| +void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, | ||||
| +		    struct brcmf_mp_device *settings); | ||||
|  #else | ||||
| -static void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio) | ||||
| +static void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, | ||||
| +			   struct brcmf_mp_device *settings) | ||||
|  { | ||||
|  } | ||||
|  #endif /* CONFIG_OF */ | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau