| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2002-2005, Instant802 Networks, Inc. | 
 | 3 |  * Copyright 2005, Devicescape Software, Inc. | 
 | 4 |  * | 
 | 5 |  * This program is free software; you can redistribute it and/or modify | 
 | 6 |  * it under the terms of the GNU General Public License version 2 as | 
 | 7 |  * published by the Free Software Foundation. | 
 | 8 |  */ | 
 | 9 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 10 | #include <linux/init.h> | 
 | 11 | #include <linux/netdevice.h> | 
 | 12 | #include <linux/types.h> | 
 | 13 | #include <linux/slab.h> | 
 | 14 | #include <linux/skbuff.h> | 
 | 15 | #include <linux/compiler.h> | 
 | 16 |  | 
 | 17 | #include <net/mac80211.h> | 
 | 18 | #include "ieee80211_i.h" | 
 | 19 | #include "ieee80211_rate.h" | 
| Jiri Benc | e9f207f | 2007-05-05 11:46:38 -0700 | [diff] [blame] | 20 | #include "debugfs.h" | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 21 |  | 
 | 22 |  | 
 | 23 | /* This is a minimal implementation of TX rate controlling that can be used | 
 | 24 |  * as the default when no improved mechanisms are available. */ | 
 | 25 |  | 
 | 26 |  | 
 | 27 | #define RATE_CONTROL_EMERG_DEC 2 | 
 | 28 | #define RATE_CONTROL_INTERVAL (HZ / 20) | 
 | 29 | #define RATE_CONTROL_MIN_TX 10 | 
 | 30 |  | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 31 | static void rate_control_rate_inc(struct ieee80211_local *local, | 
 | 32 | 				  struct sta_info *sta) | 
 | 33 | { | 
 | 34 | 	struct ieee80211_sub_if_data *sdata; | 
 | 35 | 	struct ieee80211_hw_mode *mode; | 
 | 36 | 	int i = sta->txrate; | 
 | 37 | 	int maxrate; | 
 | 38 |  | 
 | 39 | 	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); | 
 | 40 | 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { | 
 | 41 | 		/* forced unicast rate - do not change STA rate */ | 
 | 42 | 		return; | 
 | 43 | 	} | 
 | 44 |  | 
 | 45 | 	mode = local->oper_hw_mode; | 
 | 46 | 	maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1; | 
 | 47 |  | 
 | 48 | 	if (i > mode->num_rates) | 
 | 49 | 		i = mode->num_rates - 2; | 
 | 50 |  | 
 | 51 | 	while (i + 1 < mode->num_rates) { | 
 | 52 | 		i++; | 
 | 53 | 		if (sta->supp_rates & BIT(i) && | 
 | 54 | 		    mode->rates[i].flags & IEEE80211_RATE_SUPPORTED && | 
 | 55 | 		    (maxrate < 0 || i <= maxrate)) { | 
 | 56 | 			sta->txrate = i; | 
 | 57 | 			break; | 
 | 58 | 		} | 
 | 59 | 	} | 
 | 60 | } | 
 | 61 |  | 
 | 62 |  | 
 | 63 | static void rate_control_rate_dec(struct ieee80211_local *local, | 
 | 64 | 				  struct sta_info *sta) | 
 | 65 | { | 
 | 66 | 	struct ieee80211_sub_if_data *sdata; | 
 | 67 | 	struct ieee80211_hw_mode *mode; | 
 | 68 | 	int i = sta->txrate; | 
 | 69 |  | 
 | 70 | 	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); | 
 | 71 | 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { | 
 | 72 | 		/* forced unicast rate - do not change STA rate */ | 
 | 73 | 		return; | 
 | 74 | 	} | 
 | 75 |  | 
 | 76 | 	mode = local->oper_hw_mode; | 
 | 77 | 	if (i > mode->num_rates) | 
 | 78 | 		i = mode->num_rates; | 
 | 79 |  | 
 | 80 | 	while (i > 0) { | 
 | 81 | 		i--; | 
 | 82 | 		if (sta->supp_rates & BIT(i) && | 
 | 83 | 		    mode->rates[i].flags & IEEE80211_RATE_SUPPORTED) { | 
 | 84 | 			sta->txrate = i; | 
 | 85 | 			break; | 
 | 86 | 		} | 
 | 87 | 	} | 
 | 88 | } | 
 | 89 |  | 
 | 90 |  | 
 | 91 | static struct ieee80211_rate * | 
 | 92 | rate_control_lowest_rate(struct ieee80211_local *local, | 
 | 93 | 			 struct ieee80211_hw_mode *mode) | 
 | 94 | { | 
 | 95 | 	int i; | 
 | 96 |  | 
 | 97 | 	for (i = 0; i < mode->num_rates; i++) { | 
 | 98 | 		struct ieee80211_rate *rate = &mode->rates[i]; | 
 | 99 |  | 
 | 100 | 		if (rate->flags & IEEE80211_RATE_SUPPORTED) | 
 | 101 | 			return rate; | 
 | 102 | 	} | 
 | 103 |  | 
 | 104 | 	printk(KERN_DEBUG "rate_control_lowest_rate - no supported rates " | 
 | 105 | 	       "found\n"); | 
 | 106 | 	return &mode->rates[0]; | 
 | 107 | } | 
 | 108 |  | 
 | 109 |  | 
 | 110 | struct global_rate_control { | 
 | 111 | 	int dummy; | 
 | 112 | }; | 
 | 113 |  | 
 | 114 | struct sta_rate_control { | 
 | 115 | 	unsigned long last_rate_change; | 
 | 116 | 	u32 tx_num_failures; | 
 | 117 | 	u32 tx_num_xmit; | 
 | 118 |  | 
 | 119 | 	unsigned long avg_rate_update; | 
 | 120 | 	u32 tx_avg_rate_sum; | 
 | 121 | 	u32 tx_avg_rate_num; | 
| Jiri Benc | e9f207f | 2007-05-05 11:46:38 -0700 | [diff] [blame] | 122 |  | 
 | 123 | #ifdef CONFIG_MAC80211_DEBUGFS | 
 | 124 | 	struct dentry *tx_avg_rate_sum_dentry; | 
 | 125 | 	struct dentry *tx_avg_rate_num_dentry; | 
 | 126 | #endif | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 127 | }; | 
 | 128 |  | 
 | 129 |  | 
 | 130 | static void rate_control_simple_tx_status(void *priv, struct net_device *dev, | 
 | 131 | 					  struct sk_buff *skb, | 
 | 132 | 					  struct ieee80211_tx_status *status) | 
 | 133 | { | 
 | 134 | 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 
 | 135 | 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 
 | 136 | 	struct sta_info *sta; | 
 | 137 | 	struct sta_rate_control *srctrl; | 
 | 138 |  | 
 | 139 | 	sta = sta_info_get(local, hdr->addr1); | 
 | 140 |  | 
 | 141 | 	if (!sta) | 
 | 142 | 	    return; | 
 | 143 |  | 
 | 144 | 	srctrl = sta->rate_ctrl_priv; | 
 | 145 | 	srctrl->tx_num_xmit++; | 
 | 146 | 	if (status->excessive_retries) { | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 147 | 		srctrl->tx_num_failures++; | 
 | 148 | 		sta->tx_retry_failed++; | 
 | 149 | 		sta->tx_num_consecutive_failures++; | 
 | 150 | 		sta->tx_num_mpdu_fail++; | 
 | 151 | 	} else { | 
 | 152 | 		sta->last_ack_rssi[0] = sta->last_ack_rssi[1]; | 
 | 153 | 		sta->last_ack_rssi[1] = sta->last_ack_rssi[2]; | 
 | 154 | 		sta->last_ack_rssi[2] = status->ack_signal; | 
 | 155 | 		sta->tx_num_consecutive_failures = 0; | 
 | 156 | 		sta->tx_num_mpdu_ok++; | 
 | 157 | 	} | 
 | 158 | 	sta->tx_retry_count += status->retry_count; | 
 | 159 | 	sta->tx_num_mpdu_fail += status->retry_count; | 
 | 160 |  | 
 | 161 | 	if (time_after(jiffies, | 
 | 162 | 		       srctrl->last_rate_change + RATE_CONTROL_INTERVAL) && | 
 | 163 | 		srctrl->tx_num_xmit > RATE_CONTROL_MIN_TX) { | 
 | 164 | 		u32 per_failed; | 
 | 165 | 		srctrl->last_rate_change = jiffies; | 
 | 166 |  | 
 | 167 | 		per_failed = (100 * sta->tx_num_mpdu_fail) / | 
 | 168 | 			(sta->tx_num_mpdu_fail + sta->tx_num_mpdu_ok); | 
 | 169 | 		/* TODO: calculate average per_failed to make adjusting | 
 | 170 | 		 * parameters easier */ | 
 | 171 | #if 0 | 
 | 172 | 		if (net_ratelimit()) { | 
 | 173 | 			printk(KERN_DEBUG "MPDU fail=%d ok=%d per_failed=%d\n", | 
 | 174 | 			       sta->tx_num_mpdu_fail, sta->tx_num_mpdu_ok, | 
 | 175 | 			       per_failed); | 
 | 176 | 		} | 
 | 177 | #endif | 
 | 178 |  | 
| Johannes Berg | 3ef8bed | 2007-07-10 19:32:09 +0200 | [diff] [blame] | 179 | 		/* | 
 | 180 | 		 * XXX: Make these configurable once we have an | 
 | 181 | 		 * interface to the rate control algorithms | 
 | 182 | 		 */ | 
 | 183 | 		if (per_failed > RATE_CONTROL_NUM_DOWN) { | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 184 | 			rate_control_rate_dec(local, sta); | 
| Johannes Berg | 3ef8bed | 2007-07-10 19:32:09 +0200 | [diff] [blame] | 185 | 		} else if (per_failed < RATE_CONTROL_NUM_UP) { | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 186 | 			rate_control_rate_inc(local, sta); | 
 | 187 | 		} | 
 | 188 | 		srctrl->tx_avg_rate_sum += status->control.rate->rate; | 
 | 189 | 		srctrl->tx_avg_rate_num++; | 
 | 190 | 		srctrl->tx_num_failures = 0; | 
 | 191 | 		srctrl->tx_num_xmit = 0; | 
 | 192 | 	} else if (sta->tx_num_consecutive_failures >= | 
 | 193 | 		   RATE_CONTROL_EMERG_DEC) { | 
 | 194 | 		rate_control_rate_dec(local, sta); | 
 | 195 | 	} | 
 | 196 |  | 
 | 197 | 	if (srctrl->avg_rate_update + 60 * HZ < jiffies) { | 
 | 198 | 		srctrl->avg_rate_update = jiffies; | 
 | 199 | 		if (srctrl->tx_avg_rate_num > 0) { | 
 | 200 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 
| Joe Perches | 0795af5 | 2007-10-03 17:59:30 -0700 | [diff] [blame] | 201 | 			DECLARE_MAC_BUF(mac); | 
 | 202 | 			printk(KERN_DEBUG "%s: STA %s Average rate: " | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 203 | 			       "%d (%d/%d)\n", | 
| Joe Perches | 0795af5 | 2007-10-03 17:59:30 -0700 | [diff] [blame] | 204 | 			       dev->name, print_mac(mac, sta->addr), | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 205 | 			       srctrl->tx_avg_rate_sum / | 
 | 206 | 			       srctrl->tx_avg_rate_num, | 
 | 207 | 			       srctrl->tx_avg_rate_sum, | 
 | 208 | 			       srctrl->tx_avg_rate_num); | 
 | 209 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ | 
 | 210 | 			srctrl->tx_avg_rate_sum = 0; | 
 | 211 | 			srctrl->tx_avg_rate_num = 0; | 
 | 212 | 		} | 
 | 213 | 	} | 
 | 214 |  | 
 | 215 | 	sta_info_put(sta); | 
 | 216 | } | 
 | 217 |  | 
 | 218 |  | 
 | 219 | static struct ieee80211_rate * | 
 | 220 | rate_control_simple_get_rate(void *priv, struct net_device *dev, | 
 | 221 | 			     struct sk_buff *skb, | 
 | 222 | 			     struct rate_control_extra *extra) | 
 | 223 | { | 
 | 224 | 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 
 | 225 | 	struct ieee80211_sub_if_data *sdata; | 
 | 226 | 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 
 | 227 | 	struct ieee80211_hw_mode *mode = extra->mode; | 
 | 228 | 	struct sta_info *sta; | 
 | 229 | 	int rateidx, nonerp_idx; | 
 | 230 | 	u16 fc; | 
 | 231 |  | 
 | 232 | 	memset(extra, 0, sizeof(*extra)); | 
 | 233 |  | 
 | 234 | 	fc = le16_to_cpu(hdr->frame_control); | 
 | 235 | 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || | 
 | 236 | 	    (hdr->addr1[0] & 0x01)) { | 
 | 237 | 		/* Send management frames and broadcast/multicast data using | 
 | 238 | 		 * lowest rate. */ | 
 | 239 | 		/* TODO: this could probably be improved.. */ | 
 | 240 | 		return rate_control_lowest_rate(local, mode); | 
 | 241 | 	} | 
 | 242 |  | 
 | 243 | 	sta = sta_info_get(local, hdr->addr1); | 
 | 244 |  | 
 | 245 | 	if (!sta) | 
 | 246 | 		return rate_control_lowest_rate(local, mode); | 
 | 247 |  | 
 | 248 | 	sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 
 | 249 | 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) | 
 | 250 | 		sta->txrate = sdata->bss->force_unicast_rateidx; | 
 | 251 |  | 
 | 252 | 	rateidx = sta->txrate; | 
 | 253 |  | 
 | 254 | 	if (rateidx >= mode->num_rates) | 
 | 255 | 		rateidx = mode->num_rates - 1; | 
 | 256 |  | 
 | 257 | 	sta->last_txrate = rateidx; | 
 | 258 | 	nonerp_idx = rateidx; | 
 | 259 | 	while (nonerp_idx > 0 && | 
 | 260 | 	       ((mode->rates[nonerp_idx].flags & IEEE80211_RATE_ERP) || | 
 | 261 | 		!(mode->rates[nonerp_idx].flags & IEEE80211_RATE_SUPPORTED) || | 
 | 262 | 		!(sta->supp_rates & BIT(nonerp_idx)))) | 
 | 263 | 		nonerp_idx--; | 
 | 264 | 	extra->nonerp = &mode->rates[nonerp_idx]; | 
 | 265 |  | 
 | 266 | 	sta_info_put(sta); | 
 | 267 |  | 
 | 268 | 	return &mode->rates[rateidx]; | 
 | 269 | } | 
 | 270 |  | 
 | 271 |  | 
 | 272 | static void rate_control_simple_rate_init(void *priv, void *priv_sta, | 
 | 273 | 					  struct ieee80211_local *local, | 
 | 274 | 					  struct sta_info *sta) | 
 | 275 | { | 
 | 276 | 	struct ieee80211_hw_mode *mode; | 
 | 277 | 	int i; | 
 | 278 | 	sta->txrate = 0; | 
 | 279 | 	mode = local->oper_hw_mode; | 
| Larry Finger | eef6caf | 2007-07-02 22:36:38 -0700 | [diff] [blame] | 280 | 	/* TODO: This routine should consider using RSSI from previous packets | 
 | 281 | 	 * as we need to have IEEE 802.1X auth succeed immediately after assoc.. | 
 | 282 | 	 * Until that method is implemented, we will use the lowest supported rate | 
 | 283 | 	 * as a workaround, */ | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 284 | 	for (i = 0; i < mode->num_rates; i++) { | 
 | 285 | 		if ((sta->supp_rates & BIT(i)) && | 
| Larry Finger | eef6caf | 2007-07-02 22:36:38 -0700 | [diff] [blame] | 286 | 		    (mode->rates[i].flags & IEEE80211_RATE_SUPPORTED)) { | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 287 | 			sta->txrate = i; | 
| Larry Finger | eef6caf | 2007-07-02 22:36:38 -0700 | [diff] [blame] | 288 | 			break; | 
 | 289 | 		} | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 290 | 	} | 
 | 291 | } | 
 | 292 |  | 
 | 293 |  | 
 | 294 | static void * rate_control_simple_alloc(struct ieee80211_local *local) | 
 | 295 | { | 
 | 296 | 	struct global_rate_control *rctrl; | 
 | 297 |  | 
 | 298 | 	rctrl = kzalloc(sizeof(*rctrl), GFP_ATOMIC); | 
 | 299 |  | 
 | 300 | 	return rctrl; | 
 | 301 | } | 
 | 302 |  | 
 | 303 |  | 
 | 304 | static void rate_control_simple_free(void *priv) | 
 | 305 | { | 
 | 306 | 	struct global_rate_control *rctrl = priv; | 
 | 307 | 	kfree(rctrl); | 
 | 308 | } | 
 | 309 |  | 
 | 310 |  | 
 | 311 | static void rate_control_simple_clear(void *priv) | 
 | 312 | { | 
 | 313 | } | 
 | 314 |  | 
 | 315 |  | 
 | 316 | static void * rate_control_simple_alloc_sta(void *priv, gfp_t gfp) | 
 | 317 | { | 
 | 318 | 	struct sta_rate_control *rctrl; | 
 | 319 |  | 
 | 320 | 	rctrl = kzalloc(sizeof(*rctrl), gfp); | 
 | 321 |  | 
 | 322 | 	return rctrl; | 
 | 323 | } | 
 | 324 |  | 
 | 325 |  | 
 | 326 | static void rate_control_simple_free_sta(void *priv, void *priv_sta) | 
 | 327 | { | 
 | 328 | 	struct sta_rate_control *rctrl = priv_sta; | 
 | 329 | 	kfree(rctrl); | 
 | 330 | } | 
 | 331 |  | 
| Jiri Benc | e9f207f | 2007-05-05 11:46:38 -0700 | [diff] [blame] | 332 | #ifdef CONFIG_MAC80211_DEBUGFS | 
 | 333 |  | 
 | 334 | static int open_file_generic(struct inode *inode, struct file *file) | 
 | 335 | { | 
 | 336 | 	file->private_data = inode->i_private; | 
 | 337 | 	return 0; | 
 | 338 | } | 
 | 339 |  | 
 | 340 | static ssize_t sta_tx_avg_rate_sum_read(struct file *file, | 
 | 341 | 					char __user *userbuf, | 
 | 342 | 					size_t count, loff_t *ppos) | 
 | 343 | { | 
 | 344 | 	struct sta_rate_control *srctrl = file->private_data; | 
 | 345 | 	char buf[20]; | 
 | 346 |  | 
 | 347 | 	sprintf(buf, "%d\n", srctrl->tx_avg_rate_sum); | 
 | 348 | 	return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); | 
 | 349 | } | 
 | 350 |  | 
 | 351 | static const struct file_operations sta_tx_avg_rate_sum_ops = { | 
 | 352 | 	.read = sta_tx_avg_rate_sum_read, | 
 | 353 | 	.open = open_file_generic, | 
 | 354 | }; | 
 | 355 |  | 
 | 356 | static ssize_t sta_tx_avg_rate_num_read(struct file *file, | 
 | 357 | 					char __user *userbuf, | 
 | 358 | 					size_t count, loff_t *ppos) | 
 | 359 | { | 
 | 360 | 	struct sta_rate_control *srctrl = file->private_data; | 
 | 361 | 	char buf[20]; | 
 | 362 |  | 
 | 363 | 	sprintf(buf, "%d\n", srctrl->tx_avg_rate_num); | 
 | 364 | 	return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); | 
 | 365 | } | 
 | 366 |  | 
 | 367 | static const struct file_operations sta_tx_avg_rate_num_ops = { | 
 | 368 | 	.read = sta_tx_avg_rate_num_read, | 
 | 369 | 	.open = open_file_generic, | 
 | 370 | }; | 
 | 371 |  | 
 | 372 | static void rate_control_simple_add_sta_debugfs(void *priv, void *priv_sta, | 
 | 373 | 						struct dentry *dir) | 
 | 374 | { | 
 | 375 | 	struct sta_rate_control *srctrl = priv_sta; | 
 | 376 |  | 
 | 377 | 	srctrl->tx_avg_rate_num_dentry = | 
 | 378 | 		debugfs_create_file("rc_simple_sta_tx_avg_rate_num", 0400, | 
 | 379 | 				    dir, srctrl, &sta_tx_avg_rate_num_ops); | 
 | 380 | 	srctrl->tx_avg_rate_sum_dentry = | 
 | 381 | 		debugfs_create_file("rc_simple_sta_tx_avg_rate_sum", 0400, | 
 | 382 | 				    dir, srctrl, &sta_tx_avg_rate_sum_ops); | 
 | 383 | } | 
 | 384 |  | 
 | 385 | static void rate_control_simple_remove_sta_debugfs(void *priv, void *priv_sta) | 
 | 386 | { | 
 | 387 | 	struct sta_rate_control *srctrl = priv_sta; | 
 | 388 |  | 
 | 389 | 	debugfs_remove(srctrl->tx_avg_rate_sum_dentry); | 
 | 390 | 	debugfs_remove(srctrl->tx_avg_rate_num_dentry); | 
 | 391 | } | 
 | 392 | #endif | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 393 |  | 
| Johannes Berg | ac71c69 | 2007-10-28 14:17:44 +0100 | [diff] [blame] | 394 | struct rate_control_ops mac80211_rcsimple = { | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 395 | 	.name = "simple", | 
 | 396 | 	.tx_status = rate_control_simple_tx_status, | 
 | 397 | 	.get_rate = rate_control_simple_get_rate, | 
 | 398 | 	.rate_init = rate_control_simple_rate_init, | 
 | 399 | 	.clear = rate_control_simple_clear, | 
 | 400 | 	.alloc = rate_control_simple_alloc, | 
 | 401 | 	.free = rate_control_simple_free, | 
 | 402 | 	.alloc_sta = rate_control_simple_alloc_sta, | 
 | 403 | 	.free_sta = rate_control_simple_free_sta, | 
| Jiri Benc | e9f207f | 2007-05-05 11:46:38 -0700 | [diff] [blame] | 404 | #ifdef CONFIG_MAC80211_DEBUGFS | 
 | 405 | 	.add_sta_debugfs = rate_control_simple_add_sta_debugfs, | 
 | 406 | 	.remove_sta_debugfs = rate_control_simple_remove_sta_debugfs, | 
 | 407 | #endif | 
| Jiri Benc | f0706e8 | 2007-05-05 11:45:53 -0700 | [diff] [blame] | 408 | }; |