X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=wpa_supplicant%2Fwmm_ac.c;h=5625d36638b5c33dc60e1517b2f0a91c8365e9ee;hb=05aed438cd65ed2ab11856361f66f1f7d8896548;hp=13bf7a36ce136d9eb10be1c0a56745b9ff65832d;hpb=a0413b17340a18ed905edc804ee2aab09b2c3186;p=mech_eap.git diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c index 13bf7a3..5625d36 100644 --- a/wpa_supplicant/wmm_ac.c +++ b/wpa_supplicant/wmm_ac.c @@ -10,11 +10,412 @@ #include "includes.h" #include "utils/common.h" +#include "utils/list.h" +#include "utils/eloop.h" #include "common/ieee802_11_common.h" #include "wpa_supplicant_i.h" +#include "bss.h" #include "driver_i.h" #include "wmm_ac.h" +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx); + +static const enum wmm_ac up_to_ac[8] = { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO +}; + + +static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 1) & 0x0f; +} + + +static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 5) & 0x03; +} + + +static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[1] >> 3) & 0x07; +} + + +static u8 wmm_ac_direction_to_idx(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_UPLINK: + return TS_DIR_IDX_UPLINK; + case WMM_AC_DIR_DOWNLINK: + return TS_DIR_IDX_DOWNLINK; + case WMM_AC_DIR_BIDIRECTIONAL: + return TS_DIR_IDX_BIDI; + default: + wpa_printf(MSG_ERROR, "Invalid direction: %d", direction); + return WMM_AC_DIR_UPLINK; + } +} + + +static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, + const struct wmm_tspec_element *tspec) +{ + struct wmm_tspec_element *_tspec; + int ret; + u16 admitted_time = le_to_host16(tspec->medium_time); + u8 up = wmm_ac_get_user_priority(tspec); + u8 ac = up_to_ac[up]; + u8 dir = wmm_ac_get_direction(tspec); + u8 tsid = wmm_ac_get_tsid(tspec); + enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir); + + /* should have been verified before, but double-check here */ + if (wpa_s->tspecs[ac][idx]) { + wpa_printf(MSG_ERROR, + "WMM AC: tspec (ac=%d, dir=%d) already exists!", + ac, dir); + return -1; + } + + /* copy tspec */ + _tspec = os_malloc(sizeof(*_tspec)); + if (!_tspec) + return -1; + + /* store the admitted TSPEC */ + os_memcpy(_tspec, tspec, sizeof(*_tspec)); + + if (dir != WMM_AC_DIR_DOWNLINK) { + ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time); + wpa_printf(MSG_DEBUG, + "WMM AC: Add TS: addr=" MACSTR + " TSID=%u admitted time=%u, ret=%d", + MAC2STR(addr), tsid, admitted_time, ret); + if (ret < 0) { + os_free(_tspec); + return -1; + } + } + + wpa_s->tspecs[ac][idx] = _tspec; + + wpa_printf(MSG_DEBUG, "Traffic stream was created successfully"); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED + "tsid=%d addr=" MACSTR " admitted_time=%d", + tsid, MAC2STR(addr), admitted_time); + + return 0; +} + + +static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac, + enum ts_dir_idx dir) +{ + struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir]; + u8 tsid; + + if (!tspec) + return; + + tsid = wmm_ac_get_tsid(tspec); + wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid); + + /* update the driver in case of uplink/bidi */ + if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK) + wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED + "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid)); + + os_free(wpa_s->tspecs[ac][dir]); + wpa_s->tspecs[ac][dir] = NULL; +} + + +static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + + if (!req) + return; + + if (failed) + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED + "tsid=%u", wmm_ac_get_tsid(&req->tspec)); + + eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req); + wpa_s->addts_request = NULL; + os_free(req); +} + + +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wmm_ac_addts_request *addts_req = timeout_ctx; + + wpa_printf(MSG_DEBUG, + "Timeout getting ADDTS response (tsid=%d up=%d)", + wmm_ac_get_tsid(&addts_req->tspec), + wmm_ac_get_user_priority(&addts_req->tspec)); + + wmm_ac_del_req(wpa_s, 1); +} + + +static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s, + const struct wmm_ac_addts_request *req) +{ + struct wpabuf *buf; + int ret; + + wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR, + MAC2STR(req->address)); + + /* category + action code + dialog token + status + sizeof(tspec) */ + buf = wpabuf_alloc(4 + sizeof(req->tspec)); + if (!buf) { + wpa_printf(MSG_ERROR, "WMM AC: Allocation error"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_WMM); + wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ); + wpabuf_put_u8(buf, req->dialog_token); + wpabuf_put_u8(buf, 0); /* status code */ + wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec)); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret) { + wpa_printf(MSG_WARNING, + "WMM AC: Failed to send ADDTS Request"); + } + + wpabuf_free(buf); + return ret; +} + + +static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s, + const struct wmm_tspec_element *tspec, + const u8 *address) +{ + struct wpabuf *buf; + int ret; + + /* category + action code + dialog token + status + sizeof(tspec) */ + buf = wpabuf_alloc(4 + sizeof(*tspec)); + if (!buf) + return -1; + + wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address)); + + /* category + action code + dialog token + status + sizeof(tspec) */ + wpabuf_put_u8(buf, WLAN_ACTION_WMM); + wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS); + wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */ + wpabuf_put_u8(buf, 0); /* Status Code (not used) */ + wpabuf_put_data(buf, tspec, sizeof(*tspec)); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret) + wpa_printf(MSG_WARNING, "Failed to send DELTS frame"); + + wpabuf_free(buf); + return ret; +} + + +/* return the AC using the given TSPEC tid */ +static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid, + enum ts_dir_idx *dir) +{ + int ac; + enum ts_dir_idx idx; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (wpa_s->tspecs[ac][idx] && + wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) { + if (dir) + *dir = idx; + return ac; + } + } + } + + return -1; +} + + +static struct wmm_ac_addts_request * +wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s, + const struct wmm_ac_ts_setup_params *params, + const u8 *address) +{ + struct wmm_ac_addts_request *addts_req; + struct wmm_tspec_element *tspec; + u8 ac = up_to_ac[params->user_priority]; + u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd; + + addts_req = os_zalloc(sizeof(*addts_req)); + if (!addts_req) + return NULL; + + tspec = &addts_req->tspec; + os_memcpy(addts_req->address, address, ETH_ALEN); + + /* The dialog token cannot be zero */ + if (++wpa_s->wmm_ac_last_dialog_token == 0) + wpa_s->wmm_ac_last_dialog_token++; + + addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token; + tspec->eid = WLAN_EID_VENDOR_SPECIFIC; + tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */ + tspec->oui[0] = 0x00; + tspec->oui[1] = 0x50; + tspec->oui[2] = 0xf2; + tspec->oui_type = WMM_OUI_TYPE; + tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT; + tspec->version = WMM_VERSION; + + tspec->ts_info[0] = params->tsid << 1; + tspec->ts_info[0] |= params->direction << 5; + tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7; + tspec->ts_info[1] = uapsd << 2; + tspec->ts_info[1] |= params->user_priority << 3; + tspec->ts_info[2] = 0; + + tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size); + if (params->fixed_nominal_msdu) + tspec->nominal_msdu_size |= + host_to_le16(WMM_AC_FIXED_MSDU_SIZE); + + tspec->mean_data_rate = host_to_le32(params->mean_data_rate); + tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate); + tspec->surplus_bandwidth_allowance = + host_to_le16(params->surplus_bandwidth_allowance); + + return addts_req; +} + + +static int param_in_range(const char *name, long value, + long min_val, long max_val) +{ + if (value < min_val || (max_val >= 0 && value > max_val)) { + wpa_printf(MSG_DEBUG, + "WMM AC: param %s (%ld) is out of range (%ld-%ld)", + name, value, min_val, max_val); + return 0; + } + + return 1; +} + + +static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s, + u8 tsid, u8 ac, u8 dir) +{ + enum ts_dir_idx idx; + int cur_ac, existing_ts = 0, replace_ts = 0; + + cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (cur_ac >= 0) { + if (cur_ac != ac) { + wpa_printf(MSG_DEBUG, + "WMM AC: TSID %i already exists on different ac (%d)", + tsid, cur_ac); + return -1; + } + + /* same tsid - this tspec will replace the current one */ + replace_ts |= BIT(idx); + } + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (wpa_s->tspecs[ac][idx]) + existing_ts |= BIT(idx); + } + + switch (dir) { + case WMM_AC_DIR_UPLINK: + /* replace existing uplink/bidi tspecs */ + replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) | + BIT(TS_DIR_IDX_BIDI)); + break; + case WMM_AC_DIR_DOWNLINK: + /* replace existing downlink/bidi tspecs */ + replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) | + BIT(TS_DIR_IDX_BIDI)); + break; + case WMM_AC_DIR_BIDIRECTIONAL: + /* replace all existing tspecs */ + replace_ts |= existing_ts; + break; + default: + return -1; + } + + return replace_ts; +} + + +static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s, + const struct wmm_ac_ts_setup_params *params) +{ + enum wmm_ac req_ac; + +#define PARAM_IN_RANGE(field, min_value, max_value) \ + param_in_range(#field, params->field, min_value, max_value) + + if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) || + !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) || + !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) || + !PARAM_IN_RANGE(mean_data_rate, 1, -1) || + !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) || + !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY, + -1)) + return 0; +#undef PARAM_IN_RANGE + + if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK || + params->direction == WMM_TSPEC_DIRECTION_DOWNLINK || + params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) { + wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d", + params->direction); + return 0; + } + + req_ac = up_to_ac[params->user_priority]; + + /* Requested accesss category must have acm */ + if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) { + wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac); + return 0; + } + + if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac, + params->direction) < 0) + return 0; + + return 1; +} + static struct wmm_ac_assoc_data * wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, @@ -26,7 +427,7 @@ wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, int i; /* Parsing WMM Parameter Element */ - if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) != ParseOK) { + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies"); return NULL; } @@ -37,7 +438,7 @@ wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, } if (elems.wmm_len != sizeof(*wmm_params)) { - wpa_printf(MSG_WARNING, "WMM AC: Invalid WMM ie length"); + wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length"); return NULL; } @@ -83,6 +484,10 @@ static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, return -1; } + os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs)); + wpa_s->wmm_ac_last_dialog_token = 0; + wpa_s->addts_request = NULL; + assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len); if (!assoc_data) return -1; @@ -100,8 +505,29 @@ static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, } +static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap) +{ + enum ts_dir_idx idx; + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (!(dir_bitmap & BIT(idx))) + continue; + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + } +} + + static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) { + int i; + + for (i = 0; i < WMM_AC_NUM; i++) + wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); + + /* delete pending add_ts requset */ + wmm_ac_del_req(wpa_s, 1); + os_free(wpa_s->wmm_ac_assoc_info); wpa_s->wmm_ac_assoc_info = NULL; } @@ -126,3 +552,444 @@ void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s) wmm_ac_deinit(wpa_s); wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled"); } + + +int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) +{ + struct wmm_tspec_element tspec; + int ac; + enum ts_dir_idx dir; + + if (!wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_DEBUG, + "WMM AC: Failed to delete TS, WMM AC is disabled"); + return -1; + } + + ac = wmm_ac_find_tsid(wpa_s, tsid, &dir); + if (ac < 0) { + wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist"); + return -1; + } + + tspec = *wpa_s->tspecs[ac][dir]; + + wmm_ac_del_ts_idx(wpa_s, ac, dir); + + wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid); + + return 0; +} + + +int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, + struct wmm_ac_ts_setup_params *params) +{ + struct wmm_ac_addts_request *addts_req; + + if (!wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_DEBUG, + "WMM AC: Cannot add TS - missing assoc data"); + return -1; + } + + if (wpa_s->addts_request) { + wpa_printf(MSG_DEBUG, + "WMM AC: can't add TS - ADDTS request is already pending"); + return -1; + } + + /* + * we can setup downlink TS even without driver support. + * however, we need driver support for the other directions. + */ + if (params->direction != WMM_AC_DIR_DOWNLINK && + !wpa_s->wmm_ac_supported) { + wpa_printf(MSG_DEBUG, + "Cannot set uplink/bidi TS without driver support"); + return -1; + } + + if (!wmm_ac_ts_req_is_valid(wpa_s, params)) + return -1; + + wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR + " tsid=%u user priority=%u direction=%d)", + MAC2STR(wpa_s->bssid), params->tsid, + params->user_priority, params->direction); + + addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid); + if (!addts_req) + return -1; + + if (wmm_ac_send_addts_request(wpa_s, addts_req)) + goto err; + + /* save as pending and set ADDTS resp timeout to 1 second */ + wpa_s->addts_request = addts_req; + eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout, + wpa_s, addts_req); + return 0; +err: + os_free(addts_req); + return -1; +} + + +static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa, + const struct wmm_tspec_element *tspec) +{ + int ac; + u8 tsid; + enum ts_dir_idx idx; + + tsid = wmm_ac_get_tsid(tspec); + + wpa_printf(MSG_DEBUG, + "WMM AC: DELTS frame has been received TSID=%u addr=" + MACSTR, tsid, MAC2STR(sa)); + + ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (ac < 0) { + wpa_printf(MSG_DEBUG, + "WMM AC: Ignoring DELTS frame - TSID does not exist"); + return; + } + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + + wpa_printf(MSG_DEBUG, + "TS was deleted successfully (tsid=%u address=" MACSTR ")", + tsid, MAC2STR(sa)); +} + + +static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 resp_dialog_token, const u8 status_code, + const struct wmm_tspec_element *tspec) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + u8 ac, tsid, up, dir; + int replace_tspecs; + + tsid = wmm_ac_get_tsid(tspec); + dir = wmm_ac_get_direction(tspec); + up = wmm_ac_get_user_priority(tspec); + ac = up_to_ac[up]; + + /* make sure we have a matching addts request */ + if (!req || req->dialog_token != resp_dialog_token) { + wpa_printf(MSG_DEBUG, + "WMM AC: no req with dialog=%u, ignoring frame", + resp_dialog_token); + return; + } + + /* make sure the params are the same */ + if (os_memcmp(req->address, sa, ETH_ALEN) != 0 || + tsid != wmm_ac_get_tsid(&req->tspec) || + up != wmm_ac_get_user_priority(&req->tspec) || + dir != wmm_ac_get_direction(&req->tspec)) { + wpa_printf(MSG_DEBUG, + "WMM AC: ADDTS params do not match, ignoring frame"); + return; + } + + /* delete pending request */ + wmm_ac_del_req(wpa_s, 0); + + wpa_printf(MSG_DEBUG, + "ADDTS response status=%d tsid=%u up=%u direction=%u", + status_code, tsid, up, dir); + + if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) { + wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected"); + goto err_msg; + } + + replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir); + if (replace_tspecs < 0) + goto err_delts; + + wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs); + + /* when replacing tspecs - delete first */ + wmm_ac_del_ts(wpa_s, ac, replace_tspecs); + + /* Creating a new traffic stream */ + wpa_printf(MSG_DEBUG, + "WMM AC: adding a new TS with TSID=%u address="MACSTR + " medium time=%u access category=%d dir=%d ", + tsid, MAC2STR(sa), + le_to_host16(tspec->medium_time), ac, dir); + + if (wmm_ac_add_ts(wpa_s, sa, tspec)) + goto err_delts; + + return; + +err_delts: + /* ask the ap to delete the tspec */ + wmm_ac_send_delts(wpa_s, tspec, sa); +err_msg: + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u", + tsid); +} + + +void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + u8 action; + u8 dialog_token; + u8 status_code; + struct ieee802_11_elems elems; + struct wmm_tspec_element *tspec; + + if (wpa_s->wmm_ac_assoc_info == NULL) { + wpa_printf(MSG_DEBUG, + "WMM AC: WMM AC is disabled, ignoring action frame"); + return; + } + + action = data[0]; + + if (action != WMM_ACTION_CODE_ADDTS_RESP && + action != WMM_ACTION_CODE_DELTS) { + wpa_printf(MSG_DEBUG, + "WMM AC: Unknown action (%d), ignoring action frame", + action); + return; + } + + /* WMM AC action frame */ + if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR + " is other than ours, ignoring frame", MAC2STR(da)); + return; + } + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR + " different other than our bssid", MAC2STR(da)); + return; + } + + if (len < 2 + sizeof(struct wmm_tspec_element)) { + wpa_printf(MSG_DEBUG, + "WMM AC: Short ADDTS response ignored (len=%lu)", + (unsigned long) len); + return; + } + + data++; + len--; + dialog_token = data[0]; + status_code = data[1]; + + if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) { + wpa_printf(MSG_DEBUG, + "WMM AC: Could not parse WMM AC action from " MACSTR, + MAC2STR(sa)); + return; + } + + /* the struct also contains the type and value, so decrease it */ + if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) { + wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC"); + return; + } + + tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2); + + wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len); + + switch (action) { + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code, + tspec); + break; + case WMM_ACTION_CODE_DELTS: + wmm_ac_handle_delts(wpa_s, sa, tspec); + break; + default: + break; + } +} + + +static const char * get_ac_str(u8 ac) +{ + switch (ac) { + case WMM_AC_BE: + return "BE"; + case WMM_AC_BK: + return "BK"; + case WMM_AC_VI: + return "VI"; + case WMM_AC_VO: + return "VO"; + default: + return "N/A"; + } +} + + +static const char * get_direction_str(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_DOWNLINK: + return "Downlink"; + case WMM_AC_DIR_UPLINK: + return "Uplink"; + case WMM_AC_DIR_BIDIRECTIONAL: + return "Bi-directional"; + default: + return "N/A"; + } +} + + +int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info; + enum ts_dir_idx idx; + int pos = 0; + u8 ac, up; + + if (!assoc_info) { + return wpa_scnprintf(buf, buflen - pos, + "Not associated to a WMM AP, WMM AC is Disabled\n"); + } + + pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n"); + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + int ts_count = 0; + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "%s: acm=%d uapsd=%d\n", + get_ac_str(ac), + assoc_info->ac_params[ac].acm, + assoc_info->ac_params[ac].uapsd); + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + struct wmm_tspec_element *tspec; + u8 dir, tsid; + const char *dir_str; + + tspec = wpa_s->tspecs[ac][idx]; + if (!tspec) + continue; + + ts_count++; + + dir = wmm_ac_get_direction(tspec); + dir_str = get_direction_str(dir); + tsid = wmm_ac_get_tsid(tspec); + up = wmm_ac_get_user_priority(tspec); + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\tTSID=%u UP=%u\n" + "\tAddress = "MACSTR"\n" + "\tWMM AC dir = %s\n" + "\tTotal admitted time = %u\n\n", + tsid, up, + MAC2STR(wpa_s->bssid), + dir_str, + le_to_host16(tspec->medium_time)); + } + + if (!ts_count) { + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\t(No Traffic Stream)\n\n"); + } + } + + return pos; +} + + +static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count = 0; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (wpa_s->tspecs[ac][dir]) + tspecs_count++; + } + } + + return tspecs_count; +} + + +void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count; + + wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs"); + + if (!wpa_s->wmm_ac_assoc_info) + return; + + tspecs_count = wmm_ac_get_tspecs_count(wpa_s); + if (!tspecs_count) { + wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs"); + + wmm_ac_clear_saved_tspecs(wpa_s); + wpa_s->last_tspecs = os_calloc(tspecs_count, + sizeof(*wpa_s->last_tspecs)); + if (!wpa_s->last_tspecs) { + wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!"); + return; + } + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (!wpa_s->tspecs[ac][dir]) + continue; + + wpa_s->last_tspecs[wpa_s->last_tspecs_count++] = + *wpa_s->tspecs[ac][dir]; + } + } + + wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs", + wpa_s->last_tspecs_count); +} + + +void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->last_tspecs) { + wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs"); + os_free(wpa_s->last_tspecs); + wpa_s->last_tspecs = NULL; + wpa_s->last_tspecs_count = 0; + } +} + + +int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count) + return 0; + + wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs", + wpa_s->last_tspecs_count); + + for (i = 0; i < wpa_s->last_tspecs_count; i++) + wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]); + + return 0; +}