Deleted Added
full compact
acpi_toshiba.c (124442) acpi_toshiba.c (126517)
1/*-
2 * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_toshiba.c 124442 2004-01-12 19:35:31Z njl $");
29__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_toshiba.c 126517 2004-03-03 03:02:17Z njl $");
30
31#include "opt_acpi.h"
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/bus.h>
35
36#include "acpi.h"
37#include <dev/acpica/acpivar.h>
38
30
31#include "opt_acpi.h"
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/bus.h>
35
36#include "acpi.h"
37#include <dev/acpica/acpivar.h>
38
39#define _COMPONENT ACPI_TOSHIBA
40ACPI_MODULE_NAME("TOSHIBA")
41
42/*
43 * Toshiba HCI interface definitions
44 *
45 * HCI is Toshiba's "Hardware Control Interface" which is supposed to
46 * be uniform across all their models. Ideally we would just call
47 * dedicated ACPI methods instead of using this primitive interface.
48 * However, the ACPI methods seem to be incomplete in some areas (for
49 * example they allow setting, but not reading, the LCD brightness
50 * value), so this is still useful.
51 */
52
53#define METHOD_HCI "GHCI"
54#define METHOD_HCI_ENABLE "ENAB"
55
56/* Operations */
57#define HCI_SET 0xFF00
58#define HCI_GET 0xFE00
59
60/* Return codes */
61#define HCI_SUCCESS 0x0000
62#define HCI_FAILURE 0x1000
63#define HCI_NOT_SUPPORTED 0x8000
64#define HCI_EMPTY 0x8C00
65
66/* Functions */
67#define HCI_REG_LCD_BACKLIGHT 0x0002
68#define HCI_REG_FAN 0x0004
69#define HCI_REG_SYSTEM_EVENT 0x0016
70#define HCI_REG_VIDEO_OUTPUT 0x001C
71#define HCI_REG_HOTKEY_EVENT 0x001E
72#define HCI_REG_LCD_BRIGHTNESS 0x002A
73#define HCI_REG_CPU_SPEED 0x0032
74
75/* Field definitions */
76#define HCI_FAN_SHIFT 7
77#define HCI_LCD_BRIGHTNESS_BITS 3
78#define HCI_LCD_BRIGHTNESS_SHIFT (16 - HCI_LCD_BRIGHTNESS_BITS)
79#define HCI_LCD_BRIGHTNESS_MAX ((1 << HCI_LCD_BRIGHTNESS_BITS) - 1)
80#define HCI_VIDEO_OUTPUT_FLAG 0x0100
81#define HCI_VIDEO_OUTPUT_LCD 0x1
82#define HCI_VIDEO_OUTPUT_CRT 0x2
83#define HCI_VIDEO_OUTPUT_TV 0x4
84#define HCI_CPU_SPEED_BITS 3
85#define HCI_CPU_SPEED_SHIFT (16 - HCI_CPU_SPEED_BITS)
86#define HCI_CPU_SPEED_MAX ((1 << HCI_CPU_SPEED_BITS) - 1)
87
88/* Key press/release events. */
89#define FN_F1_PRESS 0x013B
90#define FN_F1_RELEASE 0x01BB
91#define FN_F2_PRESS 0x013C
92#define FN_F2_RELEASE 0x01BC
93#define FN_F3_PRESS 0x013D
94#define FN_F3_RELEASE 0x01BD
95#define FN_F4_PRESS 0x013E
96#define FN_F4_RELEASE 0x01BE
97#define FN_F5_PRESS 0x013F
98#define FN_F5_RELEASE 0x01BF
99#define FN_F6_PRESS 0x0140
100#define FN_F6_RELEASE 0x01C0
101#define FN_F7_PRESS 0x0141
102#define FN_F7_RELEASE 0x01C1
103#define FN_F8_PRESS 0x0142
104#define FN_F8_RELEASE 0x01C2
105#define FN_F9_PRESS 0x0143
106#define FN_F9_RELEASE 0x01C3
107#define FN_BS_PRESS 0x010E
108#define FN_BS_RELEASE 0x018E
109#define FN_ESC_PRESS 0x0101
110#define FN_ESC_RELEASE 0x0181
111#define FN_KNJ_PRESS 0x0129
112#define FN_KNJ_RELEASE 0x01A9
113
114/* HCI register definitions. */
115#define HCI_WORDS 6 /* Number of registers */
116#define HCI_REG_AX 0 /* Operation, then return value */
117#define HCI_REG_BX 1 /* Function */
118#define HCI_REG_CX 2 /* Argument (in or out) */
119#define HCI_REG_DX 3 /* Unused? */
120#define HCI_REG_SI 4 /* Unused? */
121#define HCI_REG_DI 5 /* Unused? */
122
123struct acpi_toshiba_softc {
124 device_t dev;
125 ACPI_HANDLE handle;
126 struct sysctl_ctx_list sysctl_ctx;
127 struct sysctl_oid *sysctl_tree;
128};
129
130/* Prototype for HCI functions for getting/setting a value. */
131typedef int hci_fn_t(ACPI_HANDLE, int, UINT32 *);
132
133static int acpi_toshiba_probe(device_t dev);
134static int acpi_toshiba_attach(device_t dev);
135static int acpi_toshiba_detach(device_t dev);
136static int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS);
137static hci_fn_t hci_force_fan;
138static hci_fn_t hci_video_output;
139static hci_fn_t hci_lcd_brightness;
140static hci_fn_t hci_lcd_backlight;
141static hci_fn_t hci_cpu_speed;
142static int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg);
143static void hci_key_action(ACPI_HANDLE h, UINT32 key);
144static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify,
145 void *context);
146
147/* Table of sysctl names and HCI functions to call. */
148static struct {
149 char *name;
150 hci_fn_t *handler;
151} sysctl_table[] = {
152 /* name, handler */
153 {"force_fan", hci_force_fan},
154 {"video_output", hci_video_output},
155 {"lcd_brightness", hci_lcd_brightness},
156 {"lcd_backlight", hci_lcd_backlight},
157 {"cpu_speed", hci_cpu_speed},
158 {NULL, NULL}
159};
160
161static device_method_t acpi_toshiba_methods[] = {
162 DEVMETHOD(device_probe, acpi_toshiba_probe),
163 DEVMETHOD(device_attach, acpi_toshiba_attach),
164 DEVMETHOD(device_detach, acpi_toshiba_detach),
165
166 {0, 0}
167};
168
169static driver_t acpi_toshiba_driver = {
170 "acpi_toshiba",
171 acpi_toshiba_methods,
172 sizeof(struct acpi_toshiba_softc),
173};
174
175static devclass_t acpi_toshiba_devclass;
176DRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass,
177 0, 0);
178MODULE_DEPEND(acpi_toshiba, acpi, 100, 100, 100);
179
180static int enable_fn_keys = 1;
181TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys);
182
183/*
184 * HID Model
185 * -------------------------------------
186 * TOS6200 Libretto L Series
187 * Dynabook Satellite 2455
188 * Dynabook SS 3500
189 * TOS6207 Dynabook SS2110 Series
190 */
191static int
192acpi_toshiba_probe(device_t dev)
193{
194 int ret = ENXIO;
195
196 if (!acpi_disabled("toshiba") &&
197 acpi_get_type(dev) == ACPI_TYPE_DEVICE &&
198 device_get_unit(dev) == 0 &&
199 (acpi_MatchHid(dev, "TOS6200") ||
200 acpi_MatchHid(dev, "TOS6207"))) {
201 device_set_desc(dev, "Toshiba HCI Extras");
202 ret = 0;
203 }
204
205 return (ret);
206}
207
208static int
209acpi_toshiba_attach(device_t dev)
210{
211 struct acpi_toshiba_softc *sc;
212 struct acpi_softc *acpi_sc;
213 ACPI_STATUS status;
214 int i;
215
39/*
40 * Toshiba HCI interface definitions
41 *
42 * HCI is Toshiba's "Hardware Control Interface" which is supposed to
43 * be uniform across all their models. Ideally we would just call
44 * dedicated ACPI methods instead of using this primitive interface.
45 * However, the ACPI methods seem to be incomplete in some areas (for
46 * example they allow setting, but not reading, the LCD brightness
47 * value), so this is still useful.
48 */
49
50#define METHOD_HCI "GHCI"
51#define METHOD_HCI_ENABLE "ENAB"
52
53/* Operations */
54#define HCI_SET 0xFF00
55#define HCI_GET 0xFE00
56
57/* Return codes */
58#define HCI_SUCCESS 0x0000
59#define HCI_FAILURE 0x1000
60#define HCI_NOT_SUPPORTED 0x8000
61#define HCI_EMPTY 0x8C00
62
63/* Functions */
64#define HCI_REG_LCD_BACKLIGHT 0x0002
65#define HCI_REG_FAN 0x0004
66#define HCI_REG_SYSTEM_EVENT 0x0016
67#define HCI_REG_VIDEO_OUTPUT 0x001C
68#define HCI_REG_HOTKEY_EVENT 0x001E
69#define HCI_REG_LCD_BRIGHTNESS 0x002A
70#define HCI_REG_CPU_SPEED 0x0032
71
72/* Field definitions */
73#define HCI_FAN_SHIFT 7
74#define HCI_LCD_BRIGHTNESS_BITS 3
75#define HCI_LCD_BRIGHTNESS_SHIFT (16 - HCI_LCD_BRIGHTNESS_BITS)
76#define HCI_LCD_BRIGHTNESS_MAX ((1 << HCI_LCD_BRIGHTNESS_BITS) - 1)
77#define HCI_VIDEO_OUTPUT_FLAG 0x0100
78#define HCI_VIDEO_OUTPUT_LCD 0x1
79#define HCI_VIDEO_OUTPUT_CRT 0x2
80#define HCI_VIDEO_OUTPUT_TV 0x4
81#define HCI_CPU_SPEED_BITS 3
82#define HCI_CPU_SPEED_SHIFT (16 - HCI_CPU_SPEED_BITS)
83#define HCI_CPU_SPEED_MAX ((1 << HCI_CPU_SPEED_BITS) - 1)
84
85/* Key press/release events. */
86#define FN_F1_PRESS 0x013B
87#define FN_F1_RELEASE 0x01BB
88#define FN_F2_PRESS 0x013C
89#define FN_F2_RELEASE 0x01BC
90#define FN_F3_PRESS 0x013D
91#define FN_F3_RELEASE 0x01BD
92#define FN_F4_PRESS 0x013E
93#define FN_F4_RELEASE 0x01BE
94#define FN_F5_PRESS 0x013F
95#define FN_F5_RELEASE 0x01BF
96#define FN_F6_PRESS 0x0140
97#define FN_F6_RELEASE 0x01C0
98#define FN_F7_PRESS 0x0141
99#define FN_F7_RELEASE 0x01C1
100#define FN_F8_PRESS 0x0142
101#define FN_F8_RELEASE 0x01C2
102#define FN_F9_PRESS 0x0143
103#define FN_F9_RELEASE 0x01C3
104#define FN_BS_PRESS 0x010E
105#define FN_BS_RELEASE 0x018E
106#define FN_ESC_PRESS 0x0101
107#define FN_ESC_RELEASE 0x0181
108#define FN_KNJ_PRESS 0x0129
109#define FN_KNJ_RELEASE 0x01A9
110
111/* HCI register definitions. */
112#define HCI_WORDS 6 /* Number of registers */
113#define HCI_REG_AX 0 /* Operation, then return value */
114#define HCI_REG_BX 1 /* Function */
115#define HCI_REG_CX 2 /* Argument (in or out) */
116#define HCI_REG_DX 3 /* Unused? */
117#define HCI_REG_SI 4 /* Unused? */
118#define HCI_REG_DI 5 /* Unused? */
119
120struct acpi_toshiba_softc {
121 device_t dev;
122 ACPI_HANDLE handle;
123 struct sysctl_ctx_list sysctl_ctx;
124 struct sysctl_oid *sysctl_tree;
125};
126
127/* Prototype for HCI functions for getting/setting a value. */
128typedef int hci_fn_t(ACPI_HANDLE, int, UINT32 *);
129
130static int acpi_toshiba_probe(device_t dev);
131static int acpi_toshiba_attach(device_t dev);
132static int acpi_toshiba_detach(device_t dev);
133static int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS);
134static hci_fn_t hci_force_fan;
135static hci_fn_t hci_video_output;
136static hci_fn_t hci_lcd_brightness;
137static hci_fn_t hci_lcd_backlight;
138static hci_fn_t hci_cpu_speed;
139static int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg);
140static void hci_key_action(ACPI_HANDLE h, UINT32 key);
141static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify,
142 void *context);
143
144/* Table of sysctl names and HCI functions to call. */
145static struct {
146 char *name;
147 hci_fn_t *handler;
148} sysctl_table[] = {
149 /* name, handler */
150 {"force_fan", hci_force_fan},
151 {"video_output", hci_video_output},
152 {"lcd_brightness", hci_lcd_brightness},
153 {"lcd_backlight", hci_lcd_backlight},
154 {"cpu_speed", hci_cpu_speed},
155 {NULL, NULL}
156};
157
158static device_method_t acpi_toshiba_methods[] = {
159 DEVMETHOD(device_probe, acpi_toshiba_probe),
160 DEVMETHOD(device_attach, acpi_toshiba_attach),
161 DEVMETHOD(device_detach, acpi_toshiba_detach),
162
163 {0, 0}
164};
165
166static driver_t acpi_toshiba_driver = {
167 "acpi_toshiba",
168 acpi_toshiba_methods,
169 sizeof(struct acpi_toshiba_softc),
170};
171
172static devclass_t acpi_toshiba_devclass;
173DRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass,
174 0, 0);
175MODULE_DEPEND(acpi_toshiba, acpi, 100, 100, 100);
176
177static int enable_fn_keys = 1;
178TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys);
179
180/*
181 * HID Model
182 * -------------------------------------
183 * TOS6200 Libretto L Series
184 * Dynabook Satellite 2455
185 * Dynabook SS 3500
186 * TOS6207 Dynabook SS2110 Series
187 */
188static int
189acpi_toshiba_probe(device_t dev)
190{
191 int ret = ENXIO;
192
193 if (!acpi_disabled("toshiba") &&
194 acpi_get_type(dev) == ACPI_TYPE_DEVICE &&
195 device_get_unit(dev) == 0 &&
196 (acpi_MatchHid(dev, "TOS6200") ||
197 acpi_MatchHid(dev, "TOS6207"))) {
198 device_set_desc(dev, "Toshiba HCI Extras");
199 ret = 0;
200 }
201
202 return (ret);
203}
204
205static int
206acpi_toshiba_attach(device_t dev)
207{
208 struct acpi_toshiba_softc *sc;
209 struct acpi_softc *acpi_sc;
210 ACPI_STATUS status;
211 int i;
212
216 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
217
218 sc = device_get_softc(dev);
219 sc->dev = dev;
220 sc->handle = acpi_get_handle(dev);
221
222 acpi_sc = acpi_device_get_parent_softc(dev);
223 sysctl_ctx_init(&sc->sysctl_ctx);
224 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
225 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
226 "toshiba", CTLFLAG_RD, 0, "");
227
228 for (i = 0; sysctl_table[i].name != NULL; i++) {
229 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
230 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
231 sysctl_table[i].name,
232 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
233 sc, i, acpi_toshiba_sysctl, "I", "");
234 }
235
236 if (enable_fn_keys != 0) {
237 status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE,
238 NULL, NULL);
239 if (ACPI_FAILURE(status)) {
240 device_printf(dev, "enable FN keys failed\n");
241 sysctl_ctx_free(&sc->sysctl_ctx);
242 return (ENXIO);
243 }
244 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
245 acpi_toshiba_notify, sc);
246 }
247
248 return (0);
249}
250
251static int
252acpi_toshiba_detach(device_t dev)
253{
254 struct acpi_toshiba_softc *sc;
255
213 sc = device_get_softc(dev);
214 sc->dev = dev;
215 sc->handle = acpi_get_handle(dev);
216
217 acpi_sc = acpi_device_get_parent_softc(dev);
218 sysctl_ctx_init(&sc->sysctl_ctx);
219 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
220 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
221 "toshiba", CTLFLAG_RD, 0, "");
222
223 for (i = 0; sysctl_table[i].name != NULL; i++) {
224 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
225 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
226 sysctl_table[i].name,
227 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
228 sc, i, acpi_toshiba_sysctl, "I", "");
229 }
230
231 if (enable_fn_keys != 0) {
232 status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE,
233 NULL, NULL);
234 if (ACPI_FAILURE(status)) {
235 device_printf(dev, "enable FN keys failed\n");
236 sysctl_ctx_free(&sc->sysctl_ctx);
237 return (ENXIO);
238 }
239 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
240 acpi_toshiba_notify, sc);
241 }
242
243 return (0);
244}
245
246static int
247acpi_toshiba_detach(device_t dev)
248{
249 struct acpi_toshiba_softc *sc;
250
256 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
257
258 sc = device_get_softc(dev);
259 if (enable_fn_keys != 0) {
260 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
261 acpi_toshiba_notify);
262 }
263 sysctl_ctx_free(&sc->sysctl_ctx);
264
265 return (0);
266}
267
268static int
269acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS)
270{
271 struct acpi_toshiba_softc *sc;
272 UINT32 arg;
273 int function, error = 0;
274 hci_fn_t *handler;
275
276 sc = (struct acpi_toshiba_softc *)oidp->oid_arg1;
277 function = oidp->oid_arg2;
278 handler = sysctl_table[function].handler;
279
280 /* Get the current value from the appropriate function. */
281 error = handler(sc->handle, HCI_GET, &arg);
282 if (error != 0)
283 return (error);
284
285 /* Send the current value to the user and return if no new value. */
286 error = sysctl_handle_int(oidp, &arg, 0, req);
287 if (error != 0 || req->newptr == NULL)
288 return (error);
289
290 /* Set the new value via the appropriate function. */
291 error = handler(sc->handle, HCI_SET, &arg);
292
293 return (error);
294}
295
296static int
297hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state)
298{
299 int ret;
300
301 if (op == HCI_SET) {
302 if (*state < 0 || *state > 1)
303 return (EINVAL);
304 *state <<= HCI_FAN_SHIFT;
305 }
306 ret = hci_call(h, op, HCI_REG_FAN, state);
307 if (ret == 0 && op == HCI_GET)
308 *state >>= HCI_FAN_SHIFT;
309 return (ret);
310}
311
312static int
313hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output)
314{
315 int ret;
316
317 if (op == HCI_SET) {
318 if (*video_output < 1 || *video_output > 7)
319 return (EINVAL);
320 *video_output |= HCI_VIDEO_OUTPUT_FLAG;
321 }
322 ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output);
323 if (ret == 0 && op == HCI_GET)
324 *video_output &= 0xff;
325 return (ret);
326}
327
328static int
329hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness)
330{
331 int ret;
332
333 if (op == HCI_SET) {
334 if (*brightness < 0 || *brightness > HCI_LCD_BRIGHTNESS_MAX)
335 return (EINVAL);
336 *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
337 }
338 ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness);
339 if (ret == 0 && op == HCI_GET)
340 *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
341 return (ret);
342}
343
344static int
345hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight)
346{
347 if (op == HCI_SET) {
348 if (*backlight < 0 || *backlight > 1)
349 return (EINVAL);
350 }
351 return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight));
352}
353
354static int
355hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed)
356{
357 int ret;
358
359 if (op == HCI_SET) {
360 if (*speed < 0 || *speed > HCI_CPU_SPEED_MAX)
361 return (EINVAL);
362 *speed <<= HCI_CPU_SPEED_SHIFT;
363 }
364 ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed);
365 if (ret == 0 && op == HCI_GET)
366 *speed >>= HCI_CPU_SPEED_SHIFT;
367 return (ret);
368}
369
370static int
371hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg)
372{
373 ACPI_OBJECT_LIST args;
374 ACPI_BUFFER results;
375 ACPI_OBJECT obj[HCI_WORDS];
376 ACPI_OBJECT *res;
377 int status, i, ret;
378
379 status = ENXIO;
380
381 for (i = 0; i < HCI_WORDS; i++) {
382 obj[i].Type = ACPI_TYPE_INTEGER;
383 obj[i].Integer.Value = 0;
384 }
385 obj[HCI_REG_AX].Integer.Value = op;
386 obj[HCI_REG_BX].Integer.Value = function;
387 if (op == HCI_SET)
388 obj[HCI_REG_CX].Integer.Value = *arg;
389
390 args.Count = HCI_WORDS;
391 args.Pointer = obj;
392 results.Pointer = NULL;
393 results.Length = ACPI_ALLOCATE_BUFFER;
394 if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results)))
395 goto end;
396 res = (ACPI_OBJECT *)results.Pointer;
397 if (!ACPI_PKG_VALID(res, HCI_WORDS)) {
398 printf("toshiba: invalid package!\n");
399 return (ENXIO);
400 }
401
402 acpi_PkgInt32(res, HCI_REG_AX, &ret);
403 if (ret == HCI_SUCCESS) {
404 if (op == HCI_GET)
405 acpi_PkgInt32(res, HCI_REG_CX, arg);
406 status = 0;
407 } else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET &&
408 ret == HCI_NOT_SUPPORTED) {
409 /*
410 * Sometimes system events are disabled without us requesting
411 * it. This workaround attempts to re-enable them.
412 */
413 i = 1;
414 hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i);
415 }
416
417end:
418 if (results.Pointer != NULL)
419 AcpiOsFree(results.Pointer);
420
421 return (status);
422}
423
424/*
425 * Perform a few actions based on the keypress. Users can extend this
426 * functionality by reading the keystrokes we send to devd(8).
427 */
428static void
429hci_key_action(ACPI_HANDLE h, UINT32 key)
430{
431 UINT32 arg;
432
433 switch (key) {
434 case FN_F6_RELEASE:
435 /* Decrease LCD brightness. */
436 hci_lcd_brightness(h, HCI_GET, &arg);
437 if (arg-- == 0)
438 arg = 0;
439 else
440 hci_lcd_brightness(h, HCI_SET, &arg);
441 break;
442 case FN_F7_RELEASE:
443 /* Increase LCD brightness. */
444 hci_lcd_brightness(h, HCI_GET, &arg);
445 if (arg++ == 7)
446 arg = 7;
447 else
448 hci_lcd_brightness(h, HCI_SET, &arg);
449 break;
450 case FN_F5_RELEASE:
451 /* Cycle through video outputs. */
452 hci_video_output(h, HCI_GET, &arg);
453 arg = (arg + 1) % 7;
454 hci_video_output(h, HCI_SET, &arg);
455 break;
456 case FN_F8_RELEASE:
457 /* Toggle LCD backlight. */
458 hci_lcd_backlight(h, HCI_GET, &arg);
459 arg = (arg != 0) ? 0 : 1;
460 hci_lcd_backlight(h, HCI_SET, &arg);
461 break;
462 case FN_ESC_RELEASE:
463 /* Toggle forcing fan on. */
464 hci_force_fan(h, HCI_GET, &arg);
465 arg = (arg != 0) ? 0 : 1;
466 hci_force_fan(h, HCI_SET, &arg);
467 break;
468 }
469}
470
471static void
472acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context)
473{
474 struct acpi_toshiba_softc *sc;
475 UINT32 key;
476
477 sc = (struct acpi_toshiba_softc *)context;
478
479 if (notify == 0x80) {
480 while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) {
481 hci_key_action(h, key);
482 acpi_UserNotify("TOSHIBA", h, (uint8_t)key);
483 }
484 } else {
485 device_printf(sc->dev, "unknown notify: 0x%x\n", notify);
486 }
487}
251 sc = device_get_softc(dev);
252 if (enable_fn_keys != 0) {
253 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
254 acpi_toshiba_notify);
255 }
256 sysctl_ctx_free(&sc->sysctl_ctx);
257
258 return (0);
259}
260
261static int
262acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS)
263{
264 struct acpi_toshiba_softc *sc;
265 UINT32 arg;
266 int function, error = 0;
267 hci_fn_t *handler;
268
269 sc = (struct acpi_toshiba_softc *)oidp->oid_arg1;
270 function = oidp->oid_arg2;
271 handler = sysctl_table[function].handler;
272
273 /* Get the current value from the appropriate function. */
274 error = handler(sc->handle, HCI_GET, &arg);
275 if (error != 0)
276 return (error);
277
278 /* Send the current value to the user and return if no new value. */
279 error = sysctl_handle_int(oidp, &arg, 0, req);
280 if (error != 0 || req->newptr == NULL)
281 return (error);
282
283 /* Set the new value via the appropriate function. */
284 error = handler(sc->handle, HCI_SET, &arg);
285
286 return (error);
287}
288
289static int
290hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state)
291{
292 int ret;
293
294 if (op == HCI_SET) {
295 if (*state < 0 || *state > 1)
296 return (EINVAL);
297 *state <<= HCI_FAN_SHIFT;
298 }
299 ret = hci_call(h, op, HCI_REG_FAN, state);
300 if (ret == 0 && op == HCI_GET)
301 *state >>= HCI_FAN_SHIFT;
302 return (ret);
303}
304
305static int
306hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output)
307{
308 int ret;
309
310 if (op == HCI_SET) {
311 if (*video_output < 1 || *video_output > 7)
312 return (EINVAL);
313 *video_output |= HCI_VIDEO_OUTPUT_FLAG;
314 }
315 ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output);
316 if (ret == 0 && op == HCI_GET)
317 *video_output &= 0xff;
318 return (ret);
319}
320
321static int
322hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness)
323{
324 int ret;
325
326 if (op == HCI_SET) {
327 if (*brightness < 0 || *brightness > HCI_LCD_BRIGHTNESS_MAX)
328 return (EINVAL);
329 *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
330 }
331 ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness);
332 if (ret == 0 && op == HCI_GET)
333 *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
334 return (ret);
335}
336
337static int
338hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight)
339{
340 if (op == HCI_SET) {
341 if (*backlight < 0 || *backlight > 1)
342 return (EINVAL);
343 }
344 return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight));
345}
346
347static int
348hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed)
349{
350 int ret;
351
352 if (op == HCI_SET) {
353 if (*speed < 0 || *speed > HCI_CPU_SPEED_MAX)
354 return (EINVAL);
355 *speed <<= HCI_CPU_SPEED_SHIFT;
356 }
357 ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed);
358 if (ret == 0 && op == HCI_GET)
359 *speed >>= HCI_CPU_SPEED_SHIFT;
360 return (ret);
361}
362
363static int
364hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg)
365{
366 ACPI_OBJECT_LIST args;
367 ACPI_BUFFER results;
368 ACPI_OBJECT obj[HCI_WORDS];
369 ACPI_OBJECT *res;
370 int status, i, ret;
371
372 status = ENXIO;
373
374 for (i = 0; i < HCI_WORDS; i++) {
375 obj[i].Type = ACPI_TYPE_INTEGER;
376 obj[i].Integer.Value = 0;
377 }
378 obj[HCI_REG_AX].Integer.Value = op;
379 obj[HCI_REG_BX].Integer.Value = function;
380 if (op == HCI_SET)
381 obj[HCI_REG_CX].Integer.Value = *arg;
382
383 args.Count = HCI_WORDS;
384 args.Pointer = obj;
385 results.Pointer = NULL;
386 results.Length = ACPI_ALLOCATE_BUFFER;
387 if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results)))
388 goto end;
389 res = (ACPI_OBJECT *)results.Pointer;
390 if (!ACPI_PKG_VALID(res, HCI_WORDS)) {
391 printf("toshiba: invalid package!\n");
392 return (ENXIO);
393 }
394
395 acpi_PkgInt32(res, HCI_REG_AX, &ret);
396 if (ret == HCI_SUCCESS) {
397 if (op == HCI_GET)
398 acpi_PkgInt32(res, HCI_REG_CX, arg);
399 status = 0;
400 } else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET &&
401 ret == HCI_NOT_SUPPORTED) {
402 /*
403 * Sometimes system events are disabled without us requesting
404 * it. This workaround attempts to re-enable them.
405 */
406 i = 1;
407 hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i);
408 }
409
410end:
411 if (results.Pointer != NULL)
412 AcpiOsFree(results.Pointer);
413
414 return (status);
415}
416
417/*
418 * Perform a few actions based on the keypress. Users can extend this
419 * functionality by reading the keystrokes we send to devd(8).
420 */
421static void
422hci_key_action(ACPI_HANDLE h, UINT32 key)
423{
424 UINT32 arg;
425
426 switch (key) {
427 case FN_F6_RELEASE:
428 /* Decrease LCD brightness. */
429 hci_lcd_brightness(h, HCI_GET, &arg);
430 if (arg-- == 0)
431 arg = 0;
432 else
433 hci_lcd_brightness(h, HCI_SET, &arg);
434 break;
435 case FN_F7_RELEASE:
436 /* Increase LCD brightness. */
437 hci_lcd_brightness(h, HCI_GET, &arg);
438 if (arg++ == 7)
439 arg = 7;
440 else
441 hci_lcd_brightness(h, HCI_SET, &arg);
442 break;
443 case FN_F5_RELEASE:
444 /* Cycle through video outputs. */
445 hci_video_output(h, HCI_GET, &arg);
446 arg = (arg + 1) % 7;
447 hci_video_output(h, HCI_SET, &arg);
448 break;
449 case FN_F8_RELEASE:
450 /* Toggle LCD backlight. */
451 hci_lcd_backlight(h, HCI_GET, &arg);
452 arg = (arg != 0) ? 0 : 1;
453 hci_lcd_backlight(h, HCI_SET, &arg);
454 break;
455 case FN_ESC_RELEASE:
456 /* Toggle forcing fan on. */
457 hci_force_fan(h, HCI_GET, &arg);
458 arg = (arg != 0) ? 0 : 1;
459 hci_force_fan(h, HCI_SET, &arg);
460 break;
461 }
462}
463
464static void
465acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context)
466{
467 struct acpi_toshiba_softc *sc;
468 UINT32 key;
469
470 sc = (struct acpi_toshiba_softc *)context;
471
472 if (notify == 0x80) {
473 while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) {
474 hci_key_action(h, key);
475 acpi_UserNotify("TOSHIBA", h, (uint8_t)key);
476 }
477 } else {
478 device_printf(sc->dev, "unknown notify: 0x%x\n", notify);
479 }
480}