Search Linux Wireless

some wireless suspend thoughts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 * kick pci driver suspend/resume out of ath9k and keep the device in
   low power while unused
 * make mac80211 send nullfunc for suspend/resume
 * make cfg80211 always call suspend/resume

This is quite hackish. It works for me, but my AP disassocs me anyway
because it probes every 5 seconds and if you don't respond ... however,
due to the nullfunc at resume time we notice very very quickly since it
sends a deauth as a response to that.

The ->shutdown hook in cfg80211 is a little odd.

To implement WoW on top of this, we need to refactor the cfg80211 hooks
and probably actually pass the information to ->stop after all, I think?
I can't think of a good way to do this.

johannes
---
 drivers/net/wireless/ath/ath9k/ath9k.h |    2 
 drivers/net/wireless/ath/ath9k/main.c  |    6 ++
 drivers/net/wireless/ath/ath9k/pci.c   |   74 +++++++++++++--------------------
 net/mac80211/ieee80211_i.h             |    2 
 net/mac80211/pm.c                      |   11 ++++
 net/mac80211/util.c                    |    8 +++
 net/wireless/core.c                    |    2 
 net/wireless/core.h                    |    1 
 net/wireless/sysfs.c                   |   27 ++++++++++--
 9 files changed, 87 insertions(+), 46 deletions(-)

--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-05-15 15:17:08.879827920 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-05-15 15:17:09.889658880 +0200
@@ -630,6 +630,8 @@ struct ieee80211_local {
 	 */
 	bool quiescing;
 
+	bool ps_before_suspend;
+
 	int tx_headroom; /* required headroom for hardware/radiotap */
 
 	/* Tasklet and skb queue to process calls from IRQ mode. All frames
--- wireless-testing.orig/net/mac80211/pm.c	2009-05-15 15:17:08.851827827 +0200
+++ wireless-testing/net/mac80211/pm.c	2009-05-15 15:17:09.891708511 +0200
@@ -16,6 +16,17 @@ int __ieee80211_suspend(struct ieee80211
 
 	ieee80211_scan_cancel(local);
 
+	local->ps_before_suspend = !!(local->hw.conf.flags & IEEE80211_CONF_PS);
+	local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+	del_timer_sync(&local->dynamic_ps_timer);
+	cancel_work_sync(&local->dynamic_ps_enable_work);
+
+	list_for_each_entry(sdata, &local->interfaces, list)
+		if (sdata->vif.type == NL80211_IFTYPE_STATION)
+			ieee80211_send_nullfunc(local, sdata, 1);
+
 	ieee80211_stop_queues_by_reason(hw,
 			IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
--- wireless-testing.orig/net/mac80211/util.c	2009-05-15 15:17:08.926586888 +0200
+++ wireless-testing/net/mac80211/util.c	2009-05-15 15:17:09.893576341 +0200
@@ -1042,6 +1042,9 @@ int ieee80211_reconfig(struct ieee80211_
 	 */
 	local->suspended = false;
 
+	if (from_suspend && local->ps_before_suspend)
+		local->hw.conf.flags |= IEEE80211_CONF_PS;
+
 	/* restart hardware */
 	if (local->open_count) {
 		res = drv_start(local);
@@ -1144,6 +1147,11 @@ int ieee80211_reconfig(struct ieee80211_
 		return 0;
 
 #ifdef CONFIG_PM
+	if (!local->ps_before_suspend)
+		list_for_each_entry(sdata, &local->interfaces, list)
+			if (sdata->vif.type == NL80211_IFTYPE_STATION)
+				ieee80211_send_nullfunc(local, sdata, 0);
+
 	local->suspended = false;
 
 	list_for_each_entry(sdata, &local->interfaces, list) {
--- wireless-testing.orig/net/wireless/sysfs.c	2009-05-15 15:17:09.063578302 +0200
+++ wireless-testing/net/wireless/sysfs.c	2009-05-15 15:26:00.778581987 +0200
@@ -55,7 +55,7 @@ static int wiphy_uevent(struct device *d
 }
 #endif
 
-static int wiphy_suspend(struct device *dev, pm_message_t state)
+static int wiphy_suspend(struct device *dev)
 {
 	struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
 	int ret = 0;
@@ -90,6 +90,28 @@ static int wiphy_resume(struct device *d
 	return ret;
 }
 
+/* XXX: for WoW, .poweroff and .suspend need to be different */
+static struct dev_pm_ops cfg80211_pm_ops = {
+	.suspend = wiphy_suspend,
+	.resume = wiphy_resume,
+	.freeze = wiphy_suspend,
+	.thaw = wiphy_resume,
+	.poweroff = wiphy_suspend,
+	.restore = wiphy_resume,
+};
+
+static void wiphy_shutdown(struct device *dev)
+{
+	wiphy_suspend(dev);
+}
+
+/*
+ * XXX: This shouldn't be necessary?!
+ */
+struct device_driver wiphy_driver = {
+	.shutdown = wiphy_shutdown,
+};
+
 struct class ieee80211_class = {
 	.name = "ieee80211",
 	.owner = THIS_MODULE,
@@ -98,8 +120,7 @@ struct class ieee80211_class = {
 #ifdef CONFIG_HOTPLUG
 	.dev_uevent = wiphy_uevent,
 #endif
-	.suspend = wiphy_suspend,
-	.resume = wiphy_resume,
+	.pm = &cfg80211_pm_ops,
 };
 
 int wiphy_sysfs_init(void)
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/pci.c	2009-05-15 15:17:09.558578206 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/pci.c	2009-05-15 15:48:20.957708212 +0200
@@ -74,7 +74,36 @@ static bool ath_pci_eeprom_read(struct a
 	return true;
 }
 
+static int ath_pci_start(struct ath_softc *sc)
+{
+	struct pci_dev *pdev = to_pci_dev(sc->dev);
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	/* Enable LED */
+	ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
+			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+
+	return 0;
+}
+
+static void ath_pci_stop(struct ath_softc *sc)
+{
+	struct pci_dev *pdev = to_pci_dev(sc->dev);
+
+	/* put device into low-power state until needed */
+	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, PCI_D3hot);
+}
+
 static struct ath_bus_ops ath_pci_bus_ops = {
+	.start = ath_pci_start,
+	.stop = ath_pci_stop,
 	.read_cachesize = ath_pci_read_cachesize,
 	.cleanup = ath_pci_cleanup,
 	.eeprom_read = ath_pci_eeprom_read,
@@ -194,6 +223,8 @@ static int ath_pci_probe(struct pci_dev 
 	       ah->hw_version.phyRev,
 	       (unsigned long)mem, pdev->irq);
 
+	ath_pci_stop(sc);
+
 	return 0;
 bad4:
 	ath_detach(sc);
@@ -217,45 +248,6 @@ static void ath_pci_remove(struct pci_de
 	ath_cleanup(sc);
 }
 
-#ifdef CONFIG_PM
-
-static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-	struct ath_wiphy *aphy = hw->priv;
-	struct ath_softc *sc = aphy->sc;
-
-	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-
-	pci_save_state(pdev);
-	pci_disable_device(pdev);
-	pci_set_power_state(pdev, PCI_D3hot);
-
-	return 0;
-}
-
-static int ath_pci_resume(struct pci_dev *pdev)
-{
-	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-	struct ath_wiphy *aphy = hw->priv;
-	struct ath_softc *sc = aphy->sc;
-	int err;
-
-	err = pci_enable_device(pdev);
-	if (err)
-		return err;
-	pci_restore_state(pdev);
-
-	/* Enable LED */
-	ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
-			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-
-	return 0;
-}
-
-#endif /* CONFIG_PM */
-
 MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
 
 static struct pci_driver ath_pci_driver = {
@@ -263,10 +255,6 @@ static struct pci_driver ath_pci_driver 
 	.id_table   = ath_pci_id_table,
 	.probe      = ath_pci_probe,
 	.remove     = ath_pci_remove,
-#ifdef CONFIG_PM
-	.suspend    = ath_pci_suspend,
-	.resume     = ath_pci_resume,
-#endif /* CONFIG_PM */
 };
 
 int ath_pci_init(void)
--- wireless-testing.orig/net/wireless/core.c	2009-05-15 15:17:09.241834437 +0200
+++ wireless-testing/net/wireless/core.c	2009-05-15 15:25:39.963705937 +0200
@@ -271,6 +271,8 @@ struct wiphy *wiphy_new(struct cfg80211_
 
 	device_initialize(&drv->wiphy.dev);
 	drv->wiphy.dev.class = &ieee80211_class;
+	/* only for ->shutdown hook */
+	drv->wiphy.dev.driver = &wiphy_driver;
 	drv->wiphy.dev.platform_data = drv;
 
 	/*
--- wireless-testing.orig/net/wireless/core.h	2009-05-15 15:17:09.320586075 +0200
+++ wireless-testing/net/wireless/core.h	2009-05-15 15:17:10.114957594 +0200
@@ -154,5 +154,6 @@ int cfg80211_leave_ibss(struct cfg80211_
 /* internal helpers */
 int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
 				   const u8 *mac_addr);
+extern struct device_driver wiphy_driver;
 
 #endif /* __NET_WIRELESS_CORE_H */
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/ath9k.h	2009-05-15 15:44:01.006837797 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/ath9k.h	2009-05-15 15:46:22.304582872 +0200
@@ -516,6 +516,8 @@ struct ath_rfkill {
 #define SC_OP_TSF_RESET         BIT(15)
 
 struct ath_bus_ops {
+	int		(*start)(struct ath_softc *sc);
+	void		(*stop)(struct ath_softc *sc);
 	void		(*read_cachesize)(struct ath_softc *sc, int *csz);
 	void		(*cleanup)(struct ath_softc *sc);
 	bool		(*eeprom_read)(struct ath_hw *ah, u32 off, u16 *data);
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/main.c	2009-05-15 15:44:19.261837573 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/main.c	2009-05-15 15:47:15.051830998 +0200
@@ -1901,6 +1901,10 @@ static int ath9k_start(struct ieee80211_
 	DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with "
 		"initial channel: %d MHz\n", curchan->center_freq);
 
+	r = sc->bus_ops->start(sc);
+	if (r)
+		return r;
+
 	mutex_lock(&sc->mutex);
 
 	if (ath9k_wiphy_started(sc)) {
@@ -2105,6 +2109,8 @@ static void ath9k_stop(struct ieee80211_
 
 	mutex_unlock(&sc->mutex);
 
+	sc->bus_ops->stop(sc);
+
 	DPRINTF(sc, ATH_DBG_CONFIG, "Driver halt\n");
 }
 


--
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

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux