| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2002-2005, Instant802 Networks, Inc. | 
 | 3 |  * Copyright 2005-2006, Devicescape Software, Inc. | 
 | 4 |  * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify | 
 | 7 |  * it under the terms of the GNU General Public License version 2 as | 
 | 8 |  * published by the Free Software Foundation. | 
 | 9 |  */ | 
 | 10 |  | 
 | 11 | #include <linux/kernel.h> | 
| Johannes Berg | ff68808 | 2007-07-27 15:43:23 +0200 | [diff] [blame] | 12 | #include <linux/rtnetlink.h> | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 13 | #include "ieee80211_rate.h" | 
 | 14 | #include "ieee80211_i.h" | 
 | 15 |  | 
 | 16 | struct rate_control_alg { | 
 | 17 | 	struct list_head list; | 
 | 18 | 	struct rate_control_ops *ops; | 
 | 19 | }; | 
 | 20 |  | 
 | 21 | static LIST_HEAD(rate_ctrl_algs); | 
 | 22 | static DEFINE_MUTEX(rate_ctrl_mutex); | 
 | 23 |  | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 24 | static char *ieee80211_default_rc_algo = CONFIG_MAC80211_RC_DEFAULT; | 
 | 25 | module_param(ieee80211_default_rc_algo, charp, 0644); | 
 | 26 | MODULE_PARM_DESC(ieee80211_default_rc_algo, | 
 | 27 | 		 "Default rate control algorithm for mac80211 to use"); | 
 | 28 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 29 | int ieee80211_rate_control_register(struct rate_control_ops *ops) | 
 | 30 | { | 
 | 31 | 	struct rate_control_alg *alg; | 
 | 32 |  | 
| Johannes Berg | ac71c69 | 2007-10-28 14:17:44 +0100 | [diff] [blame] | 33 | 	if (!ops->name) | 
 | 34 | 		return -EINVAL; | 
 | 35 |  | 
| Johannes Berg | 999acd9 | 2007-10-28 14:49:33 +0100 | [diff] [blame] | 36 | 	mutex_lock(&rate_ctrl_mutex); | 
 | 37 | 	list_for_each_entry(alg, &rate_ctrl_algs, list) { | 
 | 38 | 		if (!strcmp(alg->ops->name, ops->name)) { | 
 | 39 | 			/* don't register an algorithm twice */ | 
 | 40 | 			WARN_ON(1); | 
| Cyrill Gorcunov | b808ab1 | 2007-12-13 15:52:11 -0800 | [diff] [blame] | 41 | 			mutex_unlock(&rate_ctrl_mutex); | 
| Johannes Berg | 999acd9 | 2007-10-28 14:49:33 +0100 | [diff] [blame] | 42 | 			return -EALREADY; | 
 | 43 | 		} | 
 | 44 | 	} | 
 | 45 |  | 
| Yoann Padioleau | dd00cc4 | 2007-07-19 01:49:03 -0700 | [diff] [blame] | 46 | 	alg = kzalloc(sizeof(*alg), GFP_KERNEL); | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 47 | 	if (alg == NULL) { | 
| Johannes Berg | 999acd9 | 2007-10-28 14:49:33 +0100 | [diff] [blame] | 48 | 		mutex_unlock(&rate_ctrl_mutex); | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 49 | 		return -ENOMEM; | 
 | 50 | 	} | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 51 | 	alg->ops = ops; | 
 | 52 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 53 | 	list_add_tail(&alg->list, &rate_ctrl_algs); | 
 | 54 | 	mutex_unlock(&rate_ctrl_mutex); | 
 | 55 |  | 
 | 56 | 	return 0; | 
 | 57 | } | 
 | 58 | EXPORT_SYMBOL(ieee80211_rate_control_register); | 
 | 59 |  | 
 | 60 | void ieee80211_rate_control_unregister(struct rate_control_ops *ops) | 
 | 61 | { | 
 | 62 | 	struct rate_control_alg *alg; | 
 | 63 |  | 
 | 64 | 	mutex_lock(&rate_ctrl_mutex); | 
 | 65 | 	list_for_each_entry(alg, &rate_ctrl_algs, list) { | 
 | 66 | 		if (alg->ops == ops) { | 
 | 67 | 			list_del(&alg->list); | 
| Cyrill Gorcunov | 20880e8 | 2007-12-13 16:17:03 -0800 | [diff] [blame] | 68 | 			kfree(alg); | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 69 | 			break; | 
 | 70 | 		} | 
 | 71 | 	} | 
 | 72 | 	mutex_unlock(&rate_ctrl_mutex); | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 73 | } | 
 | 74 | EXPORT_SYMBOL(ieee80211_rate_control_unregister); | 
 | 75 |  | 
 | 76 | static struct rate_control_ops * | 
 | 77 | ieee80211_try_rate_control_ops_get(const char *name) | 
 | 78 | { | 
 | 79 | 	struct rate_control_alg *alg; | 
 | 80 | 	struct rate_control_ops *ops = NULL; | 
 | 81 |  | 
| Johannes Berg | ac71c69 | 2007-10-28 14:17:44 +0100 | [diff] [blame] | 82 | 	if (!name) | 
 | 83 | 		return NULL; | 
 | 84 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 85 | 	mutex_lock(&rate_ctrl_mutex); | 
 | 86 | 	list_for_each_entry(alg, &rate_ctrl_algs, list) { | 
| Johannes Berg | ac71c69 | 2007-10-28 14:17:44 +0100 | [diff] [blame] | 87 | 		if (!strcmp(alg->ops->name, name)) | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 88 | 			if (try_module_get(alg->ops->module)) { | 
 | 89 | 				ops = alg->ops; | 
 | 90 | 				break; | 
 | 91 | 			} | 
 | 92 | 	} | 
 | 93 | 	mutex_unlock(&rate_ctrl_mutex); | 
 | 94 | 	return ops; | 
 | 95 | } | 
 | 96 |  | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 97 | /* Get the rate control algorithm. */ | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 98 | static struct rate_control_ops * | 
 | 99 | ieee80211_rate_control_ops_get(const char *name) | 
 | 100 | { | 
 | 101 | 	struct rate_control_ops *ops; | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 102 | 	const char *alg_name; | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 103 |  | 
| Johannes Berg | ac71c69 | 2007-10-28 14:17:44 +0100 | [diff] [blame] | 104 | 	if (!name) | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 105 | 		alg_name = ieee80211_default_rc_algo; | 
 | 106 | 	else | 
 | 107 | 		alg_name = name; | 
| Johannes Berg | ac71c69 | 2007-10-28 14:17:44 +0100 | [diff] [blame] | 108 |  | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 109 | 	ops = ieee80211_try_rate_control_ops_get(alg_name); | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 110 | 	if (!ops) { | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 111 | 		request_module("rc80211_%s", alg_name); | 
 | 112 | 		ops = ieee80211_try_rate_control_ops_get(alg_name); | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 113 | 	} | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 114 | 	if (!ops && name) | 
 | 115 | 		/* try default if specific alg requested but not found */ | 
 | 116 | 		ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); | 
 | 117 |  | 
| Johannes Berg | 4b47589 | 2008-01-02 15:17:03 +0100 | [diff] [blame] | 118 | 	/* try built-in one if specific alg requested but not found */ | 
 | 119 | 	if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT)) | 
 | 120 | 		ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT); | 
 | 121 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 122 | 	return ops; | 
 | 123 | } | 
 | 124 |  | 
 | 125 | static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops) | 
 | 126 | { | 
 | 127 | 	module_put(ops->module); | 
 | 128 | } | 
 | 129 |  | 
 | 130 | struct rate_control_ref *rate_control_alloc(const char *name, | 
 | 131 | 					    struct ieee80211_local *local) | 
 | 132 | { | 
 | 133 | 	struct rate_control_ref *ref; | 
 | 134 |  | 
 | 135 | 	ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); | 
 | 136 | 	if (!ref) | 
 | 137 | 		goto fail_ref; | 
 | 138 | 	kref_init(&ref->kref); | 
 | 139 | 	ref->ops = ieee80211_rate_control_ops_get(name); | 
 | 140 | 	if (!ref->ops) | 
 | 141 | 		goto fail_ops; | 
 | 142 | 	ref->priv = ref->ops->alloc(local); | 
 | 143 | 	if (!ref->priv) | 
 | 144 | 		goto fail_priv; | 
 | 145 | 	return ref; | 
 | 146 |  | 
 | 147 | fail_priv: | 
 | 148 | 	ieee80211_rate_control_ops_put(ref->ops); | 
 | 149 | fail_ops: | 
 | 150 | 	kfree(ref); | 
 | 151 | fail_ref: | 
 | 152 | 	return NULL; | 
 | 153 | } | 
 | 154 |  | 
 | 155 | static void rate_control_release(struct kref *kref) | 
 | 156 | { | 
 | 157 | 	struct rate_control_ref *ctrl_ref; | 
 | 158 |  | 
 | 159 | 	ctrl_ref = container_of(kref, struct rate_control_ref, kref); | 
 | 160 | 	ctrl_ref->ops->free(ctrl_ref->priv); | 
 | 161 | 	ieee80211_rate_control_ops_put(ctrl_ref->ops); | 
 | 162 | 	kfree(ctrl_ref); | 
 | 163 | } | 
 | 164 |  | 
| Mattias Nissler | 1abbe49 | 2007-12-20 13:50:07 +0100 | [diff] [blame] | 165 | void rate_control_get_rate(struct net_device *dev, | 
 | 166 | 			   struct ieee80211_hw_mode *mode, struct sk_buff *skb, | 
 | 167 | 			   struct rate_selection *sel) | 
 | 168 | { | 
 | 169 | 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 
 | 170 | 	struct rate_control_ref *ref = local->rate_ctrl; | 
| Mattias Nissler | 1abbe49 | 2007-12-20 13:50:07 +0100 | [diff] [blame] | 171 | 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 
 | 172 | 	struct sta_info *sta = sta_info_get(local, hdr->addr1); | 
 | 173 | 	int i; | 
| Mattias Nissler | 1abbe49 | 2007-12-20 13:50:07 +0100 | [diff] [blame] | 174 |  | 
 | 175 | 	memset(sel, 0, sizeof(struct rate_selection)); | 
 | 176 |  | 
| Michael Wu | 2bc454b | 2007-12-25 19:33:16 -0500 | [diff] [blame] | 177 | 	ref->ops->get_rate(ref->priv, dev, mode, skb, sel); | 
| Mattias Nissler | 1abbe49 | 2007-12-20 13:50:07 +0100 | [diff] [blame] | 178 |  | 
 | 179 | 	/* Select a non-ERP backup rate. */ | 
 | 180 | 	if (!sel->nonerp) { | 
 | 181 | 		for (i = 0; i < mode->num_rates - 1; i++) { | 
 | 182 | 			struct ieee80211_rate *rate = &mode->rates[i]; | 
 | 183 | 			if (sel->rate->rate < rate->rate) | 
 | 184 | 				break; | 
 | 185 |  | 
 | 186 | 			if (rate_supported(sta, mode, i) && | 
 | 187 | 			    !(rate->flags & IEEE80211_RATE_ERP)) | 
 | 188 | 				sel->nonerp = rate; | 
 | 189 | 		} | 
 | 190 | 	} | 
 | 191 |  | 
 | 192 | 	if (sta) | 
 | 193 | 		sta_info_put(sta); | 
 | 194 | } | 
 | 195 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 196 | struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) | 
 | 197 | { | 
 | 198 | 	kref_get(&ref->kref); | 
 | 199 | 	return ref; | 
 | 200 | } | 
 | 201 |  | 
 | 202 | void rate_control_put(struct rate_control_ref *ref) | 
 | 203 | { | 
 | 204 | 	kref_put(&ref->kref, rate_control_release); | 
 | 205 | } | 
| Johannes Berg | ff68808 | 2007-07-27 15:43:23 +0200 | [diff] [blame] | 206 |  | 
 | 207 | int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, | 
 | 208 | 				 const char *name) | 
 | 209 | { | 
 | 210 | 	struct rate_control_ref *ref, *old; | 
 | 211 |  | 
 | 212 | 	ASSERT_RTNL(); | 
| Johannes Berg | f9d540e | 2007-09-28 14:02:09 +0200 | [diff] [blame] | 213 | 	if (local->open_count || netif_running(local->mdev)) | 
| Johannes Berg | ff68808 | 2007-07-27 15:43:23 +0200 | [diff] [blame] | 214 | 		return -EBUSY; | 
 | 215 |  | 
 | 216 | 	ref = rate_control_alloc(name, local); | 
 | 217 | 	if (!ref) { | 
 | 218 | 		printk(KERN_WARNING "%s: Failed to select rate control " | 
| Johannes Berg | dd1cd4c | 2007-09-18 17:29:20 -0400 | [diff] [blame] | 219 | 		       "algorithm\n", wiphy_name(local->hw.wiphy)); | 
| Johannes Berg | ff68808 | 2007-07-27 15:43:23 +0200 | [diff] [blame] | 220 | 		return -ENOENT; | 
 | 221 | 	} | 
 | 222 |  | 
 | 223 | 	old = local->rate_ctrl; | 
 | 224 | 	local->rate_ctrl = ref; | 
 | 225 | 	if (old) { | 
 | 226 | 		rate_control_put(old); | 
 | 227 | 		sta_info_flush(local, NULL); | 
 | 228 | 	} | 
 | 229 |  | 
 | 230 | 	printk(KERN_DEBUG "%s: Selected rate control " | 
| Johannes Berg | dd1cd4c | 2007-09-18 17:29:20 -0400 | [diff] [blame] | 231 | 	       "algorithm '%s'\n", wiphy_name(local->hw.wiphy), | 
| Johannes Berg | ff68808 | 2007-07-27 15:43:23 +0200 | [diff] [blame] | 232 | 	       ref->ops->name); | 
 | 233 |  | 
 | 234 |  | 
 | 235 | 	return 0; | 
 | 236 | } | 
 | 237 |  | 
 | 238 | void rate_control_deinitialize(struct ieee80211_local *local) | 
 | 239 | { | 
 | 240 | 	struct rate_control_ref *ref; | 
 | 241 |  | 
 | 242 | 	ref = local->rate_ctrl; | 
 | 243 | 	local->rate_ctrl = NULL; | 
 | 244 | 	rate_control_put(ref); | 
 | 245 | } | 
| Stefano Brivio | c21b39a | 2007-12-19 01:26:16 +0100 | [diff] [blame] | 246 |  |