[PATCH RFC 2/3] libertas: add set_power callback for iface subdrivers
Vasily Khoruzhick
anarsoul at gmail.com
Thu Apr 7 16:25:59 EDT 2011
Add set_power callback, so driver can disable device when
interface is down or before going into suspend
Signed-off-by: Vasily Khoruzhick <anarsoul at gmail.com>
---
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
More information about the libertas-dev
mailing list