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
39586 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
37
#include <contrib/dev/acpica/include/acpi.h>
38
#include <dev/acpica/acpivar.h>
39
40
#include <linux/notifier.h>
41
#include <linux/suspend.h>
42
#include <linux/uuid.h>
43
44
#include <acpi/acpi_bus.h>
45
#include <acpi/video.h>
46
47
#define ACPI_AC_CLASS "ac_adapter"
48
49
ACPI_MODULE_NAME("linux_acpi")
50
51
enum {
52
LINUX_ACPI_ACAD,
53
LINUX_ACPI_VIDEO,
54
LINUX_ACPI_TAGS /* must be last */
55
};
56
_Static_assert(LINUX_ACPI_TAGS <= LINUX_NOTIFY_TAGS,
57
"Not enough space for tags in notifier_block structure");
58
59
#ifdef DEV_ACPI
60
61
suspend_state_t pm_suspend_target_state = PM_SUSPEND_ON;
62
63
static uint32_t linux_acpi_target_sleep_state = ACPI_STATE_S0;
64
65
static eventhandler_tag resume_tag;
66
static eventhandler_tag suspend_tag;
67
68
ACPI_HANDLE
69
bsd_acpi_get_handle(device_t bsddev)
70
{
71
return (acpi_get_handle(bsddev));
72
}
73
74
bool
75
acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
76
{
77
UINT64 ret;
78
79
if (funcs == 0)
80
return (false);
81
82
/*
83
* From ACPI 6.3 spec 9.1.1:
84
* Bit 0 indicates whether there is support for any functions other
85
* than function 0 for the specified UUID and Revision ID. If set to
86
* zero, no functions are supported (other than function zero) for the
87
* specified UUID and Revision ID.
88
*/
89
funcs |= 1 << 0;
90
91
ret = acpi_DSMQuery(handle, (const uint8_t *)uuid, rev);
92
return ((ret & funcs) == funcs);
93
}
94
95
ACPI_OBJECT *
96
acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
97
int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
98
{
99
ACPI_BUFFER buf;
100
ACPI_STATUS status;
101
102
status = acpi_EvaluateDSMTyped(handle, (const uint8_t *)uuid, rev, func,
103
argv4, &buf, type);
104
return (ACPI_SUCCESS(status) ? (ACPI_OBJECT *)buf.Pointer : NULL);
105
}
106
107
union linuxkpi_acpi_object *
108
acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
109
UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
110
{
111
ACPI_BUFFER buf;
112
ACPI_STATUS status;
113
114
status = acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, rev, func,
115
(ACPI_OBJECT *)pkg, &buf);
116
return (ACPI_SUCCESS(status) ?
117
(union linuxkpi_acpi_object *)buf.Pointer : NULL);
118
}
119
120
static void
121
linux_handle_power_suspend_event(void *arg __unused)
122
{
123
/*
124
* Only support S3 for now.
125
* acpi_sleep_event isn't always called so we use power_suspend_early
126
* instead which means we don't know what state we're switching to.
127
* TODO: Make acpi_sleep_event consistent
128
*/
129
linux_acpi_target_sleep_state = ACPI_STATE_S3;
130
pm_suspend_target_state = PM_SUSPEND_MEM;
131
}
132
133
static void
134
linux_handle_power_resume_event(void *arg __unused)
135
{
136
linux_acpi_target_sleep_state = ACPI_STATE_S0;
137
pm_suspend_target_state = PM_SUSPEND_ON;
138
}
139
140
static void
141
linux_handle_acpi_acad_event(void *arg, int data)
142
{
143
struct notifier_block *nb = arg;
144
/*
145
* Event type information is lost ATM in FreeBSD ACPI event handler.
146
* Fortunately, drm-kmod do not distinct AC event types too, so we can
147
* use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler.
148
*/
149
struct acpi_bus_event abe = {
150
.device_class = ACPI_AC_CLASS,
151
.type = ACPI_NOTIFY_BUS_CHECK,
152
.data = data,
153
};
154
155
nb->notifier_call(nb, 0, &abe);
156
}
157
158
static void
159
linux_handle_acpi_video_event(void *arg, int type)
160
{
161
struct notifier_block *nb = arg;
162
struct acpi_bus_event abe = {
163
.device_class = ACPI_VIDEO_CLASS,
164
.type = type,
165
.data = 0,
166
};
167
168
nb->notifier_call(nb, 0, &abe);
169
}
170
171
int
172
register_acpi_notifier(struct notifier_block *nb)
173
{
174
nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event,
175
linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST);
176
nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event,
177
linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST);
178
179
return (0);
180
}
181
182
int
183
unregister_acpi_notifier(struct notifier_block *nb)
184
{
185
EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]);
186
EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]);
187
188
return (0);
189
}
190
191
uint32_t
192
acpi_target_system_state(void)
193
{
194
return (linux_acpi_target_sleep_state);
195
}
196
197
struct acpi_dev_present_ctx {
198
const char *hid;
199
const char *uid;
200
int64_t hrv;
201
struct acpi_device *dev;
202
};
203
204
static ACPI_STATUS
205
acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context,
206
void **result)
207
{
208
ACPI_DEVICE_INFO *devinfo;
209
struct acpi_device *dev;
210
struct acpi_dev_present_ctx *match = context;
211
bool present = false;
212
UINT32 sta, hrv;
213
int i;
214
215
if (handle == NULL)
216
return (AE_OK);
217
218
if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
219
!ACPI_DEVICE_PRESENT(sta))
220
return (AE_OK);
221
222
if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &devinfo)))
223
return (AE_OK);
224
225
if ((devinfo->Valid & ACPI_VALID_HID) != 0 &&
226
strcmp(match->hid, devinfo->HardwareId.String) == 0) {
227
present = true;
228
} else if ((devinfo->Valid & ACPI_VALID_CID) != 0) {
229
for (i = 0; i < devinfo->CompatibleIdList.Count; i++) {
230
if (strcmp(match->hid,
231
devinfo->CompatibleIdList.Ids[i].String) == 0) {
232
present = true;
233
break;
234
}
235
}
236
}
237
if (present && match->uid != NULL &&
238
((devinfo->Valid & ACPI_VALID_UID) == 0 ||
239
strcmp(match->uid, devinfo->UniqueId.String) != 0))
240
present = false;
241
242
AcpiOsFree(devinfo);
243
if (!present)
244
return (AE_OK);
245
246
if (match->hrv != -1) {
247
if (ACPI_FAILURE(acpi_GetInteger(handle, "_HRV", &hrv)))
248
return (AE_OK);
249
if (hrv != match->hrv)
250
return (AE_OK);
251
}
252
253
dev = acpi_get_device(handle);
254
if (dev == NULL)
255
return (AE_OK);
256
match->dev = dev;
257
258
return (AE_ERROR);
259
}
260
261
bool
262
lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)
263
{
264
struct acpi_dev_present_ctx match;
265
int rv;
266
267
match.hid = hid;
268
match.uid = uid;
269
match.hrv = hrv;
270
271
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
272
ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);
273
274
return (rv == AE_ERROR);
275
}
276
277
struct acpi_device *
278
lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,
279
int64_t hrv)
280
{
281
struct acpi_dev_present_ctx match;
282
int rv;
283
284
match.hid = hid;
285
match.uid = uid;
286
match.hrv = hrv;
287
match.dev = NULL;
288
289
rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
290
ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);
291
292
return (rv == AE_ERROR ? match.dev : NULL);
293
}
294
295
static void
296
linux_register_acpi_event_handlers(void *arg __unused)
297
{
298
/*
299
* XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use
300
* power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not
301
* generate acpi_sleep_event... Lid open or wake on button generates
302
* acpi_wakeup_event on one of my Dell laptops but not the other
303
* (but it does power on)... is this a general thing?
304
*/
305
resume_tag = EVENTHANDLER_REGISTER(power_resume,
306
linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST);
307
suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early,
308
linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST);
309
}
310
311
static void
312
linux_deregister_acpi_event_handlers(void *arg __unused)
313
{
314
EVENTHANDLER_DEREGISTER(power_resume, resume_tag);
315
EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag);
316
}
317
318
SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,
319
linux_register_acpi_event_handlers, NULL);
320
SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,
321
linux_deregister_acpi_event_handlers, NULL);
322
323
#else /* !DEV_ACPI */
324
325
ACPI_HANDLE
326
bsd_acpi_get_handle(device_t bsddev)
327
{
328
return (NULL);
329
}
330
331
bool
332
acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
333
{
334
return (false);
335
}
336
337
ACPI_OBJECT *
338
acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
339
int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
340
{
341
return (NULL);
342
}
343
344
union linuxkpi_acpi_object *
345
acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
346
UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
347
{
348
return (NULL);
349
}
350
351
int
352
register_acpi_notifier(struct notifier_block *nb)
353
{
354
return (0);
355
}
356
357
int
358
unregister_acpi_notifier(struct notifier_block *nb)
359
{
360
return (0);
361
}
362
363
uint32_t
364
acpi_target_system_state(void)
365
{
366
return (ACPI_STATE_S0);
367
}
368
369
bool
370
lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)
371
{
372
return (false);
373
}
374
375
struct acpi_device *
376
lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,
377
int64_t hrv)
378
{
379
return (NULL);
380
}
381
382
#endif /* !DEV_ACPI */
383
384