diff options
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r-- | net/mac80211/agg-tx.c | 186 |
1 files changed, 126 insertions, 60 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 1232d9f01ca9..0217b68c47ca 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -132,9 +132,24 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
132 | 132 | ||
133 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 133 | state = &sta->ampdu_mlme.tid_state_tx[tid]; |
134 | 134 | ||
135 | if (local->hw.ampdu_queues) | 135 | if (local->hw.ampdu_queues) { |
136 | ieee80211_stop_queue(&local->hw, sta->tid_to_tx_q[tid]); | 136 | if (initiator) { |
137 | /* | ||
138 | * Stop the AC queue to avoid issues where we send | ||
139 | * unaggregated frames already before the delba. | ||
140 | */ | ||
141 | ieee80211_stop_queue_by_reason(&local->hw, | ||
142 | local->hw.queues + sta->tid_to_tx_q[tid], | ||
143 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
144 | } | ||
137 | 145 | ||
146 | /* | ||
147 | * Pretend the driver woke the queue, just in case | ||
148 | * it disabled it before the session was stopped. | ||
149 | */ | ||
150 | ieee80211_wake_queue( | ||
151 | &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]); | ||
152 | } | ||
138 | *state = HT_AGG_STATE_REQ_STOP_BA_MSK | | 153 | *state = HT_AGG_STATE_REQ_STOP_BA_MSK | |
139 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); | 154 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); |
140 | 155 | ||
@@ -144,8 +159,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
144 | /* HW shall not deny going back to legacy */ | 159 | /* HW shall not deny going back to legacy */ |
145 | if (WARN_ON(ret)) { | 160 | if (WARN_ON(ret)) { |
146 | *state = HT_AGG_STATE_OPERATIONAL; | 161 | *state = HT_AGG_STATE_OPERATIONAL; |
147 | if (local->hw.ampdu_queues) | ||
148 | ieee80211_wake_queue(&local->hw, sta->tid_to_tx_q[tid]); | ||
149 | } | 162 | } |
150 | 163 | ||
151 | return ret; | 164 | return ret; |
@@ -189,14 +202,19 @@ static void sta_addba_resp_timer_expired(unsigned long data) | |||
189 | spin_unlock_bh(&sta->lock); | 202 | spin_unlock_bh(&sta->lock); |
190 | } | 203 | } |
191 | 204 | ||
205 | static inline int ieee80211_ac_from_tid(int tid) | ||
206 | { | ||
207 | return ieee802_1d_to_ac[tid & 7]; | ||
208 | } | ||
209 | |||
192 | int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | 210 | int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) |
193 | { | 211 | { |
194 | struct ieee80211_local *local = hw_to_local(hw); | 212 | struct ieee80211_local *local = hw_to_local(hw); |
195 | struct sta_info *sta; | 213 | struct sta_info *sta; |
196 | struct ieee80211_sub_if_data *sdata; | 214 | struct ieee80211_sub_if_data *sdata; |
197 | u16 start_seq_num; | ||
198 | u8 *state; | 215 | u8 *state; |
199 | int ret = 0; | 216 | int i, qn = -1, ret = 0; |
217 | u16 start_seq_num; | ||
200 | 218 | ||
201 | if (WARN_ON(!local->ops->ampdu_action)) | 219 | if (WARN_ON(!local->ops->ampdu_action)) |
202 | return -EINVAL; | 220 | return -EINVAL; |
@@ -209,6 +227,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
209 | ra, tid); | 227 | ra, tid); |
210 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 228 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
211 | 229 | ||
230 | if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) { | ||
231 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
232 | printk(KERN_DEBUG "rejecting on voice AC\n"); | ||
233 | #endif | ||
234 | return -EINVAL; | ||
235 | } | ||
236 | |||
212 | rcu_read_lock(); | 237 | rcu_read_lock(); |
213 | 238 | ||
214 | sta = sta_info_get(local, ra); | 239 | sta = sta_info_get(local, ra); |
@@ -217,7 +242,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
217 | printk(KERN_DEBUG "Could not find the station\n"); | 242 | printk(KERN_DEBUG "Could not find the station\n"); |
218 | #endif | 243 | #endif |
219 | ret = -ENOENT; | 244 | ret = -ENOENT; |
220 | goto exit; | 245 | goto unlock; |
221 | } | 246 | } |
222 | 247 | ||
223 | /* | 248 | /* |
@@ -230,11 +255,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
230 | sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | 255 | sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN && |
231 | sta->sdata->vif.type != NL80211_IFTYPE_AP) { | 256 | sta->sdata->vif.type != NL80211_IFTYPE_AP) { |
232 | ret = -EINVAL; | 257 | ret = -EINVAL; |
233 | goto exit; | 258 | goto unlock; |
234 | } | 259 | } |
235 | 260 | ||
236 | spin_lock_bh(&sta->lock); | 261 | spin_lock_bh(&sta->lock); |
237 | 262 | ||
263 | sdata = sta->sdata; | ||
264 | |||
238 | /* we have tried too many times, receiver does not want A-MPDU */ | 265 | /* we have tried too many times, receiver does not want A-MPDU */ |
239 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { | 266 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { |
240 | ret = -EBUSY; | 267 | ret = -EBUSY; |
@@ -252,6 +279,42 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
252 | goto err_unlock_sta; | 279 | goto err_unlock_sta; |
253 | } | 280 | } |
254 | 281 | ||
282 | if (hw->ampdu_queues) { | ||
283 | spin_lock(&local->queue_stop_reason_lock); | ||
284 | /* reserve a new queue for this session */ | ||
285 | for (i = 0; i < local->hw.ampdu_queues; i++) { | ||
286 | if (local->ampdu_ac_queue[i] < 0) { | ||
287 | qn = i; | ||
288 | local->ampdu_ac_queue[qn] = | ||
289 | ieee80211_ac_from_tid(tid); | ||
290 | break; | ||
291 | } | ||
292 | } | ||
293 | spin_unlock(&local->queue_stop_reason_lock); | ||
294 | |||
295 | if (qn < 0) { | ||
296 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
297 | printk(KERN_DEBUG "BA request denied - " | ||
298 | "queue unavailable for tid %d\n", tid); | ||
299 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
300 | ret = -ENOSPC; | ||
301 | goto err_unlock_sta; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * If we successfully allocate the session, we can't have | ||
306 | * anything going on on the queue this TID maps into, so | ||
307 | * stop it for now. This is a "virtual" stop using the same | ||
308 | * mechanism that drivers will use. | ||
309 | * | ||
310 | * XXX: queue up frames for this session in the sta_info | ||
311 | * struct instead to avoid hitting all other STAs. | ||
312 | */ | ||
313 | ieee80211_stop_queue_by_reason( | ||
314 | &local->hw, hw->queues + qn, | ||
315 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
316 | } | ||
317 | |||
255 | /* prepare A-MPDU MLME for Tx aggregation */ | 318 | /* prepare A-MPDU MLME for Tx aggregation */ |
256 | sta->ampdu_mlme.tid_tx[tid] = | 319 | sta->ampdu_mlme.tid_tx[tid] = |
257 | kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); | 320 | kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); |
@@ -262,8 +325,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
262 | tid); | 325 | tid); |
263 | #endif | 326 | #endif |
264 | ret = -ENOMEM; | 327 | ret = -ENOMEM; |
265 | goto err_unlock_sta; | 328 | goto err_return_queue; |
266 | } | 329 | } |
330 | |||
267 | /* Tx timer */ | 331 | /* Tx timer */ |
268 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = | 332 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = |
269 | sta_addba_resp_timer_expired; | 333 | sta_addba_resp_timer_expired; |
@@ -271,49 +335,25 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
271 | (unsigned long)&sta->timer_to_tid[tid]; | 335 | (unsigned long)&sta->timer_to_tid[tid]; |
272 | init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); | 336 | init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); |
273 | 337 | ||
274 | if (hw->ampdu_queues) { | ||
275 | /* create a new queue for this aggregation */ | ||
276 | ret = ieee80211_ht_agg_queue_add(local, sta, tid); | ||
277 | |||
278 | /* case no queue is available to aggregation | ||
279 | * don't switch to aggregation */ | ||
280 | if (ret) { | ||
281 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
282 | printk(KERN_DEBUG "BA request denied - " | ||
283 | "queue unavailable for tid %d\n", tid); | ||
284 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
285 | goto err_unlock_queue; | ||
286 | } | ||
287 | } | ||
288 | sdata = sta->sdata; | ||
289 | |||
290 | /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the | 338 | /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the |
291 | * call back right away, it must see that the flow has begun */ | 339 | * call back right away, it must see that the flow has begun */ |
292 | *state |= HT_ADDBA_REQUESTED_MSK; | 340 | *state |= HT_ADDBA_REQUESTED_MSK; |
293 | 341 | ||
294 | /* This is slightly racy because the queue isn't stopped */ | ||
295 | start_seq_num = sta->tid_seq[tid]; | 342 | start_seq_num = sta->tid_seq[tid]; |
296 | 343 | ||
297 | ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START, | 344 | ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START, |
298 | &sta->sta, tid, &start_seq_num); | 345 | &sta->sta, tid, &start_seq_num); |
299 | 346 | ||
300 | if (ret) { | 347 | if (ret) { |
301 | /* No need to requeue the packets in the agg queue, since we | ||
302 | * held the tx lock: no packet could be enqueued to the newly | ||
303 | * allocated queue */ | ||
304 | if (hw->ampdu_queues) | ||
305 | ieee80211_ht_agg_queue_remove(local, sta, tid, 0); | ||
306 | #ifdef CONFIG_MAC80211_HT_DEBUG | 348 | #ifdef CONFIG_MAC80211_HT_DEBUG |
307 | printk(KERN_DEBUG "BA request denied - HW unavailable for" | 349 | printk(KERN_DEBUG "BA request denied - HW unavailable for" |
308 | " tid %d\n", tid); | 350 | " tid %d\n", tid); |
309 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 351 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
310 | *state = HT_AGG_STATE_IDLE; | 352 | *state = HT_AGG_STATE_IDLE; |
311 | goto err_unlock_queue; | 353 | goto err_free; |
312 | } | 354 | } |
355 | sta->tid_to_tx_q[tid] = qn; | ||
313 | 356 | ||
314 | /* Will put all the packets in the new SW queue */ | ||
315 | if (hw->ampdu_queues) | ||
316 | ieee80211_requeue(local, ieee802_1d_to_ac[tid]); | ||
317 | spin_unlock_bh(&sta->lock); | 357 | spin_unlock_bh(&sta->lock); |
318 | 358 | ||
319 | /* send an addBA request */ | 359 | /* send an addBA request */ |
@@ -322,7 +362,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
322 | sta->ampdu_mlme.dialog_token_allocator; | 362 | sta->ampdu_mlme.dialog_token_allocator; |
323 | sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; | 363 | sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; |
324 | 364 | ||
325 | |||
326 | ieee80211_send_addba_request(sta->sdata, ra, tid, | 365 | ieee80211_send_addba_request(sta->sdata, ra, tid, |
327 | sta->ampdu_mlme.tid_tx[tid]->dialog_token, | 366 | sta->ampdu_mlme.tid_tx[tid]->dialog_token, |
328 | sta->ampdu_mlme.tid_tx[tid]->ssn, | 367 | sta->ampdu_mlme.tid_tx[tid]->ssn, |
@@ -334,15 +373,24 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
334 | #ifdef CONFIG_MAC80211_HT_DEBUG | 373 | #ifdef CONFIG_MAC80211_HT_DEBUG |
335 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | 374 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); |
336 | #endif | 375 | #endif |
337 | goto exit; | 376 | goto unlock; |
338 | 377 | ||
339 | err_unlock_queue: | 378 | err_free: |
340 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 379 | kfree(sta->ampdu_mlme.tid_tx[tid]); |
341 | sta->ampdu_mlme.tid_tx[tid] = NULL; | 380 | sta->ampdu_mlme.tid_tx[tid] = NULL; |
342 | ret = -EBUSY; | 381 | err_return_queue: |
343 | err_unlock_sta: | 382 | if (qn >= 0) { |
383 | /* We failed, so start queue again right away. */ | ||
384 | ieee80211_wake_queue_by_reason(hw, hw->queues + qn, | ||
385 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
386 | /* give queue back to pool */ | ||
387 | spin_lock(&local->queue_stop_reason_lock); | ||
388 | local->ampdu_ac_queue[qn] = -1; | ||
389 | spin_unlock(&local->queue_stop_reason_lock); | ||
390 | } | ||
391 | err_unlock_sta: | ||
344 | spin_unlock_bh(&sta->lock); | 392 | spin_unlock_bh(&sta->lock); |
345 | exit: | 393 | unlock: |
346 | rcu_read_unlock(); | 394 | rcu_read_unlock(); |
347 | return ret; | 395 | return ret; |
348 | } | 396 | } |
@@ -375,7 +423,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
375 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 423 | state = &sta->ampdu_mlme.tid_state_tx[tid]; |
376 | spin_lock_bh(&sta->lock); | 424 | spin_lock_bh(&sta->lock); |
377 | 425 | ||
378 | if (!(*state & HT_ADDBA_REQUESTED_MSK)) { | 426 | if (WARN_ON(!(*state & HT_ADDBA_REQUESTED_MSK))) { |
379 | #ifdef CONFIG_MAC80211_HT_DEBUG | 427 | #ifdef CONFIG_MAC80211_HT_DEBUG |
380 | printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", | 428 | printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", |
381 | *state); | 429 | *state); |
@@ -385,7 +433,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
385 | return; | 433 | return; |
386 | } | 434 | } |
387 | 435 | ||
388 | WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK); | 436 | if (WARN_ON(*state & HT_ADDBA_DRV_READY_MSK)) |
437 | goto out; | ||
389 | 438 | ||
390 | *state |= HT_ADDBA_DRV_READY_MSK; | 439 | *state |= HT_ADDBA_DRV_READY_MSK; |
391 | 440 | ||
@@ -393,9 +442,18 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
393 | #ifdef CONFIG_MAC80211_HT_DEBUG | 442 | #ifdef CONFIG_MAC80211_HT_DEBUG |
394 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); | 443 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); |
395 | #endif | 444 | #endif |
396 | if (hw->ampdu_queues) | 445 | if (hw->ampdu_queues) { |
397 | ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); | 446 | /* |
447 | * Wake up this queue, we stopped it earlier, | ||
448 | * this will in turn wake the entire AC. | ||
449 | */ | ||
450 | ieee80211_wake_queue_by_reason(hw, | ||
451 | hw->queues + sta->tid_to_tx_q[tid], | ||
452 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
453 | } | ||
398 | } | 454 | } |
455 | |||
456 | out: | ||
399 | spin_unlock_bh(&sta->lock); | 457 | spin_unlock_bh(&sta->lock); |
400 | rcu_read_unlock(); | 458 | rcu_read_unlock(); |
401 | } | 459 | } |
@@ -485,7 +543,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
485 | struct ieee80211_local *local = hw_to_local(hw); | 543 | struct ieee80211_local *local = hw_to_local(hw); |
486 | struct sta_info *sta; | 544 | struct sta_info *sta; |
487 | u8 *state; | 545 | u8 *state; |
488 | int agg_queue; | ||
489 | 546 | ||
490 | if (tid >= STA_TID_NUM) { | 547 | if (tid >= STA_TID_NUM) { |
491 | #ifdef CONFIG_MAC80211_HT_DEBUG | 548 | #ifdef CONFIG_MAC80211_HT_DEBUG |
@@ -527,19 +584,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
527 | ieee80211_send_delba(sta->sdata, ra, tid, | 584 | ieee80211_send_delba(sta->sdata, ra, tid, |
528 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); | 585 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); |
529 | 586 | ||
530 | if (hw->ampdu_queues) { | 587 | spin_lock_bh(&sta->lock); |
531 | agg_queue = sta->tid_to_tx_q[tid]; | ||
532 | ieee80211_ht_agg_queue_remove(local, sta, tid, 1); | ||
533 | 588 | ||
534 | /* We just requeued the all the frames that were in the | 589 | if (*state & HT_AGG_STATE_INITIATOR_MSK && |
535 | * removed queue, and since we might miss a softirq we do | 590 | hw->ampdu_queues) { |
536 | * netif_schedule_queue. ieee80211_wake_queue is not used | 591 | /* |
537 | * here as this queue is not necessarily stopped | 592 | * Wake up this queue, we stopped it earlier, |
593 | * this will in turn wake the entire AC. | ||
538 | */ | 594 | */ |
539 | netif_schedule_queue(netdev_get_tx_queue(local->mdev, | 595 | ieee80211_wake_queue_by_reason(hw, |
540 | agg_queue)); | 596 | hw->queues + sta->tid_to_tx_q[tid], |
597 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
541 | } | 598 | } |
542 | spin_lock_bh(&sta->lock); | 599 | |
543 | *state = HT_AGG_STATE_IDLE; | 600 | *state = HT_AGG_STATE_IDLE; |
544 | sta->ampdu_mlme.addba_req_num[tid] = 0; | 601 | sta->ampdu_mlme.addba_req_num[tid] = 0; |
545 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 602 | kfree(sta->ampdu_mlme.tid_tx[tid]); |
@@ -613,12 +670,21 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, | |||
613 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 670 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
614 | if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) | 671 | if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) |
615 | == WLAN_STATUS_SUCCESS) { | 672 | == WLAN_STATUS_SUCCESS) { |
673 | u8 curstate = *state; | ||
674 | |||
616 | *state |= HT_ADDBA_RECEIVED_MSK; | 675 | *state |= HT_ADDBA_RECEIVED_MSK; |
617 | sta->ampdu_mlme.addba_req_num[tid] = 0; | ||
618 | 676 | ||
619 | if (*state == HT_AGG_STATE_OPERATIONAL && | 677 | if (hw->ampdu_queues && *state != curstate && |
620 | local->hw.ampdu_queues) | 678 | *state == HT_AGG_STATE_OPERATIONAL) { |
621 | ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); | 679 | /* |
680 | * Wake up this queue, we stopped it earlier, | ||
681 | * this will in turn wake the entire AC. | ||
682 | */ | ||
683 | ieee80211_wake_queue_by_reason(hw, | ||
684 | hw->queues + sta->tid_to_tx_q[tid], | ||
685 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
686 | } | ||
687 | sta->ampdu_mlme.addba_req_num[tid] = 0; | ||
622 | 688 | ||
623 | if (local->ops->ampdu_action) { | 689 | if (local->ops->ampdu_action) { |
624 | (void)local->ops->ampdu_action(hw, | 690 | (void)local->ops->ampdu_action(hw, |