Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/compat/linuxkpi/common/src/linux_acpi.c
102423 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2018 Johannes Lundberg <[email protected]>
5
* Copyright (c) 2020 Vladimir Kondratyev <[email protected]>
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions are
9
* met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in
14
* the documentation and/or other materials provided with the
15
* distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
#include "opt_acpi.h"
31
32
#include <sys/types.h>
33
#include <sys/bus.h>
34
#include <sys/eventhandler.h>
35
#include <sys/kernel.h>
36
#include <sys/power.h>
37
38
#include <contrib/dev/acpica/include/acpi.h>
39
#include <dev/acpica/acpivar.h>
40
41
#include <linux/notifier.h>
42
#include <linux/suspend.h>
43
#include <linux/uuid.h>
44
45
#include <acpi/acpi_bus.h>
46
#include <acpi/video.h>
47
48
#define ACPI_AC_CLASS "ac_adapter"
49
50
ACPI_MODULE_NAME("linux_acpi")
51
52
enum {
53
LINUX_ACPI_ACAD,
54
LINUX_ACPI_VIDEO,
55
LINUX_ACPI_TAGS /* must be last */
56
};
57
_Static_assert(LINUX_ACPI_TAGS <= LINUX_NOTIFY_TAGS,
58
"Not enough space for tags in notifier_block structure");
59
60
#ifdef DEV_ACPI
61
62
suspend_state_t pm_suspend_target_state = PM_SUSPEND_ON;
63
64
static uint32_t linux_acpi_target_sleep_state = ACPI_STATE_S0;
65
66
static eventhandler_tag resume_tag;
67
static eventhandler_tag suspend_tag;
68
69
ACPI_HANDLE
70
bsd_acpi_get_handle(device_t bsddev)
71
{
72
return (acpi_get_handle(bsddev));
73
}
74
75
bool
76
acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
77
{
78
UINT64 ret;
79
80
if (funcs == 0)
81
return (false);
82
83
/*
84
* From ACPI 6.3 spec 9.1.1:
85
* Bit 0 indicates whether there is support for any functions other
86
* than function 0 for the specified UUID and Revision ID. If set to
87
* zero, no functions are supported (other than function zero) for the
88
* specified UUID and Revision ID.
89
*/
90
funcs |= 1 << 0;
91
92
ret = acpi_DSMQuery(handle, (const uint8_t *)uuid, rev);
93
return ((ret & funcs) == funcs);
94
}
95
96
ACPI_OBJECT *
97
acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
98
int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
99
{
100
ACPI_BUFFER buf;
101
ACPI_STATUS status;
102
103
status = acpi_EvaluateDSMTyped(handle, (const uint8_t *)uuid, rev, func,
104
argv4, &buf, type);
105
return (ACPI_SUCCESS(status) ? (ACPI_OBJECT *)buf.Pointer : NULL);
106
}
107
108
union linuxkpi_acpi_object *
109
acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
110
UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
111
{
112
ACPI_BUFFER buf;
113
ACPI_STATUS status;
114
115
status = acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, rev, func,
116
(ACPI_OBJECT *)pkg, &buf);
117
return (ACPI_SUCCESS(status) ?
118
(union linuxkpi_acpi_object *)buf.Pointer : NULL);
119
}
120
121
static void
122
linux_handle_power_suspend_event(void *arg __unused, enum power_stype stype)
123
{
124
switch (stype) {
125
case POWER_STYPE_SUSPEND_TO_IDLE:
126
/*
127
* XXX: obiwac Not 100% sure this is correct, but
128
* acpi_target_sleep_state does seem to be set to
129
* ACPI_STATE_S3 during s2idle on Linux.
130
*/
131
linux_acpi_target_sleep_state = ACPI_STATE_S3;
132
pm_suspend_target_state = PM_SUSPEND_TO_IDLE;
133
break;
134
case POWER_STYPE_SUSPEND_TO_MEM:
135
linux_acpi_target_sleep_state = ACPI_STATE_S3;
136
pm_suspend_target_state = PM_SUSPEND_MEM;
137
break;
138
default:
139
printf("%s: sleep type %d not yet supported\n",
140
__func__, stype);
141
break;
142
}
143
}
144
145
static void
146
linux_handle_power_resume_event(void *arg __unused,
147
enum power_stype stype __unused)
148
{
149
linux_acpi_target_sleep_state = ACPI_STATE_S0;
150
pm_suspend_target_state = PM_SUSPEND_ON;
151
}
152
153
static void
154
linux_handle_acpi_acad_event(void *arg, int data)
155
{
156
struct notifier_block *nb = arg;
157
/*
158
* Event type information is lost ATM in FreeBSD ACPI event handler.
159
* Fortunately, drm-kmod do not distinct AC event types too, so we can
160
* use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler.
161
*/
162
struct acpi_bus_event abe = {
163
.device_class = ACPI_AC_CLASS,
164
.type = ACPI_NOTIFY_BUS_CHECK,
165
.data = data,
166
};
167
168
nb->notifier_call(nb, 0, &abe);
169
}
170
171
static void
172
linux_handle_acpi_video_event(void *arg, int type)
173
{
174
struct notifier_block *nb = arg;
175
struct acpi_bus_event abe = {
176
.device_class = ACPI_VIDEO_CLASS,
177
.type = type,
178
.data = 0,
179
};
180
181
nb->notifier_call(nb, 0, &abe);
182
}
183
184
int
185
register_acpi_notifier(struct notifier_block *nb)
186
{
187
nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event,
188
linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST);
189
nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event,
190
linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST);
191
192
return (0);
193
}
194
195
int
196
unregister_acpi_notifier(struct notifier_block *nb)
197
{
198
EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]);
199
EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]);
200
201
return (0);
202
}
203
204
uint32_t
205
acpi_target_system_state(void)
206
{
207
return (linux_acpi_target_sleep_state);
208
}
209
210
struct acpi_dev_present_ctx {
211
const char *hid;
212
const char *uid;
213
int64_t hrv;
214
struct acpi_device *dev;
215
};
216
217
static ACPI_STATUS
218
acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context,
219
void **result)
220
{
221
ACPI_DEVICE_INFO *devinfo;
222
struct acpi_device *dev;
223
struct acpi_dev_present_ctx *match = context;
224
bool present = false;
225
UINT32 sta, hrv;
226
int i;
227
228
if (handle == NULL)
229
return (AE_OK);
230
231
if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
232
!ACPI_DEVICE_PRESENT(sta))
233
return (AE_OK);
234
235
if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &devinfo)))
236
return (AE_OK);
237
238
if ((devinfo->Valid & ACPI_VALID_HID) != 0 &&
239
strcmp(match->hid, devinfo->HardwareId.String) == 0) {
240
present = true;
241
} else if ((devinfo->Valid & ACPI_VALID_CID) != 0) {
242
for (i = 0; i < devinfo->CompatibleIdList.Count; i++) {
243
if (strcmp(match->hid,
244
devinfo->CompatibleIdList.Ids[i].String) == 0) {
245
present = true;
246
break;
247
}
248
}
249
}
250
if (present && match->uid != NULL &&
251
((devinfo->Valid & ACPI_VALID_UID) == 0 ||
252
strcmp(match->uid, devinfo->UniqueId.String) != 0))
253
present = false;
254
255
AcpiOsFree(devinfo);
256
if (!present)
257
return (AE_OK);
258
259
if (match->hrv != -1) {
260
if (ACPI_FAILURE(acpi_GetInteger(handle, "_HRV", &hrv)))
261
return (AE_OK);
262
if (hrv != match->hrv)
263
return (AE_OK);
264
}
265
266
dev = acpi_get_device(handle);
267
if (dev == NULL)
268
return (AE_OK);
269
match->dev = dev;
270
271
return (AE_ERROR);
272
}
273
274
bool
275
lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)
276
{
277
struct acpi_dev_present_ctx match;
278
int rv;
279
280
match.hid = hid;
281
match.uid = uid;
282
match.hrv = hrv;
283
284
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
285
ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);
286
287
return (rv == AE_ERROR);
288
}
289
290
struct acpi_device *
291
lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,
292
int64_t hrv)
293
{
294
struct acpi_dev_present_ctx match;
295
int rv;
296
297
match.hid = hid;
298
match.uid = uid;
299
match.hrv = hrv;
300
match.dev = NULL;
301
302
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
303
ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);
304
305
return (rv == AE_ERROR ? match.dev : NULL);
306
}
307
308
static void
309
linux_register_acpi_event_handlers(void *arg __unused)
310
{
311
/*
312
* XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use
313
* power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not
314
* generate acpi_sleep_event... Lid open or wake on button generates
315
* acpi_wakeup_event on one of my Dell laptops but not the other
316
* (but it does power on)... is this a general thing?
317
*/
318
resume_tag = EVENTHANDLER_REGISTER(power_resume,
319
linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST);
320
suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early,
321
linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST);
322
}
323
324
static void
325
linux_deregister_acpi_event_handlers(void *arg __unused)
326
{
327
EVENTHANDLER_DEREGISTER(power_resume, resume_tag);
328
EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag);
329
}
330
331
SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,
332
linux_register_acpi_event_handlers, NULL);
333
SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,
334
linux_deregister_acpi_event_handlers, NULL);
335
336
#else /* !DEV_ACPI */
337
338
ACPI_HANDLE
339
bsd_acpi_get_handle(device_t bsddev)
340
{
341
return (NULL);
342
}
343
344
bool
345
acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
346
{
347
return (false);
348
}
349
350
ACPI_OBJECT *
351
acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
352
int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
353
{
354
return (NULL);
355
}
356
357
union linuxkpi_acpi_object *
358
acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
359
UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
360
{
361
return (NULL);
362
}
363
364
int
365
register_acpi_notifier(struct notifier_block *nb)
366
{
367
return (0);
368
}
369
370
int
371
unregister_acpi_notifier(struct notifier_block *nb)
372
{
373
return (0);
374
}
375
376
uint32_t
377
acpi_target_system_state(void)
378
{
379
return (ACPI_STATE_S0);
380
}
381
382
bool
383
lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)
384
{
385
return (false);
386
}
387
388
struct acpi_device *
389
lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,
390
int64_t hrv)
391
{
392
return (NULL);
393
}
394
395
#endif /* !DEV_ACPI */
396
397