VirtualBox

source: vbox/trunk/src/VBox/Devices/Gpio/DrvGpioButton.cpp@ 105877

Last change on this file since 105877 was 104589, checked in by vboxsync, 7 months ago

Devices/Gpio/DrvGpioButton.cpp: Return an error if creating the button depress timer fails, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
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 */
62typedef 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. */
85typedef DRVGPIOBUTTON *PDRVGPIOBUTTON;
86
87
88/**
89 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
90 */
91static 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 */
106static 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 */
120static 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 */
135static DECLCALLBACK(int) drvGpioButton_SleepButtonPress(PPDMIEVENTBUTTONPORT pInterface)
136{
137 PDRVGPIOBUTTON pThis = RT_FROM_MEMBER(pInterface, DRVGPIOBUTTON, IEventButtonPort);
138
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;
144}
145
146
147/**
148 * @interface_method_impl{PDMIEVENTBUTTONPORT,pfnQueryPowerButtonHandled}
149 */
150static 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 */
163static 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 */
181static 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);
240 if (RT_FAILURE(rc))
241 return PDMDRV_SET_ERROR(pDrvIns, rc,
242 N_("Failed to create button depress timer"));
243
244 return VINF_SUCCESS;
245}
246
247
248/**
249 * GPIO button driver registration record.
250 */
251const 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};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette