diff options
author | Arend van Spriel <arend@broadcom.com> | 2013-04-03 06:40:37 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-04-03 15:07:05 -0400 |
commit | 6971280aefe437262f6d52339b0b2d5d64ab4e15 (patch) | |
tree | f1a592ec885710291e74520c41d4e4cecee0ed93 /drivers/net/wireless | |
parent | a3e993c78631b918f26db38605678889e3a5e964 (diff) |
brcmfmac: add firmware-signalling hanger functions
The hanger for firmware-signalling is used to retain information for
outstanding transmit packets that await tx status.
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Piotr Haber <phaber@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index b123a80b17cf..a6443c667e33 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | |||
@@ -267,9 +267,64 @@ struct brcmf_fws_mac_descriptor { | |||
267 | struct pktq psq; | 267 | struct pktq psq; |
268 | }; | 268 | }; |
269 | 269 | ||
270 | #define BRCMF_FWS_HANGER_MAXITEMS 1024 | ||
271 | |||
272 | /** | ||
273 | * enum brcmf_fws_hanger_item_state - state of hanger item. | ||
274 | * | ||
275 | * @WLFC_HANGER_ITEM_STATE_FREE: item is free for use. | ||
276 | * @WLFC_HANGER_ITEM_STATE_INUSE: item is in use. | ||
277 | * @WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed. | ||
278 | */ | ||
279 | enum brcmf_fws_hanger_item_state { | ||
280 | WLFC_HANGER_ITEM_STATE_FREE = 1, | ||
281 | WLFC_HANGER_ITEM_STATE_INUSE, | ||
282 | WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED | ||
283 | }; | ||
284 | |||
285 | |||
286 | /** | ||
287 | * struct brcmf_fws_hanger_item - single entry for tx pending packet. | ||
288 | * | ||
289 | * @state: entry is either free or occupied. | ||
290 | * @gen: generation. | ||
291 | * @identifier: packet identifier. | ||
292 | * @pkt: packet itself. | ||
293 | */ | ||
294 | struct brcmf_fws_hanger_item { | ||
295 | enum brcmf_fws_hanger_item_state state; | ||
296 | u8 gen; | ||
297 | u8 pad[2]; | ||
298 | u32 identifier; | ||
299 | struct sk_buff *pkt; | ||
300 | }; | ||
301 | |||
302 | /** | ||
303 | * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus. | ||
304 | * | ||
305 | * @max_items: number of packets it can hold. | ||
306 | * @pushed: packets pushed to await txstatus. | ||
307 | * @popped: packets popped upon handling txstatus. | ||
308 | * @failed_to_push: packets that could not be pushed. | ||
309 | * @failed_to_pop: packets that could not be popped. | ||
310 | * @failed_slotfind: packets for which failed to find an entry. | ||
311 | * @slot_pos: last returned item index for a free entry. | ||
312 | * @items: array of hanger items. | ||
313 | */ | ||
314 | struct brcmf_fws_hanger { | ||
315 | u32 pushed; | ||
316 | u32 popped; | ||
317 | u32 failed_to_push; | ||
318 | u32 failed_to_pop; | ||
319 | u32 failed_slotfind; | ||
320 | u32 slot_pos; | ||
321 | struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS]; | ||
322 | }; | ||
323 | |||
270 | struct brcmf_fws_info { | 324 | struct brcmf_fws_info { |
271 | struct brcmf_pub *drvr; | 325 | struct brcmf_pub *drvr; |
272 | struct brcmf_fws_stats stats; | 326 | struct brcmf_fws_stats stats; |
327 | struct brcmf_fws_hanger hanger; | ||
273 | struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; | 328 | struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; |
274 | struct brcmf_fws_mac_descriptor other; | 329 | struct brcmf_fws_mac_descriptor other; |
275 | int fifo_credit[NL80211_NUM_ACS+1+1]; | 330 | int fifo_credit[NL80211_NUM_ACS+1+1]; |
@@ -296,6 +351,145 @@ static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, | |||
296 | } | 351 | } |
297 | #undef BRCMF_FWS_TLV_DEF | 352 | #undef BRCMF_FWS_TLV_DEF |
298 | 353 | ||
354 | static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) | ||
355 | { | ||
356 | int i; | ||
357 | |||
358 | brcmf_dbg(TRACE, "enter\n"); | ||
359 | memset(hanger, 0, sizeof(*hanger)); | ||
360 | for (i = 0; i < ARRAY_SIZE(hanger->items); i++) | ||
361 | hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; | ||
362 | } | ||
363 | |||
364 | static __used u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) | ||
365 | { | ||
366 | u32 i; | ||
367 | |||
368 | brcmf_dbg(TRACE, "enter\n"); | ||
369 | i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; | ||
370 | |||
371 | while (i != h->slot_pos) { | ||
372 | if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) { | ||
373 | h->slot_pos = i; | ||
374 | return i; | ||
375 | } | ||
376 | i++; | ||
377 | if (i == BRCMF_FWS_HANGER_MAXITEMS) | ||
378 | i = 0; | ||
379 | } | ||
380 | brcmf_err("all slots occupied\n"); | ||
381 | h->failed_slotfind++; | ||
382 | return BRCMF_FWS_HANGER_MAXITEMS; | ||
383 | } | ||
384 | |||
385 | static __used int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, | ||
386 | struct sk_buff *pkt, u32 slot_id) | ||
387 | { | ||
388 | brcmf_dbg(TRACE, "enter\n"); | ||
389 | if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) | ||
390 | return -ENOENT; | ||
391 | |||
392 | if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) { | ||
393 | brcmf_err("slot is not free\n"); | ||
394 | h->failed_to_push++; | ||
395 | return -EINVAL; | ||
396 | } | ||
397 | |||
398 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE; | ||
399 | h->items[slot_id].pkt = pkt; | ||
400 | h->items[slot_id].identifier = slot_id; | ||
401 | h->pushed++; | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static __used int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, | ||
406 | u32 slot_id, struct sk_buff **pktout, | ||
407 | bool remove_item) | ||
408 | { | ||
409 | brcmf_dbg(TRACE, "enter\n"); | ||
410 | if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) | ||
411 | return -ENOENT; | ||
412 | |||
413 | if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { | ||
414 | brcmf_err("entry not in use\n"); | ||
415 | h->failed_to_pop++; | ||
416 | return -EINVAL; | ||
417 | } | ||
418 | |||
419 | *pktout = h->items[slot_id].pkt; | ||
420 | if (remove_item) { | ||
421 | h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; | ||
422 | h->items[slot_id].pkt = NULL; | ||
423 | h->items[slot_id].identifier = 0; | ||
424 | h->items[slot_id].gen = 0xff; | ||
425 | h->popped++; | ||
426 | } | ||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static __used int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, | ||
431 | u32 slot_id, u8 gen) | ||
432 | { | ||
433 | brcmf_dbg(TRACE, "enter\n"); | ||
434 | |||
435 | if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) | ||
436 | return -ENOENT; | ||
437 | |||
438 | h->items[slot_id].gen = gen; | ||
439 | |||
440 | if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_INUSE) { | ||
441 | brcmf_err("entry not in use\n"); | ||
442 | return -EINVAL; | ||
443 | } | ||
444 | |||
445 | h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED; | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static __used int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, | ||
450 | struct sk_buff *pkt, u32 slot_id, | ||
451 | int *gen) | ||
452 | { | ||
453 | brcmf_dbg(TRACE, "enter\n"); | ||
454 | *gen = 0xff; | ||
455 | |||
456 | if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) | ||
457 | return -ENOENT; | ||
458 | |||
459 | if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { | ||
460 | brcmf_err("slot not in use\n"); | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | |||
464 | *gen = hanger->items[slot_id].gen; | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | static void brcmf_fws_hanger_cleanup(struct brcmf_fws_hanger *h, | ||
469 | bool (*fn)(struct sk_buff *, void *), | ||
470 | int ifidx) | ||
471 | { | ||
472 | struct sk_buff *skb; | ||
473 | int i; | ||
474 | enum brcmf_fws_hanger_item_state s; | ||
475 | |||
476 | brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); | ||
477 | for (i = 0; i < ARRAY_SIZE(h->items); i++) { | ||
478 | s = h->items[i].state; | ||
479 | if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE || | ||
480 | s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { | ||
481 | skb = h->items[i].pkt; | ||
482 | if (fn == NULL || fn(skb, &ifidx)) { | ||
483 | /* suppress packets freed from psq */ | ||
484 | if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE) | ||
485 | brcmu_pkt_buf_free_skb(skb); | ||
486 | h->items[i].state = | ||
487 | BRCMF_FWS_HANGER_ITEM_STATE_FREE; | ||
488 | } | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | |||
299 | static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, | 493 | static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, |
300 | u8 *addr, u8 ifidx) | 494 | u8 *addr, u8 ifidx) |
301 | { | 495 | { |
@@ -379,6 +573,7 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) | |||
379 | brcmf_fws_mac_desc_cleanup(&table[i], matchfn, ifidx); | 573 | brcmf_fws_mac_desc_cleanup(&table[i], matchfn, ifidx); |
380 | 574 | ||
381 | brcmf_fws_mac_desc_cleanup(&fws->other, matchfn, ifidx); | 575 | brcmf_fws_mac_desc_cleanup(&fws->other, matchfn, ifidx); |
576 | brcmf_fws_hanger_cleanup(&fws->hanger, matchfn, ifidx); | ||
382 | } | 577 | } |
383 | 578 | ||
384 | static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) | 579 | static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) |
@@ -511,6 +706,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr) | |||
511 | goto fail; | 706 | goto fail; |
512 | } | 707 | } |
513 | 708 | ||
709 | brcmf_fws_hanger_init(&drvr->fws->hanger); | ||
710 | |||
514 | /* create debugfs file for statistics */ | 711 | /* create debugfs file for statistics */ |
515 | brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); | 712 | brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); |
516 | 713 | ||