1 /* $NetBSD: ieee80211_rssadapt.c,v 1.16 2008/11/12 12:36:28 ad Exp $ */
3 * Copyright (c) 2003, 2004 David Young. All rights reserved.
5 * Redistribution and use in source and binary forms, with or
6 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
19 * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
29 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: ieee80211_rssadapt.c,v 1.16 2008/11/12 12:36:28 ad Exp $");
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/kernel.h> /* for hz */
37 #include <sys/sysctl.h>
40 #include <net/if_media.h>
41 #include <net/if_ether.h>
43 #include <net80211/ieee80211_netbsd.h>
44 #include <net80211/ieee80211_var.h>
45 #include <net80211/ieee80211.h>
46 #include <net80211/ieee80211_rssadapt.h>
51 #define interpolate(parm, old, new) ((parm##_old * (old) + \
52 (parm##_denom - parm##_old) * (new)) / \
55 #ifdef IEEE80211_DEBUG
56 static struct timeval lastrateadapt
; /* time of last rate adaptation msg */
57 static int currssadaptps
= 0; /* rate-adaptation msgs this second */
58 static int ieee80211_adaptrate
= 4; /* rate-adaptation max msgs/sec */
60 #define RSSADAPT_DO_PRINT() \
61 ((ieee80211_rssadapt_debug > 0) && \
62 ppsratecheck(&lastrateadapt, &currssadaptps, ieee80211_adaptrate))
63 #define RSSADAPT_PRINTF(X) \
64 if (RSSADAPT_DO_PRINT()) \
67 int ieee80211_rssadapt_debug
= 0;
70 #define RSSADAPT_DO_PRINT() (0)
71 #define RSSADAPT_PRINTF(X)
74 static struct ieee80211_rssadapt_expavgctl master_expavgctl
= {
79 .rc_avgrssi_denom
= 8,
84 #ifdef IEEE80211_DEBUG
85 /* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */
87 sysctl_ieee80211_rssadapt_debug(SYSCTLFN_ARGS
)
90 struct sysctlnode node
;
93 t
= *(int*)rnode
->sysctl_data
;
94 node
.sysctl_data
= &t
;
95 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
96 if (error
|| newp
== NULL
)
101 *(int*)rnode
->sysctl_data
= t
;
105 #endif /* IEEE80211_DEBUG */
107 /* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */
109 sysctl_ieee80211_rssadapt_expavgctl(SYSCTLFN_ARGS
)
111 struct ieee80211_rssadapt_expavgctl rc
;
113 struct sysctlnode node
;
116 rc
= *(struct ieee80211_rssadapt_expavgctl
*)rnode
->sysctl_data
;
117 node
.sysctl_data
= &rc
;
118 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
119 if (error
|| newp
== NULL
)
122 if (/* rc.rc_decay_old < 0 || */
123 rc
.rc_decay_denom
< rc
.rc_decay_old
)
126 if (/* rc.rc_thresh_old < 0 || */
127 rc
.rc_thresh_denom
< rc
.rc_thresh_old
)
130 if (/* rc.rc_avgrssi_old < 0 || */
131 rc
.rc_avgrssi_denom
< rc
.rc_avgrssi_old
)
134 *(struct ieee80211_rssadapt_expavgctl
*)rnode
->sysctl_data
= rc
;
140 * Setup sysctl(3) MIB, net.ieee80211.*
142 * TBD condition CTLFLAG_PERMANENT on being a module or not
144 SYSCTL_SETUP(sysctl_ieee80211_rssadapt
,
145 "sysctl ieee80211 rssadapt subtree setup")
148 const struct sysctlnode
*node
;
150 if ((rc
= sysctl_createv(clog
, 0, NULL
, &node
,
151 CTLFLAG_PERMANENT
, CTLTYPE_NODE
, "net", NULL
,
152 NULL
, 0, NULL
, 0, CTL_NET
, CTL_EOL
)) != 0)
155 if ((rc
= sysctl_createv(clog
, 0, &node
, &node
,
156 CTLFLAG_PERMANENT
, CTLTYPE_NODE
, "link", NULL
,
157 NULL
, 0, NULL
, 0, PF_LINK
, CTL_EOL
)) != 0)
160 if ((rc
= sysctl_createv(clog
, 0, &node
, &node
,
161 CTLFLAG_PERMANENT
, CTLTYPE_NODE
, "ieee80211", NULL
,
162 NULL
, 0, NULL
, 0, CTL_CREATE
, CTL_EOL
)) != 0)
165 if ((rc
= sysctl_createv(clog
, 0, &node
, &node
,
166 CTLFLAG_PERMANENT
, CTLTYPE_NODE
, "rssadapt",
167 SYSCTL_DESCR("Received Signal Strength adaptation controls"),
168 NULL
, 0, NULL
, 0, CTL_CREATE
, CTL_EOL
)) != 0)
171 #ifdef IEEE80211_DEBUG
172 /* control debugging printfs */
173 if ((rc
= sysctl_createv(clog
, 0, &node
, NULL
,
174 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
, CTLTYPE_INT
, "debug",
175 SYSCTL_DESCR("Enable RSS adaptation debugging output"),
176 sysctl_ieee80211_rssadapt_debug
, 0, &ieee80211_rssadapt_debug
, 0,
177 CTL_CREATE
, CTL_EOL
)) != 0)
179 #endif /* IEEE80211_DEBUG */
181 /* control rate of decay for exponential averages */
182 if ((rc
= sysctl_createv(clog
, 0, &node
, NULL
,
183 CTLFLAG_PERMANENT
|CTLFLAG_READWRITE
, CTLTYPE_STRUCT
,
184 "expavgctl", SYSCTL_DESCR("RSS exponential averaging control"),
185 sysctl_ieee80211_rssadapt_expavgctl
, 0,
186 &master_expavgctl
, sizeof(master_expavgctl
), CTL_CREATE
,
192 printf("%s: sysctl_createv failed (rc = %d)\n", __func__
, rc
);
194 #endif /* __NetBSD__ */
197 ieee80211_rssadapt_choose(struct ieee80211_rssadapt
*ra
,
198 struct ieee80211_rateset
*rs
, struct ieee80211_frame
*wh
, u_int len
,
199 int fixed_rate
, const char *dvname
, int do_not_adapt
)
201 u_int16_t (*thrs
)[IEEE80211_RATE_SIZE
];
202 int flags
= 0, i
, rateidx
= 0, thridx
, top
;
204 if ((wh
->i_fc
[0] & IEEE80211_FC0_TYPE_MASK
) == IEEE80211_FC0_TYPE_CTL
)
205 flags
|= IEEE80211_RATE_BASIC
;
207 for (i
= 0, top
= IEEE80211_RSSADAPT_BKT0
;
208 i
< IEEE80211_RSSADAPT_BKTS
;
209 i
++, top
<<= IEEE80211_RSSADAPT_BKTPOWER
) {
215 thrs
= &ra
->ra_rate_thresh
[thridx
];
217 if (fixed_rate
!= -1) {
218 if ((rs
->rs_rates
[fixed_rate
] & flags
) == flags
) {
219 rateidx
= fixed_rate
;
222 flags
|= IEEE80211_RATE_BASIC
;
229 if ((rs
->rs_rates
[i
] & flags
) != flags
)
233 if ((*thrs
)[i
] < ra
->ra_avg_rssi
)
238 #ifdef IEEE80211_DEBUG
239 if (ieee80211_rssadapt_debug
&& dvname
!= NULL
) {
240 printf("%s: dst %s threshold[%d, %d.%d] %d < %d\n",
241 dvname
, ether_sprintf(wh
->i_addr1
), len
,
242 (rs
->rs_rates
[rateidx
] & IEEE80211_RATE_VAL
) / 2,
243 (rs
->rs_rates
[rateidx
] & IEEE80211_RATE_VAL
) * 5 % 10,
244 (*thrs
)[rateidx
], ra
->ra_avg_rssi
);
246 #endif /* IEEE80211_DEBUG */
251 ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt
*ra
)
256 (ra
->ra_pktrate
+ 10 * (ra
->ra_nfail
+ ra
->ra_nok
)) / 2;
257 ra
->ra_nfail
= ra
->ra_nok
= 0;
259 /* a node is eligible for its rate to be raised every 1/10 to 10
260 * seconds, more eligible in proportion to recent packet rates.
262 interval
= MAX(100000, 10000000 / MAX(1, 10 * ra
->ra_pktrate
));
263 ra
->ra_raise_interval
.tv_sec
= interval
/ (1000 * 1000);
264 ra
->ra_raise_interval
.tv_usec
= interval
% (1000 * 1000);
268 ieee80211_rssadapt_input(struct ieee80211com
*ic
, struct ieee80211_node
*ni
,
269 struct ieee80211_rssadapt
*ra
, int rssi
)
271 #ifdef IEEE80211_DEBUG
272 int last_avg_rssi
= ra
->ra_avg_rssi
;
275 ra
->ra_avg_rssi
= interpolate(master_expavgctl
.rc_avgrssi
,
276 ra
->ra_avg_rssi
, (rssi
<< 8));
278 RSSADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n",
279 ic
->ic_ifp
->if_xname
, ether_sprintf(ni
->ni_macaddr
),
280 rssi
, last_avg_rssi
, ra
->ra_avg_rssi
));
284 * Adapt the data rate to suit the conditions. When a transmitted
285 * packet is dropped after IEEE80211_RSSADAPT_RETRY_LIMIT retransmissions,
286 * raise the RSS threshold for transmitting packets of similar length at
287 * the same data rate.
290 ieee80211_rssadapt_lower_rate(struct ieee80211com
*ic
,
291 struct ieee80211_node
*ni
, struct ieee80211_rssadapt
*ra
,
292 struct ieee80211_rssdesc
*id
)
294 struct ieee80211_rateset
*rs
= &ni
->ni_rates
;
296 u_int i
, thridx
, top
;
300 if (id
->id_rateidx
>= rs
->rs_nrates
) {
301 RSSADAPT_PRINTF(("ieee80211_rssadapt_lower_rate: "
302 "%s rate #%d > #%d out of bounds\n",
303 ether_sprintf(ni
->ni_macaddr
), id
->id_rateidx
,
308 for (i
= 0, top
= IEEE80211_RSSADAPT_BKT0
;
309 i
< IEEE80211_RSSADAPT_BKTS
;
310 i
++, top
<<= IEEE80211_RSSADAPT_BKTPOWER
) {
312 if (id
->id_len
<= top
)
316 last_thr
= ra
->ra_rate_thresh
[thridx
][id
->id_rateidx
];
317 ra
->ra_rate_thresh
[thridx
][id
->id_rateidx
] =
318 interpolate(master_expavgctl
.rc_thresh
, last_thr
,
321 RSSADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n",
322 ic
->ic_ifp
->if_xname
, ether_sprintf(ni
->ni_macaddr
),
323 id
->id_rssi
, id
->id_len
,
324 (rs
->rs_rates
[id
->id_rateidx
] & IEEE80211_RATE_VAL
) / 2,
325 (rs
->rs_rates
[id
->id_rateidx
] & IEEE80211_RATE_VAL
) * 5 % 10,
326 last_thr
, ra
->ra_rate_thresh
[thridx
][id
->id_rateidx
]));
330 ieee80211_rssadapt_raise_rate(struct ieee80211com
*ic
,
331 struct ieee80211_rssadapt
*ra
, struct ieee80211_rssdesc
*id
)
333 u_int16_t (*thrs
)[IEEE80211_RATE_SIZE
], newthr
, oldthr
;
334 struct ieee80211_node
*ni
= id
->id_node
;
335 struct ieee80211_rateset
*rs
= &ni
->ni_rates
;
337 #ifdef IEEE80211_DEBUG
343 if (!ratecheck(&ra
->ra_last_raise
, &ra
->ra_raise_interval
))
346 for (i
= 0, top
= IEEE80211_RSSADAPT_BKT0
;
347 i
< IEEE80211_RSSADAPT_BKTS
;
348 i
++, top
<<= IEEE80211_RSSADAPT_BKTPOWER
) {
349 thrs
= &ra
->ra_rate_thresh
[i
];
350 if (id
->id_len
<= top
)
354 if (id
->id_rateidx
+ 1 < rs
->rs_nrates
&&
355 (*thrs
)[id
->id_rateidx
+ 1] > (*thrs
)[id
->id_rateidx
]) {
356 rate
= (rs
->rs_rates
[id
->id_rateidx
+ 1] & IEEE80211_RATE_VAL
);
358 RSSADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d ",
359 ic
->ic_ifp
->if_xname
,
360 IEEE80211_RSSADAPT_BKT0
<< (IEEE80211_RSSADAPT_BKTPOWER
* i
),
361 rate
/ 2, rate
* 5 % 10, (*thrs
)[id
->id_rateidx
+ 1]));
362 oldthr
= (*thrs
)[id
->id_rateidx
+ 1];
363 if ((*thrs
)[id
->id_rateidx
] == 0)
364 newthr
= ra
->ra_avg_rssi
;
366 newthr
= (*thrs
)[id
->id_rateidx
];
367 (*thrs
)[id
->id_rateidx
+ 1] =
368 interpolate(master_expavgctl
.rc_decay
, oldthr
, newthr
);
370 RSSADAPT_PRINTF(("-> %d\n", (*thrs
)[id
->id_rateidx
+ 1]));
373 #ifdef IEEE80211_DEBUG
374 if (RSSADAPT_DO_PRINT()) {
375 printf("%s: dst %s thresholds\n", ic
->ic_ifp
->if_xname
,
376 ether_sprintf(ni
->ni_macaddr
));
377 for (i
= 0; i
< IEEE80211_RSSADAPT_BKTS
; i
++) {
378 printf("%d-byte", IEEE80211_RSSADAPT_BKT0
<< (IEEE80211_RSSADAPT_BKTPOWER
* i
));
379 for (j
= 0; j
< rs
->rs_nrates
; j
++) {
380 rate
= (rs
->rs_rates
[j
] & IEEE80211_RATE_VAL
);
381 printf(", T[%d.%d] = %d", rate
/ 2,
382 rate
* 5 % 10, ra
->ra_rate_thresh
[i
][j
]);
387 #endif /* IEEE80211_DEBUG */