1 1.1 christos /* 2 1.1 christos * Wi-Fi Multimedia Admission Control (WMM-AC) 3 1.1 christos * Copyright(c) 2014, Intel Mobile Communication GmbH. 4 1.1 christos * Copyright(c) 2014, Intel Corporation. All rights reserved. 5 1.1 christos * 6 1.1 christos * This software may be distributed under the terms of the BSD license. 7 1.1 christos * See README for more details. 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #include "includes.h" 11 1.1 christos 12 1.1 christos #include "utils/common.h" 13 1.1 christos #include "utils/list.h" 14 1.1 christos #include "utils/eloop.h" 15 1.1 christos #include "common/ieee802_11_common.h" 16 1.1 christos #include "wpa_supplicant_i.h" 17 1.1 christos #include "bss.h" 18 1.1 christos #include "driver_i.h" 19 1.1 christos #include "wmm_ac.h" 20 1.1 christos 21 1.1 christos static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx); 22 1.1 christos 23 1.1 christos static const enum wmm_ac up_to_ac[8] = { 24 1.1 christos WMM_AC_BK, 25 1.1 christos WMM_AC_BE, 26 1.1 christos WMM_AC_BE, 27 1.1 christos WMM_AC_BK, 28 1.1 christos WMM_AC_VI, 29 1.1 christos WMM_AC_VI, 30 1.1 christos WMM_AC_VO, 31 1.1 christos WMM_AC_VO 32 1.1 christos }; 33 1.1 christos 34 1.1 christos 35 1.1 christos static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec) 36 1.1 christos { 37 1.1 christos return (tspec->ts_info[0] >> 1) & 0x0f; 38 1.1 christos } 39 1.1 christos 40 1.1 christos 41 1.1 christos static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec) 42 1.1 christos { 43 1.1 christos return (tspec->ts_info[0] >> 5) & 0x03; 44 1.1 christos } 45 1.1 christos 46 1.1 christos 47 1.1 christos static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec) 48 1.1 christos { 49 1.1 christos return (tspec->ts_info[1] >> 3) & 0x07; 50 1.1 christos } 51 1.1 christos 52 1.1 christos 53 1.1 christos static u8 wmm_ac_direction_to_idx(u8 direction) 54 1.1 christos { 55 1.1 christos switch (direction) { 56 1.1 christos case WMM_AC_DIR_UPLINK: 57 1.1 christos return TS_DIR_IDX_UPLINK; 58 1.1 christos case WMM_AC_DIR_DOWNLINK: 59 1.1 christos return TS_DIR_IDX_DOWNLINK; 60 1.1 christos case WMM_AC_DIR_BIDIRECTIONAL: 61 1.1 christos return TS_DIR_IDX_BIDI; 62 1.1 christos default: 63 1.1 christos wpa_printf(MSG_ERROR, "Invalid direction: %d", direction); 64 1.1 christos return WMM_AC_DIR_UPLINK; 65 1.1 christos } 66 1.1 christos } 67 1.1 christos 68 1.1 christos 69 1.1 christos static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, 70 1.1 christos const struct wmm_tspec_element *tspec) 71 1.1 christos { 72 1.1 christos struct wmm_tspec_element *_tspec; 73 1.1 christos int ret; 74 1.1 christos u16 admitted_time = le_to_host16(tspec->medium_time); 75 1.1 christos u8 up = wmm_ac_get_user_priority(tspec); 76 1.1 christos u8 ac = up_to_ac[up]; 77 1.1 christos u8 dir = wmm_ac_get_direction(tspec); 78 1.1 christos u8 tsid = wmm_ac_get_tsid(tspec); 79 1.1 christos enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir); 80 1.1 christos 81 1.1 christos /* should have been verified before, but double-check here */ 82 1.1 christos if (wpa_s->tspecs[ac][idx]) { 83 1.1 christos wpa_printf(MSG_ERROR, 84 1.1 christos "WMM AC: tspec (ac=%d, dir=%d) already exists!", 85 1.1 christos ac, dir); 86 1.1 christos return -1; 87 1.1 christos } 88 1.1 christos 89 1.1 christos /* copy tspec */ 90 1.1.1.2 christos _tspec = os_memdup(tspec, sizeof(*_tspec)); 91 1.1 christos if (!_tspec) 92 1.1 christos return -1; 93 1.1 christos 94 1.1 christos if (dir != WMM_AC_DIR_DOWNLINK) { 95 1.1 christos ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time); 96 1.1 christos wpa_printf(MSG_DEBUG, 97 1.1 christos "WMM AC: Add TS: addr=" MACSTR 98 1.1 christos " TSID=%u admitted time=%u, ret=%d", 99 1.1 christos MAC2STR(addr), tsid, admitted_time, ret); 100 1.1 christos if (ret < 0) { 101 1.1 christos os_free(_tspec); 102 1.1 christos return -1; 103 1.1 christos } 104 1.1 christos } 105 1.1 christos 106 1.1 christos wpa_s->tspecs[ac][idx] = _tspec; 107 1.1 christos 108 1.1 christos wpa_printf(MSG_DEBUG, "Traffic stream was created successfully"); 109 1.1 christos 110 1.1 christos wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED 111 1.1 christos "tsid=%d addr=" MACSTR " admitted_time=%d", 112 1.1 christos tsid, MAC2STR(addr), admitted_time); 113 1.1 christos 114 1.1 christos return 0; 115 1.1 christos } 116 1.1 christos 117 1.1 christos 118 1.1 christos static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac, 119 1.1 christos enum ts_dir_idx dir) 120 1.1 christos { 121 1.1 christos struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir]; 122 1.1 christos u8 tsid; 123 1.1 christos 124 1.1 christos if (!tspec) 125 1.1 christos return; 126 1.1 christos 127 1.1 christos tsid = wmm_ac_get_tsid(tspec); 128 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid); 129 1.1 christos 130 1.1 christos /* update the driver in case of uplink/bidi */ 131 1.1 christos if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK) 132 1.1 christos wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid); 133 1.1 christos 134 1.1 christos wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED 135 1.1 christos "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid)); 136 1.1 christos 137 1.1 christos os_free(wpa_s->tspecs[ac][dir]); 138 1.1 christos wpa_s->tspecs[ac][dir] = NULL; 139 1.1 christos } 140 1.1 christos 141 1.1 christos 142 1.1 christos static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed) 143 1.1 christos { 144 1.1 christos struct wmm_ac_addts_request *req = wpa_s->addts_request; 145 1.1 christos 146 1.1 christos if (!req) 147 1.1 christos return; 148 1.1 christos 149 1.1 christos if (failed) 150 1.1 christos wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED 151 1.1 christos "tsid=%u", wmm_ac_get_tsid(&req->tspec)); 152 1.1 christos 153 1.1 christos eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req); 154 1.1 christos wpa_s->addts_request = NULL; 155 1.1 christos os_free(req); 156 1.1 christos } 157 1.1 christos 158 1.1 christos 159 1.1 christos static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx) 160 1.1 christos { 161 1.1 christos struct wpa_supplicant *wpa_s = eloop_ctx; 162 1.1 christos struct wmm_ac_addts_request *addts_req = timeout_ctx; 163 1.1 christos 164 1.1 christos wpa_printf(MSG_DEBUG, 165 1.1 christos "Timeout getting ADDTS response (tsid=%d up=%d)", 166 1.1 christos wmm_ac_get_tsid(&addts_req->tspec), 167 1.1 christos wmm_ac_get_user_priority(&addts_req->tspec)); 168 1.1 christos 169 1.1 christos wmm_ac_del_req(wpa_s, 1); 170 1.1 christos } 171 1.1 christos 172 1.1 christos 173 1.1 christos static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s, 174 1.1 christos const struct wmm_ac_addts_request *req) 175 1.1 christos { 176 1.1 christos struct wpabuf *buf; 177 1.1 christos int ret; 178 1.1 christos 179 1.1 christos wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR, 180 1.1 christos MAC2STR(req->address)); 181 1.1 christos 182 1.1 christos /* category + action code + dialog token + status + sizeof(tspec) */ 183 1.1 christos buf = wpabuf_alloc(4 + sizeof(req->tspec)); 184 1.1 christos if (!buf) { 185 1.1 christos wpa_printf(MSG_ERROR, "WMM AC: Allocation error"); 186 1.1 christos return -1; 187 1.1 christos } 188 1.1 christos 189 1.1 christos wpabuf_put_u8(buf, WLAN_ACTION_WMM); 190 1.1 christos wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ); 191 1.1 christos wpabuf_put_u8(buf, req->dialog_token); 192 1.1 christos wpabuf_put_u8(buf, 0); /* status code */ 193 1.1 christos wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec)); 194 1.1 christos 195 1.1 christos ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address, 196 1.1 christos wpa_s->own_addr, wpa_s->bssid, 197 1.1 christos wpabuf_head(buf), wpabuf_len(buf), 0); 198 1.1 christos if (ret) { 199 1.1 christos wpa_printf(MSG_WARNING, 200 1.1 christos "WMM AC: Failed to send ADDTS Request"); 201 1.1 christos } 202 1.1 christos 203 1.1 christos wpabuf_free(buf); 204 1.1 christos return ret; 205 1.1 christos } 206 1.1 christos 207 1.1 christos 208 1.1 christos static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s, 209 1.1 christos const struct wmm_tspec_element *tspec, 210 1.1 christos const u8 *address) 211 1.1 christos { 212 1.1 christos struct wpabuf *buf; 213 1.1 christos int ret; 214 1.1 christos 215 1.1 christos /* category + action code + dialog token + status + sizeof(tspec) */ 216 1.1 christos buf = wpabuf_alloc(4 + sizeof(*tspec)); 217 1.1 christos if (!buf) 218 1.1 christos return -1; 219 1.1 christos 220 1.1 christos wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address)); 221 1.1 christos 222 1.1 christos /* category + action code + dialog token + status + sizeof(tspec) */ 223 1.1 christos wpabuf_put_u8(buf, WLAN_ACTION_WMM); 224 1.1 christos wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS); 225 1.1 christos wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */ 226 1.1 christos wpabuf_put_u8(buf, 0); /* Status Code (not used) */ 227 1.1 christos wpabuf_put_data(buf, tspec, sizeof(*tspec)); 228 1.1 christos 229 1.1 christos ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address, 230 1.1 christos wpa_s->own_addr, wpa_s->bssid, 231 1.1 christos wpabuf_head(buf), wpabuf_len(buf), 0); 232 1.1 christos if (ret) 233 1.1 christos wpa_printf(MSG_WARNING, "Failed to send DELTS frame"); 234 1.1 christos 235 1.1 christos wpabuf_free(buf); 236 1.1 christos return ret; 237 1.1 christos } 238 1.1 christos 239 1.1 christos 240 1.1 christos /* return the AC using the given TSPEC tid */ 241 1.1 christos static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid, 242 1.1 christos enum ts_dir_idx *dir) 243 1.1 christos { 244 1.1 christos int ac; 245 1.1 christos enum ts_dir_idx idx; 246 1.1 christos 247 1.1 christos for (ac = 0; ac < WMM_AC_NUM; ac++) { 248 1.1 christos for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 249 1.1 christos if (wpa_s->tspecs[ac][idx] && 250 1.1 christos wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) { 251 1.1 christos if (dir) 252 1.1 christos *dir = idx; 253 1.1 christos return ac; 254 1.1 christos } 255 1.1 christos } 256 1.1 christos } 257 1.1 christos 258 1.1 christos return -1; 259 1.1 christos } 260 1.1 christos 261 1.1 christos 262 1.1 christos static struct wmm_ac_addts_request * 263 1.1 christos wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s, 264 1.1 christos const struct wmm_ac_ts_setup_params *params, 265 1.1 christos const u8 *address) 266 1.1 christos { 267 1.1 christos struct wmm_ac_addts_request *addts_req; 268 1.1 christos struct wmm_tspec_element *tspec; 269 1.1 christos u8 ac = up_to_ac[params->user_priority]; 270 1.1 christos u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd; 271 1.1 christos 272 1.1 christos addts_req = os_zalloc(sizeof(*addts_req)); 273 1.1 christos if (!addts_req) 274 1.1 christos return NULL; 275 1.1 christos 276 1.1 christos tspec = &addts_req->tspec; 277 1.1 christos os_memcpy(addts_req->address, address, ETH_ALEN); 278 1.1 christos 279 1.1 christos /* The dialog token cannot be zero */ 280 1.1 christos if (++wpa_s->wmm_ac_last_dialog_token == 0) 281 1.1 christos wpa_s->wmm_ac_last_dialog_token++; 282 1.1 christos 283 1.1 christos addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token; 284 1.1 christos tspec->eid = WLAN_EID_VENDOR_SPECIFIC; 285 1.1 christos tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */ 286 1.1 christos tspec->oui[0] = 0x00; 287 1.1 christos tspec->oui[1] = 0x50; 288 1.1 christos tspec->oui[2] = 0xf2; 289 1.1 christos tspec->oui_type = WMM_OUI_TYPE; 290 1.1 christos tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT; 291 1.1 christos tspec->version = WMM_VERSION; 292 1.1 christos 293 1.1 christos tspec->ts_info[0] = params->tsid << 1; 294 1.1 christos tspec->ts_info[0] |= params->direction << 5; 295 1.1 christos tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7; 296 1.1 christos tspec->ts_info[1] = uapsd << 2; 297 1.1 christos tspec->ts_info[1] |= params->user_priority << 3; 298 1.1 christos tspec->ts_info[2] = 0; 299 1.1 christos 300 1.1 christos tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size); 301 1.1 christos if (params->fixed_nominal_msdu) 302 1.1 christos tspec->nominal_msdu_size |= 303 1.1 christos host_to_le16(WMM_AC_FIXED_MSDU_SIZE); 304 1.1 christos 305 1.1 christos tspec->mean_data_rate = host_to_le32(params->mean_data_rate); 306 1.1 christos tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate); 307 1.1 christos tspec->surplus_bandwidth_allowance = 308 1.1 christos host_to_le16(params->surplus_bandwidth_allowance); 309 1.1 christos 310 1.1 christos return addts_req; 311 1.1 christos } 312 1.1 christos 313 1.1 christos 314 1.1 christos static int param_in_range(const char *name, long value, 315 1.1 christos long min_val, long max_val) 316 1.1 christos { 317 1.1 christos if (value < min_val || (max_val >= 0 && value > max_val)) { 318 1.1 christos wpa_printf(MSG_DEBUG, 319 1.1 christos "WMM AC: param %s (%ld) is out of range (%ld-%ld)", 320 1.1 christos name, value, min_val, max_val); 321 1.1 christos return 0; 322 1.1 christos } 323 1.1 christos 324 1.1 christos return 1; 325 1.1 christos } 326 1.1 christos 327 1.1 christos 328 1.1 christos static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s, 329 1.1 christos u8 tsid, u8 ac, u8 dir) 330 1.1 christos { 331 1.1 christos enum ts_dir_idx idx; 332 1.1 christos int cur_ac, existing_ts = 0, replace_ts = 0; 333 1.1 christos 334 1.1 christos cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); 335 1.1 christos if (cur_ac >= 0) { 336 1.1 christos if (cur_ac != ac) { 337 1.1 christos wpa_printf(MSG_DEBUG, 338 1.1 christos "WMM AC: TSID %i already exists on different ac (%d)", 339 1.1 christos tsid, cur_ac); 340 1.1 christos return -1; 341 1.1 christos } 342 1.1 christos 343 1.1 christos /* same tsid - this tspec will replace the current one */ 344 1.1 christos replace_ts |= BIT(idx); 345 1.1 christos } 346 1.1 christos 347 1.1 christos for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 348 1.1 christos if (wpa_s->tspecs[ac][idx]) 349 1.1 christos existing_ts |= BIT(idx); 350 1.1 christos } 351 1.1 christos 352 1.1 christos switch (dir) { 353 1.1 christos case WMM_AC_DIR_UPLINK: 354 1.1 christos /* replace existing uplink/bidi tspecs */ 355 1.1 christos replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) | 356 1.1 christos BIT(TS_DIR_IDX_BIDI)); 357 1.1 christos break; 358 1.1 christos case WMM_AC_DIR_DOWNLINK: 359 1.1 christos /* replace existing downlink/bidi tspecs */ 360 1.1 christos replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) | 361 1.1 christos BIT(TS_DIR_IDX_BIDI)); 362 1.1 christos break; 363 1.1 christos case WMM_AC_DIR_BIDIRECTIONAL: 364 1.1 christos /* replace all existing tspecs */ 365 1.1 christos replace_ts |= existing_ts; 366 1.1 christos break; 367 1.1 christos default: 368 1.1 christos return -1; 369 1.1 christos } 370 1.1 christos 371 1.1 christos return replace_ts; 372 1.1 christos } 373 1.1 christos 374 1.1 christos 375 1.1 christos static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s, 376 1.1 christos const struct wmm_ac_ts_setup_params *params) 377 1.1 christos { 378 1.1 christos enum wmm_ac req_ac; 379 1.1 christos 380 1.1 christos #define PARAM_IN_RANGE(field, min_value, max_value) \ 381 1.1 christos param_in_range(#field, params->field, min_value, max_value) 382 1.1 christos 383 1.1 christos if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) || 384 1.1 christos !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) || 385 1.1 christos !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) || 386 1.1 christos !PARAM_IN_RANGE(mean_data_rate, 1, -1) || 387 1.1 christos !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) || 388 1.1 christos !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY, 389 1.1 christos -1)) 390 1.1 christos return 0; 391 1.1 christos #undef PARAM_IN_RANGE 392 1.1 christos 393 1.1 christos if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK || 394 1.1 christos params->direction == WMM_TSPEC_DIRECTION_DOWNLINK || 395 1.1 christos params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) { 396 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d", 397 1.1 christos params->direction); 398 1.1 christos return 0; 399 1.1 christos } 400 1.1 christos 401 1.1 christos req_ac = up_to_ac[params->user_priority]; 402 1.1 christos 403 1.1.1.4 christos /* Requested access category must have acm */ 404 1.1 christos if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) { 405 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac); 406 1.1 christos return 0; 407 1.1 christos } 408 1.1 christos 409 1.1 christos if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac, 410 1.1 christos params->direction) < 0) 411 1.1 christos return 0; 412 1.1 christos 413 1.1 christos return 1; 414 1.1 christos } 415 1.1 christos 416 1.1 christos 417 1.1 christos static struct wmm_ac_assoc_data * 418 1.1 christos wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, 419 1.1 christos size_t ies_len) 420 1.1 christos { 421 1.1 christos struct ieee802_11_elems elems; 422 1.1 christos struct wmm_parameter_element *wmm_params; 423 1.1 christos struct wmm_ac_assoc_data *assoc_data; 424 1.1 christos int i; 425 1.1 christos 426 1.1 christos /* Parsing WMM Parameter Element */ 427 1.1 christos if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { 428 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies"); 429 1.1 christos return NULL; 430 1.1 christos } 431 1.1 christos 432 1.1 christos if (!elems.wmm) { 433 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE"); 434 1.1 christos return NULL; 435 1.1 christos } 436 1.1 christos 437 1.1 christos if (elems.wmm_len != sizeof(*wmm_params)) { 438 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length"); 439 1.1 christos return NULL; 440 1.1 christos } 441 1.1 christos 442 1.1 christos wmm_params = (struct wmm_parameter_element *)(elems.wmm); 443 1.1 christos 444 1.1 christos assoc_data = os_zalloc(sizeof(*assoc_data)); 445 1.1 christos if (!assoc_data) 446 1.1 christos return NULL; 447 1.1 christos 448 1.1 christos for (i = 0; i < WMM_AC_NUM; i++) 449 1.1 christos assoc_data->ac_params[i].acm = 450 1.1 christos !!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM); 451 1.1 christos 452 1.1 christos wpa_printf(MSG_DEBUG, 453 1.1 christos "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u", 454 1.1 christos assoc_data->ac_params[WMM_AC_BE].acm, 455 1.1 christos assoc_data->ac_params[WMM_AC_BK].acm, 456 1.1 christos assoc_data->ac_params[WMM_AC_VI].acm, 457 1.1 christos assoc_data->ac_params[WMM_AC_VO].acm); 458 1.1 christos 459 1.1 christos return assoc_data; 460 1.1 christos } 461 1.1 christos 462 1.1 christos 463 1.1 christos static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, 464 1.1 christos size_t ies_len, const struct wmm_params *wmm_params) 465 1.1 christos { 466 1.1 christos struct wmm_ac_assoc_data *assoc_data; 467 1.1 christos u8 ac; 468 1.1 christos 469 1.1 christos if (wpa_s->wmm_ac_assoc_info) { 470 1.1 christos wpa_printf(MSG_ERROR, "WMM AC: Already initialized"); 471 1.1 christos return -1; 472 1.1 christos } 473 1.1 christos 474 1.1.1.3 christos if (!ies || !(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) { 475 1.1.1.3 christos /* WMM AC not in use for this connection */ 476 1.1 christos return -1; 477 1.1 christos } 478 1.1 christos 479 1.1 christos os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs)); 480 1.1 christos wpa_s->wmm_ac_last_dialog_token = 0; 481 1.1 christos wpa_s->addts_request = NULL; 482 1.1 christos 483 1.1 christos assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len); 484 1.1 christos if (!assoc_data) 485 1.1 christos return -1; 486 1.1 christos 487 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x", 488 1.1 christos wmm_params->uapsd_queues); 489 1.1 christos 490 1.1 christos for (ac = 0; ac < WMM_AC_NUM; ac++) { 491 1.1 christos assoc_data->ac_params[ac].uapsd = 492 1.1 christos !!(wmm_params->uapsd_queues & BIT(ac)); 493 1.1 christos } 494 1.1 christos 495 1.1 christos wpa_s->wmm_ac_assoc_info = assoc_data; 496 1.1 christos return 0; 497 1.1 christos } 498 1.1 christos 499 1.1 christos 500 1.1 christos static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap) 501 1.1 christos { 502 1.1 christos enum ts_dir_idx idx; 503 1.1 christos 504 1.1 christos for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 505 1.1 christos if (!(dir_bitmap & BIT(idx))) 506 1.1 christos continue; 507 1.1 christos 508 1.1 christos wmm_ac_del_ts_idx(wpa_s, ac, idx); 509 1.1 christos } 510 1.1 christos } 511 1.1 christos 512 1.1 christos 513 1.1 christos static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) 514 1.1 christos { 515 1.1 christos int i; 516 1.1 christos 517 1.1 christos for (i = 0; i < WMM_AC_NUM; i++) 518 1.1 christos wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); 519 1.1 christos 520 1.1.1.3 christos /* delete pending add_ts request */ 521 1.1 christos wmm_ac_del_req(wpa_s, 1); 522 1.1 christos 523 1.1 christos os_free(wpa_s->wmm_ac_assoc_info); 524 1.1 christos wpa_s->wmm_ac_assoc_info = NULL; 525 1.1 christos } 526 1.1 christos 527 1.1 christos 528 1.1 christos void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies, 529 1.1 christos size_t ies_len, const struct wmm_params *wmm_params) 530 1.1 christos { 531 1.1 christos if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params)) 532 1.1 christos return; 533 1.1 christos 534 1.1 christos wpa_printf(MSG_DEBUG, 535 1.1 christos "WMM AC: Valid WMM association, WMM AC is enabled"); 536 1.1 christos } 537 1.1 christos 538 1.1 christos 539 1.1 christos void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s) 540 1.1 christos { 541 1.1 christos if (!wpa_s->wmm_ac_assoc_info) 542 1.1 christos return; 543 1.1 christos 544 1.1 christos wmm_ac_deinit(wpa_s); 545 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled"); 546 1.1 christos } 547 1.1 christos 548 1.1 christos 549 1.1 christos int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) 550 1.1 christos { 551 1.1 christos struct wmm_tspec_element tspec; 552 1.1 christos int ac; 553 1.1 christos enum ts_dir_idx dir; 554 1.1 christos 555 1.1 christos if (!wpa_s->wmm_ac_assoc_info) { 556 1.1 christos wpa_printf(MSG_DEBUG, 557 1.1 christos "WMM AC: Failed to delete TS, WMM AC is disabled"); 558 1.1 christos return -1; 559 1.1 christos } 560 1.1 christos 561 1.1 christos ac = wmm_ac_find_tsid(wpa_s, tsid, &dir); 562 1.1 christos if (ac < 0) { 563 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist"); 564 1.1 christos return -1; 565 1.1 christos } 566 1.1 christos 567 1.1 christos tspec = *wpa_s->tspecs[ac][dir]; 568 1.1 christos 569 1.1 christos wmm_ac_del_ts_idx(wpa_s, ac, dir); 570 1.1 christos 571 1.1 christos wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid); 572 1.1 christos 573 1.1 christos return 0; 574 1.1 christos } 575 1.1 christos 576 1.1 christos 577 1.1 christos int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, 578 1.1 christos struct wmm_ac_ts_setup_params *params) 579 1.1 christos { 580 1.1 christos struct wmm_ac_addts_request *addts_req; 581 1.1 christos 582 1.1 christos if (!wpa_s->wmm_ac_assoc_info) { 583 1.1 christos wpa_printf(MSG_DEBUG, 584 1.1 christos "WMM AC: Cannot add TS - missing assoc data"); 585 1.1 christos return -1; 586 1.1 christos } 587 1.1 christos 588 1.1 christos if (wpa_s->addts_request) { 589 1.1 christos wpa_printf(MSG_DEBUG, 590 1.1 christos "WMM AC: can't add TS - ADDTS request is already pending"); 591 1.1 christos return -1; 592 1.1 christos } 593 1.1 christos 594 1.1 christos /* 595 1.1 christos * we can setup downlink TS even without driver support. 596 1.1 christos * however, we need driver support for the other directions. 597 1.1 christos */ 598 1.1 christos if (params->direction != WMM_AC_DIR_DOWNLINK && 599 1.1 christos !wpa_s->wmm_ac_supported) { 600 1.1 christos wpa_printf(MSG_DEBUG, 601 1.1 christos "Cannot set uplink/bidi TS without driver support"); 602 1.1 christos return -1; 603 1.1 christos } 604 1.1 christos 605 1.1 christos if (!wmm_ac_ts_req_is_valid(wpa_s, params)) 606 1.1 christos return -1; 607 1.1 christos 608 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR 609 1.1 christos " tsid=%u user priority=%u direction=%d)", 610 1.1 christos MAC2STR(wpa_s->bssid), params->tsid, 611 1.1 christos params->user_priority, params->direction); 612 1.1 christos 613 1.1 christos addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid); 614 1.1 christos if (!addts_req) 615 1.1 christos return -1; 616 1.1 christos 617 1.1 christos if (wmm_ac_send_addts_request(wpa_s, addts_req)) 618 1.1 christos goto err; 619 1.1 christos 620 1.1 christos /* save as pending and set ADDTS resp timeout to 1 second */ 621 1.1 christos wpa_s->addts_request = addts_req; 622 1.1 christos eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout, 623 1.1 christos wpa_s, addts_req); 624 1.1 christos return 0; 625 1.1 christos err: 626 1.1 christos os_free(addts_req); 627 1.1 christos return -1; 628 1.1 christos } 629 1.1 christos 630 1.1 christos 631 1.1 christos static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa, 632 1.1 christos const struct wmm_tspec_element *tspec) 633 1.1 christos { 634 1.1 christos int ac; 635 1.1 christos u8 tsid; 636 1.1 christos enum ts_dir_idx idx; 637 1.1 christos 638 1.1 christos tsid = wmm_ac_get_tsid(tspec); 639 1.1 christos 640 1.1 christos wpa_printf(MSG_DEBUG, 641 1.1 christos "WMM AC: DELTS frame has been received TSID=%u addr=" 642 1.1 christos MACSTR, tsid, MAC2STR(sa)); 643 1.1 christos 644 1.1 christos ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); 645 1.1 christos if (ac < 0) { 646 1.1 christos wpa_printf(MSG_DEBUG, 647 1.1 christos "WMM AC: Ignoring DELTS frame - TSID does not exist"); 648 1.1 christos return; 649 1.1 christos } 650 1.1 christos 651 1.1 christos wmm_ac_del_ts_idx(wpa_s, ac, idx); 652 1.1 christos 653 1.1 christos wpa_printf(MSG_DEBUG, 654 1.1 christos "TS was deleted successfully (tsid=%u address=" MACSTR ")", 655 1.1 christos tsid, MAC2STR(sa)); 656 1.1 christos } 657 1.1 christos 658 1.1 christos 659 1.1 christos static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa, 660 1.1 christos const u8 resp_dialog_token, const u8 status_code, 661 1.1 christos const struct wmm_tspec_element *tspec) 662 1.1 christos { 663 1.1 christos struct wmm_ac_addts_request *req = wpa_s->addts_request; 664 1.1 christos u8 ac, tsid, up, dir; 665 1.1 christos int replace_tspecs; 666 1.1 christos 667 1.1 christos tsid = wmm_ac_get_tsid(tspec); 668 1.1 christos dir = wmm_ac_get_direction(tspec); 669 1.1 christos up = wmm_ac_get_user_priority(tspec); 670 1.1 christos ac = up_to_ac[up]; 671 1.1 christos 672 1.1 christos /* make sure we have a matching addts request */ 673 1.1 christos if (!req || req->dialog_token != resp_dialog_token) { 674 1.1 christos wpa_printf(MSG_DEBUG, 675 1.1 christos "WMM AC: no req with dialog=%u, ignoring frame", 676 1.1 christos resp_dialog_token); 677 1.1 christos return; 678 1.1 christos } 679 1.1 christos 680 1.1 christos /* make sure the params are the same */ 681 1.1.1.4 christos if (!ether_addr_equal(req->address, sa) || 682 1.1 christos tsid != wmm_ac_get_tsid(&req->tspec) || 683 1.1 christos up != wmm_ac_get_user_priority(&req->tspec) || 684 1.1 christos dir != wmm_ac_get_direction(&req->tspec)) { 685 1.1 christos wpa_printf(MSG_DEBUG, 686 1.1 christos "WMM AC: ADDTS params do not match, ignoring frame"); 687 1.1 christos return; 688 1.1 christos } 689 1.1 christos 690 1.1 christos /* delete pending request */ 691 1.1 christos wmm_ac_del_req(wpa_s, 0); 692 1.1 christos 693 1.1 christos wpa_printf(MSG_DEBUG, 694 1.1 christos "ADDTS response status=%d tsid=%u up=%u direction=%u", 695 1.1 christos status_code, tsid, up, dir); 696 1.1 christos 697 1.1 christos if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) { 698 1.1 christos wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected"); 699 1.1 christos goto err_msg; 700 1.1 christos } 701 1.1 christos 702 1.1 christos replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir); 703 1.1 christos if (replace_tspecs < 0) 704 1.1 christos goto err_delts; 705 1.1 christos 706 1.1 christos wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs); 707 1.1 christos 708 1.1 christos /* when replacing tspecs - delete first */ 709 1.1 christos wmm_ac_del_ts(wpa_s, ac, replace_tspecs); 710 1.1 christos 711 1.1 christos /* Creating a new traffic stream */ 712 1.1 christos wpa_printf(MSG_DEBUG, 713 1.1 christos "WMM AC: adding a new TS with TSID=%u address="MACSTR 714 1.1 christos " medium time=%u access category=%d dir=%d ", 715 1.1 christos tsid, MAC2STR(sa), 716 1.1 christos le_to_host16(tspec->medium_time), ac, dir); 717 1.1 christos 718 1.1 christos if (wmm_ac_add_ts(wpa_s, sa, tspec)) 719 1.1 christos goto err_delts; 720 1.1 christos 721 1.1 christos return; 722 1.1 christos 723 1.1 christos err_delts: 724 1.1 christos /* ask the ap to delete the tspec */ 725 1.1 christos wmm_ac_send_delts(wpa_s, tspec, sa); 726 1.1 christos err_msg: 727 1.1 christos wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u", 728 1.1 christos tsid); 729 1.1 christos } 730 1.1 christos 731 1.1 christos 732 1.1 christos void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, 733 1.1 christos const u8 *sa, const u8 *data, size_t len) 734 1.1 christos { 735 1.1 christos u8 action; 736 1.1 christos u8 dialog_token; 737 1.1 christos u8 status_code; 738 1.1 christos struct ieee802_11_elems elems; 739 1.1 christos struct wmm_tspec_element *tspec; 740 1.1 christos 741 1.1 christos if (wpa_s->wmm_ac_assoc_info == NULL) { 742 1.1 christos wpa_printf(MSG_DEBUG, 743 1.1 christos "WMM AC: WMM AC is disabled, ignoring action frame"); 744 1.1 christos return; 745 1.1 christos } 746 1.1 christos 747 1.1 christos action = data[0]; 748 1.1 christos 749 1.1 christos if (action != WMM_ACTION_CODE_ADDTS_RESP && 750 1.1 christos action != WMM_ACTION_CODE_DELTS) { 751 1.1 christos wpa_printf(MSG_DEBUG, 752 1.1 christos "WMM AC: Unknown action (%d), ignoring action frame", 753 1.1 christos action); 754 1.1 christos return; 755 1.1 christos } 756 1.1 christos 757 1.1 christos /* WMM AC action frame */ 758 1.1.1.4 christos if (!ether_addr_equal(da, wpa_s->own_addr)) { 759 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR 760 1.1 christos " is other than ours, ignoring frame", MAC2STR(da)); 761 1.1 christos return; 762 1.1 christos } 763 1.1 christos 764 1.1.1.4 christos if (!ether_addr_equal(sa, wpa_s->bssid)) { 765 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR 766 1.1 christos " different other than our bssid", MAC2STR(da)); 767 1.1 christos return; 768 1.1 christos } 769 1.1 christos 770 1.1 christos if (len < 2 + sizeof(struct wmm_tspec_element)) { 771 1.1 christos wpa_printf(MSG_DEBUG, 772 1.1 christos "WMM AC: Short ADDTS response ignored (len=%lu)", 773 1.1 christos (unsigned long) len); 774 1.1 christos return; 775 1.1 christos } 776 1.1 christos 777 1.1 christos data++; 778 1.1 christos len--; 779 1.1 christos dialog_token = data[0]; 780 1.1 christos status_code = data[1]; 781 1.1 christos 782 1.1 christos if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) { 783 1.1 christos wpa_printf(MSG_DEBUG, 784 1.1 christos "WMM AC: Could not parse WMM AC action from " MACSTR, 785 1.1 christos MAC2STR(sa)); 786 1.1 christos return; 787 1.1 christos } 788 1.1 christos 789 1.1 christos /* the struct also contains the type and value, so decrease it */ 790 1.1 christos if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) { 791 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC"); 792 1.1 christos return; 793 1.1 christos } 794 1.1 christos 795 1.1 christos tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2); 796 1.1 christos 797 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR, 798 1.1 christos MAC2STR(sa)); 799 1.1 christos wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len); 800 1.1 christos 801 1.1 christos switch (action) { 802 1.1 christos case WMM_ACTION_CODE_ADDTS_RESP: 803 1.1 christos wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code, 804 1.1 christos tspec); 805 1.1 christos break; 806 1.1 christos case WMM_ACTION_CODE_DELTS: 807 1.1 christos wmm_ac_handle_delts(wpa_s, sa, tspec); 808 1.1 christos break; 809 1.1 christos default: 810 1.1 christos break; 811 1.1 christos } 812 1.1 christos } 813 1.1 christos 814 1.1 christos 815 1.1 christos static const char * get_ac_str(u8 ac) 816 1.1 christos { 817 1.1 christos switch (ac) { 818 1.1 christos case WMM_AC_BE: 819 1.1 christos return "BE"; 820 1.1 christos case WMM_AC_BK: 821 1.1 christos return "BK"; 822 1.1 christos case WMM_AC_VI: 823 1.1 christos return "VI"; 824 1.1 christos case WMM_AC_VO: 825 1.1 christos return "VO"; 826 1.1 christos default: 827 1.1 christos return "N/A"; 828 1.1 christos } 829 1.1 christos } 830 1.1 christos 831 1.1 christos 832 1.1 christos static const char * get_direction_str(u8 direction) 833 1.1 christos { 834 1.1 christos switch (direction) { 835 1.1 christos case WMM_AC_DIR_DOWNLINK: 836 1.1 christos return "Downlink"; 837 1.1 christos case WMM_AC_DIR_UPLINK: 838 1.1 christos return "Uplink"; 839 1.1 christos case WMM_AC_DIR_BIDIRECTIONAL: 840 1.1 christos return "Bi-directional"; 841 1.1 christos default: 842 1.1 christos return "N/A"; 843 1.1 christos } 844 1.1 christos } 845 1.1 christos 846 1.1 christos 847 1.1 christos int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) 848 1.1 christos { 849 1.1 christos struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info; 850 1.1 christos enum ts_dir_idx idx; 851 1.1 christos int pos = 0; 852 1.1 christos u8 ac, up; 853 1.1 christos 854 1.1 christos if (!assoc_info) { 855 1.1 christos return wpa_scnprintf(buf, buflen - pos, 856 1.1 christos "Not associated to a WMM AP, WMM AC is Disabled\n"); 857 1.1 christos } 858 1.1 christos 859 1.1 christos pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n"); 860 1.1 christos 861 1.1 christos for (ac = 0; ac < WMM_AC_NUM; ac++) { 862 1.1 christos int ts_count = 0; 863 1.1 christos 864 1.1 christos pos += wpa_scnprintf(buf + pos, buflen - pos, 865 1.1 christos "%s: acm=%d uapsd=%d\n", 866 1.1 christos get_ac_str(ac), 867 1.1 christos assoc_info->ac_params[ac].acm, 868 1.1 christos assoc_info->ac_params[ac].uapsd); 869 1.1 christos 870 1.1 christos for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 871 1.1 christos struct wmm_tspec_element *tspec; 872 1.1 christos u8 dir, tsid; 873 1.1 christos const char *dir_str; 874 1.1 christos 875 1.1 christos tspec = wpa_s->tspecs[ac][idx]; 876 1.1 christos if (!tspec) 877 1.1 christos continue; 878 1.1 christos 879 1.1 christos ts_count++; 880 1.1 christos 881 1.1 christos dir = wmm_ac_get_direction(tspec); 882 1.1 christos dir_str = get_direction_str(dir); 883 1.1 christos tsid = wmm_ac_get_tsid(tspec); 884 1.1 christos up = wmm_ac_get_user_priority(tspec); 885 1.1 christos 886 1.1 christos pos += wpa_scnprintf(buf + pos, buflen - pos, 887 1.1 christos "\tTSID=%u UP=%u\n" 888 1.1 christos "\tAddress = "MACSTR"\n" 889 1.1 christos "\tWMM AC dir = %s\n" 890 1.1 christos "\tTotal admitted time = %u\n\n", 891 1.1 christos tsid, up, 892 1.1 christos MAC2STR(wpa_s->bssid), 893 1.1 christos dir_str, 894 1.1 christos le_to_host16(tspec->medium_time)); 895 1.1 christos } 896 1.1 christos 897 1.1 christos if (!ts_count) { 898 1.1 christos pos += wpa_scnprintf(buf + pos, buflen - pos, 899 1.1 christos "\t(No Traffic Stream)\n\n"); 900 1.1 christos } 901 1.1 christos } 902 1.1 christos 903 1.1 christos return pos; 904 1.1 christos } 905 1.1 christos 906 1.1 christos 907 1.1 christos static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s) 908 1.1 christos { 909 1.1 christos int ac, dir, tspecs_count = 0; 910 1.1 christos 911 1.1 christos for (ac = 0; ac < WMM_AC_NUM; ac++) { 912 1.1 christos for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { 913 1.1 christos if (wpa_s->tspecs[ac][dir]) 914 1.1 christos tspecs_count++; 915 1.1 christos } 916 1.1 christos } 917 1.1 christos 918 1.1 christos return tspecs_count; 919 1.1 christos } 920 1.1 christos 921 1.1 christos 922 1.1 christos void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s) 923 1.1 christos { 924 1.1 christos int ac, dir, tspecs_count; 925 1.1 christos 926 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs"); 927 1.1 christos 928 1.1 christos if (!wpa_s->wmm_ac_assoc_info) 929 1.1 christos return; 930 1.1 christos 931 1.1 christos tspecs_count = wmm_ac_get_tspecs_count(wpa_s); 932 1.1 christos if (!tspecs_count) { 933 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs"); 934 1.1 christos return; 935 1.1 christos } 936 1.1 christos 937 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs"); 938 1.1 christos 939 1.1 christos wmm_ac_clear_saved_tspecs(wpa_s); 940 1.1 christos wpa_s->last_tspecs = os_calloc(tspecs_count, 941 1.1 christos sizeof(*wpa_s->last_tspecs)); 942 1.1 christos if (!wpa_s->last_tspecs) { 943 1.1 christos wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!"); 944 1.1 christos return; 945 1.1 christos } 946 1.1 christos 947 1.1 christos for (ac = 0; ac < WMM_AC_NUM; ac++) { 948 1.1 christos for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { 949 1.1 christos if (!wpa_s->tspecs[ac][dir]) 950 1.1 christos continue; 951 1.1 christos 952 1.1 christos wpa_s->last_tspecs[wpa_s->last_tspecs_count++] = 953 1.1 christos *wpa_s->tspecs[ac][dir]; 954 1.1 christos } 955 1.1 christos } 956 1.1 christos 957 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs", 958 1.1 christos wpa_s->last_tspecs_count); 959 1.1 christos } 960 1.1 christos 961 1.1 christos 962 1.1 christos void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s) 963 1.1 christos { 964 1.1 christos if (wpa_s->last_tspecs) { 965 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs"); 966 1.1 christos os_free(wpa_s->last_tspecs); 967 1.1 christos wpa_s->last_tspecs = NULL; 968 1.1 christos wpa_s->last_tspecs_count = 0; 969 1.1 christos } 970 1.1 christos } 971 1.1 christos 972 1.1 christos 973 1.1 christos int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s) 974 1.1 christos { 975 1.1 christos unsigned int i; 976 1.1 christos 977 1.1 christos if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count) 978 1.1 christos return 0; 979 1.1 christos 980 1.1 christos wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs", 981 1.1 christos wpa_s->last_tspecs_count); 982 1.1 christos 983 1.1 christos for (i = 0; i < wpa_s->last_tspecs_count; i++) 984 1.1 christos wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]); 985 1.1 christos 986 1.1 christos return 0; 987 1.1 christos } 988