[PATCH 1/2] libertas: disable functionality when interface is down
Vasily Khoruzhick
anarsoul at gmail.com
Tue Aug 2 05:02:03 EDT 2011
On Monday 01 August 2011 18:43:13 Daniel Drake wrote:
> Modify the driver so that it does not function when the interface is
> down, in preparation for runtime power management.
>
> No commands can be run while the interface is down, so the ndo_dev_stop
> routine now directly does all necessary work (including asking the device
> to disconnect from the network and disabling multicast functionality)
> directly.
>
> power_save and power_restore hooks are added meaning that card drivers
> can take steps to turn the device off when the interface is down.
>
> The MAC address can now only be changed when all interfaces are down;
> the new address will be programmed when an interface gets brought up.
> This matches mac80211 behaviour.
>
> Also, some small cleanups/simplifications were made in the surrounding
> device handling logic.
Ok, one more question: what about suspend? Maybe it worth to add flag to
disable card before going suspend and re-enable it during resume?
> Signed-off-by: Daniel Drake <dsd at laptop.org>
> ---
> drivers/net/wireless/libertas/cfg.c | 39 +++++---
> drivers/net/wireless/libertas/cfg.h | 1 +
> drivers/net/wireless/libertas/cmd.c | 6 +-
> drivers/net/wireless/libertas/decl.h | 4 +
> drivers/net/wireless/libertas/dev.h | 16 +++-
> drivers/net/wireless/libertas/if_usb.c | 2 +-
> drivers/net/wireless/libertas/main.c | 168
> ++++++++++++++++++++----------- 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 63009c7..85b3169 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 4f46bb7..a02ee15 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 dbd24a4..e08ab1d 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 da0b05b..9304e6f 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 133ff1c..8148389 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 e368b29..0775f45 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 ee28ae5..d62d1fb 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 2a635d2..138699b 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);
More information about the libertas-dev
mailing list