Commit | Line | Data |
---|---|---|
533a23a1 TK |
1 | /**************************************************************************** |
2 | * | |
3 | * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. All rights reserved | |
4 | * | |
5 | ****************************************************************************/ | |
6 | ||
7 | #include "cac.h" | |
8 | ||
9 | static struct cac_tspec *tspec_list; | |
10 | static int tspec_list_next_id; | |
11 | static u8 dialog_token_next; | |
12 | ||
13 | /* Define the meta-data info for tspec_fields */ | |
14 | static struct tspec_field tspec_fields[] = { | |
15 | { "traffic_type", 0, 1, 0x1, 0 }, | |
16 | { "tsid", 0, 1, 0xF, 1 }, | |
17 | { "direction", 0, 1, 0x3, 5 }, | |
18 | { "access_policy", 0, 1, 0x3, 7 }, /* WMM - always set to 1 */ | |
19 | { "aggregation", 0, 1, 0x1, 9 }, /* WMM - not supported */ | |
20 | { "psb", 0, 1, 0x1, 10 }, | |
21 | { "user_priority", 0, 1, 0x7, 11 }, | |
22 | { "tsinfo_ack_policy", 0, 1, 0x3, 14 }, /* WMM - not supported */ | |
23 | { "schedule", 0, 1, 0x1, 16 }, /* WMM - not supported */ | |
24 | { "nominal_msdu_size", 0, 0, 2, OFFSETOF(nominal_msdu_size) }, | |
25 | { "max_msdu_size", 0, 0, 2, OFFSETOF(maximum_msdu_size) }, | |
26 | { "min_service_interval", 0, 0, 4, OFFSETOF(minimum_service_interval) }, | |
27 | { "max_service_interval", 0, 0, 4, OFFSETOF(maximum_service_interval) }, | |
28 | { "inactivity_interval", 0, 0, 4, OFFSETOF(inactivity_interval) }, | |
29 | { "suspension_interval", 0, 0, 4, OFFSETOF(suspension_interval) }, | |
30 | { "service_start_time", 0, 0, 4, OFFSETOF(service_start_time) }, | |
31 | { "min_data_rate", 0, 0, 4, OFFSETOF(minimum_data_rate) }, | |
32 | { "mean_data_rate", 0, 0, 4, OFFSETOF(mean_data_rate) }, | |
33 | { "peak_data_rate", 0, 0, 4, OFFSETOF(peak_data_rate) }, | |
34 | { "max_burst_size", 0, 0, 4, OFFSETOF(maximum_burst_size) }, | |
35 | { "delay_bound", 0, 0, 4, OFFSETOF(delay_bound) }, | |
36 | { "min_phy_rate", 0, 0, 4, OFFSETOF(minimum_phy_rate) }, | |
37 | { "surplus_bw_allowance", 0, 0, 2, | |
38 | OFFSETOF(surplus_bandwidth_allowance) }, | |
39 | { "medium_time", 0, 0, 2, OFFSETOF(medium_time) }, | |
40 | }; | |
41 | ||
42 | /* Define the OUI type data for the corresponding IE's */ | |
43 | static const u8 TSRS_OUI_TYPE[] = { 0x00, 0x40, 0x96, 0x08 }; | |
44 | static const u8 EBW_OUI_TYPE[] = { 0x00, 0x40, 0x96, 0x0F }; | |
45 | ||
46 | static const int NUM_TSPEC_FIELDS = sizeof(tspec_fields) / sizeof(struct tspec_field); | |
47 | static u32 previous_msdu_lifetime = MAX_TRANSMIT_MSDU_LIFETIME_NOT_VALID; | |
48 | static u8 ccx_status = BSS_CCX_DISABLED; | |
49 | ||
50 | static void cac_set_ric_ie(struct slsi_dev *sdev, struct net_device *netdev); | |
51 | static int cac_get_rde_tspec_ie(struct slsi_dev *sdev, u8 *assoc_rsp_ie, int assoc_rsp_ie_len, const u8 **tspec_ie_arr); | |
533a23a1 TK |
52 | |
53 | /* Name: find_tspec_entry | |
54 | * Desc: Finds a tspec entry in the list of tspecs (tspec_list) | |
55 | * according to tspec id and status (accepted or not accepted) | |
56 | * id: the tspec id | |
57 | * accepted: 1 : accepted by AP, 0: new or rejected by AP | |
58 | * return: pointer to the tspec struct or NULL if a tspec doesn't exist | |
59 | */ | |
60 | static struct cac_tspec *find_tspec_entry(int id, int accepted) | |
61 | { | |
62 | struct cac_tspec *itr; | |
63 | ||
64 | itr = tspec_list; | |
65 | while (itr != NULL) { | |
66 | if ((itr->id == id) && (itr->accepted == accepted)) | |
67 | break; | |
68 | itr = itr->next; | |
69 | } | |
70 | return itr; | |
71 | } | |
72 | ||
73 | /* Name: cac_query_tspec_field | |
74 | * Desc: Get the value of a tspec's field. | |
75 | * sdev: pointer to the slsi_dev struct | |
76 | * entry: pointer to the tspec | |
77 | * field: the field name | |
78 | * value: poinet to the field value | |
79 | * return: 0 (success), -1 (failure) | |
80 | */ | |
81 | static int cac_query_tspec_field(struct slsi_dev *sdev, struct cac_tspec *entry, const char *field, u32 *value) | |
82 | { | |
83 | int i; | |
84 | u32 tsinfo; | |
85 | u8 mask; | |
86 | u8 *pos; | |
87 | ||
88 | if ((entry == NULL) || (field == NULL) || (value == NULL)) | |
89 | return -1; | |
90 | ||
91 | for (i = 0; i < NUM_TSPEC_FIELDS; i++) | |
92 | if (strcasecmp(field, tspec_fields[i].name) == 0) | |
93 | break; | |
94 | if (i >= NUM_TSPEC_FIELDS) { | |
95 | SLSI_ERR(sdev, "CAC: Invalid TSPEC config field\n"); | |
96 | return -1; | |
97 | } | |
98 | if (tspec_fields[i].is_tsinfo_field) { | |
99 | mask = tspec_fields[i].size; | |
100 | tsinfo = CAC_GET_LE24(&entry->tspec.ts_info[0]) & TSINFO_MASK; | |
101 | *value = (tsinfo >> tspec_fields[i].offset) & mask; | |
102 | } else { | |
103 | pos = (u8 *)(&entry->tspec) + tspec_fields[i].offset; | |
104 | if (tspec_fields[i].size == 1) | |
105 | *value = (*pos & 0xFF); | |
106 | else if (tspec_fields[i].size == 2) | |
107 | *value = CAC_GET_LE16(pos); | |
108 | else | |
109 | *value = CAC_GET_LE32(pos); | |
110 | } | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | /* Name: get_netdev_for_station | |
116 | * Desc: Get the pointer to net_device struct with vif_type == FAPI_VIFTYPE_STATION | |
117 | * sdev: pointer to the slsi_dev struct | |
118 | * return: pointer to the net_device struct or NULL if the it doesn't exist | |
119 | */ | |
120 | static struct net_device *get_netdev_for_station(struct slsi_dev *sdev) | |
121 | { | |
122 | struct net_device *dev; | |
123 | struct netdev_vif *ndev_vif; | |
124 | s32 vif; | |
125 | ||
126 | for (vif = 1; vif <= CONFIG_SCSC_WLAN_MAX_INTERFACES; vif++) { | |
127 | dev = slsi_get_netdev_locked(sdev, vif); | |
128 | if (!dev) | |
129 | continue; | |
130 | ndev_vif = netdev_priv(dev); | |
131 | if (!ndev_vif) | |
132 | continue; | |
133 | if (ndev_vif->vif_type == FAPI_VIFTYPE_STATION && | |
134 | ndev_vif->iftype == NL80211_IFTYPE_STATION) | |
135 | return dev; | |
136 | } | |
137 | return NULL; | |
138 | } | |
139 | ||
140 | /* Name: add_ebw_ie | |
141 | * Desc: Add ebw ie | |
142 | * buf: pointer to buf that the ie is going to added | |
143 | * buf_len: the byte length of the ie | |
144 | * tsid: tspec id | |
145 | * return: length of bytes that were added | |
146 | */ | |
147 | static int add_ebw_ie(u8 *buf, size_t buf_len, u8 tsid) | |
148 | { | |
149 | u8 *pos; | |
150 | ||
151 | if ((buf == NULL) || (buf_len < 8)) | |
152 | return -1; | |
153 | ||
154 | pos = buf; | |
155 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; /* element id */ | |
156 | *pos++ = 6; /* length */ | |
157 | memcpy(pos, EBW_OUI_TYPE, sizeof(EBW_OUI_TYPE)); | |
158 | pos += sizeof(EBW_OUI_TYPE); | |
159 | *pos++ = tsid; | |
160 | *pos++ = 0; | |
161 | ||
162 | return pos - buf; | |
163 | } | |
164 | ||
165 | /* Name: add_tsrs_ie | |
166 | * Desc: Add tsrs_ie | |
167 | * buf: pointer to buf that the ie is going to added | |
168 | * buf_len: the byte length of the ie | |
169 | * tsid: tspec id | |
170 | * rates: list of rates that are supported | |
171 | * num_rates: number of rates that are supported | |
172 | * return: length of bytes that were added | |
173 | */ | |
174 | static int add_tsrs_ie(u8 *buf, size_t buf_len, u8 tsid, | |
175 | u8 rates[CCX_MAX_NUM_RATES], int num_rates) | |
176 | { | |
177 | u8 *pos; | |
178 | size_t ie_len = (size_t)(7 + num_rates); | |
179 | int i; | |
180 | ||
181 | if ((buf == NULL) || (buf_len < ie_len) || (rates == NULL) || | |
182 | (num_rates > CCX_MAX_NUM_RATES)) | |
183 | return -1; | |
184 | ||
185 | pos = buf; | |
186 | memset(pos, 0, ie_len); | |
187 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; /* element id */ | |
188 | *pos++ = ie_len - 2; /* length */ | |
189 | memcpy(pos, TSRS_OUI_TYPE, sizeof(TSRS_OUI_TYPE)); | |
190 | pos += sizeof(TSRS_OUI_TYPE); | |
191 | *pos++ = tsid; | |
192 | for (i = 0; i < num_rates; i++) | |
193 | *pos++ = rates[i]; | |
194 | ||
195 | return pos - buf; | |
196 | } | |
197 | ||
198 | /* Name: bss_get_ie | |
199 | * Desc: Get the buffer of an IE that is included in a bss | |
200 | * bss: pointer to the cfg80211_bss struct | |
201 | * ie: the IE id that is going to be extracted | |
202 | * return: pointer to the start of the IE buffer | |
203 | */ | |
204 | static const u8 *bss_get_ie(struct cfg80211_bss *bss, u8 ie) | |
205 | { | |
206 | const u8 *pos; | |
207 | u8 ies_len, ies_cur_len; | |
208 | ||
209 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) | |
210 | pos = (const u8 *)(bss->ies); | |
211 | ies_len = (u8)bss->ies->len; | |
212 | #else | |
213 | pos = (const u8 *)(bss->information_elements); | |
214 | ies_len = (u8)bss->len_information_elements; | |
215 | #endif | |
216 | ies_cur_len = 1; | |
217 | ||
218 | while (ies_cur_len <= ies_len) { | |
219 | if (pos[0] == ie) | |
220 | return pos; | |
221 | ||
222 | pos += 2 + pos[1]; | |
223 | ies_cur_len++; | |
224 | } | |
225 | ||
226 | return NULL; | |
227 | } | |
228 | ||
229 | /* Name: bss_get_bit_rates | |
230 | * Desc: Get the buffer of an IE that is included in a bss | |
231 | * bss: pointer to the cfg80211_bss struct | |
232 | * rates: the rates that are supported | |
233 | * return: 0 (succes), -1 (failure) | |
234 | */ | |
235 | static int bss_get_bit_rates(struct cfg80211_bss *bss, u8 **rates) | |
236 | { | |
237 | const u8 *ie, *ie2; | |
238 | int i, j; | |
239 | unsigned int len; | |
240 | u8 *r; | |
241 | ||
242 | ie = bss_get_ie(bss, WLAN_EID_SUPP_RATES); | |
243 | ie2 = bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); | |
244 | ||
245 | len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0); | |
246 | ||
247 | if (!len) | |
248 | return -1; | |
249 | ||
250 | r = kmalloc(len, GFP_KERNEL); | |
251 | if (!r) | |
252 | return -1; | |
253 | ||
254 | for (i = 0; ie && i < ie[1]; i++) | |
255 | r[i] = ie[i + 2] & 0x7f; | |
256 | ||
257 | for (j = 0; ie2 && j < ie2[1]; j++) | |
258 | r[i + j] = ie2[j + 2] & 0x7f; | |
259 | ||
260 | *rates = r; | |
261 | return len; | |
262 | } | |
263 | ||
264 | /* Name: cac_send_addts | |
265 | * Desc: Build and send the ADDTS action frame | |
266 | * sdev: pointer to the slsi_dev struct | |
267 | * id: the tspec id that is going to be included in the ADDTS action frame | |
268 | * ebw: 1 (add ebw IE), 0 (don't add ebw IE) | |
269 | * return: 0 (succes), -1 (failure) | |
270 | */ | |
271 | static int cac_send_addts(struct slsi_dev *sdev, int id, int ebw) | |
272 | { | |
273 | struct action_addts_req *req; | |
274 | size_t extra_ie_len = 50; | |
275 | int ie_len = 0; | |
276 | size_t req_len; | |
277 | struct cac_tspec *entry; | |
278 | u8 tsid, i; | |
279 | u8 *rates; | |
280 | u8 rate = 0; | |
281 | u8 *pos; | |
282 | int num_rates; | |
283 | struct netdev_vif *ndev_vif; | |
284 | struct net_device *netdev; | |
285 | u16 host_tag = slsi_tx_mgmt_host_tag(sdev); | |
286 | struct ieee80211_hdr *hdr; | |
287 | u8 *buf = NULL; | |
288 | u8 *bssid; | |
289 | u8 r = 0; | |
290 | ||
291 | entry = find_tspec_entry(id, 0); | |
292 | if (entry == NULL) { | |
293 | SLSI_ERR(sdev, "CAC-ADDTS: Invalid TSPEC ID\n"); | |
294 | return -1; | |
295 | } | |
296 | ||
297 | SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex); | |
298 | netdev = get_netdev_for_station(sdev); | |
299 | if (netdev == NULL) { | |
300 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
301 | return -1; | |
302 | } | |
303 | ndev_vif = netdev_priv(netdev); | |
304 | if ((ndev_vif == NULL) || (ndev_vif->sta.sta_bss == NULL)) { | |
305 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
306 | return -1; | |
307 | } | |
308 | SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); | |
309 | ||
310 | if ((!ndev_vif->activated) || (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) || | |
311 | (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) { | |
312 | SLSI_ERR(sdev, "CAC-ADDTS: Not connected, can't send ADDTS\n"); | |
313 | r = -1; | |
314 | goto exit; | |
315 | } | |
316 | bssid = ndev_vif->sta.sta_bss->bssid; | |
317 | if (entry->accepted) { | |
318 | SLSI_ERR(sdev, "CAC-ADDTS: TSPEC already accepted\n"); | |
319 | r = -1; | |
320 | goto exit; | |
321 | } | |
322 | ||
323 | buf = kmalloc(IEEE80211_HEADER_SIZE + sizeof(*req) + extra_ie_len, GFP_KERNEL); | |
324 | if (buf == NULL) { | |
325 | SLSI_ERR(sdev, "CAC-ADDTS: Failed to allocate ADDTS request\n"); | |
326 | r = -1; | |
327 | goto exit; | |
328 | } | |
329 | ||
330 | hdr = (struct ieee80211_hdr *)buf; | |
331 | hdr->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, IEEE80211_STYPE_ACTION); | |
332 | SLSI_ETHER_COPY(hdr->addr1, bssid); | |
333 | SLSI_ETHER_COPY(hdr->addr2, sdev->hw_addr); | |
334 | SLSI_ETHER_COPY(hdr->addr3, bssid); | |
335 | ||
336 | req = (struct action_addts_req *)(buf + IEEE80211_HEADER_SIZE); | |
337 | req->hdr.category = WLAN_CATEGORY_WMM; | |
338 | req->hdr.action = WMM_ACTION_CODE_ADDTS_REQ; | |
339 | if (dialog_token_next == 0) | |
340 | dialog_token_next++; | |
341 | req->hdr.dialog_token = dialog_token_next++; | |
342 | req->hdr.status_code = 0; | |
343 | tsid = (CAC_GET_LE24(req->tspec.ts_info) >> 1) & 0xF; | |
344 | ||
345 | /* Find the value of PSB in TSPEC. If PSB is unspecified; fill the | |
346 | * value from UAPSD value stored in Peer structure for the AC | |
347 | */ | |
348 | if (entry->psb_specified == 0) { | |
349 | struct slsi_peer *peer; | |
350 | u32 priority; | |
351 | ||
352 | peer = slsi_get_peer_from_qs(sdev, netdev, SLSI_STA_PEER_QUEUESET); | |
353 | if (!peer) { | |
354 | SLSI_ERR(sdev, "CAC-ADDTS: no Peer found\n"); | |
355 | r = -1; | |
356 | goto exit_free_buf; | |
357 | } | |
358 | ||
359 | cac_query_tspec_field(sdev, entry, "user_priority", &priority); | |
360 | if (peer->uapsd & BIT(slsi_frame_priority_to_ac_queue(priority))) | |
361 | entry->tspec.ts_info[1] |= 0x04; | |
362 | } | |
363 | memcpy(&req->tspec, &entry->tspec, sizeof(entry->tspec)); | |
364 | req_len = sizeof(*req); | |
365 | pos = (u8 *)(req + 1); | |
366 | entry->ebw = ebw ? 1 : 0; | |
367 | ||
368 | if (ebw) { | |
369 | ie_len += add_ebw_ie(pos, extra_ie_len, tsid); | |
370 | if (ie_len <= 0) | |
371 | SLSI_ERR(sdev, "CAC-ADDTS: Failed to add EBW IE\n"); | |
372 | } | |
373 | ||
374 | /* Add tsrs IE in case of ccx enabled bss */ | |
375 | if (ccx_status == BSS_CCX_ENABLED) { | |
376 | num_rates = bss_get_bit_rates(ndev_vif->sta.sta_bss, &rates); | |
377 | if (num_rates <= 0) | |
378 | rate = 12; /* Default to 6Mbps */ | |
379 | else { | |
380 | for (i = 0; i < num_rates; i++) | |
381 | if ((rates[i] > rate) && (rates[i] <= 48)) | |
382 | rate = rates[i]; | |
383 | kfree(rates); | |
384 | } | |
385 | ||
386 | do { | |
387 | /* if the nominal rate is equal to minimum_phy_rate | |
388 | * don't add the tsrs_ie | |
389 | */ | |
390 | if ((rate * TSRS_RATE_PER_UNIT) == req->tspec.minimum_phy_rate) | |
391 | break; | |
392 | ||
393 | if ((rate * TSRS_RATE_PER_UNIT) > req->tspec.minimum_phy_rate) { | |
394 | ie_len += add_tsrs_ie(pos + ie_len, extra_ie_len - ie_len, | |
395 | tsid, &rate, 1); | |
396 | if (ie_len <= 0) { | |
397 | SLSI_ERR(sdev, "CAC-ADDTS: Failed to add TSRS IE\n"); | |
398 | r = -1; | |
399 | goto exit_free_buf; | |
400 | } | |
401 | } else { /* only the "<" case is possible */ | |
402 | SLSI_ERR(sdev, "CAC-ADDTS: BSS rate too low\n"); | |
403 | r = -1; | |
404 | goto exit_free_buf; | |
405 | } | |
406 | } while (0); | |
407 | } | |
408 | ||
409 | if (slsi_mlme_send_frame_mgmt(sdev, netdev, buf, (IEEE80211_HEADER_SIZE + req_len + ie_len), | |
410 | FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, | |
411 | host_tag, 0, sdev->fw_dwell_time, 0) != 0) { | |
412 | SLSI_ERR(sdev, "CAC-ADDTS: Failed to send ADDTS request\n"); | |
413 | r = -1; | |
414 | goto exit_free_buf; | |
415 | } | |
416 | entry->dialog_token = req->hdr.dialog_token; | |
417 | ||
418 | exit_free_buf: | |
419 | kfree(buf); | |
420 | exit: | |
421 | SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); | |
422 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
423 | return r; | |
424 | } | |
425 | ||
426 | /* Name: cac_send_delts | |
427 | * Desc: Build and send the DELTS action frame | |
428 | * sdev: pointer to the slsi_dev struct | |
429 | * id: the tspec id that is going the DELTS action frame to send for | |
430 | * return: 0 (succes), -1 (failure) | |
431 | */ | |
432 | static int cac_send_delts(struct slsi_dev *sdev, int id) | |
433 | { | |
434 | struct action_delts_req *req; | |
435 | struct cac_tspec *entry; | |
436 | size_t req_len; | |
437 | u32 priority; | |
438 | int rc; | |
439 | struct netdev_vif *ndev_vif; | |
440 | struct net_device *netdev; | |
441 | u16 host_tag = slsi_tx_mgmt_host_tag(sdev); | |
442 | struct ieee80211_hdr *hdr; | |
443 | u8 *buf = NULL; | |
444 | u8 *bssid; | |
445 | u8 r = 0; | |
446 | struct slsi_peer *stapeer; | |
447 | ||
448 | entry = find_tspec_entry(id , 1); | |
449 | if (entry == NULL) { | |
450 | SLSI_ERR(sdev, "CAC-DELTS: no TSPEC has been established for tsid=%d\n", id); | |
451 | return -1; | |
452 | } | |
453 | ||
454 | SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex); | |
455 | netdev = get_netdev_for_station(sdev); | |
456 | if (netdev == NULL) { | |
457 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
458 | return -1; | |
459 | } | |
460 | ndev_vif = netdev_priv(netdev); | |
461 | if ((ndev_vif == NULL) || (ndev_vif->sta.sta_bss == NULL)) { | |
462 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
463 | return -1; | |
464 | } | |
465 | SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); | |
466 | ||
467 | if ((!ndev_vif->activated) || (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) || | |
468 | (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) { | |
469 | SLSI_ERR(sdev, "CAC-DELTS: Not connected, can't send DELTS\n"); | |
470 | r = -1; | |
471 | goto exit; | |
472 | } | |
473 | ||
474 | stapeer = slsi_get_peer_from_qs(sdev, netdev, SLSI_STA_PEER_QUEUESET); | |
475 | if (WARN_ON(!stapeer)) { | |
476 | r = -1; | |
477 | goto exit; | |
478 | } | |
479 | ||
480 | bssid = ndev_vif->sta.sta_bss->bssid; | |
481 | buf = kmalloc(24 + sizeof(*req), GFP_KERNEL); | |
482 | if (buf == NULL) { | |
483 | SLSI_ERR(sdev, "CAC-DELTS: Failed to allocate DELTS request\n"); | |
484 | r = -1; | |
485 | goto exit; | |
486 | } | |
487 | hdr = (struct ieee80211_hdr *)buf; | |
488 | hdr->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, IEEE80211_STYPE_ACTION); | |
489 | SLSI_ETHER_COPY(hdr->addr1, bssid); | |
490 | SLSI_ETHER_COPY(hdr->addr2, sdev->hw_addr); | |
491 | SLSI_ETHER_COPY(hdr->addr3, bssid); | |
492 | req = (struct action_delts_req *)(buf + 24); | |
493 | req_len = sizeof(*req); | |
494 | req->hdr.category = WLAN_CATEGORY_WMM; | |
495 | req->hdr.action = WMM_ACTION_CODE_DELTS; | |
496 | req->hdr.dialog_token = 0; | |
497 | req->hdr.status_code = 0; | |
498 | memcpy(&req->tspec, &entry->tspec, sizeof(entry->tspec)); | |
499 | ||
500 | /* TODO_HARDMAC: If PMF is negotiated over the link, the host shall not | |
501 | * issue this primitive before pairwise keys have been installed in F/W . | |
502 | */ | |
503 | if (slsi_mlme_send_frame_mgmt(sdev, netdev, buf, (IEEE80211_HEADER_SIZE + req_len), FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME, FAPI_MESSAGETYPE_IEEE80211_ACTION, host_tag, 0, 0, 0) != 0) { | |
504 | SLSI_ERR(sdev, "CAC-DELTS: Failed to send DELTS request\n"); | |
505 | r = -1; | |
506 | goto exit_free_buf; | |
507 | } | |
508 | rc = cac_query_tspec_field(sdev, entry, "user_priority", &priority); | |
509 | if (rc != 0) { | |
510 | SLSI_ERR(sdev, "CAC-DELTS: Error in reading priority from tspec!\n"); | |
511 | r = -1; | |
512 | goto exit_free_buf; | |
513 | } | |
514 | ||
515 | if (slsi_mlme_del_traffic_parameters(sdev, netdev, priority) != 0) { | |
516 | SLSI_ERR(sdev, "CAC-DELTS: Failed to send DELTS request\n"); | |
517 | r = -1; | |
518 | goto exit_free_buf; | |
519 | } | |
520 | ||
521 | /* BlockAck Control Req was previously used to enable blockack for VO & VI. This | |
522 | * signal is removed and expected to be replaced with MIBs - not able to see | |
523 | * through the haze yet!. Need to take approp. action when the cloud clears. | |
524 | * Historical Data: | |
525 | * if the DELTS request is for UP = 4 or 5 then generate a | |
526 | * MLME-BLOCKACK-CONTROL.request so that no BlockAck is negotiated | |
527 | * on AC_VI. And leave AC_BE enabled | |
528 | */ | |
529 | ||
530 | entry->accepted = 0; /* DELTS sent successfully */ | |
531 | sdev->tspec_error_code = 0; | |
532 | stapeer->tspec_established &= ~BIT(priority); | |
533 | /* update RIC in add_info_elements for assoc req */ | |
534 | cac_set_ric_ie(sdev, netdev); | |
535 | ||
536 | if (ccx_status == BSS_CCX_ENABLED && previous_msdu_lifetime != MAX_TRANSMIT_MSDU_LIFETIME_NOT_VALID) | |
537 | if (slsi_send_max_transmit_msdu_lifetime(sdev, netdev, previous_msdu_lifetime) != 0) { | |
538 | SLSI_ERR(sdev, "CAC-DELTS: slsi_send_max_msdu_lifetime failed"); | |
539 | goto exit_free_buf; | |
540 | } | |
541 | exit_free_buf: | |
542 | kfree(buf); | |
543 | exit: | |
544 | SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); | |
545 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
546 | return r; | |
547 | } | |
548 | ||
549 | /* Name: cac_create_tspec | |
550 | * Desc: Create a tspec entry and added it to the tspec list | |
551 | * sdev: pointer to the slsi_dev struct | |
552 | * id: the id of the tspec that is included in DELTS action frame | |
553 | * return: 0 (succes), -1 (failure) | |
554 | */ | |
555 | static int cac_create_tspec(struct slsi_dev *sdev, char *args) | |
556 | { | |
557 | struct cac_tspec *entry; | |
558 | int id; | |
559 | u8 tid_auto_done = 0; | |
560 | struct netdev_vif *ndev_vif; | |
561 | struct net_device *netdev; | |
562 | ||
563 | SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex); | |
564 | netdev = get_netdev_for_station(sdev); | |
565 | if (netdev == NULL) { | |
566 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
567 | return -1; | |
568 | } | |
569 | ndev_vif = netdev_priv(netdev); | |
570 | if (ndev_vif == NULL) { | |
571 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
572 | return -1; | |
573 | } | |
574 | SLSI_MUTEX_LOCK(ndev_vif->vif_mutex); | |
575 | ||
576 | if ((!ndev_vif->activated) || (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) || | |
577 | (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED)) { | |
578 | SLSI_ERR(sdev, "CAC-ADDTS: Not connected, can't create TSPEC\n"); | |
579 | SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); | |
580 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
581 | return -1; | |
582 | } | |
583 | SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); | |
584 | SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex); | |
585 | ||
586 | if (args == NULL) { | |
587 | /* No input for tid, so we use the auto increment*/ | |
588 | if (tspec_list_next_id <= 7) { | |
589 | id = tspec_list_next_id++; | |
590 | } else { | |
591 | id = 0; | |
592 | tspec_list_next_id = 0; | |
593 | tspec_list_next_id++; | |
594 | } | |
595 | tid_auto_done = 1; | |
596 | } | |
597 | ||
598 | if ((!tid_auto_done) && (strtoint(args, &id) < 0)) { | |
599 | /* Invalid input for tid, so we use the auto increment*/ | |
600 | if (tspec_list_next_id <= 7) { | |
601 | id = tspec_list_next_id++; | |
602 | } else { | |
603 | id = 0; | |
604 | tspec_list_next_id = 0; | |
605 | tspec_list_next_id++; | |
606 | } | |
607 | } | |
608 | ||
609 | if (id < TSID_MIN || id > TSID_MAX) { | |
610 | SLSI_ERR(sdev, "CAC: Invalid TSID =%d, must be in range 0-7\n", id); | |
611 | return -1; | |
612 | } | |
613 | ||
614 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | |
615 | if (entry == NULL) { | |
616 | SLSI_ERR(sdev, "CAC: Failed to allocate TSPEC\n"); | |
617 | return -1; | |
618 | } | |
619 | ||
620 | entry->id = id; | |
621 | entry->tspec.eid = WLAN_EID_VENDOR_SPECIFIC; | |
622 | entry->tspec.length = sizeof(entry->tspec) - sizeof(entry->tspec.eid) - sizeof(entry->tspec.length); | |
623 | CAC_PUT_BE24(entry->tspec.oui, WLAN_OUI_MICROSOFT); | |
624 | entry->tspec.oui_type = WLAN_OUI_TYPE_MICROSOFT_WMM; | |
625 | entry->tspec.oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT; | |
626 | entry->tspec.version = WMM_VERSION; | |
627 | entry->accepted = 0; | |
628 | entry->psb_specified = 0; | |
629 | /* Setting the 7th bit of ts info to 1, as its a fixed reserved bit. */ | |
630 | entry->tspec.ts_info[0] = 0x80; | |
631 | ||
632 | entry->next = tspec_list; | |
633 | tspec_list = entry; | |
634 | SLSI_DBG1(sdev, SLSI_MLME, "CAC: Created TSPEC entry for id =%d\n", id); | |
635 | ||
636 | return entry->id; | |
637 | } | |
638 | ||
639 | /* Name: cac_delete_tspec | |
640 | * Desc: delete a tspec from the list of the tspecs | |
641 | * sdev: pointer to the slsi_dev struct | |
642 | * id: the id of the tspec that will be deleted | |
643 | * return: 0 (succes), -1 (failure) | |
644 | */ | |
645 | static int cac_delete_tspec(struct slsi_dev *sdev, int id) | |
646 | { | |
647 | struct cac_tspec *itr; | |
648 | struct cac_tspec *prev; | |
649 | ||
650 | itr = tspec_list; | |
651 | prev = NULL; | |
652 | while (itr != NULL) { | |
653 | if (itr->id == id) { | |
654 | if (prev) | |
655 | prev->next = itr->next; | |
656 | else | |
657 | tspec_list = itr->next; | |
658 | ||
659 | if (itr->accepted) | |
660 | cac_send_delts(sdev, itr->id); | |
661 | ||
662 | SLSI_DBG3(sdev, SLSI_MLME, "CAC: TSPEC entry deleted for id =%d\n", id); | |
663 | kfree(itr); | |
664 | ||
665 | return 0; | |
666 | } | |
667 | prev = itr; | |
668 | itr = itr->next; | |
669 | } | |
670 | SLSI_ERR(sdev, "CAC: Couldn't find TSPEC with id %d for deletion", id); | |
671 | ||
672 | return -1; | |
673 | } | |
674 | ||
675 | /* Name: cac_delete_tspec_by_state | |
676 | * Desc: delete a tspec from the list of the tspecs based on id and state | |
677 | * sdev: pointer to the slsi_dev struct | |
678 | * id: the id of the tspec that will be deleted | |
679 | * accepted: 0 - not yet accepted by AP, 1- accepted by AP | |
680 | * return: 0 (succes), -1 (failure) | |
681 | */ | |
682 | static int cac_delete_tspec_by_state(struct slsi_dev *sdev, int id, int accepted) | |
683 | { | |
684 | struct cac_tspec *itr; | |
685 | struct cac_tspec *prev; | |
686 | ||
687 | itr = tspec_list; | |
688 | prev = NULL; | |
689 | while (itr != NULL) { | |
690 | if ((itr->id == id) && (itr->accepted == accepted)) { | |
691 | if (prev) | |
692 | prev->next = itr->next; | |
693 | else | |
694 | tspec_list = itr->next; | |
695 | ||
696 | SLSI_DBG3(sdev, SLSI_MLME, "CAC: Deleting TSPEC 0x%p with ID %d (accepted =%d)\n", itr, id, accepted); | |
697 | kfree(itr); | |
698 | return 0; | |
699 | } | |
700 | prev = itr; | |
701 | itr = itr->next; | |
702 | } | |
703 | SLSI_ERR(sdev, "CAC: Couldn't find TSPEC with ID %d (accepted =%d)\n", id, accepted); | |
704 | ||
705 | return -1; | |
706 | } | |
707 | ||
708 | /* Name: cac_config_tspec | |
709 | * Desc: Set a field's value of a tspec | |
710 | * sdev: pointer to the slsi_dev struct | |
711 | * id: the id of the tspec that will be configured | |
712 | * field: the field name that will be changed | |
713 | * value: the value of the field | |
714 | * return: 0 (succes), -1 (failure) | |
715 | */ | |
716 | static int cac_config_tspec(struct slsi_dev *sdev, int id, const char *field, u32 value) | |
717 | { | |
718 | struct cac_tspec *entry; | |
719 | int i; | |
720 | u32 max = 0xFFFFFFFF; | |
721 | u32 tsinfo; | |
722 | u8 mask; | |
723 | u8 *pos; | |
724 | ||
725 | if (field == NULL) | |
726 | return -1; | |
727 | ||
728 | entry = find_tspec_entry(id, 0); | |
729 | if (entry == NULL) { | |
730 | SLSI_ERR(sdev, "CAC: Invalid TSPEC ID\n"); | |
731 | return -1; | |
732 | } | |
733 | ||
734 | for (i = 0; i < NUM_TSPEC_FIELDS; i++) | |
735 | if (strcasecmp(field, tspec_fields[i].name) == 0) | |
736 | break; | |
737 | if (i >= NUM_TSPEC_FIELDS) { | |
738 | SLSI_ERR(sdev, "CAC: Invalid TSPEC config field\n"); | |
739 | return -1; | |
740 | } | |
741 | if (tspec_fields[i].read_only) { | |
742 | SLSI_ERR(sdev, "CAC: TSPEC field is read-only\n"); | |
743 | return -1; | |
744 | } | |
745 | if (tspec_fields[i].is_tsinfo_field) { | |
746 | mask = tspec_fields[i].size; | |
747 | if (strcasecmp(field, "psb") == 0) { | |
748 | if (value <= mask) | |
749 | entry->psb_specified = 1; | |
750 | else | |
751 | return 0; | |
752 | } | |
753 | if (value > mask) { | |
754 | SLSI_ERR(sdev, "CAC: TSPEC config value exceeded maximum for %s\n", tspec_fields[i].name); | |
755 | return -1; | |
756 | } | |
757 | ||
758 | tsinfo = CAC_GET_LE24(&entry->tspec.ts_info[0]); | |
759 | tsinfo &= ~(u32)(mask << tspec_fields[i].offset); | |
760 | tsinfo |= (u32)((value & mask) << tspec_fields[i].offset); | |
761 | CAC_PUT_LE24(entry->tspec.ts_info, tsinfo); | |
762 | } else { | |
763 | if (tspec_fields[i].size < 4) | |
764 | max = ((1 << (tspec_fields[i].size * 8)) - 1); | |
765 | ||
766 | if (value > max) { | |
767 | SLSI_ERR(sdev, "CAC: TSPEC config value exceeded maximumfor %s\n", tspec_fields[i].name); | |
768 | return -1; | |
769 | } | |
770 | ||
771 | pos = (u8 *)(&entry->tspec) + tspec_fields[i].offset; | |
772 | if (tspec_fields[i].size == 1) | |
773 | *pos = (value & 0xFF); | |
774 | else if (tspec_fields[i].size == 2) | |
775 | CAC_PUT_LE16(pos, value); | |
776 | else | |
777 | CAC_PUT_LE32(pos, value); | |
778 | } | |
779 | ||
780 | return 0; | |
781 | } | |
782 | ||
783 | /* Name: cac_ctrl_create_tspec | |
784 | * Desc: public function to create tspec | |
785 | * sdev: pointer to the slsi_dev struct | |
786 | * return: tspec id | |
787 | */ | |
788 | int cac_ctrl_create_tspec(struct slsi_dev *sdev, char *args) | |
789 | { | |
790 | int id; | |
791 | ||
792 | id = cac_create_tspec(sdev, args); | |
793 | if (id < 0) | |
794 | return -1; | |
795 | ||
796 | return id; | |
797 | } | |
798 | ||
799 | /* Name: cac_ctrl_delete_tspec | |
800 | * Desc: public function to delete tspec | |
801 | * sdev: pointer to the slsi_dev struct | |
802 | * args:pointer to a buffer that contains the agrs for deleting tspec from the list | |
803 | * return: 0 (succes), -1 (failure) | |
804 | */ | |
805 | int cac_ctrl_delete_tspec(struct slsi_dev *sdev, char *args) | |
806 | { | |
807 | int id; | |
808 | ||
809 | if (strtoint(args, &id) < 0) { | |
810 | SLSI_ERR(sdev, "CAC-DELETE-TSPEC: Invalid TSPEC ID\n"); | |
811 | return -1; | |
812 | } | |
813 | ||
814 | if (cac_delete_tspec(sdev, id) < 0) | |
815 | return -1; | |
816 | ||
817 | return 0; | |
818 | } | |
819 | ||
820 | /* Name: cac_ctrl_config_tspec | |
821 | * Desc: public function to configure a tspec | |
822 | * sdev: pointer to the slsi_dev struct | |
823 | * args: pointer to a buffer that contains the agrs for tspec configuration | |
824 | * return: 0 (succes), -1 (failure) | |
825 | */ | |
826 | int cac_ctrl_config_tspec(struct slsi_dev *sdev, char *args) | |
827 | { | |
828 | char *id; | |
829 | char *field; | |
830 | char *value; | |
831 | int tspec_id; | |
832 | u32 val; | |
833 | ||
834 | id = args; | |
835 | field = strchr(id, ' '); | |
836 | if (field == NULL) { | |
837 | SLSI_ERR(sdev, "CAC: field string is NULL\n"); | |
838 | return -1; | |
839 | } | |
840 | *field++ = '\0'; | |
841 | value = strchr(field, ' '); | |
842 | if (value == NULL) { | |
843 | SLSI_ERR(sdev, "CAC: field value is NULL\n"); | |
844 | return -1; | |
845 | } | |
846 | *value++ = '\0'; | |
847 | ||
848 | if (strtoint(id, &tspec_id) < 0) { | |
849 | SLSI_ERR(sdev, "CAC: Conversion error for tspecid\n"); | |
850 | return -1; | |
851 | } | |
852 | ||
853 | if (strtoint(value, &val) < 0) { | |
854 | SLSI_ERR(sdev, "CAC: Conversion error for tspecid value\n"); | |
855 | return -1; | |
856 | } | |
857 | ||
858 | if (cac_config_tspec(sdev, tspec_id, field, val) < 0) | |
859 | return -1; | |
860 | ||
861 | return 0; | |
862 | } | |
863 | ||
864 | /* Name: cac_ctrl_send_addts | |
865 | * Desc: public function to send ADDTS action frame | |
866 | * sdev: pointer to the slsi_dev struct | |
867 | * args: buffer that contains the agrs for ADDTS request | |
868 | * return: 0 (succes), -1 (failure) | |
869 | */ | |
870 | int cac_ctrl_send_addts(struct slsi_dev *sdev, char *args) | |
871 | { | |
872 | char *id_str; | |
873 | char *ebw_str; | |
874 | int id; | |
875 | int ebw = 0; | |
876 | ||
877 | if (args == NULL) | |
878 | return -1; | |
879 | ||
880 | id_str = args; | |
881 | ebw_str = strchr(id_str, ' '); | |
882 | if (ebw_str != NULL) { | |
883 | *ebw_str++ = '\0'; | |
884 | if (!strncmp(ebw_str, "ebw", 3)) | |
885 | ebw = 1; | |
886 | } | |
887 | if (strtoint(id_str, &id) < 0) { | |
888 | SLSI_ERR(sdev, "CAC: Conversion error for tspecid value\n"); | |
889 | return -1; | |
890 | } | |
891 | if (cac_send_addts(sdev, id, ebw) < 0) | |
892 | return -1; | |
893 | ||
894 | return 0; | |
895 | } | |
896 | ||
897 | /* Name: cac_ctrl_send_delts | |
898 | * Desc: public function to send DELTS action frame | |
899 | * sdev: pointer to the slsi_dev struct | |
900 | * args: buffer that contains the agrs for DELTS request | |
901 | * return: 0 (succes), -1 (failure) | |
902 | */ | |
903 | int cac_ctrl_send_delts(struct slsi_dev *sdev, char *args) | |
904 | { | |
905 | int id; | |
906 | ||
907 | if (args == NULL) | |
908 | return -1; | |
909 | ||
910 | if (strtoint(args, &id) < 0) { | |
911 | SLSI_ERR(sdev, "CAC: Invalid TSPEC ID\n"); | |
912 | return -1; | |
913 | } | |
914 | if (cac_send_delts(sdev, id) < 0) | |
915 | return -1; | |
916 | ||
917 | return 0; | |
918 | } | |
919 | ||
920 | /* Name: cac_process_delts_req | |
921 | * Desc: process a DELTS request | |
922 | * sdev: pointer to the slsi_dev struct | |
923 | * req: buffer of the DELTS request | |
924 | * return: 0 (succes), -1 (failure) | |
925 | */ | |
926 | static void cac_process_delts_req(struct slsi_dev *sdev, struct net_device *netdev, struct action_delts_req *req) | |
927 | { | |
928 | struct netdev_vif *ndev_vif = netdev_priv(netdev); | |
929 | struct cac_tspec *itr; | |
930 | u32 priority; | |
931 | int rc; | |
932 | struct slsi_peer *stapeer; | |
933 | u8 tid; | |
934 | ||
935 | WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)); | |
936 | ||
937 | if ((!ndev_vif->activated) || (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) || | |
938 | (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED) || (ndev_vif->sta.sta_bss == NULL)) { | |
939 | SLSI_ERR(sdev, "CAC: Not connected, Unexpected DELTS request\n"); | |
940 | return; | |
941 | } | |
942 | ||
943 | stapeer = slsi_get_peer_from_qs(sdev, netdev, SLSI_STA_PEER_QUEUESET); | |
944 | if (WARN_ON(!stapeer)) | |
945 | return; | |
946 | ||
947 | tid = (CAC_GET_LE24(req->tspec.ts_info) >> 1) & 0xF; | |
948 | SLSI_DBG1(sdev, SLSI_MLME, "CAC: TID in delts request =%d\n", tid); | |
949 | ||
950 | itr = find_tspec_entry(tid, 1); | |
951 | if (itr == NULL) { | |
952 | SLSI_ERR(sdev, "CAC: No matching TSPEC found\n"); | |
953 | return; | |
954 | } | |
955 | ||
956 | rc = cac_query_tspec_field(sdev, itr, "user_priority", &priority); | |
957 | if (rc != 0) { | |
958 | SLSI_ERR(sdev, "CAC: Missing priority from TSPEC!\n"); | |
959 | return; | |
960 | } | |
961 | ||
962 | if (slsi_mlme_del_traffic_parameters(sdev, netdev, priority) != 0) { | |
963 | SLSI_ERR(sdev, "CAC: Failed to send DEL-TRAFFIC_PARAMETERS request\n"); | |
964 | return; | |
965 | } | |
966 | ||
967 | /* BlockAck Control Req was previously used to enable blockack for VO & VI. This | |
968 | * signal is removed and expected to be replaced with MIBs - not able to see | |
969 | * through the haze yet!. Need to take approp. action when the cloud clears. | |
970 | * Historical Data: | |
971 | * if the DELTS request is for UP = 4 or 5 then generate a | |
972 | * MLME-BLOCKACK-CONTROL.request so that no BlockAck is negotiated | |
973 | * on AC_VI. And leave AC_BE enabled | |
974 | */ | |
975 | ||
976 | itr->accepted = 0; /* del traffic parameters sent successfully */ | |
977 | stapeer->tspec_established &= ~BIT(priority); | |
978 | SLSI_DBG1(sdev, SLSI_MLME, "tspec_established =%x\n", stapeer->tspec_established); | |
979 | /* update RIC in add_info_elements for assoc req */ | |
980 | cac_set_ric_ie(sdev, netdev); | |
981 | ||
982 | if (ccx_status == BSS_CCX_ENABLED && previous_msdu_lifetime != MAX_TRANSMIT_MSDU_LIFETIME_NOT_VALID) | |
983 | if (slsi_send_max_transmit_msdu_lifetime(sdev, netdev, previous_msdu_lifetime) != 0) { | |
984 | SLSI_ERR(sdev, "CAC: slsi_send_max_msdu_lifetime failed"); | |
985 | return; | |
986 | } | |
987 | } | |
988 | ||
989 | /* Name: cac_find_edca_ie | |
990 | * Desc: Finds the edca IE in the ADDTS response action frame | |
991 | * sdev: pointer to the slsi_dev struct | |
992 | * ie: buffer of the edca IE | |
993 | * tsid: the tsid that is included in the edca IE | |
994 | * lifetime: the lifetime value that is included in the edca IE | |
995 | * return: 0 (succes), -1 (failure) | |
996 | */ | |
997 | static int cac_find_edca_ie(const u8 *ie, size_t ie_len, u8 *tsid, u16 *lifetime) | |
998 | { | |
999 | const u8 *pos = ie; | |
1000 | ||
1001 | if ((ie == NULL) || (ie_len < 9) || | |
1002 | (tsid == NULL) || (lifetime == NULL)) | |
1003 | return -1; | |
1004 | ||
1005 | pos = cfg80211_find_vendor_ie(WLAN_OUI_CISCO, WLAN_OUI_TYPE_CISCO_EDCA, ie, ie_len); | |
1006 | if (pos && (pos + 9 <= ie + ie_len)) { | |
1007 | *tsid = pos[6]; | |
1008 | *lifetime = CAC_GET_LE16(&pos[7]); | |
1009 | return 0; | |
1010 | } | |
1011 | ||
1012 | return -1; | |
1013 | } | |
1014 | ||
1015 | /* Name: cac_process_addts_rsp | |
1016 | * Desc: parsing of the addts response | |
1017 | * sdev: pointer to the slsi_dev struct | |
1018 | * rsp: the buffer of the ADDTS response received | |
1019 | * ie_len: the length of the buffer | |
1020 | * return: 0 (succes), -1 (failure) | |
1021 | */ | |
1022 | static void cac_process_addts_rsp(struct slsi_dev *sdev, struct net_device *netdev, struct action_addts_rsp *rsp, const u8 *ie, size_t ie_len) | |
1023 | { | |
1024 | struct netdev_vif *ndev_vif = netdev_priv(netdev); | |
1025 | struct cac_tspec *itr, *entry; | |
1026 | struct wmm_tspec_element *tspec; | |
1027 | u32 priority, prev_priority; | |
1028 | int rc; | |
1029 | u8 tsid; | |
1030 | u16 msdu_lifetime; | |
1031 | struct slsi_peer *peer; | |
1032 | u16 medium_time; | |
1033 | ||
1034 | WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex)); | |
1035 | ||
1036 | SLSI_DBG1(sdev, SLSI_MLME, "\n"); | |
1037 | ||
1038 | if ((!ndev_vif->activated) || (ndev_vif->vif_type != FAPI_VIFTYPE_STATION) || | |
1039 | (ndev_vif->sta.vif_status != SLSI_VIF_STATUS_CONNECTED) || (ndev_vif->sta.sta_bss == NULL)) { | |
1040 | SLSI_ERR(sdev, "CAC: Not connected, INVALID state for ADDTS response\n"); | |
1041 | return; | |
1042 | } | |
1043 | ||
1044 | peer = slsi_get_peer_from_qs(sdev, netdev, SLSI_STA_PEER_QUEUESET); | |
1045 | if (WARN_ON(!peer)) | |
1046 | return; | |
1047 | ||
1048 | itr = tspec_list; | |
1049 | while (itr != NULL) { | |
1050 | if (itr->dialog_token == rsp->hdr.dialog_token) { | |
1051 | itr->dialog_token = 0; /*reset the dialog token to avoid any incorrect matches if AP send incorrect value*/ | |
1052 | break; | |
1053 | } | |
1054 | itr = itr->next; | |
1055 | } | |
1056 | if (itr == NULL) { | |
1057 | SLSI_ERR(sdev, "CAC: No matching TSPEC found for ADDTS response\n"); | |
1058 | return; | |
1059 | } | |
1060 | ||
1061 | if (rsp->hdr.status_code != ADDTS_STATUS_ACCEPTED) { | |
1062 | SLSI_ERR(sdev, "CAC: TSPEC rejected (status=0x%02X)", rsp->hdr.status_code); | |
1063 | cac_delete_tspec_by_state(sdev, itr->id , 0); | |
1064 | return; | |
1065 | } | |
1066 | ||
1067 | if ((ccx_status == BSS_CCX_ENABLED) && cac_find_edca_ie(ie, ie_len, &tsid, &msdu_lifetime) != 0) | |
1068 | msdu_lifetime = MSDU_LIFETIME_DEFAULT; | |
1069 | ||
1070 | tspec = (struct wmm_tspec_element *)(rsp + 1); | |
1071 | medium_time = tspec->medium_time; | |
1072 | ||
1073 | rc = cac_query_tspec_field(sdev, itr, "user_priority", &priority); | |
1074 | SLSI_DBG1(sdev, SLSI_MLME, "CAC: Priority for current tspec id %d=%d\n", itr->id, priority); | |
1075 | ||
1076 | if (peer->tspec_established == 0) | |
1077 | goto set_params; | |
1078 | ||
1079 | SLSI_DBG1(sdev, SLSI_MLME, "TSPEC already established\n"); | |
1080 | ||
1081 | /* TSPEC is already established . Check if it is for same UP / UP mapping to same AC | |
1082 | * If same UP (or UP mapping to same AC) : set params with modified values | |
1083 | * If not, set traffic params for this priority (new AC) | |
1084 | */ | |
1085 | switch (priority) { | |
1086 | /*AC_BK*/ | |
1087 | case FAPI_PRIORITY_QOS_UP1: | |
1088 | case FAPI_PRIORITY_QOS_UP2: | |
1089 | if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP1)) | |
1090 | prev_priority = FAPI_PRIORITY_QOS_UP1; | |
1091 | else if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP2)) | |
1092 | prev_priority = FAPI_PRIORITY_QOS_UP2; | |
1093 | else | |
1094 | goto set_params; | |
1095 | break; | |
1096 | ||
1097 | /*AC_BE*/ | |
1098 | case FAPI_PRIORITY_QOS_UP0: | |
1099 | case FAPI_PRIORITY_QOS_UP3: | |
1100 | if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP0)) | |
1101 | prev_priority = FAPI_PRIORITY_QOS_UP0; | |
1102 | else if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP3)) | |
1103 | prev_priority = FAPI_PRIORITY_QOS_UP3; | |
1104 | else | |
1105 | goto set_params; | |
1106 | break; | |
1107 | ||
1108 | /*AC_VI*/ | |
1109 | case FAPI_PRIORITY_QOS_UP4: | |
1110 | case FAPI_PRIORITY_QOS_UP5: | |
1111 | if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP4)) | |
1112 | prev_priority = FAPI_PRIORITY_QOS_UP4; | |
1113 | else if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP5)) | |
1114 | prev_priority = FAPI_PRIORITY_QOS_UP5; | |
1115 | else | |
1116 | goto set_params; | |
1117 | break; | |
1118 | ||
1119 | /*AC_VO*/ | |
1120 | case FAPI_PRIORITY_QOS_UP6: | |
1121 | case FAPI_PRIORITY_QOS_UP7: | |
1122 | if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP6)) | |
1123 | prev_priority = FAPI_PRIORITY_QOS_UP6; | |
1124 | else if (peer->tspec_established & BIT(FAPI_PRIORITY_QOS_UP7)) | |
1125 | prev_priority = FAPI_PRIORITY_QOS_UP7; | |
1126 | else | |
1127 | goto set_params; | |
1128 | break; | |
1129 | /* invalid*/ | |
1130 | default: | |
1131 | SLSI_ERR(sdev, "CAC: Invalid UP in the request\n"); | |
1132 | return; | |
1133 | } | |
1134 | ||
1135 | /* Look for TSPEC entry for initial request */ | |
1136 | entry = find_tspec_entry(itr->id, 1); | |
1137 | if (entry) { /*same TID*/ | |
1138 | cac_query_tspec_field(sdev, entry, "user_priority", &prev_priority); | |
1139 | SLSI_DBG1(sdev, SLSI_MLME, "CAC: Modify TSPEC (prev_priority =%d)\n", prev_priority); | |
1140 | /* On receiving the new medium time (second ADDTS Response) , driver shall issue | |
1141 | * mlme-set-traffic-parameters.request with the received medium time. | |
1142 | * Use UP from old entry so FW can replace the medium time | |
1143 | * Delete the old entry in host, and replace UP in new entry. | |
1144 | */ | |
1145 | cac_delete_tspec_by_state(sdev, entry->id, 1); | |
1146 | if (priority != prev_priority) { | |
1147 | itr->tspec.ts_info[1] &= ~(7 << 3) ; /*clear the value*/ | |
1148 | itr->tspec.ts_info[1] |= prev_priority << 3 ; /*set the value*/ | |
1149 | priority = prev_priority; | |
1150 | } | |
1151 | ||
1152 | } else { | |
1153 | /* Two distinct TSes are being admitted, so the driver needs to add both allocated medium time | |
1154 | * The UP must be set to the same value of the first mlme-set-traffic-parameters.request so that | |
1155 | * the FW replaces the current medium time with the new medium time. | |
1156 | */ | |
1157 | SLSI_DBG1(sdev, SLSI_MLME, "CAC: Modify TSPEC for different TID\n"); | |
1158 | entry = tspec_list; | |
1159 | while (entry != NULL) { | |
1160 | if ((entry->accepted) && ((entry->tspec.ts_info[1] >> 3 & 0x07) == prev_priority)) { /*initial TS entry for same priority*/ | |
1161 | medium_time += entry->tspec.medium_time; | |
1162 | priority = prev_priority; | |
1163 | break; | |
1164 | } | |
1165 | entry = entry->next; | |
1166 | } | |
1167 | if (entry == NULL) { | |
1168 | SLSI_ERR(sdev, "CAC: Failed to find entry for prev established TSPEC!!\n"); | |
1169 | return; | |
1170 | } | |
1171 | } | |
1172 | ||
1173 | set_params: | |
1174 | SLSI_DBG1(sdev, SLSI_MLME, "sending traffic params tid [%d]", itr->id); | |
1175 | if (slsi_mlme_set_traffic_parameters(sdev, netdev, priority, medium_time, tspec->minimum_data_rate, ndev_vif->sta.sta_bss->bssid) != 0) { | |
1176 | SLSI_ERR(sdev, "CAC: Failed to send SET_TRAFFIC_PARAMETERS request\n"); | |
1177 | return; | |
1178 | } | |
1179 | ||
1180 | /*update the TSPEC with medium_time allocated by AP*/ | |
1181 | itr->tspec.medium_time = medium_time; | |
1182 | ||
1183 | /* BlockAck Control Req was previously used to enable blockack for VO & VI. This | |
1184 | * signal is removed and expected to be replaced with MIBs - not able to see | |
1185 | * through the haze yet!. Need to take approp. action when the cloud clears. | |
1186 | * Historical Data: | |
1187 | * Currently the firmware autonomously negotiates BlockAck agreement for AC_BE. | |
1188 | * It is required for WMM-AC certification to use BlockAck for AC_VI. | |
1189 | * So if a TSPEC for AC_VI (UP = 5 0r 4) is successfully negotiated, the host | |
1190 | * generates an MLME-BLOCKACK-CONTROL.request, identifying that a BlockAck for the | |
1191 | * corresponding Priority (direction set to Any) should be enabled, i.e. the F/W | |
1192 | * will accept a downlink requested BlockAck Request, and will try to set-up an | |
1193 | * uplink BlockAck Request for that priority (TID). | |
1194 | * Bits for AC_BE should always be set | |
1195 | * For WMM-AC certification, if the EDCA parameters for both VO and VI are same | |
1196 | * during association and both are ACM = 1, then don't use BlockAck for AC_VI. | |
1197 | */ | |
1198 | ||
1199 | /* Add store in MIB the msdu_lifetime value in case of ccx enabled bss */ | |
1200 | if (ccx_status == BSS_CCX_ENABLED) { | |
1201 | if ((slsi_read_max_transmit_msdu_lifetime(sdev, netdev, &previous_msdu_lifetime)) != 0) { | |
1202 | previous_msdu_lifetime = MAX_TRANSMIT_MSDU_LIFETIME_NOT_VALID; | |
1203 | SLSI_ERR(sdev, "CAC: slsi_read_max_msdu_lifetime failed"); | |
1204 | return; | |
1205 | } | |
1206 | ||
1207 | if (slsi_send_max_transmit_msdu_lifetime(sdev, netdev, msdu_lifetime) != 0) { | |
1208 | SLSI_ERR(sdev, "CAC: slsi_send_max_msdu_lifetime failed"); | |
1209 | return; | |
1210 | } | |
1211 | } | |
1212 | ||
1213 | itr->accepted = 1; /* add_tspec accepted by AP*/ | |
1214 | sdev->tspec_error_code = 0; /* add_tspec response received */ | |
1215 | peer->tspec_established |= BIT(priority); | |
1216 | /* update RIC in add_info_elements for assoc req */ | |
1217 | cac_set_ric_ie(sdev, netdev); | |
1218 | } | |
1219 | ||
1220 | /* Name: cac_rx_wmm_action | |
1221 | * Desc: Get the action frame received and call the corresponding process routine | |
1222 | * sdev: pointer to the slsi_dev struct | |
1223 | * data: buffer to the action frame received | |
1224 | * len: the length in bytes of the action frame | |
1225 | */ | |
1226 | void cac_rx_wmm_action(struct slsi_dev *sdev, struct net_device *netdev, struct ieee80211_mgmt *data, size_t len) | |
1227 | { | |
1228 | struct ieee80211_mgmt *mgmt = data; | |
1229 | struct action_addts_rsp *addts; | |
1230 | ||
1231 | if ((sdev == NULL) || (data == NULL) || (netdev == NULL) || (len == 0)) | |
1232 | return; | |
1233 | ||
1234 | if (mgmt->u.action.u.wme_action.action_code == WMM_ACTION_CODE_ADDTS_RESP) { | |
1235 | addts = (struct action_addts_rsp *)&mgmt->u.action; | |
1236 | cac_process_addts_rsp(sdev, netdev, addts, mgmt->u.action.u.wme_action.variable, len - sizeof(*addts) + 1); | |
1237 | } else if (mgmt->u.action.u.wme_action.action_code == WMM_ACTION_CODE_DELTS) { | |
1238 | cac_process_delts_req(sdev, netdev, (struct action_delts_req *)&mgmt->u.action); | |
1239 | } | |
1240 | } | |
1241 | ||
1242 | /* Name: cac_get_active_tspecs | |
1243 | * Desc: | |
1244 | * tspecs: the list of active tspecs | |
1245 | * return: 0 (succes), -1 (failure) | |
1246 | */ | |
1247 | int cac_get_active_tspecs(struct cac_activated_tspec **tspecs) | |
1248 | { | |
1249 | struct cac_tspec *itr = tspec_list; | |
1250 | int count = 0; | |
1251 | int i = 0; | |
1252 | ||
1253 | if (tspecs == NULL) | |
1254 | return -1; | |
1255 | ||
1256 | while (itr != NULL) { | |
1257 | if (itr->accepted) | |
1258 | count++; | |
1259 | itr = itr->next; | |
1260 | } | |
1261 | *tspecs = kmalloc_array((size_t)count, sizeof(struct cac_activated_tspec), GFP_KERNEL); | |
1262 | itr = tspec_list; | |
1263 | while (itr != NULL) { | |
1264 | if (itr->accepted) { | |
1265 | tspecs[i]->ebw = itr->ebw; | |
1266 | memcpy(&tspecs[i]->tspec, &itr->tspec, sizeof(itr->tspec)); | |
1267 | i++; | |
1268 | } | |
1269 | itr = itr->next; | |
1270 | } | |
1271 | ||
1272 | return count; | |
1273 | } | |
1274 | ||
1275 | /********************************************************* | |
1276 | * call cac_delete_tspec_list to delete all tspecs | |
1277 | * when the device is disconnecting | |
1278 | */ | |
1279 | /* Name: cac_delete_tspec_list | |
1280 | * Desc: | |
1281 | * sdev: pointer to the slsi_dev struct | |
1282 | * return: None | |
1283 | */ | |
1284 | void cac_delete_tspec_list(struct slsi_dev *sdev) | |
1285 | { | |
1286 | struct cac_tspec *itr = tspec_list; | |
1287 | struct cac_tspec *temp = NULL; | |
1288 | ||
1289 | SLSI_UNUSED_PARAMETER(sdev); | |
1290 | ||
1291 | while (itr != NULL) { | |
1292 | itr->accepted = 0; | |
1293 | itr->dialog_token = 0; | |
1294 | temp = itr; | |
1295 | itr = itr->next; | |
1296 | kfree(temp); | |
1297 | } | |
1298 | tspec_list = NULL; | |
1299 | } | |
1300 | ||
1301 | void cac_deactivate_tspecs(struct slsi_dev *sdev) | |
1302 | { | |
1303 | struct cac_tspec *itr = tspec_list; | |
1304 | ||
1305 | SLSI_UNUSED_PARAMETER(sdev); | |
1306 | ||
1307 | while (itr) { | |
1308 | itr->accepted = 0; | |
1309 | itr->dialog_token = 0; | |
1310 | itr = itr->next; | |
1311 | } | |
1312 | } | |
1313 | ||
1314 | static void cac_set_ric_ie(struct slsi_dev *sdev, struct net_device *netdev) | |
1315 | { | |
1316 | struct cac_tspec *itr = tspec_list; | |
1317 | int tspec_count = 0; | |
1318 | int buf_len = 0; | |
1319 | u8 *buff, *add_info_ies; | |
1320 | struct wmm_tspec_element *tspec_ie; | |
1321 | int i = 0; | |
1322 | struct netdev_vif *ndev_vif = netdev_priv(netdev); | |
1323 | ||
1324 | while (itr) { | |
1325 | if (itr->accepted) | |
1326 | tspec_count++; | |
1327 | itr = itr->next; | |
1328 | } | |
1329 | ||
1330 | if (tspec_count == 0) { | |
1331 | slsi_mlme_add_info_elements(sdev, netdev, FAPI_PURPOSE_ASSOCIATION_REQUEST, | |
1332 | ndev_vif->sta.assoc_req_add_info_elem, | |
1333 | ndev_vif->sta.assoc_req_add_info_elem_len); | |
1334 | return; | |
1335 | } | |
1336 | ||
1337 | /* RDE (6 bytes), WMM TSPEC * tspec_count bytes*/ | |
1338 | buf_len = 6 + (sizeof(struct wmm_tspec_element) * tspec_count); | |
1339 | buf_len += ndev_vif->sta.assoc_req_add_info_elem_len; | |
1340 | add_info_ies = kmalloc(buf_len, GFP_KERNEL); | |
1341 | if (!add_info_ies) { | |
1342 | SLSI_ERR(sdev, "malloc fail. size:%d\n", buf_len); | |
1343 | return; | |
1344 | } | |
1345 | memcpy(add_info_ies, ndev_vif->sta.assoc_req_add_info_elem, ndev_vif->sta.assoc_req_add_info_elem_len); | |
1346 | ||
1347 | buff = add_info_ies + ndev_vif->sta.assoc_req_add_info_elem_len; | |
1348 | buff[0] = WLAN_EID_RIC_DATA; | |
1349 | buff[1] = 4; | |
1350 | buff[2] = 0; /* random identifier */ | |
1351 | /* buff[3]: resource desc count update after filling TSPEC */ | |
1352 | buff[4] = 0; /* buff[4]-buff[5] status code. set to success */ | |
1353 | buff[5] = 0; | |
1354 | ||
1355 | itr = tspec_list; | |
1356 | i = 0; | |
1357 | while (itr) { | |
1358 | if (itr->accepted) { | |
1359 | tspec_ie = (struct wmm_tspec_element *)&buff[6 + i * sizeof(struct wmm_tspec_element)]; | |
1360 | memcpy(tspec_ie, &itr->tspec, sizeof(struct wmm_tspec_element)); | |
1361 | ((struct wmm_tspec_element *)tspec_ie)->medium_time = 0; | |
1362 | i++; | |
1363 | } | |
1364 | itr = itr->next; | |
1365 | } | |
1366 | buff[3] = i; | |
1367 | slsi_mlme_add_info_elements(sdev, netdev, FAPI_PURPOSE_ASSOCIATION_REQUEST, add_info_ies, buf_len); | |
1368 | kfree(add_info_ies); | |
1369 | } | |
1370 | ||
1371 | static int cac_get_rde_tspec_ie(struct slsi_dev *sdev, u8 *assoc_rsp_ie, int assoc_rsp_ie_len, const u8 **tspec_ie_arr) | |
1372 | { | |
1373 | const u8 *ie; | |
1374 | u16 status; | |
1375 | int tspec_count = 0, i = 0; | |
1376 | ||
1377 | ie = assoc_rsp_ie; | |
1378 | ||
1379 | /* Find total number of RDE TSPEC */ | |
1380 | while (ie && (assoc_rsp_ie_len > ie - assoc_rsp_ie)) { | |
1381 | ie = cfg80211_find_ie(WLAN_EID_RIC_DATA, ie, assoc_rsp_ie_len - (ie - assoc_rsp_ie)); | |
1382 | if (!ie) | |
1383 | break; | |
1384 | status = CAC_GET_LE16(&ie[4]); | |
1385 | if (status != 0) | |
1386 | continue; | |
1387 | ||
1388 | tspec_count += ie[3]; /* TSPEC descriptor count */ | |
1389 | ie = ie + ie[1]; | |
1390 | } | |
1391 | ||
1392 | /* limit WMM TSPEC count to TSID_MAX */ | |
1393 | if (tspec_count > TSID_MAX) { | |
1394 | SLSI_DBG1(sdev, SLSI_MLME, "received %d TSPEC but can accommodate only %d\n", tspec_count, TSID_MAX); | |
1395 | tspec_count = TSID_MAX; | |
1396 | } | |
1397 | ||
1398 | /* Get all WMM TSPEC IE pointers */ | |
1399 | ie = cfg80211_find_ie(WLAN_EID_RIC_DATA, assoc_rsp_ie, assoc_rsp_ie_len); | |
1400 | while (i < tspec_count && ie) { | |
1401 | ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WMM, ie, | |
1402 | assoc_rsp_ie_len - (ie - assoc_rsp_ie)); | |
1403 | if (!ie) | |
1404 | break; | |
1405 | /* re-assoc-res can contain wmm parameter IE and wmm TSPEC IE. | |
1406 | * we want wmm TSPEC Element) | |
1407 | */ | |
1408 | if (ie[1] > 6 && ie[6] == WMM_OUI_SUBTYPE_TSPEC_ELEMENT) { | |
1409 | tspec_ie_arr[i] = ie; | |
1410 | i++; | |
1411 | } | |
1412 | ie += ie[1]; | |
1413 | } | |
1414 | ||
1415 | return i; | |
1416 | } | |
1417 | ||
1418 | void cac_update_roam_traffic_params(struct slsi_dev *sdev, struct net_device *dev) | |
1419 | { | |
1420 | const u8 *tspec_ie_arr[TSID_MAX]; | |
1421 | int assoc_rsp_tspec_count, i; | |
1422 | u32 priority; | |
1423 | struct cac_tspec *itr; | |
1424 | struct wmm_tspec_element *assoc_rsp_tspec; | |
1425 | struct slsi_peer *peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET); | |
1426 | struct netdev_vif *ndev_vif = netdev_priv(dev); | |
1427 | ||
1428 | SLSI_DBG3(sdev, SLSI_MLME, "\n"); | |
1429 | ||
1430 | /* Roamed to new AP. TSPEC admitted to previous AP are no more valid. | |
1431 | * Set all TSPEC to not admitted | |
1432 | */ | |
1433 | cac_deactivate_tspecs(sdev); | |
1434 | ||
1435 | if (!peer) { | |
1436 | SLSI_ERR(sdev, "AP peer entry not found\n"); | |
1437 | return; | |
1438 | } | |
1439 | ||
1440 | /* Find all the admitted TSPECs in assoc resp. */ | |
1441 | assoc_rsp_tspec_count = cac_get_rde_tspec_ie(sdev, peer->assoc_resp_ie->data, | |
1442 | peer->assoc_resp_ie->len, tspec_ie_arr); | |
1443 | ||
1444 | SLSI_DBG3(sdev, SLSI_MLME, "assoc_rsp_tspec_count:%d\n", assoc_rsp_tspec_count); | |
1445 | ||
1446 | if (!assoc_rsp_tspec_count) | |
1447 | return; | |
1448 | ||
1449 | /* update the admitted TSPECs from assoc resp and set traffic params in FW.*/ | |
1450 | for (i = 0; i < assoc_rsp_tspec_count; i++) { | |
1451 | assoc_rsp_tspec = (struct wmm_tspec_element *)tspec_ie_arr[i]; | |
1452 | SLSI_DBG3(sdev, SLSI_MLME, "rsp_tspec:[%d] ts: [%x|%x|%x] medium time[%x]\n", i, | |
1453 | assoc_rsp_tspec->ts_info[0], assoc_rsp_tspec->ts_info[1], assoc_rsp_tspec->ts_info[2], | |
1454 | assoc_rsp_tspec->medium_time); | |
1455 | ||
1456 | itr = find_tspec_entry((assoc_rsp_tspec->ts_info[0] & 0x1E) >> 1, 0); | |
1457 | if (!itr) { | |
1458 | SLSI_DBG3(sdev, SLSI_MLME, "tspec entry not found\n"); | |
1459 | continue; | |
1460 | } | |
1461 | ||
1462 | itr->tspec.medium_time = assoc_rsp_tspec->medium_time; | |
1463 | itr->tspec.minimum_data_rate = assoc_rsp_tspec->minimum_data_rate; | |
1464 | itr->accepted = 1; | |
1465 | cac_query_tspec_field(sdev, itr, "user_priority", &priority); | |
1466 | peer->tspec_established |= BIT(priority); | |
1467 | SLSI_DBG3(sdev, SLSI_MLME, "tspec admitted id[%d]\n", itr->id); | |
1468 | slsi_mlme_set_traffic_parameters(sdev, dev, priority, assoc_rsp_tspec->medium_time, | |
1469 | assoc_rsp_tspec->minimum_data_rate, ndev_vif->sta.sta_bss->bssid); | |
1470 | } | |
1471 | } | |
1472 |