[101618] | 1 | /* $Id: DrvGpioButton.cpp 104589 2024-05-13 12:14:37Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * DrvGpioButton - Virtual GPIO driver for power/sleep button presses.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
| 7 | * Copyright (C) 2023 Oracle and/or its affiliates.
|
---|
| 8 | *
|
---|
| 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
| 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
| 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
| 32 | #define LOG_GROUP LOG_GROUP_DRV_GPIO
|
---|
| 33 |
|
---|
| 34 | #include <VBox/vmm/pdmdrv.h>
|
---|
| 35 | #include <VBox/log.h>
|
---|
| 36 | #include <iprt/assert.h>
|
---|
| 37 | #include <iprt/string.h>
|
---|
| 38 | #include <iprt/uuid.h>
|
---|
| 39 |
|
---|
| 40 | #include "VBoxDD.h"
|
---|
| 41 |
|
---|
| 42 |
|
---|
| 43 | /*********************************************************************************************************************************
|
---|
| 44 | * Defined Constants And Macros *
|
---|
| 45 | *********************************************************************************************************************************/
|
---|
| 46 |
|
---|
| 47 | /** The power button is currently pressed. */
|
---|
| 48 | #define DRV_GPIO_BUTTON_PRESSED_POWER RT_BIT_32(0)
|
---|
| 49 | /** The sleep button is currently pressed. */
|
---|
| 50 | #define DRV_GPIO_BUTTON_PRESSED_SLEEP RT_BIT_32(1)
|
---|
| 51 |
|
---|
| 52 |
|
---|
| 53 | /*********************************************************************************************************************************
|
---|
| 54 | * Structures and Typedefs *
|
---|
| 55 | *********************************************************************************************************************************/
|
---|
| 56 | /**
|
---|
| 57 | * GPIO button driver instance data.
|
---|
| 58 | *
|
---|
| 59 | * @implements PDMIEVENTBUTTONPORT
|
---|
| 60 | * @implements PDMIGPIOCONNECTOR
|
---|
| 61 | */
|
---|
| 62 | typedef struct DRVGPIOBUTTON
|
---|
| 63 | {
|
---|
| 64 | /** The button event interface for use by Main. */
|
---|
| 65 | PDMIEVENTBUTTONPORT IEventButtonPort;
|
---|
| 66 | /** The GPIO interface interface. */
|
---|
| 67 | PDMIGPIOCONNECTOR IGpioConnector;
|
---|
| 68 | /** The GPIO port interface above. */
|
---|
| 69 | PPDMIGPIOPORT pGpioPort;
|
---|
| 70 | /** Pointer to the driver instance. */
|
---|
| 71 | PPDMDRVINS pDrvIns;
|
---|
| 72 |
|
---|
| 73 | /** Currently pressed button. */
|
---|
| 74 | volatile uint32_t fButtonsPressed;
|
---|
| 75 |
|
---|
| 76 | /** The power button GPIO line to trigger. */
|
---|
| 77 | uint32_t uPowerButtonGpio;
|
---|
| 78 | /** The sleep button GPIO line to trigger. */
|
---|
| 79 | uint32_t uSleepButtonGpio;
|
---|
| 80 |
|
---|
| 81 | TMTIMERHANDLE hTimerDepress;
|
---|
| 82 |
|
---|
| 83 | } DRVGPIOBUTTON;
|
---|
| 84 | /** Pointer to a GPIO button driver instance. */
|
---|
| 85 | typedef DRVGPIOBUTTON *PDRVGPIOBUTTON;
|
---|
| 86 |
|
---|
| 87 |
|
---|
| 88 | /**
|
---|
| 89 | * @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
---|
| 90 | */
|
---|
| 91 | static DECLCALLBACK(void *) drvGpioButton_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
|
---|
| 92 | {
|
---|
| 93 | PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
|
---|
| 94 | PDRVGPIOBUTTON pThis = PDMINS_2_DATA(pDrvIns, PDRVGPIOBUTTON);
|
---|
| 95 |
|
---|
| 96 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
|
---|
| 97 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIGPIOCONNECTOR, &pThis->IGpioConnector);
|
---|
| 98 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIEVENTBUTTONPORT, &pThis->IEventButtonPort);
|
---|
| 99 | return NULL;
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 |
|
---|
| 103 | /**
|
---|
| 104 | * @interface_method_impl{PDMIEVENTBUTTONPORT,pfnQueryGuestCanHandleButtonEvents}
|
---|
| 105 | */
|
---|
| 106 | static DECLCALLBACK(int) drvGpioButton_QueryGuestCanHandleButtonEvents(PPDMIEVENTBUTTONPORT pInterface, bool *pfCanHandleButtonEvents)
|
---|
| 107 | {
|
---|
| 108 | PDRVGPIOBUTTON pThis = RT_FROM_MEMBER(pInterface, DRVGPIOBUTTON, IEventButtonPort);
|
---|
| 109 |
|
---|
| 110 | /** @todo Better interface for this. */
|
---|
| 111 | *pfCanHandleButtonEvents = pThis->pGpioPort->pfnGpioLineIsInput(pThis->pGpioPort, pThis->uPowerButtonGpio)
|
---|
| 112 | || pThis->pGpioPort->pfnGpioLineIsInput(pThis->pGpioPort, pThis->uSleepButtonGpio);
|
---|
| 113 | return VINF_SUCCESS;
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 |
|
---|
| 117 | /**
|
---|
| 118 | * @interface_method_impl{PDMIEVENTBUTTONPORT,pfnPowerButtonPress}
|
---|
| 119 | */
|
---|
| 120 | static DECLCALLBACK(int) drvGpioButton_PowerButtonPress(PPDMIEVENTBUTTONPORT pInterface)
|
---|
| 121 | {
|
---|
| 122 | PDRVGPIOBUTTON pThis = RT_FROM_MEMBER(pInterface, DRVGPIOBUTTON, IEventButtonPort);
|
---|
| 123 |
|
---|
| 124 | ASMAtomicOrU32(&pThis->fButtonsPressed, DRV_GPIO_BUTTON_PRESSED_POWER);
|
---|
| 125 | int rc = pThis->pGpioPort->pfnGpioLineChange(pThis->pGpioPort, pThis->uPowerButtonGpio, true /*fVal*/);
|
---|
| 126 | if (RT_SUCCESS(rc))
|
---|
| 127 | rc = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimerDepress, 250);
|
---|
| 128 | return rc;
|
---|
| 129 | }
|
---|
| 130 |
|
---|
| 131 |
|
---|
| 132 | /**
|
---|
| 133 | * @interface_method_impl{PDMIEVENTBUTTONPORT,pfnSleepButtonPress}
|
---|
| 134 | */
|
---|
| 135 | static DECLCALLBACK(int) drvGpioButton_SleepButtonPress(PPDMIEVENTBUTTONPORT pInterface)
|
---|
| 136 | {
|
---|
| 137 | PDRVGPIOBUTTON pThis = RT_FROM_MEMBER(pInterface, DRVGPIOBUTTON, IEventButtonPort);
|
---|
| 138 |
|
---|
[101624] | 139 | ASMAtomicOrU32(&pThis->fButtonsPressed, DRV_GPIO_BUTTON_PRESSED_SLEEP);
|
---|
| 140 | int rc = pThis->pGpioPort->pfnGpioLineChange(pThis->pGpioPort, pThis->uSleepButtonGpio, true /*fVal*/);
|
---|
| 141 | if (RT_SUCCESS(rc))
|
---|
| 142 | rc = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimerDepress, 250);
|
---|
| 143 | return rc;
|
---|
[101618] | 144 | }
|
---|
| 145 |
|
---|
| 146 |
|
---|
| 147 | /**
|
---|
| 148 | * @interface_method_impl{PDMIEVENTBUTTONPORT,pfnQueryPowerButtonHandled}
|
---|
| 149 | */
|
---|
| 150 | static DECLCALLBACK(int) drvGpioButton_QueryPowerButtonHandled(PPDMIEVENTBUTTONPORT pInterface, bool *pfHandled)
|
---|
| 151 | {
|
---|
| 152 | RT_NOREF(pInterface);
|
---|
| 153 |
|
---|
| 154 | /** @todo */
|
---|
| 155 | *pfHandled = true;
|
---|
| 156 | return VINF_SUCCESS;
|
---|
| 157 | }
|
---|
| 158 |
|
---|
| 159 |
|
---|
| 160 | /**
|
---|
| 161 | * Timer callback that depresses a button.
|
---|
| 162 | */
|
---|
| 163 | static DECLCALLBACK(void) drvGpioButtonTimerDepress(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
|
---|
| 164 | {
|
---|
| 165 | PDRVGPIOBUTTON pThis = PDMINS_2_DATA(pDrvIns, PDRVGPIOBUTTON);
|
---|
| 166 | Assert(hTimer == pThis->hTimerDepress); RT_NOREF(hTimer, pvUser);
|
---|
| 167 |
|
---|
| 168 | uint32_t fButtonsPressed = ASMAtomicXchgU32(&pThis->fButtonsPressed, 0);
|
---|
| 169 | if (fButtonsPressed & DRV_GPIO_BUTTON_PRESSED_POWER)
|
---|
| 170 | pThis->pGpioPort->pfnGpioLineChange(pThis->pGpioPort, pThis->uPowerButtonGpio, false /*fVal*/);
|
---|
| 171 | if (fButtonsPressed & DRV_GPIO_BUTTON_PRESSED_SLEEP)
|
---|
| 172 | pThis->pGpioPort->pfnGpioLineChange(pThis->pGpioPort, pThis->uSleepButtonGpio, false /*fVal*/);
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 |
|
---|
| 176 | /**
|
---|
| 177 | * Construct an GPIO button driver instance.
|
---|
| 178 | *
|
---|
| 179 | * @copydoc FNPDMDRVCONSTRUCT
|
---|
| 180 | */
|
---|
| 181 | static DECLCALLBACK(int) drvGpioButtonConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
|
---|
| 182 | {
|
---|
| 183 | RT_NOREF(pCfg, fFlags);
|
---|
| 184 | PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
|
---|
| 185 | PDRVGPIOBUTTON pThis = PDMINS_2_DATA(pDrvIns, PDRVGPIOBUTTON);
|
---|
| 186 | PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
|
---|
| 187 | int rc = VINF_SUCCESS;
|
---|
| 188 |
|
---|
| 189 | /*
|
---|
| 190 | * Init the static parts.
|
---|
| 191 | */
|
---|
| 192 | /* IBase */
|
---|
| 193 | pDrvIns->IBase.pfnQueryInterface = drvGpioButton_QueryInterface;
|
---|
| 194 | /* IEventButtonPort */
|
---|
| 195 | pThis->IEventButtonPort.pfnQueryGuestCanHandleButtonEvents = drvGpioButton_QueryGuestCanHandleButtonEvents;
|
---|
| 196 | pThis->IEventButtonPort.pfnPowerButtonPress = drvGpioButton_PowerButtonPress;
|
---|
| 197 | pThis->IEventButtonPort.pfnSleepButtonPress = drvGpioButton_SleepButtonPress;
|
---|
| 198 | pThis->IEventButtonPort.pfnQueryPowerButtonHandled = drvGpioButton_QueryPowerButtonHandled;
|
---|
| 199 |
|
---|
| 200 | pThis->pDrvIns = pDrvIns;
|
---|
| 201 | pThis->fButtonsPressed = 0;
|
---|
| 202 |
|
---|
| 203 | /*
|
---|
| 204 | * Validate and read the configuration.
|
---|
| 205 | */
|
---|
| 206 | PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "PowerButtonGpio"
|
---|
| 207 | "|SleepButtonGpio",
|
---|
| 208 | "");
|
---|
| 209 |
|
---|
| 210 | rc = pHlp->pfnCFGMQueryU32(pCfg, "PowerButtonGpio", &pThis->uPowerButtonGpio);
|
---|
| 211 | if (RT_FAILURE(rc))
|
---|
| 212 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
| 213 | N_("Configuration error: Failed to get the \"PowerButtonGpio\" value"));
|
---|
| 214 |
|
---|
| 215 | rc = pHlp->pfnCFGMQueryU32(pCfg, "SleepButtonGpio", &pThis->uSleepButtonGpio);
|
---|
| 216 | if (RT_FAILURE(rc))
|
---|
| 217 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
| 218 | N_("Configuration error: Failed to get the \"SleepButtonGpio\" value"));
|
---|
| 219 |
|
---|
| 220 | /*
|
---|
| 221 | * Check that no-one is attached to us.
|
---|
| 222 | */
|
---|
| 223 | AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
|
---|
| 224 | ("Configuration error: Not possible to attach anything to this driver!\n"),
|
---|
| 225 | VERR_PDM_DRVINS_NO_ATTACH);
|
---|
| 226 |
|
---|
| 227 | /*
|
---|
| 228 | * Query the GPIO port interface.
|
---|
| 229 | */
|
---|
| 230 | pThis->pGpioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIGPIOPORT);
|
---|
| 231 | if (!pThis->pGpioPort)
|
---|
| 232 | {
|
---|
| 233 | AssertMsgFailed(("Configuration error: the above device/driver didn't export the GPIO port interface!\n"));
|
---|
| 234 | return VERR_PDM_MISSING_INTERFACE_ABOVE;
|
---|
| 235 | }
|
---|
| 236 |
|
---|
| 237 | rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_VIRTUAL, drvGpioButtonTimerDepress, NULL,
|
---|
| 238 | TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
|
---|
| 239 | "Button depress timer", &pThis->hTimerDepress);
|
---|
[104589] | 240 | if (RT_FAILURE(rc))
|
---|
| 241 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
| 242 | N_("Failed to create button depress timer"));
|
---|
[101618] | 243 |
|
---|
| 244 | return VINF_SUCCESS;
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 |
|
---|
| 248 | /**
|
---|
| 249 | * GPIO button driver registration record.
|
---|
| 250 | */
|
---|
| 251 | const PDMDRVREG g_DrvGpioButton =
|
---|
| 252 | {
|
---|
| 253 | /* .u32Version = */ PDM_DRVREG_VERSION,
|
---|
| 254 | /* .szName = */ "GpioButton",
|
---|
| 255 | /* .szRCMod = */ "",
|
---|
| 256 | /* .szR0Mod = */ "",
|
---|
| 257 | /* .pszDescription = */ "GPIO Button Driver",
|
---|
| 258 | /* .fFlags = */ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
---|
| 259 | /* .fClass = */ PDM_DRVREG_CLASS_GPIO,
|
---|
| 260 | /* .cMaxInstances = */ UINT32_MAX,
|
---|
| 261 | /* .cbInstance = */ sizeof(DRVGPIOBUTTON),
|
---|
| 262 | /* .pfnConstruct = */ drvGpioButtonConstruct,
|
---|
| 263 | /* .pfnDestruct = */ NULL,
|
---|
| 264 | /* .pfnRelocate = */ NULL,
|
---|
| 265 | /* .pfnIOCtl = */ NULL,
|
---|
| 266 | /* .pfnPowerOn = */ NULL,
|
---|
| 267 | /* .pfnReset = */ NULL,
|
---|
| 268 | /* .pfnSuspend = */ NULL,
|
---|
| 269 | /* .pfnResume = */ NULL,
|
---|
| 270 | /* .pfnAttach = */ NULL,
|
---|
| 271 | /* .pfnDetach = */ NULL,
|
---|
| 272 | /* .pfnPowerOff = */ NULL,
|
---|
| 273 | /* .pfnSoftReset = */ NULL,
|
---|
| 274 | /* .u32EndVersion = */ PDM_DRVREG_VERSION
|
---|
| 275 | };
|
---|