diff options
author | Holger Schurig <hs4233@mail.mn-solutions.de> | 2008-04-01 08:50:43 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-04-16 15:59:56 -0400 |
commit | 7919b89c8276d657976d4d4d6b7cb58ea1aa08c3 (patch) | |
tree | 31fc24e2f8b7d8eeee67347333e078591796d4b7 /drivers/net/wireless/libertas/main.c | |
parent | 98dd6a575928ed9c42130d208e6bfb0f7a914d5a (diff) |
libertas: convert libertas driver to use an event/cmdresp queue
This patch (co-developed by Dan Williams and Holger Schurig) uses a kfifo
object for events and a swapping buffer scheme for the command response to
preserve the zero-copy semantics of the CF driver and keep memory usage low.
The main thread should only ever touch the buffer indexed by priv->resp_idx,
while the interface code is free to write to the second buffer, then swap
priv->resp_idx under the driver spinlock. The firmware specs only permit
one in-flight command, so there will only ever be one command response to
process at a time.
Signed-off-by: Holger Schurig <hs4233@mail.mn-solutions.de>
Signed-off-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/main.c')
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 158 |
1 files changed, 88 insertions, 70 deletions
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 8f1122610974..406f54d40956 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/netdevice.h> | 10 | #include <linux/netdevice.h> |
11 | #include <linux/if_arp.h> | 11 | #include <linux/if_arp.h> |
12 | #include <linux/kthread.h> | 12 | #include <linux/kthread.h> |
13 | #include <linux/kfifo.h> | ||
13 | 14 | ||
14 | #include <net/iw_handler.h> | 15 | #include <net/iw_handler.h> |
15 | #include <net/ieee80211.h> | 16 | #include <net/ieee80211.h> |
@@ -480,10 +481,9 @@ static void lbs_tx_timeout(struct net_device *dev) | |||
480 | 481 | ||
481 | dev->trans_start = jiffies; | 482 | dev->trans_start = jiffies; |
482 | 483 | ||
483 | if (priv->currenttxskb) { | 484 | if (priv->currenttxskb) |
484 | priv->eventcause = 0x01000000; | 485 | lbs_send_tx_feedback(priv, 0); |
485 | lbs_send_tx_feedback(priv); | 486 | |
486 | } | ||
487 | /* XX: Shouldn't we also call into the hw-specific driver | 487 | /* XX: Shouldn't we also call into the hw-specific driver |
488 | to kick it somehow? */ | 488 | to kick it somehow? */ |
489 | lbs_host_to_card_done(priv); | 489 | lbs_host_to_card_done(priv); |
@@ -659,7 +659,6 @@ static int lbs_thread(void *data) | |||
659 | struct net_device *dev = data; | 659 | struct net_device *dev = data; |
660 | struct lbs_private *priv = dev->priv; | 660 | struct lbs_private *priv = dev->priv; |
661 | wait_queue_t wait; | 661 | wait_queue_t wait; |
662 | u8 ireg = 0; | ||
663 | 662 | ||
664 | lbs_deb_enter(LBS_DEB_THREAD); | 663 | lbs_deb_enter(LBS_DEB_THREAD); |
665 | 664 | ||
@@ -667,9 +666,10 @@ static int lbs_thread(void *data) | |||
667 | 666 | ||
668 | for (;;) { | 667 | for (;;) { |
669 | int shouldsleep; | 668 | int shouldsleep; |
669 | u8 resp_idx; | ||
670 | 670 | ||
671 | lbs_deb_thread( "main-thread 111: intcounter=%d currenttxskb=%p dnld_sent=%d\n", | 671 | lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", |
672 | priv->intcounter, priv->currenttxskb, priv->dnld_sent); | 672 | priv->currenttxskb, priv->dnld_sent); |
673 | 673 | ||
674 | add_wait_queue(&priv->waitq, &wait); | 674 | add_wait_queue(&priv->waitq, &wait); |
675 | set_current_state(TASK_INTERRUPTIBLE); | 675 | set_current_state(TASK_INTERRUPTIBLE); |
@@ -681,8 +681,6 @@ static int lbs_thread(void *data) | |||
681 | shouldsleep = 1; /* We need to wait until we're _told_ to die */ | 681 | shouldsleep = 1; /* We need to wait until we're _told_ to die */ |
682 | else if (priv->psstate == PS_STATE_SLEEP) | 682 | else if (priv->psstate == PS_STATE_SLEEP) |
683 | shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ | 683 | shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ |
684 | else if (priv->intcounter) | ||
685 | shouldsleep = 0; /* Interrupt pending. Deal with it now */ | ||
686 | else if (priv->cmd_timed_out) | 684 | else if (priv->cmd_timed_out) |
687 | shouldsleep = 0; /* Command timed out. Recover */ | 685 | shouldsleep = 0; /* Command timed out. Recover */ |
688 | else if (!priv->fw_ready) | 686 | else if (!priv->fw_ready) |
@@ -695,29 +693,34 @@ static int lbs_thread(void *data) | |||
695 | shouldsleep = 1; /* Can't send a command; one already running */ | 693 | shouldsleep = 1; /* Can't send a command; one already running */ |
696 | else if (!list_empty(&priv->cmdpendingq)) | 694 | else if (!list_empty(&priv->cmdpendingq)) |
697 | shouldsleep = 0; /* We have a command to send */ | 695 | shouldsleep = 0; /* We have a command to send */ |
696 | else if (__kfifo_len(priv->event_fifo)) | ||
697 | shouldsleep = 0; /* We have an event to process */ | ||
698 | else if (priv->resp_len[priv->resp_idx]) | ||
699 | shouldsleep = 0; /* We have a command response */ | ||
698 | else | 700 | else |
699 | shouldsleep = 1; /* No command */ | 701 | shouldsleep = 1; /* No command */ |
700 | 702 | ||
701 | if (shouldsleep) { | 703 | if (shouldsleep) { |
702 | lbs_deb_thread("main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", | 704 | lbs_deb_thread("sleeping, connect_status %d, " |
703 | priv->connect_status, priv->intcounter, | 705 | "ps_mode %d, ps_state %d\n", |
704 | priv->psmode, priv->psstate); | 706 | priv->connect_status, |
707 | priv->psmode, priv->psstate); | ||
705 | spin_unlock_irq(&priv->driver_lock); | 708 | spin_unlock_irq(&priv->driver_lock); |
706 | schedule(); | 709 | schedule(); |
707 | } else | 710 | } else |
708 | spin_unlock_irq(&priv->driver_lock); | 711 | spin_unlock_irq(&priv->driver_lock); |
709 | 712 | ||
710 | lbs_deb_thread("main-thread 222 (waking up): intcounter=%d currenttxskb=%p dnld_sent=%d\n", | 713 | lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", |
711 | priv->intcounter, priv->currenttxskb, priv->dnld_sent); | 714 | priv->currenttxskb, priv->dnld_sent); |
712 | 715 | ||
713 | set_current_state(TASK_RUNNING); | 716 | set_current_state(TASK_RUNNING); |
714 | remove_wait_queue(&priv->waitq, &wait); | 717 | remove_wait_queue(&priv->waitq, &wait); |
715 | 718 | ||
716 | lbs_deb_thread("main-thread 333: intcounter=%d currenttxskb=%p dnld_sent=%d\n", | 719 | lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", |
717 | priv->intcounter, priv->currenttxskb, priv->dnld_sent); | 720 | priv->currenttxskb, priv->dnld_sent); |
718 | 721 | ||
719 | if (kthread_should_stop()) { | 722 | if (kthread_should_stop()) { |
720 | lbs_deb_thread("main-thread: break from main thread\n"); | 723 | lbs_deb_thread("break from main thread\n"); |
721 | break; | 724 | break; |
722 | } | 725 | } |
723 | 726 | ||
@@ -726,35 +729,23 @@ static int lbs_thread(void *data) | |||
726 | continue; | 729 | continue; |
727 | } | 730 | } |
728 | 731 | ||
729 | spin_lock_irq(&priv->driver_lock); | 732 | lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", |
730 | 733 | priv->currenttxskb, priv->dnld_sent); | |
731 | if (priv->intcounter) { | ||
732 | u8 int_status; | ||
733 | 734 | ||
734 | priv->intcounter = 0; | 735 | spin_lock_irq(&priv->driver_lock); |
735 | int_status = priv->hw_get_int_status(priv, &ireg); | 736 | /* Process any pending command response */ |
736 | 737 | resp_idx = priv->resp_idx; | |
737 | if (int_status) { | 738 | if (priv->resp_len[resp_idx]) { |
738 | lbs_deb_thread("main-thread: reading HOST_INT_STATUS_REG failed\n"); | ||
739 | spin_unlock_irq(&priv->driver_lock); | ||
740 | continue; | ||
741 | } | ||
742 | priv->hisregcpy |= ireg; | ||
743 | } | ||
744 | |||
745 | lbs_deb_thread("main-thread 444: intcounter=%d currenttxskb=%p dnld_sent=%d\n", | ||
746 | priv->intcounter, priv->currenttxskb, priv->dnld_sent); | ||
747 | |||
748 | /* command response? */ | ||
749 | if (priv->hisregcpy & MRVDRV_CMD_UPLD_RDY) { | ||
750 | lbs_deb_thread("main-thread: cmd response ready\n"); | ||
751 | |||
752 | priv->hisregcpy &= ~MRVDRV_CMD_UPLD_RDY; | ||
753 | spin_unlock_irq(&priv->driver_lock); | 739 | spin_unlock_irq(&priv->driver_lock); |
754 | lbs_process_rx_command(priv); | 740 | lbs_process_command_response(priv, |
741 | priv->resp_buf[resp_idx], | ||
742 | priv->resp_len[resp_idx]); | ||
755 | spin_lock_irq(&priv->driver_lock); | 743 | spin_lock_irq(&priv->driver_lock); |
744 | priv->resp_len[resp_idx] = 0; | ||
756 | } | 745 | } |
746 | spin_unlock_irq(&priv->driver_lock); | ||
757 | 747 | ||
748 | /* command timeout stuff */ | ||
758 | if (priv->cmd_timed_out && priv->cur_cmd) { | 749 | if (priv->cmd_timed_out && priv->cur_cmd) { |
759 | struct cmd_ctrl_node *cmdnode = priv->cur_cmd; | 750 | struct cmd_ctrl_node *cmdnode = priv->cur_cmd; |
760 | 751 | ||
@@ -775,21 +766,18 @@ static int lbs_thread(void *data) | |||
775 | } | 766 | } |
776 | priv->cmd_timed_out = 0; | 767 | priv->cmd_timed_out = 0; |
777 | 768 | ||
778 | /* Any Card Event */ | 769 | /* Process hardware events, e.g. card removed, link lost */ |
779 | if (priv->hisregcpy & MRVDRV_CARDEVENT) { | 770 | spin_lock_irq(&priv->driver_lock); |
780 | lbs_deb_thread("main-thread: Card Event Activity\n"); | 771 | while (__kfifo_len(priv->event_fifo)) { |
781 | 772 | u32 event; | |
782 | priv->hisregcpy &= ~MRVDRV_CARDEVENT; | ||
783 | 773 | ||
784 | if (priv->hw_read_event_cause(priv)) { | 774 | __kfifo_get(priv->event_fifo, (unsigned char *) &event, |
785 | lbs_pr_alert("main-thread: hw_read_event_cause failed\n"); | 775 | sizeof(event)); |
786 | spin_unlock_irq(&priv->driver_lock); | ||
787 | continue; | ||
788 | } | ||
789 | spin_unlock_irq(&priv->driver_lock); | ||
790 | lbs_process_event(priv); | ||
791 | } else | ||
792 | spin_unlock_irq(&priv->driver_lock); | 776 | spin_unlock_irq(&priv->driver_lock); |
777 | lbs_process_event(priv, event); | ||
778 | spin_lock_irq(&priv->driver_lock); | ||
779 | } | ||
780 | spin_unlock_irq(&priv->driver_lock); | ||
793 | 781 | ||
794 | if (!priv->fw_ready) | 782 | if (!priv->fw_ready) |
795 | continue; | 783 | continue; |
@@ -798,8 +786,10 @@ static int lbs_thread(void *data) | |||
798 | if (priv->psstate == PS_STATE_PRE_SLEEP && | 786 | if (priv->psstate == PS_STATE_PRE_SLEEP && |
799 | !priv->dnld_sent && !priv->cur_cmd) { | 787 | !priv->dnld_sent && !priv->cur_cmd) { |
800 | if (priv->connect_status == LBS_CONNECTED) { | 788 | if (priv->connect_status == LBS_CONNECTED) { |
801 | lbs_deb_thread("main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p dnld_sent=%d cur_cmd=%p, confirm now\n", | 789 | lbs_deb_thread("pre-sleep, currenttxskb %p, " |
802 | priv->intcounter, priv->currenttxskb, priv->dnld_sent, priv->cur_cmd); | 790 | "dnld_sent %d, cur_cmd %p\n", |
791 | priv->currenttxskb, priv->dnld_sent, | ||
792 | priv->cur_cmd); | ||
803 | 793 | ||
804 | lbs_ps_confirm_sleep(priv); | 794 | lbs_ps_confirm_sleep(priv); |
805 | } else { | 795 | } else { |
@@ -809,7 +799,8 @@ static int lbs_thread(void *data) | |||
809 | * after firmware fixes it | 799 | * after firmware fixes it |
810 | */ | 800 | */ |
811 | priv->psstate = PS_STATE_AWAKE; | 801 | priv->psstate = PS_STATE_AWAKE; |
812 | lbs_pr_alert("main-thread: ignore PS_SleepConfirm in non-connected state\n"); | 802 | lbs_pr_alert("ignore PS_SleepConfirm in " |
803 | "non-connected state\n"); | ||
813 | } | 804 | } |
814 | } | 805 | } |
815 | 806 | ||
@@ -1046,7 +1037,18 @@ static int lbs_init_adapter(struct lbs_private *priv) | |||
1046 | /* Allocate the command buffers */ | 1037 | /* Allocate the command buffers */ |
1047 | if (lbs_allocate_cmd_buffer(priv)) { | 1038 | if (lbs_allocate_cmd_buffer(priv)) { |
1048 | lbs_pr_err("Out of memory allocating command buffers\n"); | 1039 | lbs_pr_err("Out of memory allocating command buffers\n"); |
1049 | ret = -1; | 1040 | ret = -ENOMEM; |
1041 | goto out; | ||
1042 | } | ||
1043 | priv->resp_idx = 0; | ||
1044 | priv->resp_len[0] = priv->resp_len[1] = 0; | ||
1045 | |||
1046 | /* Create the event FIFO */ | ||
1047 | priv->event_fifo = kfifo_alloc(sizeof(u32) * 16, GFP_KERNEL, NULL); | ||
1048 | if (IS_ERR(priv->event_fifo)) { | ||
1049 | lbs_pr_err("Out of memory allocating event FIFO buffer\n"); | ||
1050 | ret = -ENOMEM; | ||
1051 | goto out; | ||
1050 | } | 1052 | } |
1051 | 1053 | ||
1052 | out: | 1054 | out: |
@@ -1060,6 +1062,8 @@ static void lbs_free_adapter(struct lbs_private *priv) | |||
1060 | lbs_deb_enter(LBS_DEB_MAIN); | 1062 | lbs_deb_enter(LBS_DEB_MAIN); |
1061 | 1063 | ||
1062 | lbs_free_cmd_buffer(priv); | 1064 | lbs_free_cmd_buffer(priv); |
1065 | if (priv->event_fifo) | ||
1066 | kfifo_free(priv->event_fifo); | ||
1063 | del_timer(&priv->command_timer); | 1067 | del_timer(&priv->command_timer); |
1064 | kfree(priv->networks); | 1068 | kfree(priv->networks); |
1065 | priv->networks = NULL; | 1069 | priv->networks = NULL; |
@@ -1434,27 +1438,41 @@ out: | |||
1434 | return ret; | 1438 | return ret; |
1435 | } | 1439 | } |
1436 | 1440 | ||
1437 | /** | 1441 | void lbs_queue_event(struct lbs_private *priv, u32 event) |
1438 | * @brief This function handles the interrupt. it will change PS | 1442 | { |
1439 | * state if applicable. it will wake up main_thread to handle | 1443 | unsigned long flags; |
1440 | * the interrupt event as well. | 1444 | |
1441 | * | 1445 | lbs_deb_enter(LBS_DEB_THREAD); |
1442 | * @param dev A pointer to net_device structure | 1446 | spin_lock_irqsave(&priv->driver_lock, flags); |
1443 | * @return n/a | 1447 | |
1444 | */ | 1448 | if (priv->psstate == PS_STATE_SLEEP) |
1445 | void lbs_interrupt(struct lbs_private *priv) | 1449 | priv->psstate = PS_STATE_AWAKE; |
1450 | |||
1451 | __kfifo_put(priv->event_fifo, (unsigned char *) &event, sizeof(u32)); | ||
1452 | |||
1453 | wake_up_interruptible(&priv->waitq); | ||
1454 | |||
1455 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
1456 | lbs_deb_leave(LBS_DEB_THREAD); | ||
1457 | } | ||
1458 | EXPORT_SYMBOL_GPL(lbs_queue_event); | ||
1459 | |||
1460 | void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) | ||
1446 | { | 1461 | { |
1447 | lbs_deb_enter(LBS_DEB_THREAD); | 1462 | lbs_deb_enter(LBS_DEB_THREAD); |
1448 | 1463 | ||
1449 | lbs_deb_thread("lbs_interrupt: intcounter=%d\n", priv->intcounter); | ||
1450 | priv->intcounter++; | ||
1451 | if (priv->psstate == PS_STATE_SLEEP) | 1464 | if (priv->psstate == PS_STATE_SLEEP) |
1452 | priv->psstate = PS_STATE_AWAKE; | 1465 | priv->psstate = PS_STATE_AWAKE; |
1466 | |||
1467 | /* Swap buffers by flipping the response index */ | ||
1468 | BUG_ON(resp_idx > 1); | ||
1469 | priv->resp_idx = resp_idx; | ||
1470 | |||
1453 | wake_up_interruptible(&priv->waitq); | 1471 | wake_up_interruptible(&priv->waitq); |
1454 | 1472 | ||
1455 | lbs_deb_leave(LBS_DEB_THREAD); | 1473 | lbs_deb_leave(LBS_DEB_THREAD); |
1456 | } | 1474 | } |
1457 | EXPORT_SYMBOL_GPL(lbs_interrupt); | 1475 | EXPORT_SYMBOL_GPL(lbs_notify_command_response); |
1458 | 1476 | ||
1459 | static int __init lbs_init_module(void) | 1477 | static int __init lbs_init_module(void) |
1460 | { | 1478 | { |