/** * lbs_thread - handles the major jobs in the LBS driver. * It handles all events generated by firmware, RX data received * from firmware and TX data sent from kernel. * * @data: A pointer to &lbs_thread structure * returns: 0 */ static int lbs_thread(void *data) { struct net_device *dev = data; struct lbs_private *priv = dev->ml_priv; wait_queue_t wait; lbs_deb_enter(LBS_DEB_THREAD); init_waitqueue_entry(&wait, current); for (;;) { int shouldsleep; u8 resp_idx; lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); add_wait_queue(&priv->waitq, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&priv->driver_lock); if (kthread_should_stop()) shouldsleep = 0; /* Bye */ else if (priv->surpriseremoved) shouldsleep = 1; /* We need to wait until we're _told_ to die */ else if (priv->psstate == PS_STATE_SLEEP) shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ else if (priv->cmd_timed_out) shouldsleep = 0; /* Command timed out. Recover */ else if (!priv->fw_ready) shouldsleep = 1; /* Firmware not ready. We're waiting for it */ else if (priv->dnld_sent) shouldsleep = 1; /* Something is en route to the device already */ else if (priv->tx_pending_len > 0) shouldsleep = 0; /* We've a packet to send */ else if (priv->resp_len[priv->resp_idx]) shouldsleep = 0; /* We have a command response */ else if (priv->cur_cmd) shouldsleep = 1; /* Can't send a command; one already running */ else if (!list_empty(&priv->cmdpendingq) && !(priv->wakeup_dev_required)) shouldsleep = 0; /* We have a command to send */ else if (kfifo_len(&priv->event_fifo)) shouldsleep = 0; /* We have an event to process */ else shouldsleep = 1; /* No command */ if (shouldsleep) { lbs_deb_thread("sleeping, connect_status %d, " "psmode %d, psstate %d\n", priv->connect_status, priv->psmode, priv->psstate); spin_unlock_irq(&priv->driver_lock); schedule(); } else spin_unlock_irq(&priv->driver_lock); lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", priv->currenttxskb, priv->dnld_sent); set_current_state(TASK_RUNNING); remove_wait_queue(&priv->waitq, &wait); lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); if (kthread_should_stop()) { lbs_deb_thread("break from main thread\n"); break; } if (priv->surpriseremoved) { lbs_deb_thread("adapter removed; waiting to die...\n"); continue; } lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); /* Process any pending command response */ spin_lock_irq(&priv->driver_lock); resp_idx = priv->resp_idx; if (priv->resp_len[resp_idx]) { spin_unlock_irq(&priv->driver_lock); lbs_process_command_response(priv, priv->resp_buf[resp_idx], priv->resp_len[resp_idx]); spin_lock_irq(&priv->driver_lock); priv->resp_len[resp_idx] = 0; } spin_unlock_irq(&priv->driver_lock); /* Process hardware events, e.g. card removed, link lost */ spin_lock_irq(&priv->driver_lock); while (kfifo_len(&priv->event_fifo)) { u32 event; if (kfifo_out(&priv->event_fifo, (unsigned char *) &event, sizeof(event)) != sizeof(event)) break; spin_unlock_irq(&priv->driver_lock); lbs_process_event(priv, event); spin_lock_irq(&priv->driver_lock); } spin_unlock_irq(&priv->driver_lock); if (priv->wakeup_dev_required) { lbs_deb_thread("Waking up device...\n"); /* Wake up device */ if (priv->exit_deep_sleep(priv)) lbs_deb_thread("Wakeup device failed\n"); continue; } /* command timeout stuff */ if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; netdev_info(dev, "Timeout submitting command 0x%04x\n", le16_to_cpu(cmdnode->cmdbuf->command)); lbs_complete_command(priv, cmdnode, -ETIMEDOUT); if (priv->reset_card) priv->reset_card(priv); } priv->cmd_timed_out = 0; if (!priv->fw_ready) continue; /* Check if we need to confirm Sleep Request received previously */ if (priv->psstate == PS_STATE_PRE_SLEEP && !priv->dnld_sent && !priv->cur_cmd) { if (priv->connect_status == LBS_CONNECTED) { lbs_deb_thread("pre-sleep, currenttxskb %p, " "dnld_sent %d, cur_cmd %p\n", priv->currenttxskb, priv->dnld_sent, priv->cur_cmd); lbs_ps_confirm_sleep(priv); } else { /* workaround for firmware sending * deauth/linkloss event immediately * after sleep request; remove this * after firmware fixes it */ priv->psstate = PS_STATE_AWAKE; netdev_alert(dev, "ignore PS_SleepConfirm in non-connected state\n"); } } /* The PS state is changed during processing of Sleep Request * event above */ if ((priv->psstate == PS_STATE_SLEEP) || (priv->psstate == PS_STATE_PRE_SLEEP)) continue; if (priv->is_deep_sleep) continue; /* Execute the next command */ if (!priv->dnld_sent && !priv->cur_cmd) lbs_execute_next_command(priv); spin_lock_irq(&priv->driver_lock); if (!priv->dnld_sent && priv->tx_pending_len > 0) { int ret = priv->hw_host_to_card(priv, MVMS_DAT, priv->tx_pending_buf, priv->tx_pending_len); if (ret) { lbs_deb_tx("host_to_card failed %d\n", ret); priv->dnld_sent = DNLD_RES_RECEIVED; } else { mod_timer(&priv->tx_lockup_timer, jiffies + (HZ * 5)); } priv->tx_pending_len = 0; if (!priv->currenttxskb) { /* We can wake the queues immediately if we aren't waiting for TX feedback */ if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); if (priv->mesh_dev && netif_running(priv->mesh_dev)) netif_wake_queue(priv->mesh_dev); } } spin_unlock_irq(&priv->driver_lock); } del_timer(&priv->command_timer); del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_THREAD); return 0; }
/** * lbs_add_card - adds the card. It will probe the * card, allocate the lbs_priv and initialize the device. * * @card: A pointer to card * @dmdev: A pointer to &struct device * returns: A pointer to &struct lbs_private structure */ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) { struct net_device *dev; struct wireless_dev *wdev; struct lbs_private *priv = NULL; lbs_deb_enter(LBS_DEB_MAIN); /* Allocate an Ethernet device and register it */ wdev = lbs_cfg_alloc(dmdev); if (IS_ERR(wdev)) { pr_err("cfg80211 init failed\n"); goto done; } wdev->iftype = NL80211_IFTYPE_STATION; priv = wdev_priv(wdev); priv->wdev = wdev; if (lbs_init_adapter(priv)) { pr_err("failed to initialize adapter structure\n"); goto err_wdev; } dev = alloc_netdev(0, "wlan%d", ether_setup); if (!dev) { dev_err(dmdev, "no memory for network device instance\n"); goto err_adapter; } dev->ieee80211_ptr = wdev; dev->ml_priv = priv; SET_NETDEV_DEV(dev, dmdev); wdev->netdev = dev; priv->dev = dev; dev->netdev_ops = &lbs_netdev_ops; dev->watchdog_timeo = 5 * HZ; dev->ethtool_ops = &lbs_ethtool_ops; dev->flags |= IFF_BROADCAST | IFF_MULTICAST; priv->card = card; strcpy(dev->name, "wlan%d"); lbs_deb_thread("Starting main thread...\n"); init_waitqueue_head(&priv->waitq); priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); if (IS_ERR(priv->main_thread)) { lbs_deb_thread("Error creating main thread.\n"); goto err_ndev; } priv->work_thread = create_singlethread_workqueue("lbs_worker"); INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); priv->wol_criteria = EHS_REMOVE_WAKEUP; priv->wol_gpio = 0xff; priv->wol_gap = 20; priv->ehs_remove_supported = true; goto done; err_ndev: free_netdev(dev); err_adapter: lbs_free_adapter(priv); err_wdev: lbs_cfg_free(priv); priv = NULL; done: lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); return priv; }
//HelloX: This thread will be waken up by other routines through the calling of //wake_up_interruptible routine,in HelloX's implementation,this mechanism should be //replaced by event,a dedicated event object is defined under lbs_private structure, //the lbs_thread should wait on this event object when shouldsleep = 1. //Other routines who want to process lbs command,should set the event object status //to signal,thus lead the wake up of lbs_thread. int lbs_thread(struct lbs_private *priv) { //struct net_device *dev = data; //struct lbs_private *priv = dev->ml_priv; //wait_queue_t wait; int shouldsleep; u8 resp_idx; //lbs_deb_enter(LBS_DEB_THREAD); //init_waitqueue_entry(&wait, current); do{ /*lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); */ /*add_wait_queue(&priv->waitq, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&priv->driver_lock); if (kthread_should_stop()) shouldsleep = 0; */ if (priv->surpriseremoved) shouldsleep = 1; /* We need to wait until we're _told_ to die */ else if (priv->psstate == PS_STATE_SLEEP) shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ else if (priv->cmd_timed_out) shouldsleep = 0; /* Command timed out. Recover */ else if (!priv->fw_ready) shouldsleep = 1; /* Firmware not ready. We're waiting for it */ else if (priv->dnld_sent) shouldsleep = 1; /* Something is en route to the device already */ //else if (priv->tx_pending_len > 0) // shouldsleep = 0; /* We've a packet to send */ else if (priv->resp_len[priv->resp_idx]) shouldsleep = 0; /* We have a command response */ else if (priv->cur_cmd) shouldsleep = 1; /* Can't send a command; one already running */ else if (!list_empty(&priv->cmdpendingq)) shouldsleep = 0; /* We have a command to send */ /* else if (__kfifo_len(priv->event_fifo)) shouldsleep = 0; */ /* We have an event to process */ else shouldsleep = 1; /* No command */ if (shouldsleep) { /*lbs_deb_thread("sleeping, connect_status %d, " "psmode %d, psstate %d\n", priv->connect_status, priv->psmode, priv->psstate);*/ //spin_unlock_irq(&priv->driver_lock); //schedule(); break; //HelloX: This break clause might be added by the modifier,to giveup execute. //Should remove this clause when implement this thread under HelloX. } /*else spin_unlock_irq(&priv->driver_lock);*/ /*lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", priv->currenttxskb, priv->dnld_sent);*/ /*set_current_state(TASK_RUNNING); remove_wait_queue(&priv->waitq, &wait);*/ //HelloX: Should reset the event status to non-signaling here,to achieve sychronization //purpose. /*lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent); if (kthread_should_stop()) { lbs_deb_thread("break from main thread\n"); break; }*/ if (priv->surpriseremoved) { lbs_deb_thread("adapter removed; waiting to die...\n"); continue; } /*lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", priv->currenttxskb, priv->dnld_sent);*/ /* Process any pending command response */ //spin_lock_irq(&priv->driver_lock); resp_idx = priv->resp_idx; if (priv->resp_len[resp_idx]) { // spin_unlock_irq(&priv->driver_lock); lbs_process_command_response(priv, priv->resp_buf[resp_idx], priv->resp_len[resp_idx]); //spin_lock_irq(&priv->driver_lock); priv->resp_len[resp_idx] = 0; } //spin_unlock_irq(&priv->driver_lock); /* command timeout stuff */ if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; if (++priv->nr_retries > 0) { lbs_pr_info("Excessive timeouts submitting " "command 0x%04x\n", le16_to_cpu(cmdnode->cmdbuf->command)); lbs_complete_command(priv, cmdnode, -ETIMEDOUT); priv->nr_retries = 0; /*if (priv->reset_card) priv->reset_card(priv);*/ } else { priv->cur_cmd = NULL; priv->dnld_sent = DNLD_RES_RECEIVED; lbs_pr_info("requeueing command 0x%04x due " "to timeout (#%d)\n", le16_to_cpu(cmdnode->cmdbuf->command), priv->nr_retries); /* Stick it back at the _top_ of the pending queue for immediate resubmission */ list_add(&cmdnode->list, &priv->cmdpendingq); } } priv->cmd_timed_out = 0; /* Process hardware events, e.g. card removed, link lost */ //spin_lock_irq(&priv->driver_lock); /* while (__kfifo_len(priv->event_fifo)) { u32 event; __kfifo_get(priv->event_fifo,(unsigned char *)&event,sizeof(event)); //spin_unlock_irq(&priv->driver_lock); lbs_process_event(priv, event); //spin_lock_irq(&priv->driver_lock); } */ //spin_unlock_irq(&priv->driver_lock); if (!priv->fw_ready) continue; /* Check if we need to confirm Sleep Request received previously */ if (priv->psstate == PS_STATE_PRE_SLEEP && !priv->dnld_sent && !priv->cur_cmd) { if (priv->connect_status == LBS_CONNECTED) { /*lbs_deb_thread("pre-sleep, currenttxskb %p, " "dnld_sent %d, cur_cmd %p\n", priv->currenttxskb, priv->dnld_sent, priv->cur_cmd);*/ //lbs_ps_confirm_sleep(priv); lbs_pr_alert("ignore PS_SleepConfirm in " "non-connected state\n"); while(1){ if(priv->psstate != PS_STATE_PRE_SLEEP)//bug break; } } else { /* workaround for firmware sending * deauth/linkloss event immediately * after sleep request; remove this * after firmware fixes it */ priv->psstate = PS_STATE_AWAKE; lbs_pr_alert("ignore PS_SleepConfirm in " "non-connected state\n"); } } /* The PS state is changed during processing of Sleep Request * event above */ if ((priv->psstate == PS_STATE_SLEEP) || (priv->psstate == PS_STATE_PRE_SLEEP)) continue; /* Execute the next command */ if (!priv->dnld_sent && !priv->cur_cmd) lbs_execute_next_command(priv); /* Wake-up command waiters which can't sleep in * lbs_prepare_and_send_command */ /*if (!list_empty(&priv->cmdpendingq)) wake_up_all(&priv->cmd_pending);*/ #ifdef MASK_DEBUG //spin_lock_irq(&priv->driver_lock); if (!priv->dnld_sent && priv->tx_pending_len > 0) { int ret = priv->hw_host_to_card(priv, MVMS_DAT, priv->tx_pending_buf, priv->tx_pending_len); if (ret) { lbs_deb_tx("host_to_card failed %d\n", ret); priv->dnld_sent = DNLD_RES_RECEIVED; } priv->tx_pending_len = 0; /*if (!priv->currenttxskb) { //We can wake the queues immediately if we aren't // waiting for TX feedback if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); if (priv->mesh_dev && priv->mesh_connect_status == LBS_CONNECTED) netif_wake_queue(priv->mesh_dev); }*/ } #endif //spin_unlock_irq(&priv->driver_lock); }while(0); //HelloX: Maybe modified by the previous author,should change to while(1) clause when //migrate to HelloX. //del_timer(&priv->command_timer); //wake_up_all(&priv->cmd_pending); //lbs_deb_leave("leave main thread!\n"); return 0; }