Path: blob/main/sys/compat/linuxkpi/common/src/linuxkpi_80211_pm.c
283313 views
/*1* Copyright (c) 2025 The FreeBSD Foundation2*3* This software was developed by Björn Zeeb under sponsorship from4* the FreeBSD Foundation.5*6* SPDX-License-Identifier: BSD-2-Clause7*/89#include <sys/param.h>10#include <sys/kernel.h>11#include <sys/bus.h>12#include <sys/module.h>1314#include <linux/pci.h>15#include <dev/pci/pcivar.h>16#include "linux_80211.h"1718#include <net80211/ieee80211_var.h>1920struct lkpi_80211_pm_softc {21/* PCI */22int (*suspend) (struct pci_dev *pdev, pm_message_t state);23int (*resume) (struct pci_dev *pdev);24};2526static int27lkpi_80211_pm_suspend(struct pci_dev *pdev, pm_message_t state)28{29const struct dev_pm_ops *pmops;30struct lkpi_80211_pm_softc *sc;31struct ieee80211com *ic;32device_t dev;33int error;3435dev = device_find_child(pdev->dev.bsddev, "lkpi80211_pm",36DEVICE_UNIT_ANY);37if (dev == NULL) {38/* Must not happen, so abort suspend if it does. */39device_printf(pdev->dev.bsddev,40"%s: cannot find lkpi80211_pm child for %s\n",41__func__, device_get_name(pdev->dev.bsddev));42return (ENXIO);43}44sc = device_get_softc(dev);45error = 0;4647/* Call order: wireless then pdev. */4849ic = ieee80211_find_com(device_get_nameunit(pdev->dev.bsddev));50if (ic != NULL) {51error = lkpi_80211_suspend(ic, state);52} else {53device_printf(pdev->dev.bsddev,54"%s: WARNING: wireless device not found\n", __func__);55}56if (error != 0)57goto err;5859/* Logic duplicated from linux_pci_suspend(). */60pmops = pdev->pdrv->driver.pm;61if (sc->suspend != NULL)62error = sc->suspend(pdev, state);63else if (pmops != NULL && pmops->suspend != NULL) {64error = -pmops->suspend(&pdev->dev);65if (error == 0 && pmops->suspend_late != NULL)66error = -pmops->suspend_late(&pdev->dev);67if (error == 0 && pmops->suspend_noirq != NULL)68error = -pmops->suspend_noirq(&pdev->dev);69}7071err:72if (error < 0)73error = -error;7475if (error != 0)76device_printf(pdev->dev.bsddev,77"%s: WARNING: SUSPEND FAILED: %d\n", __func__, error);7879return (error);80}8182static int83lkpi_80211_pm_resume(struct pci_dev *pdev)84{85const struct dev_pm_ops *pmops;86struct lkpi_80211_pm_softc *sc;87struct ieee80211com *ic;88device_t dev;89int error;9091dev = device_find_child(pdev->dev.bsddev, "lkpi80211_pm",92DEVICE_UNIT_ANY);93if (dev == NULL) {94/* Must not happen, so abort suspend if it does. */95device_printf(pdev->dev.bsddev,96"%s: cannot find lkpi80211_pm child\n", __func__);97return (ENXIO);98}99sc = device_get_softc(dev);100error = 0;101102/* Call order: pdev then wireless. */103104/* Logic duplicated from linux_pci_resume(). */105pmops = pdev->pdrv->driver.pm;106if (sc->resume != NULL) {107error = sc->resume(pdev);108} else if (pmops != NULL && pmops->resume != NULL) {109if (pmops->resume_early != NULL)110error = -pmops->resume_early(&pdev->dev);111if (error == 0 && pmops->resume != NULL)112error = -pmops->resume(&pdev->dev);113}114if (error != 0)115device_printf(pdev->dev.bsddev, "%s: resume failed!\n", __func__);116/* Do not error out but give wireless also a chance. */117118ic = ieee80211_find_com(device_get_nameunit(pdev->dev.bsddev));119if (ic != NULL) {120error = lkpi_80211_resume(ic);121} else {122device_printf(pdev->dev.bsddev,123"%s: WARNING: wireless device not found\n", __func__);124}125126if (error < 0)127error = -error;128129return (error);130}131132/* -------------------------------------------------------------------------- */133static void134lkpi_80211_pm_identify(driver_t *driver, device_t parent)135{136137/* Make sure we're not being doubly invoked per parent. */138if (device_find_child(parent, driver->name, DEVICE_UNIT_ANY) != NULL)139return;140141/* Make sure this is PCI for now. */142if (!is_pci_device(parent))143return;144145if (BUS_ADD_CHILD(parent, 0, driver->name, DEVICE_UNIT_ANY) == NULL)146device_printf(parent, "%s: failed to add child\n", __func__);147}148149static int150lkpi_80211_pm_probe(device_t dev)151{152device_set_descf(dev, "LinuxKPI 802.11 %s mac80211 PM",153device_get_nameunit(device_get_parent(dev)));154return (BUS_PROBE_DEFAULT);155}156157static int158lkpi_80211_pm_attach(device_t dev)159{160struct lkpi_80211_pm_softc *sc;161struct pci_dev *pdev;162163sc = device_get_softc(dev);164pdev = device_get_softc(device_get_parent(dev));165166/* Intercept the driver suspend/resume calls. */167sc->suspend = pdev->pdrv->suspend;168pdev->pdrv->suspend = lkpi_80211_pm_suspend;169sc->resume = pdev->pdrv->resume;170pdev->pdrv->resume = lkpi_80211_pm_resume;171172return (0);173}174175static int176lkpi_80211_pm_detach(device_t dev)177{178struct lkpi_80211_pm_softc *sc;179struct pci_dev *pdev;180181sc = device_get_softc(dev);182pdev = device_get_softc(device_get_parent(dev));183184/* Restore the original notifications. */185pdev->pdrv->suspend = sc->suspend;186pdev->pdrv->resume = sc->resume;187188return (0);189}190191static device_method_t lkpi_80211_pm_methods[] = {192/* Device interface */193DEVMETHOD(device_identify, lkpi_80211_pm_identify),194DEVMETHOD(device_probe, lkpi_80211_pm_probe),195DEVMETHOD(device_attach, lkpi_80211_pm_attach),196DEVMETHOD(device_detach, lkpi_80211_pm_detach),197/*198* Do not think about device_suspend/resume here.199* We are not a PCI device and LinuxKPI PCI linux_pci_suspend/resume200* are getting the notifications so we have to hijack the201* LinuxKPI upcalls.202*/203204DEVMETHOD_END205};206207driver_t lkpi_80211_pm_driver = {208"lkpi80211_pm",209lkpi_80211_pm_methods,210sizeof(struct lkpi_80211_pm_softc),211};212213MODULE_DEPEND(lkpi80211_pm, linuxkpi_wlan, 1, 1, 1);214MODULE_VERSION(lkpi80211_pm, 1);215216217