Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/acpi_support/acpi_ibm.c
39492 views
1
/*-
2
* Copyright (c) 2004 Takanori Watanabe
3
* Copyright (c) 2005 Markus Brueffer <[email protected]>
4
* All rights reserved.
5
* Copyright (c) 2020 Ali Abdallah <[email protected]>
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are 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 the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/cdefs.h>
30
/*
31
* Driver for extra ACPI-controlled gadgets found on ThinkPad laptops.
32
* Inspired by the ibm-acpi and tpb projects which implement these features
33
* on Linux.
34
*
35
* acpi-ibm: <http://ibm-acpi.sourceforge.net/>
36
* tpb: <http://www.nongnu.org/tpb/>
37
*/
38
39
#include "opt_acpi.h"
40
#include "opt_evdev.h"
41
#include <sys/param.h>
42
#include <sys/systm.h>
43
#include <sys/kernel.h>
44
#include <sys/bus.h>
45
#include <machine/cpufunc.h>
46
47
#include <contrib/dev/acpica/include/acpi.h>
48
#include <contrib/dev/acpica/include/accommon.h>
49
50
#include "acpi_if.h"
51
#include <sys/module.h>
52
#include <dev/acpica/acpivar.h>
53
#include <dev/led/led.h>
54
#include <sys/power.h>
55
#include <sys/sbuf.h>
56
#include <sys/sysctl.h>
57
#include <isa/rtc.h>
58
59
#ifdef EVDEV_SUPPORT
60
#include <dev/evdev/input.h>
61
#include <dev/evdev/evdev.h>
62
#endif
63
64
#define _COMPONENT ACPI_OEM
65
ACPI_MODULE_NAME("IBM")
66
67
/* Internal methods */
68
#define ACPI_IBM_METHOD_EVENTS 1
69
#define ACPI_IBM_METHOD_EVENTMASK 2
70
#define ACPI_IBM_METHOD_HOTKEY 3
71
#define ACPI_IBM_METHOD_BRIGHTNESS 4
72
#define ACPI_IBM_METHOD_VOLUME 5
73
#define ACPI_IBM_METHOD_MUTE 6
74
#define ACPI_IBM_METHOD_THINKLIGHT 7
75
#define ACPI_IBM_METHOD_BLUETOOTH 8
76
#define ACPI_IBM_METHOD_WLAN 9
77
#define ACPI_IBM_METHOD_FANSPEED 10
78
#define ACPI_IBM_METHOD_FANLEVEL 11
79
#define ACPI_IBM_METHOD_FANSTATUS 12
80
#define ACPI_IBM_METHOD_THERMAL 13
81
#define ACPI_IBM_METHOD_HANDLEREVENTS 14
82
#define ACPI_IBM_METHOD_MIC_LED 15
83
#define ACPI_IBM_METHOD_PRIVACYGUARD 16
84
85
/* Hotkeys/Buttons */
86
#define IBM_RTC_HOTKEY1 0x64
87
#define IBM_RTC_MASK_HOME (1 << 0)
88
#define IBM_RTC_MASK_SEARCH (1 << 1)
89
#define IBM_RTC_MASK_MAIL (1 << 2)
90
#define IBM_RTC_MASK_WLAN (1 << 5)
91
#define IBM_RTC_HOTKEY2 0x65
92
#define IBM_RTC_MASK_THINKPAD (1 << 3)
93
#define IBM_RTC_MASK_ZOOM (1 << 5)
94
#define IBM_RTC_MASK_VIDEO (1 << 6)
95
#define IBM_RTC_MASK_HIBERNATE (1 << 7)
96
#define IBM_RTC_THINKLIGHT 0x66
97
#define IBM_RTC_MASK_THINKLIGHT (1 << 4)
98
#define IBM_RTC_SCREENEXPAND 0x67
99
#define IBM_RTC_MASK_SCREENEXPAND (1 << 5)
100
#define IBM_RTC_BRIGHTNESS 0x6c
101
#define IBM_RTC_MASK_BRIGHTNESS (1 << 5)
102
#define IBM_RTC_VOLUME 0x6e
103
#define IBM_RTC_MASK_VOLUME (1 << 7)
104
105
/* Embedded Controller registers */
106
#define IBM_EC_BRIGHTNESS 0x31
107
#define IBM_EC_MASK_BRI 0x7
108
#define IBM_EC_VOLUME 0x30
109
#define IBM_EC_MASK_VOL 0xf
110
#define IBM_EC_MASK_MUTE (1 << 6)
111
#define IBM_EC_FANSTATUS 0x2F
112
#define IBM_EC_MASK_FANLEVEL 0x3f
113
#define IBM_EC_MASK_FANUNTHROTTLED (1 << 6)
114
#define IBM_EC_MASK_FANSTATUS (1 << 7)
115
#define IBM_EC_FANSPEED 0x84
116
117
/* CMOS Commands */
118
#define IBM_CMOS_VOLUME_DOWN 0
119
#define IBM_CMOS_VOLUME_UP 1
120
#define IBM_CMOS_VOLUME_MUTE 2
121
#define IBM_CMOS_BRIGHTNESS_UP 4
122
#define IBM_CMOS_BRIGHTNESS_DOWN 5
123
124
/* ACPI methods */
125
#define IBM_NAME_KEYLIGHT "KBLT"
126
#define IBM_NAME_WLAN_BT_GET "GBDC"
127
#define IBM_NAME_WLAN_BT_SET "SBDC"
128
#define IBM_NAME_MASK_BT (1 << 1)
129
#define IBM_NAME_MASK_WLAN (1 << 2)
130
#define IBM_NAME_THERMAL_GET "TMP7"
131
#define IBM_NAME_THERMAL_UPDT "UPDT"
132
#define IBM_NAME_PRIVACYGUARD_GET "GSSS"
133
#define IBM_NAME_PRIVACYGUARD_SET "SSSS"
134
135
#define IBM_NAME_EVENTS_STATUS_GET "DHKC"
136
#define IBM_NAME_EVENTS_MASK_GET "DHKN"
137
#define IBM_NAME_EVENTS_STATUS_SET "MHKC"
138
#define IBM_NAME_EVENTS_MASK_SET "MHKM"
139
#define IBM_NAME_EVENTS_GET "MHKP"
140
#define IBM_NAME_EVENTS_AVAILMASK "MHKA"
141
142
/* Event Code */
143
#define IBM_EVENT_LCD_BACKLIGHT 0x03
144
#define IBM_EVENT_SUSPEND_TO_RAM 0x04
145
#define IBM_EVENT_BLUETOOTH 0x05
146
#define IBM_EVENT_SCREEN_EXPAND 0x07
147
#define IBM_EVENT_SUSPEND_TO_DISK 0x0c
148
#define IBM_EVENT_BRIGHTNESS_UP 0x10
149
#define IBM_EVENT_BRIGHTNESS_DOWN 0x11
150
#define IBM_EVENT_THINKLIGHT 0x12
151
#define IBM_EVENT_ZOOM 0x14
152
#define IBM_EVENT_VOLUME_UP 0x15
153
#define IBM_EVENT_VOLUME_DOWN 0x16
154
#define IBM_EVENT_MUTE 0x17
155
#define IBM_EVENT_ACCESS_IBM_BUTTON 0x18
156
157
/* Device-specific register flags */
158
#define IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT 0x10000
159
#define IBM_FLAG_PRIVACYGUARD_ON 0x1
160
161
#define ABS(x) (((x) < 0)? -(x) : (x))
162
163
struct acpi_ibm_softc {
164
device_t dev;
165
ACPI_HANDLE handle;
166
167
/* Embedded controller */
168
device_t ec_dev;
169
ACPI_HANDLE ec_handle;
170
171
/* CMOS */
172
ACPI_HANDLE cmos_handle;
173
174
/* Fan status */
175
ACPI_HANDLE fan_handle;
176
int fan_levels;
177
178
/* Keylight commands and states */
179
ACPI_HANDLE light_handle;
180
int light_cmd_on;
181
int light_cmd_off;
182
int light_val;
183
int light_get_supported;
184
int light_set_supported;
185
186
/* led(4) interface */
187
struct cdev *led_dev;
188
int led_busy;
189
int led_state;
190
191
/* Mic led handle */
192
ACPI_HANDLE mic_led_handle;
193
int mic_led_state;
194
195
int wlan_bt_flags;
196
int thermal_updt_supported;
197
198
unsigned int events_availmask;
199
unsigned int events_initialmask;
200
int events_mask_supported;
201
int events_enable;
202
203
unsigned int handler_events;
204
205
struct sysctl_ctx_list *sysctl_ctx;
206
struct sysctl_oid *sysctl_tree;
207
#ifdef EVDEV_SUPPORT
208
struct evdev_dev *evdev;
209
#endif
210
};
211
212
static struct {
213
char *name;
214
int method;
215
char *description;
216
int flag_rdonly;
217
} acpi_ibm_sysctls[] = {
218
{
219
.name = "events",
220
.method = ACPI_IBM_METHOD_EVENTS,
221
.description = "ACPI events enable",
222
},
223
{
224
.name = "eventmask",
225
.method = ACPI_IBM_METHOD_EVENTMASK,
226
.description = "ACPI eventmask",
227
},
228
{
229
.name = "hotkey",
230
.method = ACPI_IBM_METHOD_HOTKEY,
231
.description = "Key Status",
232
.flag_rdonly = 1
233
},
234
{
235
.name = "lcd_brightness",
236
.method = ACPI_IBM_METHOD_BRIGHTNESS,
237
.description = "LCD Brightness",
238
},
239
{
240
.name = "volume",
241
.method = ACPI_IBM_METHOD_VOLUME,
242
.description = "Volume",
243
},
244
{
245
.name = "mute",
246
.method = ACPI_IBM_METHOD_MUTE,
247
.description = "Mute",
248
},
249
{
250
.name = "thinklight",
251
.method = ACPI_IBM_METHOD_THINKLIGHT,
252
.description = "Thinklight enable",
253
},
254
{
255
.name = "bluetooth",
256
.method = ACPI_IBM_METHOD_BLUETOOTH,
257
.description = "Bluetooth enable",
258
},
259
{
260
.name = "wlan",
261
.method = ACPI_IBM_METHOD_WLAN,
262
.description = "WLAN enable",
263
.flag_rdonly = 1
264
},
265
{
266
.name = "fan_speed",
267
.method = ACPI_IBM_METHOD_FANSPEED,
268
.description = "Fan speed",
269
.flag_rdonly = 1
270
},
271
{
272
.name = "fan_level",
273
.method = ACPI_IBM_METHOD_FANLEVEL,
274
.description = "Fan level, 0-7 (recommended max), "
275
"8 (unthrottled, full-speed)",
276
},
277
{
278
.name = "fan",
279
.method = ACPI_IBM_METHOD_FANSTATUS,
280
.description = "Fan enable",
281
},
282
{
283
.name = "mic_led",
284
.method = ACPI_IBM_METHOD_MIC_LED,
285
.description = "Mic led",
286
},
287
{
288
.name = "privacyguard",
289
.method = ACPI_IBM_METHOD_PRIVACYGUARD,
290
.description = "PrivacyGuard enable",
291
},
292
{ NULL, 0, NULL, 0 }
293
};
294
295
/*
296
* Per-model default list of event mask.
297
*/
298
#define ACPI_IBM_HKEY_RFKILL_MASK (1 << 4)
299
#define ACPI_IBM_HKEY_DSWITCH_MASK (1 << 6)
300
#define ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK (1 << 15)
301
#define ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK (1 << 16)
302
#define ACPI_IBM_HKEY_SEARCH_MASK (1 << 18)
303
#define ACPI_IBM_HKEY_MICMUTE_MASK (1 << 26)
304
#define ACPI_IBM_HKEY_SETTINGS_MASK (1 << 28)
305
#define ACPI_IBM_HKEY_VIEWOPEN_MASK (1 << 30)
306
#define ACPI_IBM_HKEY_VIEWALL_MASK (1 << 31)
307
308
struct acpi_ibm_models {
309
const char *maker;
310
const char *product;
311
uint32_t eventmask;
312
} acpi_ibm_models[] = {
313
{ "LENOVO", "20BSCTO1WW",
314
ACPI_IBM_HKEY_RFKILL_MASK |
315
ACPI_IBM_HKEY_DSWITCH_MASK |
316
ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK |
317
ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK |
318
ACPI_IBM_HKEY_SEARCH_MASK |
319
ACPI_IBM_HKEY_MICMUTE_MASK |
320
ACPI_IBM_HKEY_SETTINGS_MASK |
321
ACPI_IBM_HKEY_VIEWOPEN_MASK |
322
ACPI_IBM_HKEY_VIEWALL_MASK
323
}
324
};
325
326
ACPI_SERIAL_DECL(ibm, "ThinkPad ACPI Extras");
327
328
static int acpi_ibm_probe(device_t dev);
329
static int acpi_ibm_attach(device_t dev);
330
static int acpi_ibm_detach(device_t dev);
331
static int acpi_ibm_resume(device_t dev);
332
333
static void ibm_led(void *softc, int onoff);
334
static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused);
335
336
static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS);
337
static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method);
338
static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method);
339
static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val);
340
341
static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val);
342
static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS);
343
static int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS);
344
static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context);
345
346
static int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg);
347
static int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg);
348
static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg);
349
static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg);
350
static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg);
351
static int acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc);
352
static ACPI_STATUS acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg);
353
static ACPI_STATUS acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg);
354
355
static int acpi_status_to_errno(ACPI_STATUS status);
356
357
static device_method_t acpi_ibm_methods[] = {
358
/* Device interface */
359
DEVMETHOD(device_probe, acpi_ibm_probe),
360
DEVMETHOD(device_attach, acpi_ibm_attach),
361
DEVMETHOD(device_detach, acpi_ibm_detach),
362
DEVMETHOD(device_resume, acpi_ibm_resume),
363
364
DEVMETHOD_END
365
};
366
367
static driver_t acpi_ibm_driver = {
368
"acpi_ibm",
369
acpi_ibm_methods,
370
sizeof(struct acpi_ibm_softc),
371
};
372
373
DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, 0, 0);
374
MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
375
#ifdef EVDEV_SUPPORT
376
MODULE_DEPEND(acpi_ibm, evdev, 1, 1, 1);
377
#endif
378
static char *ibm_ids[] = {"IBM0068", "LEN0068", "LEN0268", NULL};
379
380
static int
381
acpi_status_to_errno(ACPI_STATUS status)
382
{
383
switch (status) {
384
case AE_OK:
385
return (0);
386
case AE_BAD_PARAMETER:
387
return (EINVAL);
388
default:
389
return (ENODEV);
390
}
391
}
392
393
static void
394
ibm_led(void *softc, int onoff)
395
{
396
struct acpi_ibm_softc *sc = softc;
397
398
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
399
400
if (sc->led_busy)
401
return;
402
403
sc->led_busy = 1;
404
sc->led_state = onoff;
405
406
AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc);
407
}
408
409
static void
410
ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused)
411
{
412
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
413
414
ACPI_SERIAL_BEGIN(ibm);
415
acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state);
416
ACPI_SERIAL_END(ibm);
417
418
sc->led_busy = 0;
419
}
420
421
static int
422
acpi_ibm_mic_led_set(struct acpi_ibm_softc *sc, int arg)
423
{
424
ACPI_OBJECT_LIST input;
425
ACPI_OBJECT params[1];
426
ACPI_STATUS status;
427
428
if (arg < 0 || arg > 1)
429
return (EINVAL);
430
431
if (sc->mic_led_handle) {
432
params[0].Type = ACPI_TYPE_INTEGER;
433
params[0].Integer.Value = 0;
434
/* mic led: 0 off, 2 on */
435
if (arg == 1)
436
params[0].Integer.Value = 2;
437
438
input.Pointer = params;
439
input.Count = 1;
440
441
status = AcpiEvaluateObject(sc->handle, "MMTS", &input, NULL);
442
if (ACPI_SUCCESS(status))
443
sc->mic_led_state = arg;
444
return (status);
445
}
446
447
return (0);
448
}
449
450
static int
451
acpi_ibm_probe(device_t dev)
452
{
453
int rv;
454
455
if (acpi_disabled("ibm") || device_get_unit(dev) != 0)
456
return (ENXIO);
457
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids, NULL);
458
459
if (rv <= 0)
460
device_set_desc(dev, "ThinkPad ACPI Extras");
461
462
return (rv);
463
}
464
465
static int
466
acpi_ibm_attach(device_t dev)
467
{
468
int i;
469
int hkey;
470
struct acpi_ibm_softc *sc;
471
char *maker, *product;
472
ACPI_OBJECT_LIST input;
473
ACPI_OBJECT params[1];
474
ACPI_OBJECT out_obj;
475
ACPI_BUFFER result;
476
devclass_t ec_devclass;
477
478
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
479
480
sc = device_get_softc(dev);
481
sc->dev = dev;
482
sc->handle = acpi_get_handle(dev);
483
484
/* Look for the first embedded controller */
485
if (!(ec_devclass = devclass_find ("acpi_ec"))) {
486
if (bootverbose)
487
device_printf(dev, "Couldn't find acpi_ec devclass\n");
488
return (EINVAL);
489
}
490
if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
491
if (bootverbose)
492
device_printf(dev, "Couldn't find acpi_ec device\n");
493
return (EINVAL);
494
}
495
sc->ec_handle = acpi_get_handle(sc->ec_dev);
496
497
#ifdef EVDEV_SUPPORT
498
sc->evdev = evdev_alloc();
499
evdev_set_name(sc->evdev, device_get_desc(dev));
500
evdev_set_phys(sc->evdev, device_get_nameunit(dev));
501
evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1);
502
evdev_support_event(sc->evdev, EV_SYN);
503
evdev_support_event(sc->evdev, EV_KEY);
504
evdev_support_key(sc->evdev, KEY_BRIGHTNESSUP);
505
evdev_support_key(sc->evdev, KEY_BRIGHTNESSDOWN);
506
507
if (evdev_register(sc->evdev) != 0)
508
return (ENXIO);
509
#endif
510
511
/* Get the sysctl tree */
512
sc->sysctl_ctx = device_get_sysctl_ctx(dev);
513
sc->sysctl_tree = device_get_sysctl_tree(dev);
514
515
/* Look for event mask and hook up the nodes */
516
sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
517
IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
518
519
if (sc->events_mask_supported) {
520
SYSCTL_ADD_UINT(sc->sysctl_ctx,
521
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "initialmask",
522
CTLFLAG_RD, &sc->events_initialmask, 0,
523
"Initial eventmask");
524
525
if (ACPI_SUCCESS (acpi_GetInteger(sc->handle, "MHKV", &hkey))) {
526
device_printf(dev, "Firmware version is 0x%X\n", hkey);
527
switch (hkey >> 8) {
528
case 1:
529
/* The availmask is the bitmask of supported events */
530
if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
531
IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
532
sc->events_availmask = 0xffffffff;
533
break;
534
535
case 2:
536
result.Length = sizeof(out_obj);
537
result.Pointer = &out_obj;
538
params[0].Type = ACPI_TYPE_INTEGER;
539
params[0].Integer.Value = 1;
540
input.Pointer = params;
541
input.Count = 1;
542
543
sc->events_availmask = 0xffffffff;
544
545
if (ACPI_SUCCESS(AcpiEvaluateObject (sc->handle,
546
IBM_NAME_EVENTS_AVAILMASK, &input, &result)))
547
sc->events_availmask = out_obj.Integer.Value;
548
break;
549
default:
550
device_printf(dev, "Unknown firmware version 0x%x\n", hkey);
551
break;
552
}
553
} else
554
sc->events_availmask = 0xffffffff;
555
556
SYSCTL_ADD_UINT(sc->sysctl_ctx,
557
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
558
"availmask", CTLFLAG_RD,
559
&sc->events_availmask, 0, "Mask of supported events");
560
}
561
562
/* Hook up proc nodes */
563
for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
564
if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method))
565
continue;
566
567
if (acpi_ibm_sysctls[i].flag_rdonly != 0) {
568
SYSCTL_ADD_PROC(sc->sysctl_ctx,
569
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
570
acpi_ibm_sysctls[i].name,
571
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
572
sc, i, acpi_ibm_sysctl, "I",
573
acpi_ibm_sysctls[i].description);
574
} else {
575
SYSCTL_ADD_PROC(sc->sysctl_ctx,
576
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
577
acpi_ibm_sysctls[i].name,
578
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
579
sc, i, acpi_ibm_sysctl, "I",
580
acpi_ibm_sysctls[i].description);
581
}
582
}
583
584
/* Hook up thermal node */
585
if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) {
586
SYSCTL_ADD_PROC(sc->sysctl_ctx,
587
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "thermal",
588
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
589
acpi_ibm_thermal_sysctl, "I", "Thermal zones");
590
}
591
592
/* Hook up handlerevents node */
593
if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) {
594
SYSCTL_ADD_PROC(sc->sysctl_ctx,
595
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "handlerevents",
596
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
597
acpi_ibm_handlerevents_sysctl, "I",
598
"devd(8) events handled by acpi_ibm");
599
}
600
601
/* Handle notifies */
602
AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
603
acpi_ibm_notify, dev);
604
605
/* Hook up light to led(4) */
606
if (sc->light_set_supported)
607
sc->led_dev = led_create_state(ibm_led, sc, "thinklight",
608
(sc->light_val ? 1 : 0));
609
610
/* Enable per-model events. */
611
maker = kern_getenv("smbios.system.maker");
612
product = kern_getenv("smbios.system.product");
613
if (maker == NULL || product == NULL)
614
goto nosmbios;
615
616
for (i = 0; i < nitems(acpi_ibm_models); i++) {
617
if (strcmp(maker, acpi_ibm_models[i].maker) == 0 &&
618
strcmp(product, acpi_ibm_models[i].product) == 0) {
619
ACPI_SERIAL_BEGIN(ibm);
620
acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK,
621
acpi_ibm_models[i].eventmask);
622
ACPI_SERIAL_END(ibm);
623
}
624
}
625
626
nosmbios:
627
freeenv(maker);
628
freeenv(product);
629
630
/* Enable events by default. */
631
ACPI_SERIAL_BEGIN(ibm);
632
acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 1);
633
ACPI_SERIAL_END(ibm);
634
635
return (0);
636
}
637
638
static int
639
acpi_ibm_detach(device_t dev)
640
{
641
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
642
643
struct acpi_ibm_softc *sc = device_get_softc(dev);
644
645
/* Disable events and restore eventmask */
646
ACPI_SERIAL_BEGIN(ibm);
647
acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0);
648
acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask);
649
ACPI_SERIAL_END(ibm);
650
651
AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify);
652
653
if (sc->led_dev != NULL)
654
led_destroy(sc->led_dev);
655
656
#ifdef EVDEV_SUPPORT
657
evdev_free(sc->evdev);
658
#endif
659
660
return (0);
661
}
662
663
static int
664
acpi_ibm_resume(device_t dev)
665
{
666
struct acpi_ibm_softc *sc = device_get_softc(dev);
667
668
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
669
670
ACPI_SERIAL_BEGIN(ibm);
671
for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
672
int val;
673
674
val = acpi_ibm_sysctl_get(sc, i);
675
676
if (acpi_ibm_sysctls[i].flag_rdonly != 0)
677
continue;
678
679
acpi_ibm_sysctl_set(sc, i, val);
680
}
681
ACPI_SERIAL_END(ibm);
682
683
/* The mic led does not turn back on when sysctl_set is called in the above loop */
684
acpi_ibm_mic_led_set(sc, sc->mic_led_state);
685
686
return (0);
687
}
688
689
static int
690
acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val)
691
{
692
ACPI_OBJECT arg[2];
693
ACPI_OBJECT_LIST args;
694
ACPI_STATUS status;
695
696
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
697
ACPI_SERIAL_ASSERT(ibm);
698
699
args.Count = 2;
700
args.Pointer = arg;
701
arg[0].Type = ACPI_TYPE_INTEGER;
702
arg[1].Type = ACPI_TYPE_INTEGER;
703
704
for (int i = 0; i < 32; ++i) {
705
arg[0].Integer.Value = i + 1;
706
arg[1].Integer.Value = (((1 << i) & val) != 0);
707
status = AcpiEvaluateObject(sc->handle,
708
IBM_NAME_EVENTS_MASK_SET, &args, NULL);
709
710
if (ACPI_FAILURE(status))
711
return (status);
712
}
713
714
return (0);
715
}
716
717
static int
718
acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)
719
{
720
struct acpi_ibm_softc *sc;
721
int arg;
722
int error = 0;
723
int function;
724
int method;
725
726
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
727
728
sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
729
function = oidp->oid_arg2;
730
method = acpi_ibm_sysctls[function].method;
731
732
ACPI_SERIAL_BEGIN(ibm);
733
arg = acpi_ibm_sysctl_get(sc, method);
734
error = sysctl_handle_int(oidp, &arg, 0, req);
735
736
/* Sanity check */
737
if (error != 0 || req->newptr == NULL)
738
goto out;
739
740
/* Update */
741
error = acpi_ibm_sysctl_set(sc, method, arg);
742
743
out:
744
ACPI_SERIAL_END(ibm);
745
return (error);
746
}
747
748
static int
749
acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method)
750
{
751
UINT64 val_ec;
752
int val = 0, key;
753
754
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
755
ACPI_SERIAL_ASSERT(ibm);
756
757
switch (method) {
758
case ACPI_IBM_METHOD_EVENTS:
759
acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val);
760
break;
761
762
case ACPI_IBM_METHOD_EVENTMASK:
763
if (sc->events_mask_supported)
764
acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val);
765
break;
766
767
case ACPI_IBM_METHOD_HOTKEY:
768
/*
769
* Construct the hotkey as a bitmask as illustrated below.
770
* Note that whenever a key was pressed, the respecting bit
771
* toggles and nothing else changes.
772
* +--+--+-+-+-+-+-+-+-+-+-+-+
773
* |11|10|9|8|7|6|5|4|3|2|1|0|
774
* +--+--+-+-+-+-+-+-+-+-+-+-+
775
* | | | | | | | | | | | |
776
* | | | | | | | | | | | +- Home Button
777
* | | | | | | | | | | +--- Search Button
778
* | | | | | | | | | +----- Mail Button
779
* | | | | | | | | +------- Thinkpad Button
780
* | | | | | | | +--------- Zoom (Fn + Space)
781
* | | | | | | +----------- WLAN Button
782
* | | | | | +------------- Video Button
783
* | | | | +--------------- Hibernate Button
784
* | | | +----------------- Thinklight Button
785
* | | +------------------- Screen expand (Fn + F8)
786
* | +--------------------- Brightness
787
* +------------------------ Volume/Mute
788
*/
789
key = rtcin(IBM_RTC_HOTKEY1);
790
val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key;
791
key = rtcin(IBM_RTC_HOTKEY2);
792
val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key;
793
val |= (IBM_RTC_MASK_ZOOM & key) >> 1;
794
key = rtcin(IBM_RTC_THINKLIGHT);
795
val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
796
key = rtcin(IBM_RTC_SCREENEXPAND);
797
val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
798
key = rtcin(IBM_RTC_BRIGHTNESS);
799
val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5;
800
key = rtcin(IBM_RTC_VOLUME);
801
val |= (IBM_RTC_MASK_VOLUME & key) << 4;
802
break;
803
804
case ACPI_IBM_METHOD_BRIGHTNESS:
805
ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
806
val = val_ec & IBM_EC_MASK_BRI;
807
break;
808
809
case ACPI_IBM_METHOD_VOLUME:
810
ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
811
val = val_ec & IBM_EC_MASK_VOL;
812
break;
813
814
case ACPI_IBM_METHOD_MUTE:
815
ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
816
val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
817
break;
818
819
case ACPI_IBM_METHOD_THINKLIGHT:
820
if (sc->light_get_supported)
821
acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val);
822
else
823
val = sc->light_val;
824
break;
825
826
case ACPI_IBM_METHOD_BLUETOOTH:
827
acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
828
sc->wlan_bt_flags = val;
829
val = ((val & IBM_NAME_MASK_BT) != 0);
830
break;
831
832
case ACPI_IBM_METHOD_WLAN:
833
acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
834
sc->wlan_bt_flags = val;
835
val = ((val & IBM_NAME_MASK_WLAN) != 0);
836
break;
837
838
case ACPI_IBM_METHOD_FANSPEED:
839
if (sc->fan_handle) {
840
if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val)))
841
val = -1;
842
} else {
843
ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2);
844
val = val_ec;
845
}
846
break;
847
848
case ACPI_IBM_METHOD_FANLEVEL:
849
/*
850
* The IBM_EC_FANSTATUS register works as follows:
851
* Bit 0-5 indicate the level at which the fan operates. Only
852
* values between 0 and 7 have an effect. Everything
853
* above 7 is treated the same as level 7
854
* Bit 6 overrides the fan speed limit if set to 1
855
* Bit 7 indicates at which mode the fan operates:
856
* manual (0) or automatic (1)
857
*/
858
if (!sc->fan_handle) {
859
ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
860
if (val_ec & IBM_EC_MASK_FANUNTHROTTLED)
861
val = 8;
862
else
863
val = val_ec & IBM_EC_MASK_FANLEVEL;
864
}
865
break;
866
867
case ACPI_IBM_METHOD_FANSTATUS:
868
if (!sc->fan_handle) {
869
ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
870
val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS;
871
} else
872
val = -1;
873
break;
874
875
case ACPI_IBM_METHOD_MIC_LED:
876
if (sc->mic_led_handle)
877
return sc->mic_led_state;
878
else
879
val = -1;
880
break;
881
882
case ACPI_IBM_METHOD_PRIVACYGUARD:
883
val = acpi_ibm_privacyguard_get(sc);
884
break;
885
}
886
887
return (val);
888
}
889
890
static int
891
acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
892
{
893
int val;
894
UINT64 val_ec;
895
ACPI_STATUS status;
896
897
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
898
ACPI_SERIAL_ASSERT(ibm);
899
900
switch (method) {
901
case ACPI_IBM_METHOD_EVENTS:
902
if (arg < 0 || arg > 1)
903
return (EINVAL);
904
905
status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg);
906
if (ACPI_FAILURE(status))
907
return (status);
908
if (sc->events_mask_supported)
909
return acpi_ibm_eventmask_set(sc, sc->events_availmask);
910
break;
911
912
case ACPI_IBM_METHOD_EVENTMASK:
913
if (sc->events_mask_supported)
914
return acpi_ibm_eventmask_set(sc, arg);
915
break;
916
917
case ACPI_IBM_METHOD_BRIGHTNESS:
918
return acpi_ibm_brightness_set(sc, arg);
919
break;
920
921
case ACPI_IBM_METHOD_VOLUME:
922
return acpi_ibm_volume_set(sc, arg);
923
break;
924
925
case ACPI_IBM_METHOD_MUTE:
926
return acpi_ibm_mute_set(sc, arg);
927
break;
928
929
case ACPI_IBM_METHOD_MIC_LED:
930
return acpi_ibm_mic_led_set(sc, arg);
931
break;
932
933
case ACPI_IBM_METHOD_THINKLIGHT:
934
return acpi_ibm_thinklight_set(sc, arg);
935
break;
936
937
case ACPI_IBM_METHOD_BLUETOOTH:
938
return acpi_ibm_bluetooth_set(sc, arg);
939
break;
940
941
case ACPI_IBM_METHOD_PRIVACYGUARD:
942
return (acpi_status_to_errno(acpi_ibm_privacyguard_set(sc, arg)));
943
break;
944
945
case ACPI_IBM_METHOD_FANLEVEL:
946
if (arg < 0 || arg > 8)
947
return (EINVAL);
948
949
if (!sc->fan_handle) {
950
/* Read the current fan status. */
951
ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
952
val = val_ec & ~(IBM_EC_MASK_FANLEVEL |
953
IBM_EC_MASK_FANUNTHROTTLED);
954
955
if (arg == 8)
956
/* Full speed, set the unthrottled bit. */
957
val |= 7 | IBM_EC_MASK_FANUNTHROTTLED;
958
else
959
val |= arg;
960
961
return (ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val,
962
1));
963
}
964
break;
965
966
case ACPI_IBM_METHOD_FANSTATUS:
967
if (arg < 0 || arg > 1)
968
return (EINVAL);
969
970
if (!sc->fan_handle) {
971
/* Read the current fanstatus */
972
ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
973
974
return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS,
975
(arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1);
976
}
977
break;
978
}
979
980
return (0);
981
}
982
983
static int
984
acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
985
{
986
int dummy;
987
ACPI_OBJECT_TYPE cmos_t;
988
ACPI_HANDLE ledb_handle;
989
990
switch (method) {
991
case ACPI_IBM_METHOD_EVENTS:
992
return (TRUE);
993
994
case ACPI_IBM_METHOD_EVENTMASK:
995
return (sc->events_mask_supported);
996
997
case ACPI_IBM_METHOD_HOTKEY:
998
case ACPI_IBM_METHOD_BRIGHTNESS:
999
case ACPI_IBM_METHOD_VOLUME:
1000
case ACPI_IBM_METHOD_MUTE:
1001
/* EC is required here, which was already checked before */
1002
return (TRUE);
1003
1004
case ACPI_IBM_METHOD_MIC_LED:
1005
if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "MMTS", &sc->mic_led_handle)))
1006
{
1007
/* Turn off mic led by default */
1008
acpi_ibm_mic_led_set(sc, 0);
1009
return (TRUE);
1010
} else
1011
sc->mic_led_handle = NULL;
1012
return (FALSE);
1013
1014
case ACPI_IBM_METHOD_THINKLIGHT:
1015
sc->cmos_handle = NULL;
1016
sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
1017
sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val));
1018
1019
if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) ||
1020
ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) ||
1021
ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) &&
1022
ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
1023
cmos_t == ACPI_TYPE_METHOD) {
1024
sc->light_cmd_on = 0x0c;
1025
sc->light_cmd_off = 0x0d;
1026
sc->cmos_handle = sc->light_handle;
1027
}
1028
else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) {
1029
sc->light_cmd_on = 1;
1030
sc->light_cmd_off = 0;
1031
} else
1032
sc->light_handle = NULL;
1033
1034
sc->light_set_supported = (sc->light_handle &&
1035
ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle)));
1036
1037
if (sc->light_get_supported)
1038
return (TRUE);
1039
1040
if (sc->light_set_supported) {
1041
sc->light_val = 0;
1042
return (TRUE);
1043
}
1044
1045
return (FALSE);
1046
1047
case ACPI_IBM_METHOD_BLUETOOTH:
1048
case ACPI_IBM_METHOD_WLAN:
1049
if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy)))
1050
return (TRUE);
1051
return (FALSE);
1052
1053
case ACPI_IBM_METHOD_FANSPEED:
1054
/*
1055
* Some models report the fan speed in levels from 0-7
1056
* Newer models report it contiguously
1057
*/
1058
sc->fan_levels =
1059
(ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) ||
1060
ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle)));
1061
return (TRUE);
1062
1063
case ACPI_IBM_METHOD_FANLEVEL:
1064
case ACPI_IBM_METHOD_FANSTATUS:
1065
/*
1066
* Fan status is only supported on those models,
1067
* which report fan RPM contiguously, not in levels
1068
*/
1069
if (sc->fan_levels)
1070
return (FALSE);
1071
return (TRUE);
1072
1073
case ACPI_IBM_METHOD_THERMAL:
1074
if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) {
1075
sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy));
1076
return (TRUE);
1077
}
1078
return (FALSE);
1079
1080
case ACPI_IBM_METHOD_HANDLEREVENTS:
1081
return (TRUE);
1082
1083
case ACPI_IBM_METHOD_PRIVACYGUARD:
1084
return (acpi_ibm_privacyguard_get(sc) != -1);
1085
}
1086
return (FALSE);
1087
}
1088
1089
static int
1090
acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
1091
{
1092
struct acpi_ibm_softc *sc;
1093
int error = 0;
1094
char temp_cmd[] = "TMP0";
1095
int temp[8];
1096
1097
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1098
1099
sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
1100
1101
ACPI_SERIAL_BEGIN(ibm);
1102
1103
for (int i = 0; i < 8; ++i) {
1104
temp_cmd[3] = '0' + i;
1105
1106
/*
1107
* The TMPx methods seem to return +/- 128 or 0
1108
* when the respecting sensor is not available
1109
*/
1110
if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
1111
&temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0)
1112
temp[i] = -1;
1113
else if (sc->thermal_updt_supported)
1114
/* Temperature is reported in tenth of Kelvin */
1115
temp[i] = (temp[i] - 2731 + 5) / 10;
1116
}
1117
1118
error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req);
1119
1120
ACPI_SERIAL_END(ibm);
1121
return (error);
1122
}
1123
1124
static int
1125
acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS)
1126
{
1127
struct acpi_ibm_softc *sc;
1128
int error = 0;
1129
struct sbuf sb;
1130
char *cp, *ep;
1131
int l, val;
1132
unsigned int handler_events;
1133
char temp[128];
1134
1135
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1136
1137
sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
1138
1139
if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL)
1140
return (ENOMEM);
1141
1142
ACPI_SERIAL_BEGIN(ibm);
1143
1144
/* Get old values if this is a get request. */
1145
if (req->newptr == NULL) {
1146
for (int i = 0; i < 8 * sizeof(sc->handler_events); i++)
1147
if (sc->handler_events & (1 << i))
1148
sbuf_printf(&sb, "0x%02x ", i + 1);
1149
if (sbuf_len(&sb) == 0)
1150
sbuf_printf(&sb, "NONE");
1151
}
1152
1153
sbuf_trim(&sb);
1154
sbuf_finish(&sb);
1155
strlcpy(temp, sbuf_data(&sb), sizeof(temp));
1156
sbuf_delete(&sb);
1157
1158
error = sysctl_handle_string(oidp, temp, sizeof(temp), req);
1159
1160
/* Check for error or no change */
1161
if (error != 0 || req->newptr == NULL)
1162
goto out;
1163
1164
/* If the user is setting a string, parse it. */
1165
handler_events = 0;
1166
cp = temp;
1167
while (*cp) {
1168
if (isspace(*cp)) {
1169
cp++;
1170
continue;
1171
}
1172
1173
ep = cp;
1174
1175
while (*ep && !isspace(*ep))
1176
ep++;
1177
1178
l = ep - cp;
1179
if (l == 0)
1180
break;
1181
1182
if (strncmp(cp, "NONE", 4) == 0) {
1183
cp = ep;
1184
continue;
1185
}
1186
1187
if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x'))
1188
val = strtoul(cp, &ep, 16);
1189
else
1190
val = strtoul(cp, &ep, 10);
1191
1192
if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) {
1193
cp[l] = '\0';
1194
device_printf(sc->dev, "invalid event code: %s\n", cp);
1195
error = EINVAL;
1196
goto out;
1197
}
1198
1199
handler_events |= 1 << (val - 1);
1200
1201
cp = ep;
1202
}
1203
1204
sc->handler_events = handler_events;
1205
out:
1206
ACPI_SERIAL_END(ibm);
1207
return (error);
1208
}
1209
1210
static int
1211
acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg)
1212
{
1213
int val, step;
1214
UINT64 val_ec;
1215
ACPI_OBJECT Arg;
1216
ACPI_OBJECT_LIST Args;
1217
ACPI_STATUS status;
1218
1219
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1220
ACPI_SERIAL_ASSERT(ibm);
1221
1222
if (arg < 0 || arg > 7)
1223
return (EINVAL);
1224
1225
/* Read the current brightness */
1226
status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
1227
if (ACPI_FAILURE(status))
1228
return (status);
1229
1230
if (sc->cmos_handle) {
1231
val = val_ec & IBM_EC_MASK_BRI;
1232
1233
Args.Count = 1;
1234
Args.Pointer = &Arg;
1235
Arg.Type = ACPI_TYPE_INTEGER;
1236
Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP :
1237
IBM_CMOS_BRIGHTNESS_DOWN;
1238
1239
step = (arg > val) ? 1 : -1;
1240
for (int i = val; i != arg; i += step) {
1241
status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1242
&Args, NULL);
1243
if (ACPI_FAILURE(status)) {
1244
/* Record the last value */
1245
if (i != val) {
1246
ACPI_EC_WRITE(sc->ec_dev,
1247
IBM_EC_BRIGHTNESS, i - step, 1);
1248
}
1249
return (status);
1250
}
1251
}
1252
}
1253
1254
return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
1255
}
1256
1257
static int
1258
acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg)
1259
{
1260
int val;
1261
1262
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1263
ACPI_SERIAL_ASSERT(ibm);
1264
1265
if (arg < 0 || arg > 1)
1266
return (EINVAL);
1267
1268
val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT :
1269
sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
1270
return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
1271
}
1272
1273
static int
1274
acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg)
1275
{
1276
ACPI_OBJECT Arg;
1277
ACPI_OBJECT_LIST Args;
1278
ACPI_STATUS status;
1279
1280
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1281
ACPI_SERIAL_ASSERT(ibm);
1282
1283
if (arg < 0 || arg > 1)
1284
return (EINVAL);
1285
1286
if (sc->light_set_supported) {
1287
Args.Count = 1;
1288
Args.Pointer = &Arg;
1289
Arg.Type = ACPI_TYPE_INTEGER;
1290
Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
1291
1292
status = AcpiEvaluateObject(sc->light_handle, NULL,
1293
&Args, NULL);
1294
if (ACPI_SUCCESS(status))
1295
sc->light_val = arg;
1296
return (status);
1297
}
1298
1299
return (0);
1300
}
1301
1302
/*
1303
* Helper function to make a get or set ACPI call to the PrivacyGuard handle.
1304
* Only meant to be used internally by the get/set functions below.
1305
*/
1306
static ACPI_STATUS
1307
acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg)
1308
{
1309
ACPI_OBJECT Arg;
1310
ACPI_OBJECT_LIST Args;
1311
ACPI_STATUS status;
1312
ACPI_OBJECT out_obj;
1313
ACPI_BUFFER result;
1314
1315
Arg.Type = ACPI_TYPE_INTEGER;
1316
Arg.Integer.Value = (write ? *arg : 0);
1317
Args.Count = 1;
1318
Args.Pointer = &Arg;
1319
result.Length = sizeof(out_obj);
1320
result.Pointer = &out_obj;
1321
1322
status = AcpiEvaluateObject(sc->handle,
1323
(write ? IBM_NAME_PRIVACYGUARD_SET : IBM_NAME_PRIVACYGUARD_GET),
1324
&Args, &result);
1325
if (ACPI_SUCCESS(status) && !write)
1326
*arg = out_obj.Integer.Value;
1327
1328
return (status);
1329
}
1330
1331
/*
1332
* Returns -1 if the device is not present.
1333
*/
1334
static int
1335
acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc)
1336
{
1337
ACPI_STATUS status;
1338
int val;
1339
1340
status = acpi_ibm_privacyguard_acpi_call(sc, false, &val);
1341
if (ACPI_SUCCESS(status) &&
1342
(val & IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT))
1343
return (val & IBM_FLAG_PRIVACYGUARD_ON);
1344
1345
return (-1);
1346
}
1347
1348
static ACPI_STATUS
1349
acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg)
1350
{
1351
if (arg < 0 || arg > 1)
1352
return (AE_BAD_PARAMETER);
1353
1354
return (acpi_ibm_privacyguard_acpi_call(sc, true, &arg));
1355
}
1356
1357
static int
1358
acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg)
1359
{
1360
int val, step;
1361
UINT64 val_ec;
1362
ACPI_OBJECT Arg;
1363
ACPI_OBJECT_LIST Args;
1364
ACPI_STATUS status;
1365
1366
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1367
ACPI_SERIAL_ASSERT(ibm);
1368
1369
if (arg < 0 || arg > 14)
1370
return (EINVAL);
1371
1372
/* Read the current volume */
1373
status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1374
if (ACPI_FAILURE(status))
1375
return (status);
1376
1377
if (sc->cmos_handle) {
1378
val = val_ec & IBM_EC_MASK_VOL;
1379
1380
Args.Count = 1;
1381
Args.Pointer = &Arg;
1382
Arg.Type = ACPI_TYPE_INTEGER;
1383
Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP :
1384
IBM_CMOS_VOLUME_DOWN;
1385
1386
step = (arg > val) ? 1 : -1;
1387
for (int i = val; i != arg; i += step) {
1388
status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1389
&Args, NULL);
1390
if (ACPI_FAILURE(status)) {
1391
/* Record the last value */
1392
if (i != val) {
1393
val_ec = i - step +
1394
(val_ec & (~IBM_EC_MASK_VOL));
1395
ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME,
1396
val_ec, 1);
1397
}
1398
return (status);
1399
}
1400
}
1401
}
1402
1403
val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL));
1404
return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
1405
}
1406
1407
static int
1408
acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg)
1409
{
1410
UINT64 val_ec;
1411
ACPI_OBJECT Arg;
1412
ACPI_OBJECT_LIST Args;
1413
ACPI_STATUS status;
1414
1415
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1416
ACPI_SERIAL_ASSERT(ibm);
1417
1418
if (arg < 0 || arg > 1)
1419
return (EINVAL);
1420
1421
status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1422
if (ACPI_FAILURE(status))
1423
return (status);
1424
1425
if (sc->cmos_handle) {
1426
Args.Count = 1;
1427
Args.Pointer = &Arg;
1428
Arg.Type = ACPI_TYPE_INTEGER;
1429
Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
1430
1431
status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
1432
if (ACPI_FAILURE(status))
1433
return (status);
1434
}
1435
1436
val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE :
1437
val_ec & (~IBM_EC_MASK_MUTE);
1438
return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
1439
}
1440
1441
static void
1442
acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg)
1443
{
1444
int val;
1445
UINT64 val_ec;
1446
ACPI_STATUS status;
1447
1448
ACPI_SERIAL_BEGIN(ibm);
1449
switch (arg) {
1450
case IBM_EVENT_SUSPEND_TO_RAM:
1451
power_pm_suspend(POWER_SLEEP_STATE_SUSPEND);
1452
break;
1453
1454
case IBM_EVENT_BLUETOOTH:
1455
acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0));
1456
break;
1457
1458
case IBM_EVENT_BRIGHTNESS_UP:
1459
case IBM_EVENT_BRIGHTNESS_DOWN:
1460
/* Read the current brightness */
1461
status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS,
1462
&val_ec, 1);
1463
if (ACPI_FAILURE(status))
1464
return;
1465
1466
val = val_ec & IBM_EC_MASK_BRI;
1467
val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1;
1468
acpi_ibm_brightness_set(sc, val);
1469
break;
1470
1471
case IBM_EVENT_THINKLIGHT:
1472
acpi_ibm_thinklight_set(sc, (sc->light_val == 0));
1473
break;
1474
1475
case IBM_EVENT_VOLUME_UP:
1476
case IBM_EVENT_VOLUME_DOWN:
1477
/* Read the current volume */
1478
status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1479
if (ACPI_FAILURE(status))
1480
return;
1481
1482
val = val_ec & IBM_EC_MASK_VOL;
1483
val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1;
1484
acpi_ibm_volume_set(sc, val);
1485
break;
1486
1487
case IBM_EVENT_MUTE:
1488
/* Read the current value */
1489
status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1490
if (ACPI_FAILURE(status))
1491
return;
1492
1493
val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
1494
acpi_ibm_mute_set(sc, (val == 0));
1495
break;
1496
1497
default:
1498
break;
1499
}
1500
ACPI_SERIAL_END(ibm);
1501
}
1502
1503
static void
1504
acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1505
{
1506
int event, arg, type;
1507
device_t dev = context;
1508
struct acpi_ibm_softc *sc = device_get_softc(dev);
1509
1510
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
1511
1512
if (notify != 0x80)
1513
device_printf(dev, "Unknown notify\n");
1514
1515
for (;;) {
1516
acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event);
1517
if (event == 0)
1518
break;
1519
1520
type = (event >> 12) & 0xf;
1521
arg = event & 0xfff;
1522
switch (type) {
1523
case 1:
1524
if (!(sc->events_availmask & (1 << (arg - 1)))) {
1525
device_printf(dev, "Unknown key %d\n", arg);
1526
break;
1527
}
1528
1529
/* Execute event handler */
1530
if (sc->handler_events & (1 << (arg - 1)))
1531
acpi_ibm_eventhandler(sc, (arg & 0xff));
1532
#ifdef EVDEV_SUPPORT
1533
else if ((arg & 0xff) == IBM_EVENT_BRIGHTNESS_UP ||
1534
(arg & 0xff) == IBM_EVENT_BRIGHTNESS_DOWN) {
1535
uint16_t key;
1536
1537
key = arg == IBM_EVENT_BRIGHTNESS_UP ?
1538
KEY_BRIGHTNESSUP : KEY_BRIGHTNESSDOWN;
1539
evdev_push_key(sc->evdev, key, 1);
1540
evdev_sync(sc->evdev);
1541
evdev_push_key(sc->evdev, key, 0);
1542
evdev_sync(sc->evdev);
1543
}
1544
#endif
1545
1546
/* Notify devd(8) */
1547
acpi_UserNotify("IBM", h, (arg & 0xff));
1548
break;
1549
default:
1550
break;
1551
}
1552
}
1553
}
1554
1555