#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <linux/pci.h>
#include <dev/pci/pcivar.h>
#include "linux_80211.h"
#include <net80211/ieee80211_var.h>
struct lkpi_80211_pm_softc {
int (*suspend) (struct pci_dev *pdev, pm_message_t state);
int (*resume) (struct pci_dev *pdev);
};
static int
lkpi_80211_pm_suspend(struct pci_dev *pdev, pm_message_t state)
{
const struct dev_pm_ops *pmops;
struct lkpi_80211_pm_softc *sc;
struct ieee80211com *ic;
device_t dev;
int error;
dev = device_find_child(pdev->dev.bsddev, "lkpi80211_pm",
DEVICE_UNIT_ANY);
if (dev == NULL) {
device_printf(pdev->dev.bsddev,
"%s: cannot find lkpi80211_pm child for %s\n",
__func__, device_get_name(pdev->dev.bsddev));
return (ENXIO);
}
sc = device_get_softc(dev);
error = 0;
ic = ieee80211_find_com(device_get_nameunit(pdev->dev.bsddev));
if (ic != NULL) {
error = lkpi_80211_suspend(ic, state);
} else {
device_printf(pdev->dev.bsddev,
"%s: WARNING: wireless device not found\n", __func__);
}
if (error != 0)
goto err;
pmops = pdev->pdrv->driver.pm;
if (sc->suspend != NULL)
error = sc->suspend(pdev, state);
else if (pmops != NULL && pmops->suspend != NULL) {
error = -pmops->suspend(&pdev->dev);
if (error == 0 && pmops->suspend_late != NULL)
error = -pmops->suspend_late(&pdev->dev);
if (error == 0 && pmops->suspend_noirq != NULL)
error = -pmops->suspend_noirq(&pdev->dev);
}
err:
if (error < 0)
error = -error;
if (error != 0)
device_printf(pdev->dev.bsddev,
"%s: WARNING: SUSPEND FAILED: %d\n", __func__, error);
return (error);
}
static int
lkpi_80211_pm_resume(struct pci_dev *pdev)
{
const struct dev_pm_ops *pmops;
struct lkpi_80211_pm_softc *sc;
struct ieee80211com *ic;
device_t dev;
int error;
dev = device_find_child(pdev->dev.bsddev, "lkpi80211_pm",
DEVICE_UNIT_ANY);
if (dev == NULL) {
device_printf(pdev->dev.bsddev,
"%s: cannot find lkpi80211_pm child\n", __func__);
return (ENXIO);
}
sc = device_get_softc(dev);
error = 0;
pmops = pdev->pdrv->driver.pm;
if (sc->resume != NULL) {
error = sc->resume(pdev);
} else if (pmops != NULL && pmops->resume != NULL) {
if (pmops->resume_early != NULL)
error = -pmops->resume_early(&pdev->dev);
if (error == 0 && pmops->resume != NULL)
error = -pmops->resume(&pdev->dev);
}
if (error != 0)
device_printf(pdev->dev.bsddev, "%s: resume failed!\n", __func__);
ic = ieee80211_find_com(device_get_nameunit(pdev->dev.bsddev));
if (ic != NULL) {
error = lkpi_80211_resume(ic);
} else {
device_printf(pdev->dev.bsddev,
"%s: WARNING: wireless device not found\n", __func__);
}
if (error < 0)
error = -error;
return (error);
}
static void
lkpi_80211_pm_identify(driver_t *driver, device_t parent)
{
if (device_find_child(parent, driver->name, DEVICE_UNIT_ANY) != NULL)
return;
if (!is_pci_device(parent))
return;
if (BUS_ADD_CHILD(parent, 0, driver->name, DEVICE_UNIT_ANY) == NULL)
device_printf(parent, "%s: failed to add child\n", __func__);
}
static int
lkpi_80211_pm_probe(device_t dev)
{
device_set_descf(dev, "LinuxKPI 802.11 %s mac80211 PM",
device_get_nameunit(device_get_parent(dev)));
return (BUS_PROBE_DEFAULT);
}
static int
lkpi_80211_pm_attach(device_t dev)
{
struct lkpi_80211_pm_softc *sc;
struct pci_dev *pdev;
sc = device_get_softc(dev);
pdev = device_get_softc(device_get_parent(dev));
sc->suspend = pdev->pdrv->suspend;
pdev->pdrv->suspend = lkpi_80211_pm_suspend;
sc->resume = pdev->pdrv->resume;
pdev->pdrv->resume = lkpi_80211_pm_resume;
return (0);
}
static int
lkpi_80211_pm_detach(device_t dev)
{
struct lkpi_80211_pm_softc *sc;
struct pci_dev *pdev;
sc = device_get_softc(dev);
pdev = device_get_softc(device_get_parent(dev));
pdev->pdrv->suspend = sc->suspend;
pdev->pdrv->resume = sc->resume;
return (0);
}
static device_method_t lkpi_80211_pm_methods[] = {
DEVMETHOD(device_identify, lkpi_80211_pm_identify),
DEVMETHOD(device_probe, lkpi_80211_pm_probe),
DEVMETHOD(device_attach, lkpi_80211_pm_attach),
DEVMETHOD(device_detach, lkpi_80211_pm_detach),
DEVMETHOD_END
};
driver_t lkpi_80211_pm_driver = {
"lkpi80211_pm",
lkpi_80211_pm_methods,
sizeof(struct lkpi_80211_pm_softc),
};
MODULE_DEPEND(lkpi80211_pm, linuxkpi_wlan, 1, 1, 1);
MODULE_VERSION(lkpi80211_pm, 1);