diff options
-rw-r--r-- | drivers/net/wireless/libertas/cfg.c | 39 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cfg.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/decl.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/dev.h | 16 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_usb.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 168 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/mesh.c | 9 |
8 files changed, 163 insertions, 82 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 63009c7eb2f..85b3169c40d 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -712,7 +712,7 @@ static void lbs_scan_worker(struct work_struct *work) if (priv->scan_channel < priv->scan_req->n_channels) { cancel_delayed_work(&priv->scan_work); - if (!priv->stopping) + if (netif_running(priv->dev)) queue_delayed_work(priv->work_thread, &priv->scan_work, msecs_to_jiffies(300)); } @@ -1409,31 +1409,23 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, return ret; } -static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) +int lbs_disconnect(struct lbs_private *priv, u16 reason) { - struct lbs_private *priv = wiphy_priv(wiphy); struct cmd_ds_802_11_deauthenticate cmd; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); - - /* store for lbs_cfg_ret_disconnect() */ - priv->disassoc_reason = reason_code; + int ret; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); /* Mildly ugly to use a locally store my own BSSID ... */ memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); - cmd.reasoncode = cpu_to_le16(reason_code); + cmd.reasoncode = cpu_to_le16(reason); - if (lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd)) - return -EFAULT; + ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); + if (ret) + return ret; cfg80211_disconnected(priv->dev, - priv->disassoc_reason, + reason, NULL, 0, GFP_KERNEL); priv->connect_status = LBS_DISCONNECTED; @@ -1441,6 +1433,21 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); + + /* store for lbs_cfg_ret_disconnect() */ + priv->disassoc_reason = reason_code; + + return lbs_disconnect(priv, reason_code); +} static int lbs_cfg_set_default_key(struct wiphy *wiphy, struct net_device *netdev, diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h index 4f46bb744be..a02ee151710 100644 --- a/drivers/net/wireless/libertas/cfg.h +++ b/drivers/net/wireless/libertas/cfg.h @@ -17,5 +17,6 @@ void lbs_send_disconnect_notification(struct lbs_private *priv); void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); void lbs_scan_deinit(struct lbs_private *priv); +int lbs_disconnect(struct lbs_private *priv, u16 reason); #endif diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index dbd24a4607e..e08ab1de3d9 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -1088,7 +1088,7 @@ void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) __lbs_cleanup_and_insert_cmd(priv, cmd); priv->cur_cmd = NULL; - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); } void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, @@ -1627,7 +1627,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); /* Wake up main thread to execute next command */ - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); cmdnode = ERR_PTR(-ENOBUFS); goto done; } @@ -1647,7 +1647,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, cmdnode->cmdwaitqwoken = 0; lbs_queue_cmd(priv, cmdnode); - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); done: lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index da0b05bb89f..9304e6fc421 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h @@ -43,10 +43,14 @@ int lbs_start_card(struct lbs_private *priv); void lbs_stop_card(struct lbs_private *priv); void lbs_host_to_card_done(struct lbs_private *priv); +int lbs_start_iface(struct lbs_private *priv); +int lbs_stop_iface(struct lbs_private *priv); + int lbs_rtap_supported(struct lbs_private *priv); int lbs_set_mac_address(struct net_device *dev, void *addr); void lbs_set_multicast_list(struct net_device *dev); +void lbs_update_mcast(struct lbs_private *priv); int lbs_suspend(struct lbs_private *priv); int lbs_resume(struct lbs_private *priv); diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 133ff1cac52..814838916b8 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -46,7 +46,6 @@ struct lbs_private { /* CFG80211 */ struct wireless_dev *wdev; bool wiphy_registered; - bool stopping; struct cfg80211_scan_request *scan_req; u8 assoc_bss[ETH_ALEN]; u8 disassoc_reason; @@ -96,11 +95,14 @@ struct lbs_private { /* Hardware access */ void *card; + bool iface_running; u8 fw_ready; u8 surpriseremoved; u8 setup_fw_on_resume; int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); void (*reset_card) (struct lbs_private *priv); + int (*power_save) (struct lbs_private *priv); + int (*power_restore) (struct lbs_private *priv); int (*enter_deep_sleep) (struct lbs_private *priv); int (*exit_deep_sleep) (struct lbs_private *priv); int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); @@ -182,4 +184,16 @@ struct lbs_private { extern struct cmd_confirm_sleep confirm_sleep; +/* Check if there is an interface active. */ +static inline int lbs_iface_active(struct lbs_private *priv) +{ + int r; + + r = netif_running(priv->dev); + if (priv->mesh_dev); + r |= netif_running(priv->dev); + + return r; +} + #endif diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index ca6e0a411e9..0c846f5a646 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -956,7 +956,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp, priv->dnld_sent = DNLD_RES_RECEIVED; spin_unlock_irqrestore(&priv->driver_lock, flags); - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); return ret; } diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index ee28ae51093..d62d1fb4177 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -99,6 +99,37 @@ u8 lbs_data_rate_to_fw_index(u32 rate) return 0; } +int lbs_start_iface(struct lbs_private *priv) +{ + struct cmd_ds_802_11_mac_address cmd; + int ret; + + if (priv->power_restore) { + ret = priv->power_restore(priv); + if (ret) + return ret; + } + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); + if (ret) { + lbs_deb_net("set MAC address failed\n"); + goto err; + } + + lbs_update_channel(priv); + + priv->iface_running = true; + return 0; + +err: + if (priv->power_save) + priv->power_save(priv); + return ret; +} /** * lbs_dev_open - open the ethX interface @@ -112,23 +143,64 @@ static int lbs_dev_open(struct net_device *dev) int ret = 0; lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } spin_lock_irq(&priv->driver_lock); - priv->stopping = false; - if (priv->connect_status == LBS_CONNECTED) - netif_carrier_on(dev); - else - netif_carrier_off(dev); + netif_carrier_off(dev); if (!priv->tx_pending_len) netif_wake_queue(dev); spin_unlock_irq(&priv->driver_lock); + +out: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } +static bool lbs_command_queue_empty(struct lbs_private *priv) +{ + unsigned long flags; + bool ret; + spin_lock_irqsave(&priv->driver_lock, flags); + ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return ret; +} + +int lbs_stop_iface(struct lbs_private *priv) +{ + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MAIN); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->iface_running = false; + kfree_skb(priv->currenttxskb); + priv->currenttxskb = NULL; + priv->tx_pending_len = 0; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + cancel_work_sync(&priv->mcast_work); + + /* Disable command processing, and wait for all commands to complete */ + lbs_deb_main("waiting for commands to complete\n"); + wait_event(priv->waitq, lbs_command_queue_empty(priv)); + lbs_deb_main("all commands completed\n"); + + if (priv->power_save) + ret = priv->power_save(priv); + + lbs_deb_leave(LBS_DEB_MAIN); + return ret; +} + /** * lbs_eth_stop - close the ethX interface * @@ -141,18 +213,25 @@ static int lbs_eth_stop(struct net_device *dev) lbs_deb_enter(LBS_DEB_NET); + if (priv->connect_status == LBS_CONNECTED) + lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); + spin_lock_irq(&priv->driver_lock); - priv->stopping = true; netif_stop_queue(dev); spin_unlock_irq(&priv->driver_lock); - schedule_work(&priv->mcast_work); + lbs_update_mcast(priv); cancel_delayed_work_sync(&priv->scan_work); if (priv->scan_req) { cfg80211_scan_done(priv->scan_req, false); priv->scan_req = NULL; } + netif_carrier_off(priv->dev); + + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); + lbs_deb_leave(LBS_DEB_NET); return 0; } @@ -170,7 +249,7 @@ void lbs_host_to_card_done(struct lbs_private *priv) /* Wake main thread if commands are pending */ if (!priv->cur_cmd || priv->tx_pending_len > 0) { if (!priv->wakeup_dev_required) - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); } spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -183,29 +262,24 @@ int lbs_set_mac_address(struct net_device *dev, void *addr) int ret = 0; struct lbs_private *priv = dev->ml_priv; struct sockaddr *phwaddr = addr; - struct cmd_ds_802_11_mac_address cmd; lbs_deb_enter(LBS_DEB_NET); + /* + * Can only set MAC address when all interfaces are down, to be written + * to the hardware when one of them is brought up. + */ + if (lbs_iface_active(priv)) + return -EBUSY; + /* In case it was called from the mesh device */ dev = priv->dev; - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN); - - ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); - if (ret) { - lbs_deb_net("set MAC address failed\n"); - goto done; - } - memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); if (priv->mesh_dev) memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); -done: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } @@ -259,18 +333,18 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, return i; } -static void lbs_set_mcast_worker(struct work_struct *work) +void lbs_update_mcast(struct lbs_private *priv) { - struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); struct cmd_ds_mac_multicast_adr mcast_cmd; - int dev_flags; + int dev_flags = 0; int nr_addrs; int old_mac_control = priv->mac_control; lbs_deb_enter(LBS_DEB_NET); - dev_flags = priv->dev->flags; - if (priv->mesh_dev) + if (netif_running(priv->dev)) + dev_flags |= priv->dev->flags; + if (priv->mesh_dev && netif_running(priv->mesh_dev)) dev_flags |= priv->mesh_dev->flags; if (dev_flags & IFF_PROMISC) { @@ -316,6 +390,12 @@ static void lbs_set_mcast_worker(struct work_struct *work) lbs_deb_leave(LBS_DEB_NET); } +static void lbs_set_mcast_worker(struct work_struct *work) +{ + struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); + lbs_update_mcast(priv); +} + void lbs_set_multicast_list(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; @@ -648,7 +728,7 @@ static void lbs_cmd_timeout_handler(unsigned long data) if (priv->dnld_sent == DNLD_CMD_SENT) priv->dnld_sent = DNLD_RES_RECEIVED; - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); out: spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_CMD); @@ -890,10 +970,6 @@ void lbs_remove_card(struct lbs_private *priv) lbs_remove_mesh(priv); lbs_scan_deinit(priv); - dev = priv->dev; - - cancel_work_sync(&priv->mcast_work); - /* worker thread destruction blocks on the in-flight command which * should have been cleared already in lbs_stop_card(). */ @@ -964,8 +1040,6 @@ int lbs_start_card(struct lbs_private *priv) if (lbs_mesh_activated(priv)) lbs_start_mesh(priv); - lbs_update_channel(priv); - lbs_debugfs_init_one(priv, dev); netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); @@ -982,8 +1056,6 @@ EXPORT_SYMBOL_GPL(lbs_start_card); void lbs_stop_card(struct lbs_private *priv) { struct net_device *dev; - struct cmd_ctrl_node *cmdnode; - unsigned long flags; lbs_deb_enter(LBS_DEB_MAIN); @@ -996,30 +1068,6 @@ void lbs_stop_card(struct lbs_private *priv) lbs_debugfs_remove_one(priv); lbs_deinit_mesh(priv); - - /* Delete the timeout of the currently processing command */ - del_timer_sync(&priv->command_timer); - del_timer_sync(&priv->auto_deepsleep_timer); - - /* Flush pending command nodes */ - spin_lock_irqsave(&priv->driver_lock, flags); - lbs_deb_main("clearing pending commands\n"); - list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { - cmdnode->result = -ENOENT; - cmdnode->cmdwaitqwoken = 1; - wake_up(&cmdnode->cmdwait_q); - } - - /* Flush the command the card is currently processing */ - if (priv->cur_cmd) { - lbs_deb_main("clearing current command\n"); - priv->cur_cmd->result = -ENOENT; - priv->cur_cmd->cmdwaitqwoken = 1; - wake_up(&priv->cur_cmd->cmdwait_q); - } - lbs_deb_main("done clearing commands\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - unregister_netdev(dev); out: @@ -1040,7 +1088,7 @@ void lbs_queue_event(struct lbs_private *priv, u32 event) kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_THREAD); @@ -1058,7 +1106,7 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) BUG_ON(resp_idx > 1); priv->resp_idx = resp_idx; - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); lbs_deb_leave(LBS_DEB_THREAD); } diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 2a635d279ff..138699baf90 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -924,7 +924,9 @@ static int lbs_mesh_stop(struct net_device *dev) spin_unlock_irq(&priv->driver_lock); - schedule_work(&priv->mcast_work); + lbs_update_mcast(priv); + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); lbs_deb_leave(LBS_DEB_MESH); return 0; @@ -942,6 +944,11 @@ static int lbs_mesh_dev_open(struct net_device *dev) int ret = 0; lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } spin_lock_irq(&priv->driver_lock); |