Add set_power callback, so driver can disable device when interface is down or before going into suspend Signed-off-by: Vasily Khoruzhick <anarsoul@xxxxxxxxx> --- drivers/net/wireless/libertas/cmd.c | 18 ++++- drivers/net/wireless/libertas/dev.h | 3 +- drivers/net/wireless/libertas/main.c | 177 ++++++++++++++++++++++------------ 3 files changed, 135 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 7e8a658..d0e7df6 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -84,6 +84,7 @@ static u8 is_command_allowed_in_ps(u16 cmd) int lbs_update_hw_spec(struct lbs_private *priv) { struct cmd_ds_get_hw_spec cmd; + struct cmd_ds_802_11_mac_address hw_addr_cmd; int ret = -1; u32 i; @@ -151,8 +152,20 @@ int lbs_update_hw_spec(struct lbs_private *priv) memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN); priv->copied_hwaddr = 1; + } else { + /* Copy addr back to hw */ + memcpy(priv->current_addr, priv->dev->dev_addr, ETH_ALEN); + hw_addr_cmd.hdr.size = cpu_to_le16(sizeof(hw_addr_cmd)); + hw_addr_cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(hw_addr_cmd.macadd, priv->current_addr, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, + &hw_addr_cmd); + if (ret) + lbs_deb_net("set MAC address failed\n"); } + out: lbs_deb_leave(LBS_DEB_CMD); return ret; @@ -1111,6 +1124,7 @@ out: void lbs_set_mac_control(struct lbs_private *priv) { + int ret; struct cmd_ds_mac_control cmd; lbs_deb_enter(LBS_DEB_CMD); @@ -1119,7 +1133,9 @@ void lbs_set_mac_control(struct lbs_private *priv) cmd.action = cpu_to_le16(priv->mac_control); cmd.reserved = 0; - lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); + ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); + if (ret) + lbs_deb_net("set MAC control failed\n"); lbs_deb_leave(LBS_DEB_CMD); } diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index bc461eb..9a873bc 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -90,12 +90,13 @@ struct lbs_private { void *card; u8 fw_ready; u8 surpriseremoved; - u8 setup_fw_on_resume; + int power_on_cnt; int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); void (*reset_card) (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); + void (*set_power) (struct lbs_private *priv, int enable); /* Adapter info (from EEPROM) */ u32 fwrelease; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index ca8149c..7922ead 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -89,6 +89,73 @@ u8 lbs_data_rate_to_fw_index(u32 rate) return 0; } +/** + * @brief This function gets the HW spec from the firmware and sets + * some basic parameters. + * + * @param priv A pointer to struct lbs_private structure + * @return 0 or -1 + */ +static int lbs_setup_firmware(struct lbs_private *priv) +{ + int ret = -1; + s16 curlevel = 0, minlevel = 0, maxlevel = 0; + + lbs_deb_enter(LBS_DEB_FW); + + /* Read MAC address from firmware */ + memset(priv->current_addr, 0xff, ETH_ALEN); + ret = lbs_update_hw_spec(priv); + if (ret) + goto done; + + /* Read power levels if available */ + ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); + if (ret == 0) { + priv->txpower_cur = curlevel; + priv->txpower_min = minlevel; + priv->txpower_max = maxlevel; + } + + /* Send cmd to FW to enable 11D function */ + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); + + lbs_set_mac_control(priv); +done: + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} + +/** + * @brief This function enables device + * + * @param priv A pointer to struct lbs_private structure + */ +static void lbs_power_on(struct lbs_private *priv) +{ + if (priv->set_power) { + priv->power_on_cnt++; + if (priv->power_on_cnt == 1) { + priv->set_power(priv, 1); + lbs_setup_firmware(priv); + lbs_init_mesh(priv); + } + } +} + +/** + * @brief This function disables device + * + * @param priv A pointer to struct lbs_private structure + */ +static void lbs_power_off(struct lbs_private *priv) +{ + if (priv->set_power) { + priv->power_on_cnt--; + if (priv->power_on_cnt == 0) + priv->set_power(priv, 0); + } +} /** * @brief This function opens the ethX interface @@ -103,6 +170,8 @@ static int lbs_dev_open(struct net_device *dev) lbs_deb_enter(LBS_DEB_NET); + lbs_power_on(priv); + spin_lock_irq(&priv->driver_lock); priv->stopping = false; @@ -136,13 +205,14 @@ static int lbs_eth_stop(struct net_device *dev) netif_stop_queue(dev); spin_unlock_irq(&priv->driver_lock); - schedule_work(&priv->mcast_work); cancel_delayed_work_sync(&priv->scan_work); if (priv->scan_req) { cfg80211_scan_done(priv->scan_req, false); priv->scan_req = NULL; } + lbs_power_off(priv); + lbs_deb_leave(LBS_DEB_NET); return 0; } @@ -201,15 +271,16 @@ int lbs_set_mac_address(struct net_device *dev, void *addr) /* In case it was called from the mesh device */ dev = priv->dev; + if (priv->power_on_cnt) { + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN); - 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; + 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); @@ -539,59 +610,27 @@ static int lbs_thread(void *data) return 0; } -/** - * @brief This function gets the HW spec from the firmware and sets - * some basic parameters. - * - * @param priv A pointer to struct lbs_private structure - * @return 0 or -1 - */ -static int lbs_setup_firmware(struct lbs_private *priv) -{ - int ret = -1; - s16 curlevel = 0, minlevel = 0, maxlevel = 0; - - lbs_deb_enter(LBS_DEB_FW); - - /* Read MAC address from firmware */ - memset(priv->current_addr, 0xff, ETH_ALEN); - ret = lbs_update_hw_spec(priv); - if (ret) - goto done; - - /* Read power levels if available */ - ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); - if (ret == 0) { - priv->txpower_cur = curlevel; - priv->txpower_min = minlevel; - priv->txpower_max = maxlevel; - } - - /* Send cmd to FW to enable 11D function */ - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); - - lbs_set_mac_control(priv); -done: - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} - int lbs_suspend(struct lbs_private *priv) { - int ret; + int ret = 0; lbs_deb_enter(LBS_DEB_FW); - if (priv->is_deep_sleep) { - ret = lbs_set_deep_sleep(priv, 0); - if (ret) { - lbs_pr_err("deep sleep cancellation failed: %d\n", ret); - return ret; + if (priv->set_power) + lbs_power_off(priv); + else { + if (priv->is_deep_sleep) { + ret = lbs_set_deep_sleep(priv, 0); + if (ret) { + lbs_pr_err( + "deep sleep cancellation failed: %d\n", ret); + return ret; + } + priv->deep_sleep_required = 1; } - priv->deep_sleep_required = 1; - } - ret = lbs_set_host_sleep(priv, 1); + ret = lbs_set_host_sleep(priv, 1); + } netif_device_detach(priv->dev); if (priv->mesh_dev) @@ -604,26 +643,26 @@ EXPORT_SYMBOL_GPL(lbs_suspend); int lbs_resume(struct lbs_private *priv) { - int ret; + int ret = 0; lbs_deb_enter(LBS_DEB_FW); - ret = lbs_set_host_sleep(priv, 0); + if (priv->set_power) + lbs_power_on(priv); + else + ret = lbs_set_host_sleep(priv, 0); netif_device_attach(priv->dev); if (priv->mesh_dev) netif_device_attach(priv->mesh_dev); - if (priv->deep_sleep_required) { + if (!priv->set_power && priv->deep_sleep_required) { priv->deep_sleep_required = 0; ret = lbs_set_deep_sleep(priv, 1); if (ret) lbs_pr_err("deep sleep activation failed: %d\n", ret); } - if (priv->setup_fw_on_resume) - ret = lbs_setup_firmware(priv); - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); return ret; } @@ -917,6 +956,9 @@ void lbs_remove_card(struct lbs_private *priv) priv->surpriseremoved = 1; kthread_stop(priv->main_thread); + /* Disable card power if it's still on */ + lbs_power_off(priv); + lbs_free_adapter(priv); lbs_cfg_free(priv); free_netdev(dev); @@ -944,6 +986,16 @@ int lbs_start_card(struct lbs_private *priv) lbs_deb_enter(LBS_DEB_MAIN); + /* We're not using lbs_power_on here because we don't want + * to setup firmware twice. + * TODO: replace following code with lbs_power_on() when set_power + * callback become mandatory. + */ + if (priv->set_power) { + priv->power_on_cnt++; + priv->set_power(priv, 1); + } + /* poke the firmware */ ret = lbs_setup_firmware(priv); if (ret) @@ -964,6 +1016,9 @@ int lbs_start_card(struct lbs_private *priv) ret = 0; + /* Init is done, so we can disable card power */ + lbs_power_off(priv); + done: lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); return ret; -- 1.7.4.1 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html