#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/firmware.h>
+#include <linux/suspend.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
return err;
}
+#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
static void orinoco_cache_fw(struct orinoco_private *priv, int ap)
{
const struct firmware *fw_entry = NULL;
priv->cached_pri_fw = NULL;
priv->cached_fw = NULL;
}
+#else
+#define orinoco_cache_fw(priv, ap)
+#define orinoco_uncache_fw(priv)
+#endif
/********************************************************************/
/* Device methods */
struct orinoco_rx_data *rx_data, *temp;
struct hermes_rx_descriptor *desc;
struct sk_buff *skb;
+ unsigned long flags;
+
+ /* orinoco_rx requires the driver lock, and we also need to
+ * protect priv->rx_list, so just hold the lock over the
+ * lot.
+ *
+ * If orinoco_lock fails, we've unplugged the card. In this
+ * case just abort. */
+ if (orinoco_lock(priv, &flags) != 0)
+ return;
/* extract desc and skb from queue */
list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
kfree(desc);
}
+
+ orinoco_unlock(priv, &flags);
}
/********************************************************************/
union iwreq_data wrqu;
int err;
- err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID,
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
if (err != 0)
return;
if (!priv->has_wpa)
return;
- err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
sizeof(buf), NULL, &buf);
if (err != 0)
return;
if (!priv->has_wpa)
return;
- err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
sizeof(buf), NULL, &buf);
if (err != 0)
return;
return IRQ_HANDLED;
}
+/********************************************************************/
+/* Power management */
+/********************************************************************/
+#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT)
+static int orinoco_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event,
+ void *unused)
+{
+ struct orinoco_private *priv = container_of(notifier,
+ struct orinoco_private,
+ pm_notifier);
+
+ /* All we need to do is cache the firmware before suspend, and
+ * release it when we come out.
+ *
+ * Only need to do this if we're downloading firmware. */
+ if (!priv->do_fw_download)
+ return NOTIFY_DONE;
+
+ switch (pm_event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ orinoco_cache_fw(priv, 0);
+ break;
+
+ case PM_POST_RESTORE:
+ /* Restore from hibernation failed. We need to clean
+ * up in exactly the same way, so fall through. */
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ orinoco_uncache_fw(priv);
+ break;
+
+ case PM_RESTORE_PREPARE:
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
+#define orinoco_pm_notifier NULL
+#endif
+
/********************************************************************/
/* Initialization */
/********************************************************************/
}
if (priv->do_fw_download) {
+#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT
orinoco_cache_fw(priv, 0);
+#endif
err = orinoco_download(priv);
if (err)
priv->cached_pri_fw = NULL;
priv->cached_fw = NULL;
+ /* Register PM notifiers */
+ priv->pm_notifier.notifier_call = orinoco_pm_notifier;
+ register_pm_notifier(&priv->pm_notifier);
+
return dev;
}
void free_orinocodev(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_rx_data *rx_data, *temp;
- /* No need to empty priv->rx_list: if the tasklet is scheduled
- * when we call tasklet_kill it will run one final time,
- * emptying the list */
+ /* If the tasklet is scheduled when we call tasklet_kill it
+ * will run one final time. However the tasklet will only
+ * drain priv->rx_list if the hw is still available. */
tasklet_kill(&priv->rx_tasklet);
+ /* Explicitly drain priv->rx_list */
+ list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
+ list_del(&rx_data->list);
+
+ dev_kfree_skb(rx_data->skb);
+ kfree(rx_data->desc);
+ kfree(rx_data);
+ }
+
+ unregister_pm_notifier(&priv->pm_notifier);
orinoco_uncache_fw(priv);
priv->wpa_ie_len = 0;