1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2007, 2008 Rui Paulo <rpaulo@FreeBSD.org>
5 * All rights reserved.
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 ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30 /*
31 * Driver for Apple's System Management Console (SMC).
32 * SMC can be found on the MacBook, MacBook Pro and Mac Mini.
33 *
34 * Inspired by the Linux applesmc driver.
35 */
36
37 #include "opt_asmc.h"
38
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/conf.h>
42 #include <sys/endian.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/malloc.h>
46 #include <sys/module.h>
47 #include <sys/mutex.h>
48 #include <sys/sysctl.h>
49 #include <sys/systm.h>
50 #include <sys/taskqueue.h>
51 #include <sys/rman.h>
52
53 #include <machine/resource.h>
54 #include <netinet/in.h>
55
56 #include <contrib/dev/acpica/include/acpi.h>
57
58 #include <dev/acpica/acpivar.h>
59 #include <dev/asmc/asmcvar.h>
60
61 #include <dev/backlight/backlight.h>
62 #include "backlight_if.h"
63
64 /*
65 * Device interface.
66 */
67 static int asmc_probe(device_t dev);
68 static int asmc_attach(device_t dev);
69 static int asmc_detach(device_t dev);
70 static int asmc_resume(device_t dev);
71
72 /*
73 * Backlight interface.
74 */
75 static int asmc_backlight_update_status(device_t dev,
76 struct backlight_props *props);
77 static int asmc_backlight_get_status(device_t dev,
78 struct backlight_props *props);
79 static int asmc_backlight_get_info(device_t dev, struct backlight_info *info);
80
81 /*
82 * SMC functions.
83 */
84 static int asmc_init(device_t dev);
85 static int asmc_command(device_t dev, uint8_t command);
86 static int asmc_wait(device_t dev, uint8_t val);
87 static int asmc_wait_ack(device_t dev, uint8_t val, int amount);
88 static int asmc_key_write(device_t dev, const char *key, uint8_t *buf,
89 uint8_t len);
90 static int asmc_key_read(device_t dev, const char *key, uint8_t *buf,
91 uint8_t);
92 static int asmc_fan_count(device_t dev);
93 static int asmc_fan_getvalue(device_t dev, const char *key, int fan);
94 static int asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed);
95 static int asmc_temp_getvalue(device_t dev, const char *key);
96 static int asmc_sms_read(device_t, const char *key, int16_t *val);
97 static void asmc_sms_calibrate(device_t dev);
98 static int asmc_sms_intrfast(void *arg);
99 static void asmc_sms_printintr(device_t dev, uint8_t);
100 static void asmc_sms_task(void *arg, int pending);
101 static void asmc_sms_init(device_t dev);
102 static void asmc_detect_capabilities(device_t dev);
103 #ifdef ASMC_DEBUG
104 void asmc_dumpall(device_t);
105 static int asmc_key_dump(device_t, int);
106 #endif
107
108 /*
109 * Sysctl handlers.
110 */
111 static int asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS);
112 static int asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS);
113 static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
114 static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
115 static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
116 static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
117 static int asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS);
118 static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
119 static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
120 static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
121 static int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
122 static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
123 static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
124 static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS);
125 static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS);
126 static int asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS);
127
128 static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *);
129
130 #ifdef ASMC_DEBUG
131 /* Raw key access */
132 static int asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS);
133 static int asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS);
134 static int asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS);
135 static int asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS);
136 #endif
137
138 /* Voltage/Current/Power/Light sensor support */
139 static int asmc_sensor_read(device_t, const char *, int *);
140 static int asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS);
141 static int asmc_detect_sensors(device_t);
142 static int asmc_key_dump_by_index(device_t, int, char *, char *, uint8_t *);
143 static int asmc_key_search(device_t, const char *, unsigned int *);
144 static const char *asmc_temp_desc(const char *key);
145
146 /*
147 * SMC temperature key descriptions.
148 * These are universal across all Intel Apple hardware.
149 */
150 static const struct {
151 const char *key;
152 const char *desc;
153 } asmc_temp_descs[] = {
154 /* Ambient / airflow */
155 { "TA0P", "Ambient" },
156 { "TA0S", "PCIe Slot 1 Ambient" },
157 { "TA0p", "Ambient Air" },
158 { "TA1P", "Ambient 2" },
159 { "TA1S", "PCIe Slot 1 PCB" },
160 { "TA1p", "Ambient Air 2" },
161 { "TA2P", "Ambient 3" },
162 { "TA2S", "PCIe Slot 2 Ambient" },
163 { "TA3S", "PCIe Slot 2 PCB" },
164 { "TA0V", "Ambient" },
165 { "TALP", "Ambient Light Proximity" },
166 { "TaLC", "Airflow Left" },
167 { "TaRC", "Airflow Right" },
168 { "Ta0P", "Airflow Proximity" },
169 /* Battery / enclosure */
170 { "TB0T", "Enclosure Bottom" },
171 { "TB1T", "Battery 1" },
172 { "TB2T", "Battery 2" },
173 { "TB3T", "Battery 3" },
174 { "TBXT", "Battery" },
175 { "Tb0P", "BLC Proximity" },
176 /* CPU */
177 { "TC0C", "CPU Core 1" },
178 { "TC0D", "CPU Die" },
179 { "TC0E", "CPU 1" },
180 { "TC0F", "CPU 2" },
181 { "TC0G", "CPU Package GPU" },
182 { "TC0H", "CPU Heatsink" },
183 { "TC0h", "CPU Heatsink" },
184 { "TC0J", "CPU" },
185 { "TC0P", "CPU Proximity" },
186 { "TC0c", "CPU Core 1 PECI" },
187 { "TC0d", "CPU Die PECI" },
188 { "TC0p", "CPU Proximity" },
189 { "TC1C", "CPU Core 2" },
190 { "TC1c", "CPU Core 2 PECI" },
191 { "TC1P", "CPU Proximity 2" },
192 { "TC2C", "CPU Core 3" },
193 { "TC2P", "CPU Proximity 3" },
194 { "TC2c", "CPU Core 3 PECI" },
195 { "TC3C", "CPU Core 4" },
196 { "TC3P", "CPU Proximity 4" },
197 { "TC3c", "CPU Core 4 PECI" },
198 { "TC4C", "CPU Core 5" },
199 { "TC5C", "CPU Core 6" },
200 { "TC6C", "CPU Core 7" },
201 { "TC7C", "CPU Core 8" },
202 { "TC8C", "CPU Core 9" },
203 { "TCGC", "PECI GPU" },
204 { "TCGc", "PECI GPU" },
205 { "TCHP", "Charger Proximity" },
206 { "TCSA", "PECI SA" },
207 { "TCSC", "PECI SA" },
208 { "TCSc", "PECI SA" },
209 { "TCTD", "CPU DTS" },
210 { "TCXC", "PECI CPU" },
211 { "TCXc", "PECI CPU" },
212 { "TCPG", "CPU Package GPU" },
213 { "TCXR", "CPU PECI DTS" },
214 /* CPU dual-socket (Mac Pro) */
215 { "TCAG", "CPU A Package" },
216 { "TCAH", "CPU A Heatsink" },
217 { "TCBG", "CPU B Package" },
218 { "TCBH", "CPU B Heatsink" },
219 /* GPU */
220 { "TG0C", "GPU Core" },
221 { "TG0D", "GPU Diode" },
222 { "TG0H", "GPU Heatsink" },
223 { "TG0M", "GPU Memory" },
224 { "TG0P", "GPU Proximity" },
225 { "TG0T", "GPU Diode" },
226 { "TG0V", "GPU" },
227 { "TG0d", "GPU Die" },
228 { "TG0h", "GPU Heatsink" },
229 { "TG0p", "GPU Proximity" },
230 { "TGTV", "GPU" },
231 { "TG1D", "GPU 2 Diode" },
232 { "TG1H", "GPU 2 Heatsink" },
233 { "TG1P", "GPU 2 Proximity" },
234 { "TG1d", "GPU 2 Die" },
235 { "TGVP", "GPU Memory Proximity" },
236 /* Storage */
237 { "TH0A", "SSD A" },
238 { "TH0B", "SSD B" },
239 { "TH0C", "SSD C" },
240 { "TH0F", "SSD" },
241 { "TH0O", "HDD" },
242 { "TH0P", "HDD Proximity" },
243 { "TH0R", "SSD" },
244 { "TH0V", "SSD" },
245 { "TH0a", "SSD A" },
246 { "TH0b", "SSD B" },
247 { "TH0c", "SSD C" },
248 { "TH1O", "HDD 2" },
249 { "TH1P", "HDD Bay 2" },
250 { "TH2P", "HDD Bay 3" },
251 { "TH3P", "HDD Bay 4" },
252 { "Th0H", "Heatpipe 1" },
253 { "Th0N", "SSD" },
254 { "Th1H", "Heatpipe 2" },
255 { "Th2H", "Heatpipe 3" },
256 /* Thunderbolt */
257 { "THSP", "Thunderbolt Proximity" },
258 { "TI0P", "Thunderbolt 1" },
259 { "TI0p", "Thunderbolt 1" },
260 { "TI1P", "Thunderbolt 2" },
261 { "TI1p", "Thunderbolt 2" },
262 { "TTLD", "Thunderbolt Left" },
263 { "TTRD", "Thunderbolt Right" },
264 { "Te0T", "Thunderbolt Diode" },
265 { "Te0t", "Thunderbolt Diode" },
266 /* LCD */
267 { "TL0P", "LCD Proximity" },
268 { "TL0V", "LCD" },
269 { "TL0p", "LCD Proximity" },
270 { "TL1P", "LCD Panel 1" },
271 { "TL1V", "LCD 1" },
272 { "TL1p", "LCD Panel 1" },
273 { "TL1v", "LCD 1" },
274 { "TL2V", "LCD 2" },
275 { "TLAV", "LCD" },
276 { "TLBV", "LCD" },
277 { "TLCV", "LCD" },
278 /* Memory */
279 { "TM0P", "Memory Proximity" },
280 { "TM0S", "Memory Slot 1" },
281 { "TM0p", "Memory Proximity" },
282 { "TM1P", "Memory Riser A 2" },
283 { "TM1S", "Memory Slot 2" },
284 { "Tm0P", "Memory Proximity" },
285 { "Tm0p", "Memory Proximity" },
286 { "Tm1P", "Memory Proximity 2" },
287 { "TMBS", "Memory Bank" },
288 { "TMCD", "Memory DIMM" },
289 /* Northbridge / MCH */
290 { "TN0C", "Northbridge Core" },
291 { "TN0D", "Northbridge Diode" },
292 { "TN0H", "MCH Heatsink" },
293 { "TN0P", "Northbridge Proximity" },
294 { "TN1D", "MCH Die 2" },
295 { "TN1P", "Northbridge Proximity 2" },
296 /* PCH */
297 { "TP0P", "PCH Proximity" },
298 { "TP0p", "PCH Proximity" },
299 { "TPCD", "PCH Die" },
300 { "TPCd", "PCH Die" },
301 /* Optical drive */
302 { "TO0P", "Optical Drive" },
303 { "TO0p", "Optical Drive" },
304 /* Power supply */
305 { "Tp0C", "Power Supply" },
306 { "Tp0P", "Power Supply Proximity" },
307 { "Tp1C", "Power Supply 2" },
308 { "Tp1P", "Power Supply Component" },
309 { "Tp1p", "Power Supply Component" },
310 { "Tp2P", "Power Supply 2" },
311 { "Tp2h", "Power Supply 2" },
312 { "Tp2H", "Power Supply 2" },
313 { "Tp3P", "Power Supply 3 Inlet" },
314 { "Tp3h", "Power Supply 3" },
315 { "Tp3H", "Power Supply 3" },
316 { "Tp4P", "Power Supply 4" },
317 { "Tp5P", "Power Supply 5" },
318 /* Palm rest / trackpad */
319 { "Ts0P", "Palm Rest" },
320 { "Ts0S", "Memory Proximity" },
321 { "Ts1P", "Palm Rest 2" },
322 { "Ts1S", "Palm Rest 2" },
323 /* Wireless */
324 { "TW0P", "Wireless Proximity" },
325 { "TW0p", "Wireless Proximity" },
326 { "TBLR", "Bluetooth" },
327 /* Camera */
328 { "TS2P", "Camera Proximity" },
329 { "TS2V", "Camera" },
330 { "TS2p", "Camera Proximity" },
331 /* Expansion */
332 { "TS0C", "Expansion Slots" },
333 { "TS0P", "Expansion Proximity" },
334 { "TS0V", "Expansion" },
335 { "TS0p", "Expansion Proximity" },
336 /* Air vent */
337 { "TV0P", "Air Vent" },
338 /* VRM */
339 { "Tv0S", "VRM 1" },
340 { "Tv1S", "VRM 2" },
341 /* Misc */
342 { "TTF0", "Fan" },
343 { "TMLB", "Logic Board" },
344 };
345
346 static const char *
asmc_temp_desc(const char * key)347 asmc_temp_desc(const char *key)
348 {
349 unsigned int i;
350
351 for (i = 0; i < nitems(asmc_temp_descs); i++) {
352 if (strcmp(asmc_temp_descs[i].key, key) == 0)
353 return (asmc_temp_descs[i].desc);
354 }
355 return ("Temperature");
356 }
357
358 /*
359 * Driver methods.
360 */
361 static device_method_t asmc_methods[] = {
362 DEVMETHOD(device_probe, asmc_probe),
363 DEVMETHOD(device_attach, asmc_attach),
364 DEVMETHOD(device_detach, asmc_detach),
365 DEVMETHOD(device_resume, asmc_resume),
366
367 /* Backlight interface */
368 DEVMETHOD(backlight_update_status, asmc_backlight_update_status),
369 DEVMETHOD(backlight_get_status, asmc_backlight_get_status),
370 DEVMETHOD(backlight_get_info, asmc_backlight_get_info),
371
372 DEVMETHOD_END
373 };
374
375 static driver_t asmc_driver = {
376 "asmc",
377 asmc_methods,
378 sizeof(struct asmc_softc)
379 };
380
381 /*
382 * Debugging
383 */
384 #define _COMPONENT ACPI_OEM
385 ACPI_MODULE_NAME("ASMC")
386 #ifdef ASMC_DEBUG
387 #define ASMC_DPRINTF(str, ...) device_printf(dev, str, ##__VA_ARGS__)
388 #else
389 #define ASMC_DPRINTF(str, ...)
390 #endif
391
392 /* NB: can't be const */
393 static char *asmc_ids[] = { "APP0001", NULL };
394
395 static unsigned int light_control = 0;
396
397 ACPI_PNP_INFO(asmc_ids);
398 DRIVER_MODULE(asmc, acpi, asmc_driver, NULL, NULL);
399 MODULE_DEPEND(asmc, acpi, 1, 1, 1);
400 MODULE_DEPEND(asmc, backlight, 1, 1, 1);
401
402 static int
asmc_probe(device_t dev)403 asmc_probe(device_t dev)
404 {
405 char *product;
406 int rv;
407
408 if (resource_disabled("asmc", 0))
409 return (ENXIO);
410 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asmc_ids, NULL);
411 if (rv > 0)
412 return (rv);
413 product = kern_getenv("smbios.system.product");
414 device_set_descf(dev, "Apple %s", product ? product : "SMC");
415 freeenv(product);
416 return (rv);
417 }
418
419 static int
asmc_attach(device_t dev)420 asmc_attach(device_t dev)
421 {
422 int i, j;
423 int ret;
424 char name[2];
425 struct asmc_softc *sc = device_get_softc(dev);
426 struct sysctl_ctx_list *sysctlctx;
427 struct sysctl_oid *sysctlnode;
428
429 sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
430 &sc->sc_rid_port, RF_ACTIVE);
431 if (sc->sc_ioport == NULL) {
432 device_printf(dev, "unable to allocate IO port\n");
433 return (ENOMEM);
434 }
435
436 sysctlctx = device_get_sysctl_ctx(dev);
437 sysctlnode = device_get_sysctl_tree(dev);
438
439 mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
440
441 /* Read SMC revision, key count, fan count */
442 ret = asmc_init(dev);
443 if (ret != 0) {
444 device_printf(dev, "SMC not responding\n");
445 goto err;
446 }
447
448 /* Probe SMC keys to detect capabilities */
449 asmc_detect_capabilities(dev);
450
451 /* Auto-detect and register voltage/current/power/ambient/temp sensors */
452 asmc_detect_sensors(dev);
453
454 /*
455 * dev.asmc.n.fan.* tree.
456 */
457 sc->sc_fan_tree[0] = SYSCTL_ADD_NODE(sysctlctx,
458 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "fan",
459 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Root Tree");
460
461 for (i = 1; i <= sc->sc_nfan; i++) {
462 j = i - 1;
463 name[0] = '0' + j;
464 name[1] = 0;
465 sc->sc_fan_tree[i] = SYSCTL_ADD_NODE(sysctlctx,
466 SYSCTL_CHILDREN(sc->sc_fan_tree[0]), OID_AUTO, name,
467 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Fan Subtree");
468
469 SYSCTL_ADD_PROC(sysctlctx,
470 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
471 OID_AUTO, "id",
472 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j,
473 asmc_mb_sysctl_fanid, "I", "Fan ID");
474
475 SYSCTL_ADD_PROC(sysctlctx,
476 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
477 OID_AUTO, "speed",
478 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j,
479 asmc_mb_sysctl_fanspeed, "I", "Fan speed in RPM");
480
481 if (sc->sc_has_safespeed) {
482 SYSCTL_ADD_PROC(sysctlctx,
483 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
484 OID_AUTO, "safespeed",
485 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, j,
486 asmc_mb_sysctl_fansafespeed, "I",
487 "Fan safe speed in RPM");
488 }
489
490 SYSCTL_ADD_PROC(sysctlctx,
491 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
492 OID_AUTO, "minspeed",
493 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
494 asmc_mb_sysctl_fanminspeed, "I",
495 "Fan minimum speed in RPM");
496
497 SYSCTL_ADD_PROC(sysctlctx,
498 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
499 OID_AUTO, "maxspeed",
500 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
501 asmc_mb_sysctl_fanmaxspeed, "I",
502 "Fan maximum speed in RPM");
503
504 SYSCTL_ADD_PROC(sysctlctx,
505 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
506 OID_AUTO, "targetspeed",
507 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
508 asmc_mb_sysctl_fantargetspeed, "I",
509 "Fan target speed in RPM");
510
511 SYSCTL_ADD_PROC(sysctlctx,
512 SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
513 OID_AUTO, "manual",
514 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, j,
515 asmc_mb_sysctl_fanmanual, "I",
516 "Fan manual mode (0=auto, 1=manual)");
517 }
518
519 /*
520 * dev.asmc.n.temp tree.
521 */
522 sc->sc_temp_tree = SYSCTL_ADD_NODE(sysctlctx,
523 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "temp",
524 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Temperature sensors");
525
526 for (i = 0; i < sc->sc_temp_count; i++) {
527 SYSCTL_ADD_PROC(sysctlctx,
528 SYSCTL_CHILDREN(sc->sc_temp_tree),
529 OID_AUTO, sc->sc_temp_sensors[i],
530 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, i,
531 asmc_temp_sysctl, "I",
532 asmc_temp_desc(sc->sc_temp_sensors[i]));
533 }
534
535 /*
536 * dev.asmc.n.light
537 */
538 if (sc->sc_has_light) {
539 sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
540 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
541 CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
542 "Keyboard backlight sensors");
543
544 SYSCTL_ADD_PROC(sysctlctx,
545 SYSCTL_CHILDREN(sc->sc_light_tree),
546 OID_AUTO, "left",
547 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
548 dev, 0,
549 sc->sc_light_len == ASMC_LIGHT_LONGLEN ?
550 asmc_mbp_sysctl_light_left_10byte :
551 asmc_mbp_sysctl_light_left,
552 "I", "Keyboard backlight left sensor");
553
554 if (sc->sc_light_len != ASMC_LIGHT_LONGLEN &&
555 asmc_key_getinfo(dev, ASMC_KEY_LIGHTRIGHT,
556 NULL, NULL) == 0) {
557 SYSCTL_ADD_PROC(sysctlctx,
558 SYSCTL_CHILDREN(sc->sc_light_tree),
559 OID_AUTO, "right",
560 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
561 dev, 0,
562 asmc_mbp_sysctl_light_right, "I",
563 "Keyboard backlight right sensor");
564 }
565
566 SYSCTL_ADD_PROC(sysctlctx,
567 SYSCTL_CHILDREN(sc->sc_light_tree),
568 OID_AUTO, "control",
569 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE,
570 dev, 0, asmc_mbp_sysctl_light_control, "I",
571 "Keyboard backlight brightness control");
572
573 sc->sc_kbd_bkl = backlight_register("asmc", dev);
574 if (sc->sc_kbd_bkl == NULL) {
575 device_printf(dev, "Can not register backlight\n");
576 ret = ENXIO;
577 goto err;
578 }
579 }
580
581 #ifdef ASMC_DEBUG
582 /*
583 * Raw SMC key access for debugging.
584 */
585 sc->sc_raw_tree = SYSCTL_ADD_NODE(sysctlctx,
586 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
587 "raw", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Raw SMC key access");
588
589 SYSCTL_ADD_PROC(sysctlctx,
590 SYSCTL_CHILDREN(sc->sc_raw_tree),
591 OID_AUTO, "key",
592 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
593 dev, 0, asmc_raw_key_sysctl, "A",
594 "SMC key name (4 chars)");
595
596 SYSCTL_ADD_PROC(sysctlctx,
597 SYSCTL_CHILDREN(sc->sc_raw_tree),
598 OID_AUTO, "value",
599 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
600 dev, 0, asmc_raw_value_sysctl, "A",
601 "SMC key value (hex string)");
602
603 SYSCTL_ADD_PROC(sysctlctx,
604 SYSCTL_CHILDREN(sc->sc_raw_tree),
605 OID_AUTO, "len",
606 CTLTYPE_U8 | CTLFLAG_RD | CTLFLAG_MPSAFE,
607 dev, 0, asmc_raw_len_sysctl, "CU",
608 "SMC key value length");
609
610 SYSCTL_ADD_PROC(sysctlctx,
611 SYSCTL_CHILDREN(sc->sc_raw_tree),
612 OID_AUTO, "type",
613 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
614 dev, 0, asmc_raw_type_sysctl, "A",
615 "SMC key type (4 chars)");
616 #endif
617
618 if (!sc->sc_has_sms)
619 goto nosms;
620
621 /*
622 * Initialize SMS hardware.
623 */
624 asmc_sms_init(dev);
625
626 /*
627 * dev.asmc.n.sms tree.
628 */
629 sc->sc_sms_tree = SYSCTL_ADD_NODE(sysctlctx,
630 SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "sms",
631 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Sudden Motion Sensor");
632
633 SYSCTL_ADD_PROC(sysctlctx,
634 SYSCTL_CHILDREN(sc->sc_sms_tree),
635 OID_AUTO, "x",
636 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
637 dev, 0, asmc_mb_sysctl_sms_x, "I",
638 "Sudden Motion Sensor X value");
639
640 SYSCTL_ADD_PROC(sysctlctx,
641 SYSCTL_CHILDREN(sc->sc_sms_tree),
642 OID_AUTO, "y",
643 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
644 dev, 0, asmc_mb_sysctl_sms_y, "I",
645 "Sudden Motion Sensor Y value");
646
647 SYSCTL_ADD_PROC(sysctlctx,
648 SYSCTL_CHILDREN(sc->sc_sms_tree),
649 OID_AUTO, "z",
650 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
651 dev, 0, asmc_mb_sysctl_sms_z, "I",
652 "Sudden Motion Sensor Z value");
653
654 /*
655 * Need a taskqueue to send devctl_notify() events
656 * when the SMS interrupt us.
657 *
658 * PI_REALTIME is used due to the sensitivity of the
659 * interrupt. An interrupt from the SMS means that the
660 * disk heads should be turned off as quickly as possible.
661 *
662 * We only need to do this for the non INTR_FILTER case.
663 */
664 sc->sc_sms_tq = NULL;
665 TASK_INIT(&sc->sc_sms_task, 0, asmc_sms_task, sc);
666 sc->sc_sms_tq = taskqueue_create_fast("asmc_taskq", M_WAITOK,
667 taskqueue_thread_enqueue, &sc->sc_sms_tq);
668 taskqueue_start_threads(&sc->sc_sms_tq, 1, PI_REALTIME, "%s sms taskq",
669 device_get_nameunit(dev));
670 /*
671 * Allocate an IRQ for the SMS.
672 */
673 sc->sc_rid_irq = 0;
674 sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_rid_irq,
675 RF_ACTIVE);
676 if (sc->sc_irq == NULL) {
677 device_printf(dev, "unable to allocate IRQ resource\n");
678 ret = ENXIO;
679 goto err;
680 }
681
682 ret = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE,
683 asmc_sms_intrfast, NULL, dev, &sc->sc_cookie);
684 if (ret) {
685 device_printf(dev, "unable to setup SMS IRQ\n");
686 goto err;
687 }
688
689 nosms:
690 return (0);
691
692 err:
693 asmc_detach(dev);
694
695 return (ret);
696 }
697
698 static int
asmc_detach(device_t dev)699 asmc_detach(device_t dev)
700 {
701 struct asmc_softc *sc = device_get_softc(dev);
702
703 if (sc->sc_kbd_bkl != NULL)
704 backlight_destroy(sc->sc_kbd_bkl);
705
706 /* Free temperature sensor key arrays */
707 for (int i = 0; i < sc->sc_temp_count; i++)
708 free(sc->sc_temp_sensors[i], M_DEVBUF);
709
710 /* Free sensor key arrays */
711 for (int i = 0; i < sc->sc_voltage_count; i++)
712 free(sc->sc_voltage_sensors[i], M_DEVBUF);
713 for (int i = 0; i < sc->sc_current_count; i++)
714 free(sc->sc_current_sensors[i], M_DEVBUF);
715 for (int i = 0; i < sc->sc_power_count; i++)
716 free(sc->sc_power_sensors[i], M_DEVBUF);
717 for (int i = 0; i < sc->sc_light_count; i++)
718 free(sc->sc_light_sensors[i], M_DEVBUF);
719
720 if (sc->sc_sms_tq) {
721 taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
722 taskqueue_free(sc->sc_sms_tq);
723 sc->sc_sms_tq = NULL;
724 }
725 if (sc->sc_cookie) {
726 bus_teardown_intr(dev, sc->sc_irq, sc->sc_cookie);
727 sc->sc_cookie = NULL;
728 }
729 if (sc->sc_irq) {
730 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid_irq,
731 sc->sc_irq);
732 sc->sc_irq = NULL;
733 }
734 if (sc->sc_ioport) {
735 bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_rid_port,
736 sc->sc_ioport);
737 sc->sc_ioport = NULL;
738 }
739 if (mtx_initialized(&sc->sc_mtx)) {
740 mtx_destroy(&sc->sc_mtx);
741 }
742
743 return (0);
744 }
745
746 static int
asmc_resume(device_t dev)747 asmc_resume(device_t dev)
748 {
749 uint8_t buf[2];
750
751 buf[0] = light_control;
752 buf[1] = 0x00;
753 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf));
754
755 return (0);
756 }
757
758 #ifdef ASMC_DEBUG
759 void
asmc_dumpall(device_t dev)760 asmc_dumpall(device_t dev)
761 {
762 struct asmc_softc *sc = device_get_softc(dev);
763 int i;
764
765 if (sc->sc_nkeys == 0) {
766 device_printf(dev, "asmc_dumpall: key count not available\n");
767 return;
768 }
769
770 device_printf(dev, "asmc_dumpall: dumping %d keys\n", sc->sc_nkeys);
771 for (i = 0; i < sc->sc_nkeys; i++)
772 asmc_key_dump(dev, i);
773 }
774 #endif
775
776 /*
777 * Initialize SMC: read revision, key count, fan count.
778 * SMS initialization is handled separately in asmc_sms_init().
779 */
780 static int
asmc_init(device_t dev)781 asmc_init(device_t dev)
782 {
783 struct asmc_softc *sc = device_get_softc(dev);
784 struct sysctl_ctx_list *sysctlctx;
785 uint8_t buf[6];
786 int error;
787
788 sysctlctx = device_get_sysctl_ctx(dev);
789
790 error = asmc_key_read(dev, ASMC_KEY_REV, buf, 6);
791 if (error != 0)
792 goto out;
793 device_printf(dev, "SMC revision: %x.%x%x%x\n", buf[0], buf[1], buf[2],
794 ntohs(*(uint16_t *)buf + 4));
795
796 /* Auto power-on after AC power loss (AUPO). */
797 if (asmc_key_read(dev, ASMC_KEY_AUPO, buf, 1) == 0) {
798 SYSCTL_ADD_PROC(sysctlctx,
799 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
800 OID_AUTO, "auto_poweron",
801 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
802 dev, 0, asmc_aupo_sysctl, "I",
803 "Auto power-on after AC power loss (0=off, 1=on)");
804 }
805
806 sc->sc_nfan = asmc_fan_count(dev);
807 if (sc->sc_nfan > ASMC_MAXFANS) {
808 device_printf(dev,
809 "more than %d fans were detected. Please report this.\n",
810 ASMC_MAXFANS);
811 sc->sc_nfan = ASMC_MAXFANS;
812 }
813
814 /*
815 * Read and cache the number of SMC keys (32 bit buffer)
816 */
817 if (asmc_key_read(dev, ASMC_NKEYS, buf, 4) == 0) {
818 sc->sc_nkeys = be32dec(buf);
819 if (bootverbose)
820 device_printf(dev, "number of keys: %d\n",
821 sc->sc_nkeys);
822 } else {
823 sc->sc_nkeys = 0;
824 }
825
826 out:
827 #ifdef ASMC_DEBUG
828 asmc_dumpall(dev);
829 #endif
830 return (error);
831 }
832
833 /*
834 * Initialize the Sudden Motion Sensor hardware.
835 * Called from asmc_attach() after capabilities are detected.
836 */
837 static void
asmc_sms_init(device_t dev)838 asmc_sms_init(device_t dev)
839 {
840 struct asmc_softc *sc = device_get_softc(dev);
841 uint8_t buf[2];
842 int i;
843
844 /*
845 * We are ready to receive interrupts from the SMS.
846 */
847 buf[0] = 0x01;
848 ASMC_DPRINTF(("intok key\n"));
849 asmc_key_write(dev, ASMC_KEY_INTOK, buf, 1);
850 DELAY(50);
851
852 /*
853 * Initiate the polling intervals.
854 */
855 buf[0] = 20; /* msecs */
856 ASMC_DPRINTF(("low int key\n"));
857 asmc_key_write(dev, ASMC_KEY_SMS_LOW_INT, buf, 1);
858 DELAY(200);
859
860 buf[0] = 20; /* msecs */
861 ASMC_DPRINTF(("high int key\n"));
862 asmc_key_write(dev, ASMC_KEY_SMS_HIGH_INT, buf, 1);
863 DELAY(200);
864
865 buf[0] = 0x00;
866 buf[1] = 0x60;
867 ASMC_DPRINTF(("sms low key\n"));
868 asmc_key_write(dev, ASMC_KEY_SMS_LOW, buf, 2);
869 DELAY(200);
870
871 buf[0] = 0x01;
872 buf[1] = 0xc0;
873 ASMC_DPRINTF(("sms high key\n"));
874 asmc_key_write(dev, ASMC_KEY_SMS_HIGH, buf, 2);
875 DELAY(200);
876
877 /*
878 * I'm not sure what this key does, but it seems to be
879 * required.
880 */
881 buf[0] = 0x01;
882 ASMC_DPRINTF(("sms flag key\n"));
883 asmc_key_write(dev, ASMC_KEY_SMS_FLAG, buf, 1);
884 DELAY(100);
885
886 sc->sc_sms_intr_works = 0;
887
888 /*
889 * Retry SMS initialization 1000 times
890 * (takes approx. 2 seconds in worst case)
891 */
892 for (i = 0; i < 1000; i++) {
893 if (asmc_key_read(dev, ASMC_KEY_SMS, buf, 2) == 0 &&
894 (buf[0] == ASMC_SMS_INIT1 && buf[1] == ASMC_SMS_INIT2)) {
895 sc->sc_sms_intr_works = 1;
896 goto done;
897 }
898 buf[0] = ASMC_SMS_INIT1;
899 buf[1] = ASMC_SMS_INIT2;
900 ASMC_DPRINTF(("sms key\n"));
901 asmc_key_write(dev, ASMC_KEY_SMS, buf, 2);
902 DELAY(50);
903 }
904 device_printf(dev, "WARNING: Sudden Motion Sensor not initialized!\n");
905
906 done:
907 asmc_sms_calibrate(dev);
908 }
909
910 /*
911 * Probe SMC keys to detect hardware capabilities.
912 */
913 static void
asmc_detect_capabilities(device_t dev)914 asmc_detect_capabilities(device_t dev)
915 {
916 struct asmc_softc *sc = device_get_softc(dev);
917 uint8_t len;
918 char type[ASMC_TYPELEN + 1];
919
920 /* SMS: require all keys used by asmc_sms_init() */
921 sc->sc_has_sms =
922 (asmc_key_getinfo(dev, ASMC_KEY_SMS,
923 &len, type) == 0 &&
924 asmc_key_getinfo(dev, ASMC_KEY_SMS_X,
925 &len, type) == 0 &&
926 asmc_key_getinfo(dev, ASMC_KEY_SMS_Y,
927 &len, type) == 0 &&
928 asmc_key_getinfo(dev, ASMC_KEY_SMS_Z,
929 &len, type) == 0 &&
930 asmc_key_getinfo(dev, ASMC_KEY_SMS_LOW,
931 &len, type) == 0 &&
932 asmc_key_getinfo(dev, ASMC_KEY_SMS_HIGH,
933 &len, type) == 0 &&
934 asmc_key_getinfo(dev, ASMC_KEY_SMS_LOW_INT,
935 &len, type) == 0 &&
936 asmc_key_getinfo(dev, ASMC_KEY_SMS_HIGH_INT,
937 &len, type) == 0 &&
938 asmc_key_getinfo(dev, ASMC_KEY_SMS_FLAG,
939 &len, type) == 0 &&
940 asmc_key_getinfo(dev, ASMC_KEY_INTOK,
941 &len, type) == 0);
942
943 /* Light sensor: require ALV0 (len 6 or 10) and LKSB */
944 if (asmc_key_getinfo(dev, ASMC_KEY_LIGHTLEFT,
945 &len, type) == 0 &&
946 (len == ASMC_LIGHT_SHORTLEN || len == ASMC_LIGHT_LONGLEN) &&
947 asmc_key_getinfo(dev, ASMC_KEY_LIGHTVALUE,
948 NULL, NULL) == 0) {
949 sc->sc_has_light = 1;
950 sc->sc_light_len = len;
951 } else {
952 sc->sc_has_light = 0;
953 sc->sc_light_len = 0;
954 }
955
956 /* Fan safe speed */
957 sc->sc_has_safespeed =
958 (asmc_key_getinfo(dev, ASMC_KEY_FANSAFESPEED0,
959 &len, type) == 0);
960
961 /* Ambient light interrupt source */
962 sc->sc_has_alsl =
963 (asmc_key_getinfo(dev, ASMC_KEY_LIGHTSRC,
964 &len, type) == 0);
965
966 if (bootverbose)
967 device_printf(dev,
968 "capabilities: sms=%d light=%d (len=%d) safespeed=%d alsl=%d\n",
969 sc->sc_has_sms, sc->sc_has_light, sc->sc_light_len,
970 sc->sc_has_safespeed, sc->sc_has_alsl);
971 }
972
973 /*
974 * We need to make sure that the SMC acks the byte sent.
975 * Just wait up to (amount * 10) ms.
976 */
977 static int
asmc_wait_ack(device_t dev,uint8_t val,int amount)978 asmc_wait_ack(device_t dev, uint8_t val, int amount)
979 {
980 struct asmc_softc *sc = device_get_softc(dev);
981 u_int i;
982
983 val = val & ASMC_STATUS_MASK;
984
985 for (i = 0; i < amount; i++) {
986 if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
987 return (0);
988 DELAY(10);
989 }
990
991 return (1);
992 }
993
994 /*
995 * We need to make sure that the SMC acks the byte sent.
996 * Just wait up to 100 ms.
997 */
998 static int
asmc_wait(device_t dev,uint8_t val)999 asmc_wait(device_t dev, uint8_t val)
1000 {
1001 #ifdef ASMC_DEBUG
1002 struct asmc_softc *sc;
1003 #endif
1004
1005 if (asmc_wait_ack(dev, val, 1000) == 0)
1006 return (0);
1007
1008 #ifdef ASMC_DEBUG
1009 sc = device_get_softc(dev);
1010
1011 device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__,
1012 val & ASMC_STATUS_MASK, ASMC_CMDPORT_READ(sc));
1013 #endif
1014 return (1);
1015 }
1016
1017 /*
1018 * Send the given command, retrying up to 10 times if
1019 * the acknowledgement fails.
1020 */
1021 static int
asmc_command(device_t dev,uint8_t command)1022 asmc_command(device_t dev, uint8_t command)
1023 {
1024 int i;
1025 struct asmc_softc *sc = device_get_softc(dev);
1026
1027 for (i = 0; i < 10; i++) {
1028 ASMC_CMDPORT_WRITE(sc, command);
1029 if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
1030 return (0);
1031 }
1032 }
1033
1034 #ifdef ASMC_DEBUG
1035 device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
1036 ASMC_CMDPORT_READ(sc));
1037 #endif
1038 return (1);
1039 }
1040
1041 static int
asmc_key_read(device_t dev,const char * key,uint8_t * buf,uint8_t len)1042 asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
1043 {
1044 int i, error = 1, try = 0;
1045 struct asmc_softc *sc = device_get_softc(dev);
1046
1047 mtx_lock_spin(&sc->sc_mtx);
1048
1049 begin:
1050 if (asmc_command(dev, ASMC_CMDREAD))
1051 goto out;
1052
1053 for (i = 0; i < 4; i++) {
1054 ASMC_DATAPORT_WRITE(sc, key[i]);
1055 if (asmc_wait(dev, 0x04))
1056 goto out;
1057 }
1058
1059 ASMC_DATAPORT_WRITE(sc, len);
1060
1061 for (i = 0; i < len; i++) {
1062 if (asmc_wait(dev, 0x05))
1063 goto out;
1064 buf[i] = ASMC_DATAPORT_READ(sc);
1065 }
1066
1067 error = 0;
1068 out:
1069 if (error) {
1070 if (++try < 10)
1071 goto begin;
1072 device_printf(dev, "%s for key %s failed %d times, giving up\n",
1073 __func__, key, try);
1074 }
1075
1076 mtx_unlock_spin(&sc->sc_mtx);
1077
1078 return (error);
1079 }
1080
1081 #ifdef ASMC_DEBUG
1082 static int
asmc_key_dump(device_t dev,int number)1083 asmc_key_dump(device_t dev, int number)
1084 {
1085 struct asmc_softc *sc = device_get_softc(dev);
1086 char key[ASMC_KEYLEN + 1] = { 0 };
1087 char type[ASMC_KEYINFO_RESPLEN + 1] = { 0 };
1088 uint8_t index[4];
1089 uint8_t v[ASMC_MAXVAL];
1090 uint8_t maxlen;
1091 int i, error = 1, try = 0;
1092
1093 mtx_lock_spin(&sc->sc_mtx);
1094
1095 index[0] = (number >> 24) & 0xff;
1096 index[1] = (number >> 16) & 0xff;
1097 index[2] = (number >> 8) & 0xff;
1098 index[3] = number & 0xff;
1099
1100 begin:
1101 if (asmc_command(dev, ASMC_CMDGETBYINDEX))
1102 goto out;
1103
1104 for (i = 0; i < ASMC_KEYLEN; i++) {
1105 ASMC_DATAPORT_WRITE(sc, index[i]);
1106 if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1107 goto out;
1108 }
1109
1110 ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN);
1111
1112 for (i = 0; i < ASMC_KEYLEN; i++) {
1113 if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1114 goto out;
1115 key[i] = ASMC_DATAPORT_READ(sc);
1116 }
1117
1118 /* Get key info (length + type). */
1119 if (asmc_command(dev, ASMC_CMDGETINFO))
1120 goto out;
1121
1122 for (i = 0; i < ASMC_KEYLEN; i++) {
1123 ASMC_DATAPORT_WRITE(sc, key[i]);
1124 if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1125 goto out;
1126 }
1127
1128 ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
1129
1130 for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
1131 if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1132 goto out;
1133 type[i] = ASMC_DATAPORT_READ(sc);
1134 }
1135
1136 error = 0;
1137 out:
1138 if (error) {
1139 if (++try < ASMC_MAXRETRIES)
1140 goto begin;
1141 device_printf(dev,
1142 "%s for key %d failed %d times, giving up\n",
1143 __func__, number, try);
1144 }
1145 mtx_unlock_spin(&sc->sc_mtx);
1146
1147 if (error)
1148 return (error);
1149
1150 maxlen = type[0];
1151 type[0] = ' ';
1152 type[5] = '\0';
1153 if (maxlen > sizeof(v))
1154 maxlen = sizeof(v);
1155
1156 memset(v, 0, sizeof(v));
1157 error = asmc_key_read(dev, key, v, maxlen);
1158 if (error)
1159 return (error);
1160
1161 device_printf(dev, "key %d: %s, type%s (len %d), data",
1162 number, key, type, maxlen);
1163 for (i = 0; i < maxlen; i++)
1164 printf(" %02x", v[i]);
1165 printf("\n");
1166
1167 return (0);
1168 }
1169 #endif /* ASMC_DEBUG */
1170
1171 /*
1172 * Get key info (length and type) from SMC using command 0x13.
1173 * If len is non-NULL, stores the key's value length.
1174 * If type is non-NULL, stores the 4-char type string (must be at least 5 bytes).
1175 */
1176 static int
asmc_key_getinfo(device_t dev,const char * key,uint8_t * len,char * type)1177 asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type)
1178 {
1179 struct asmc_softc *sc = device_get_softc(dev);
1180 uint8_t info[ASMC_KEYINFO_RESPLEN];
1181 int i, error = -1, try = 0;
1182
1183 mtx_lock_spin(&sc->sc_mtx);
1184
1185 begin:
1186 if (asmc_command(dev, ASMC_CMDGETINFO))
1187 goto out;
1188
1189 for (i = 0; i < ASMC_KEYLEN; i++) {
1190 ASMC_DATAPORT_WRITE(sc, key[i]);
1191 if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1192 goto out;
1193 }
1194
1195 ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
1196
1197 for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
1198 if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1199 goto out;
1200 info[i] = ASMC_DATAPORT_READ(sc);
1201 }
1202
1203 error = 0;
1204 out:
1205 if (error && ++try < ASMC_MAXRETRIES)
1206 goto begin;
1207 mtx_unlock_spin(&sc->sc_mtx);
1208
1209 if (error == 0) {
1210 if (len != NULL)
1211 *len = info[0];
1212 if (type != NULL) {
1213 for (i = 0; i < ASMC_TYPELEN; i++)
1214 type[i] = info[i + 1];
1215 type[ASMC_TYPELEN] = '\0';
1216 }
1217 }
1218 return (error);
1219 }
1220
1221 #ifdef ASMC_DEBUG
1222 /*
1223 * Raw SMC key access sysctls - enables reading/writing any SMC key by name
1224 * Usage:
1225 * sysctl dev.asmc.0.raw.key=TC0P # Set key, auto-detects length
1226 * sysctl dev.asmc.0.raw.value # Read current value (hex bytes)
1227 * sysctl dev.asmc.0.raw.value=01 # Write new value
1228 */
1229 static int
asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS)1230 asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS)
1231 {
1232 device_t dev = (device_t) arg1;
1233 struct asmc_softc *sc = device_get_softc(dev);
1234 char newkey[ASMC_KEYLEN + 1];
1235 uint8_t keylen;
1236 int error;
1237
1238 strlcpy(newkey, sc->sc_rawkey, sizeof(newkey));
1239 error = sysctl_handle_string(oidp, newkey, sizeof(newkey), req);
1240 if (error || req->newptr == NULL)
1241 return (error);
1242
1243 if (strlen(newkey) != ASMC_KEYLEN)
1244 return (EINVAL);
1245
1246 /* Get key info to auto-detect length and type */
1247 if (asmc_key_getinfo(dev, newkey, &keylen, sc->sc_rawtype) != 0)
1248 return (ENOENT);
1249
1250 if (keylen > ASMC_MAXVAL)
1251 keylen = ASMC_MAXVAL;
1252
1253 strlcpy(sc->sc_rawkey, newkey, sizeof(sc->sc_rawkey));
1254 sc->sc_rawlen = keylen;
1255 memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval));
1256
1257 /* Read the key value */
1258 asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen);
1259
1260 return (0);
1261 }
1262
1263 static int
asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS)1264 asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS)
1265 {
1266 device_t dev = (device_t) arg1;
1267 struct asmc_softc *sc = device_get_softc(dev);
1268 char hexbuf[ASMC_MAXVAL * 2 + 1];
1269 int error, i;
1270
1271 /* Refresh from SMC if a key has been selected. */
1272 if (sc->sc_rawkey[0] != '\0') {
1273 asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval,
1274 sc->sc_rawlen > 0 ? sc->sc_rawlen : ASMC_MAXVAL);
1275 }
1276
1277 /* Format as hex string */
1278 for (i = 0; i < sc->sc_rawlen && i < ASMC_MAXVAL; i++)
1279 snprintf(hexbuf + i * 2, 3, "%02x", sc->sc_rawval[i]);
1280 hexbuf[i * 2] = '\0';
1281
1282 error = sysctl_handle_string(oidp, hexbuf, sizeof(hexbuf), req);
1283 if (error || req->newptr == NULL)
1284 return (error);
1285
1286 /* Reject writes until a key is selected via raw.key. */
1287 if (sc->sc_rawkey[0] == '\0')
1288 return (EINVAL);
1289
1290 memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval));
1291 for (i = 0; i < sc->sc_rawlen && hexbuf[i*2] && hexbuf[i*2+1]; i++) {
1292 unsigned int val;
1293 char tmp[3] = { hexbuf[i*2], hexbuf[i*2+1], 0 };
1294 if (sscanf(tmp, "%02x", &val) == 1)
1295 sc->sc_rawval[i] = (uint8_t)val;
1296 }
1297
1298 if (asmc_key_write(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen) != 0)
1299 return (EIO);
1300
1301 return (0);
1302 }
1303
1304 static int
asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS)1305 asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS)
1306 {
1307 device_t dev = (device_t) arg1;
1308 struct asmc_softc *sc = device_get_softc(dev);
1309
1310 return (sysctl_handle_8(oidp, &sc->sc_rawlen, 0, req));
1311 }
1312
1313 static int
asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS)1314 asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS)
1315 {
1316 device_t dev = (device_t) arg1;
1317 struct asmc_softc *sc = device_get_softc(dev);
1318
1319 return (sysctl_handle_string(oidp, sc->sc_rawtype,
1320 sizeof(sc->sc_rawtype), req));
1321 }
1322 #endif
1323
1324 /*
1325 * Convert signed fixed-point SMC values to milli-units.
1326 * Format "spXY" means signed with X integer bits and Y fraction bits.
1327 */
1328 static int
asmc_sp78_to_milli(const uint8_t * buf)1329 asmc_sp78_to_milli(const uint8_t *buf)
1330 {
1331 int16_t val = (int16_t)be16dec(buf);
1332
1333 return ((int)val * 1000) / 256;
1334 }
1335
1336 static int
asmc_sp87_to_milli(const uint8_t * buf)1337 asmc_sp87_to_milli(const uint8_t *buf)
1338 {
1339 int16_t val = (int16_t)be16dec(buf);
1340
1341 return ((int)val * 1000) / 128;
1342 }
1343
1344 static int
asmc_sp4b_to_milli(const uint8_t * buf)1345 asmc_sp4b_to_milli(const uint8_t *buf)
1346 {
1347 int16_t val = (int16_t)be16dec(buf);
1348
1349 return ((int)val * 1000) / 2048;
1350 }
1351
1352 static int
asmc_sp5a_to_milli(const uint8_t * buf)1353 asmc_sp5a_to_milli(const uint8_t *buf)
1354 {
1355 int16_t val = (int16_t)be16dec(buf);
1356
1357 return ((int)val * 1000) / 1024;
1358 }
1359
1360 static int
asmc_sp69_to_milli(const uint8_t * buf)1361 asmc_sp69_to_milli(const uint8_t *buf)
1362 {
1363 int16_t val = (int16_t)be16dec(buf);
1364
1365 return ((int)val * 1000) / 512;
1366 }
1367
1368 static int
asmc_sp96_to_milli(const uint8_t * buf)1369 asmc_sp96_to_milli(const uint8_t *buf)
1370 {
1371 int16_t val = (int16_t)be16dec(buf);
1372
1373 return ((int)val * 1000) / 64;
1374 }
1375
1376 static int
asmc_sp2d_to_milli(const uint8_t * buf)1377 asmc_sp2d_to_milli(const uint8_t *buf)
1378 {
1379 int16_t val = (int16_t)be16dec(buf);
1380
1381 return ((int)val * 1000) / 8192;
1382 }
1383
1384 static bool
asmc_sensor_type_supported(const char * type)1385 asmc_sensor_type_supported(const char *type)
1386 {
1387
1388 return (strncmp(type, "sp78", 4) == 0 ||
1389 strncmp(type, "sp87", 4) == 0 ||
1390 strncmp(type, "sp4b", 4) == 0 ||
1391 strncmp(type, "sp5a", 4) == 0 ||
1392 strncmp(type, "sp69", 4) == 0 ||
1393 strncmp(type, "sp96", 4) == 0 ||
1394 strncmp(type, "sp2d", 4) == 0 ||
1395 strncmp(type, "ui16", 4) == 0);
1396 }
1397
1398 /*
1399 * Generic sensor value reader with automatic type conversion.
1400 * Reads an SMC key, detects its type, and converts to millivalue.
1401 */
1402 static int
asmc_sensor_read(device_t dev,const char * key,int * millivalue)1403 asmc_sensor_read(device_t dev, const char *key, int *millivalue)
1404 {
1405 uint8_t buf[2];
1406 char type[ASMC_TYPELEN + 1];
1407 uint8_t len;
1408 int error;
1409
1410 error = asmc_key_getinfo(dev, key, &len, type);
1411 if (error != 0)
1412 return (error);
1413
1414 if (len != 2) {
1415 if (bootverbose)
1416 device_printf(dev,
1417 "%s: key %s unexpected length %d\n",
1418 __func__, key, len);
1419 return (ENXIO);
1420 }
1421
1422 error = asmc_key_read(dev, key, buf, sizeof(buf));
1423 if (error != 0)
1424 return (error);
1425
1426 if (strncmp(type, "sp78", 4) == 0) {
1427 *millivalue = asmc_sp78_to_milli(buf);
1428 } else if (strncmp(type, "sp87", 4) == 0) {
1429 *millivalue = asmc_sp87_to_milli(buf);
1430 } else if (strncmp(type, "sp4b", 4) == 0) {
1431 *millivalue = asmc_sp4b_to_milli(buf);
1432 } else if (strncmp(type, "sp5a", 4) == 0) {
1433 *millivalue = asmc_sp5a_to_milli(buf);
1434 } else if (strncmp(type, "sp69", 4) == 0) {
1435 *millivalue = asmc_sp69_to_milli(buf);
1436 } else if (strncmp(type, "sp96", 4) == 0) {
1437 *millivalue = asmc_sp96_to_milli(buf);
1438 } else if (strncmp(type, "sp2d", 4) == 0) {
1439 *millivalue = asmc_sp2d_to_milli(buf);
1440 } else if (strncmp(type, "ui16", 4) == 0) {
1441 *millivalue = be16dec(buf);
1442 } else {
1443 if (bootverbose)
1444 device_printf(dev,
1445 "%s: unknown type '%s' for key %s\n",
1446 __func__, type, key);
1447 return (ENXIO);
1448 }
1449
1450 return (0);
1451 }
1452
1453 /*
1454 * Generic sensor sysctl handler for voltage/current/power/light sensors.
1455 * arg2 encodes: sensor_type (high byte) | sensor_index (low byte)
1456 * Sensor types: 'V'=voltage, 'I'=current, 'P'=power, 'L'=light
1457 */
1458 static int
asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS)1459 asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS)
1460 {
1461 device_t dev = (device_t) arg1;
1462 struct asmc_softc *sc = device_get_softc(dev);
1463 int error, val;
1464 int sensor_type = (arg2 >> 8) & 0xFF;
1465 int sensor_idx = arg2 & 0xFF;
1466 const char *key = NULL;
1467
1468 /* Select sensor based on type and index */
1469 switch (sensor_type) {
1470 case 'V': /* Voltage */
1471 if (sensor_idx < sc->sc_voltage_count)
1472 key = sc->sc_voltage_sensors[sensor_idx];
1473 break;
1474 case 'I': /* Current */
1475 if (sensor_idx < sc->sc_current_count)
1476 key = sc->sc_current_sensors[sensor_idx];
1477 break;
1478 case 'P': /* Power */
1479 if (sensor_idx < sc->sc_power_count)
1480 key = sc->sc_power_sensors[sensor_idx];
1481 break;
1482 case 'L': /* Light */
1483 if (sensor_idx < sc->sc_light_count)
1484 key = sc->sc_light_sensors[sensor_idx];
1485 break;
1486 default:
1487 return (EINVAL);
1488 }
1489
1490 if (key == NULL)
1491 return (ENOENT);
1492
1493 error = asmc_sensor_read(dev, key, &val);
1494 if (error != 0)
1495 return (error);
1496
1497 return (sysctl_handle_int(oidp, &val, 0, req));
1498 }
1499
1500 /*
1501 * Scan a range of SMC key indices, adding matching sensors.
1502 * Only considers 2-byte keys with a supported type.
1503 */
1504 static void
asmc_scan_sensor_range(device_t dev,unsigned int start,unsigned int end,char prefix,int * countp,char ** sensors,int maxcount)1505 asmc_scan_sensor_range(device_t dev, unsigned int start,
1506 unsigned int end, char prefix, int *countp, char **sensors,
1507 int maxcount)
1508 {
1509 char key[ASMC_KEYLEN + 1];
1510 char type[ASMC_TYPELEN + 1];
1511 uint8_t len;
1512 unsigned int i;
1513 char *sensor_key;
1514
1515 for (i = start; i < end; i++) {
1516 if (asmc_key_dump_by_index(dev, i, key, type, &len))
1517 continue;
1518 if (key[0] != prefix || len != 2)
1519 continue;
1520 if (!asmc_sensor_type_supported(type))
1521 continue;
1522 if (*countp >= maxcount)
1523 break;
1524 sensor_key = malloc(ASMC_KEYLEN + 1,
1525 M_DEVBUF, M_WAITOK);
1526 memcpy(sensor_key, key, ASMC_KEYLEN + 1);
1527 sensors[(*countp)++] = sensor_key;
1528 }
1529 }
1530
1531 static int
asmc_detect_sensors(device_t dev)1532 asmc_detect_sensors(device_t dev)
1533 {
1534 struct asmc_softc *sc = device_get_softc(dev);
1535 struct sysctl_ctx_list *sysctlctx;
1536 struct sysctl_oid *tree_node;
1537 char key[ASMC_KEYLEN + 1];
1538 char type[ASMC_TYPELEN + 1];
1539 uint8_t len;
1540 unsigned int start, end, i;
1541 int error;
1542 char *sensor_key;
1543
1544 sc->sc_voltage_count = 0;
1545 sc->sc_current_count = 0;
1546 sc->sc_power_count = 0;
1547 sc->sc_light_count = 0;
1548 sc->sc_temp_count = 0;
1549
1550 if (sc->sc_nkeys == 0)
1551 return (0);
1552
1553 /*
1554 * Temperature sensors: binary search for T..U range,
1555 * then filter by type sp78.
1556 */
1557 error = asmc_key_search(dev, "T\0\0\0", &start);
1558 if (error == 0)
1559 error = asmc_key_search(dev, "U\0\0\0", &end);
1560 if (error == 0) {
1561 for (i = start; i < end; i++) {
1562 if (asmc_key_dump_by_index(dev, i,
1563 key, type, &len))
1564 continue;
1565 if (len != 2 ||
1566 strncmp(type, "sp78", 4) != 0)
1567 continue;
1568 if (sc->sc_temp_count >= ASMC_TEMP_MAX)
1569 break;
1570 sensor_key = malloc(ASMC_KEYLEN + 1,
1571 M_DEVBUF, M_WAITOK);
1572 memcpy(sensor_key, key, ASMC_KEYLEN + 1);
1573 sc->sc_temp_sensors[sc->sc_temp_count++] =
1574 sensor_key;
1575 }
1576 }
1577
1578 /* Voltage sensors: V..W range */
1579 error = asmc_key_search(dev, "V\0\0\0", &start);
1580 if (error == 0)
1581 error = asmc_key_search(dev, "W\0\0\0", &end);
1582 if (error == 0)
1583 asmc_scan_sensor_range(dev, start, end, 'V',
1584 &sc->sc_voltage_count, sc->sc_voltage_sensors,
1585 ASMC_MAX_SENSORS);
1586
1587 /* Current sensors: I..J range */
1588 error = asmc_key_search(dev, "I\0\0\0", &start);
1589 if (error == 0)
1590 error = asmc_key_search(dev, "J\0\0\0", &end);
1591 if (error == 0)
1592 asmc_scan_sensor_range(dev, start, end, 'I',
1593 &sc->sc_current_count, sc->sc_current_sensors,
1594 ASMC_MAX_SENSORS);
1595
1596 /* Power sensors: P..Q range */
1597 error = asmc_key_search(dev, "P\0\0\0", &start);
1598 if (error == 0)
1599 error = asmc_key_search(dev, "Q\0\0\0", &end);
1600 if (error == 0)
1601 asmc_scan_sensor_range(dev, start, end, 'P',
1602 &sc->sc_power_count, sc->sc_power_sensors,
1603 ASMC_MAX_SENSORS);
1604
1605 /* Ambient light sensors: AL* in A..B range */
1606 error = asmc_key_search(dev, "A\0\0\0", &start);
1607 if (error == 0)
1608 error = asmc_key_search(dev, "B\0\0\0", &end);
1609 if (error == 0) {
1610 for (i = start; i < end; i++) {
1611 if (asmc_key_dump_by_index(dev, i,
1612 key, type, &len))
1613 continue;
1614 if (key[0] != 'A' || key[1] != 'L' ||
1615 (key[2] != 'V' && key[2] != 'S') ||
1616 len != 2)
1617 continue;
1618 if (!asmc_sensor_type_supported(type))
1619 continue;
1620 if (sc->sc_light_count >= ASMC_MAX_SENSORS)
1621 break;
1622 sensor_key = malloc(ASMC_KEYLEN + 1,
1623 M_DEVBUF, M_WAITOK);
1624 memcpy(sensor_key, key, ASMC_KEYLEN + 1);
1625 sc->sc_light_sensors[sc->sc_light_count++] =
1626 sensor_key;
1627 }
1628 }
1629
1630 if (bootverbose)
1631 device_printf(dev,
1632 "detected %d temp, %d voltage, %d current, "
1633 "%d power, %d light sensors\n",
1634 sc->sc_temp_count, sc->sc_voltage_count,
1635 sc->sc_current_count,
1636 sc->sc_power_count, sc->sc_light_count);
1637
1638 /* Register sysctls for detected sensors */
1639 sysctlctx = device_get_sysctl_ctx(dev);
1640
1641 /* Voltage sensors */
1642 if (sc->sc_voltage_count > 0) {
1643 tree_node = SYSCTL_ADD_NODE(sysctlctx,
1644 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1645 "voltage", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Voltage sensors (millivolts)");
1646
1647 for (i = 0; i < sc->sc_voltage_count; i++) {
1648 SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1649 OID_AUTO, sc->sc_voltage_sensors[i],
1650 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1651 dev, ('V' << 8) | i, asmc_sensor_sysctl, "I",
1652 "Voltage sensor (millivolts)");
1653 }
1654 }
1655
1656 /* Current sensors */
1657 if (sc->sc_current_count > 0) {
1658 tree_node = SYSCTL_ADD_NODE(sysctlctx,
1659 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1660 "current", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Current sensors (milliamps)");
1661
1662 for (i = 0; i < sc->sc_current_count; i++) {
1663 SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1664 OID_AUTO, sc->sc_current_sensors[i],
1665 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1666 dev, ('I' << 8) | i, asmc_sensor_sysctl, "I",
1667 "Current sensor (milliamps)");
1668 }
1669 }
1670
1671 /* Power sensors */
1672 if (sc->sc_power_count > 0) {
1673 tree_node = SYSCTL_ADD_NODE(sysctlctx,
1674 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1675 "power", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Power sensors (milliwatts)");
1676
1677 for (i = 0; i < sc->sc_power_count; i++) {
1678 SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1679 OID_AUTO, sc->sc_power_sensors[i],
1680 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1681 dev, ('P' << 8) | i, asmc_sensor_sysctl, "I",
1682 "Power sensor (milliwatts)");
1683 }
1684 }
1685
1686 /* Ambient light sensors */
1687 if (sc->sc_light_count > 0) {
1688 tree_node = SYSCTL_ADD_NODE(sysctlctx,
1689 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1690 "ambient", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Ambient light sensors");
1691
1692 for (i = 0; i < sc->sc_light_count; i++) {
1693 SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
1694 OID_AUTO, sc->sc_light_sensors[i],
1695 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
1696 dev, ('L' << 8) | i, asmc_sensor_sysctl, "I",
1697 "Light sensor value");
1698 }
1699 }
1700
1701 return (0);
1702 }
1703
1704 /*
1705 * Helper function to get key info by index (for sensor detection).
1706 */
1707 static int
asmc_key_dump_by_index(device_t dev,int index,char * key_out,char * type_out,uint8_t * len_out)1708 asmc_key_dump_by_index(device_t dev, int index, char *key_out,
1709 char *type_out, uint8_t *len_out)
1710 {
1711 struct asmc_softc *sc = device_get_softc(dev);
1712 uint8_t index_buf[ASMC_KEYLEN];
1713 uint8_t key_buf[ASMC_KEYLEN];
1714 uint8_t info_buf[ASMC_KEYINFO_RESPLEN];
1715 int error = ENXIO, try = 0;
1716 int i;
1717
1718 mtx_lock_spin(&sc->sc_mtx);
1719
1720 index_buf[0] = (index >> 24) & 0xff;
1721 index_buf[1] = (index >> 16) & 0xff;
1722 index_buf[2] = (index >> 8) & 0xff;
1723 index_buf[3] = index & 0xff;
1724
1725 begin:
1726 if (asmc_command(dev, ASMC_CMDGETBYINDEX))
1727 goto out;
1728
1729 for (i = 0; i < ASMC_KEYLEN; i++) {
1730 ASMC_DATAPORT_WRITE(sc, index_buf[i]);
1731 if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1732 goto out;
1733 }
1734
1735 ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN);
1736
1737 for (i = 0; i < ASMC_KEYLEN; i++) {
1738 if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1739 goto out;
1740 key_buf[i] = ASMC_DATAPORT_READ(sc);
1741 }
1742
1743 if (asmc_command(dev, ASMC_CMDGETINFO))
1744 goto out;
1745
1746 for (i = 0; i < ASMC_KEYLEN; i++) {
1747 ASMC_DATAPORT_WRITE(sc, key_buf[i]);
1748 if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
1749 goto out;
1750 }
1751
1752 ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
1753
1754 for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
1755 if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
1756 goto out;
1757 info_buf[i] = ASMC_DATAPORT_READ(sc);
1758 }
1759
1760 memcpy(key_out, key_buf, ASMC_KEYLEN);
1761 key_out[ASMC_KEYLEN] = '\0';
1762 *len_out = info_buf[0];
1763 memcpy(type_out, &info_buf[1], ASMC_TYPELEN);
1764 type_out[ASMC_TYPELEN] = '\0';
1765 error = 0;
1766
1767 out:
1768 if (error) {
1769 if (++try < ASMC_MAXRETRIES)
1770 goto begin;
1771 }
1772
1773 mtx_unlock_spin(&sc->sc_mtx);
1774 return (error);
1775 }
1776
1777 /*
1778 * Binary search for the first key index >= prefix.
1779 * SMC keys are sorted, so this finds the lower bound efficiently.
1780 */
1781 static int
asmc_key_search(device_t dev,const char * prefix,unsigned int * idx)1782 asmc_key_search(device_t dev, const char *prefix, unsigned int *idx)
1783 {
1784 struct asmc_softc *sc = device_get_softc(dev);
1785 unsigned int lo, hi, mid;
1786 char key[ASMC_KEYLEN + 1];
1787 char type[ASMC_TYPELEN + 1];
1788 uint8_t len;
1789 int error;
1790
1791 lo = 0;
1792 hi = sc->sc_nkeys;
1793 while (lo < hi) {
1794 mid = lo + (hi - lo) / 2;
1795 error = asmc_key_dump_by_index(dev, mid,
1796 key, type, &len);
1797 if (error != 0)
1798 return (error);
1799 if (strncmp(key, prefix, ASMC_KEYLEN) < 0)
1800 lo = mid + 1;
1801 else
1802 hi = mid;
1803 }
1804 *idx = lo;
1805 return (0);
1806 }
1807
1808 static int
asmc_key_write(device_t dev,const char * key,uint8_t * buf,uint8_t len)1809 asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
1810 {
1811 int i, error = -1, try = 0;
1812 struct asmc_softc *sc = device_get_softc(dev);
1813
1814 mtx_lock_spin(&sc->sc_mtx);
1815
1816 begin:
1817 ASMC_DPRINTF(("cmd port: cmd write\n"));
1818 if (asmc_command(dev, ASMC_CMDWRITE))
1819 goto out;
1820
1821 ASMC_DPRINTF(("data port: key\n"));
1822 for (i = 0; i < 4; i++) {
1823 ASMC_DATAPORT_WRITE(sc, key[i]);
1824 if (asmc_wait(dev, 0x04))
1825 goto out;
1826 }
1827 ASMC_DPRINTF(("data port: length\n"));
1828 ASMC_DATAPORT_WRITE(sc, len);
1829
1830 ASMC_DPRINTF(("data port: buffer\n"));
1831 for (i = 0; i < len; i++) {
1832 if (asmc_wait(dev, 0x04))
1833 goto out;
1834 ASMC_DATAPORT_WRITE(sc, buf[i]);
1835 }
1836
1837 error = 0;
1838 out:
1839 if (error) {
1840 if (++try < 10)
1841 goto begin;
1842 device_printf(dev, "%s for key %s failed %d times, giving up\n",
1843 __func__, key, try);
1844 }
1845
1846 mtx_unlock_spin(&sc->sc_mtx);
1847
1848 return (error);
1849 }
1850
1851 /*
1852 * Fan control functions.
1853 */
1854 static int
asmc_fan_count(device_t dev)1855 asmc_fan_count(device_t dev)
1856 {
1857 uint8_t buf[1];
1858
1859 if (asmc_key_read(dev, ASMC_KEY_FANCOUNT, buf, sizeof(buf)) != 0)
1860 return (-1);
1861
1862 return (buf[0]);
1863 }
1864
1865 static int
asmc_fan_getvalue(device_t dev,const char * key,int fan)1866 asmc_fan_getvalue(device_t dev, const char *key, int fan)
1867 {
1868 int speed;
1869 uint8_t buf[2];
1870 char fankey[5];
1871
1872 snprintf(fankey, sizeof(fankey), key, fan);
1873 if (asmc_key_read(dev, fankey, buf, sizeof(buf)) != 0)
1874 return (-1);
1875 speed = (buf[0] << 6) | (buf[1] >> 2);
1876
1877 return (speed);
1878 }
1879
1880 static char *
asmc_fan_getstring(device_t dev,const char * key,int fan,uint8_t * buf,uint8_t buflen)1881 asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf,
1882 uint8_t buflen)
1883 {
1884 char fankey[5];
1885 char *desc;
1886
1887 snprintf(fankey, sizeof(fankey), key, fan);
1888 if (asmc_key_read(dev, fankey, buf, buflen) != 0)
1889 return (NULL);
1890 desc = buf + 4;
1891
1892 return (desc);
1893 }
1894
1895 static int
asmc_fan_setvalue(device_t dev,const char * key,int fan,int speed)1896 asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed)
1897 {
1898 uint8_t buf[2];
1899 char fankey[5];
1900
1901 speed *= 4;
1902
1903 buf[0] = speed >> 8;
1904 buf[1] = speed;
1905
1906 snprintf(fankey, sizeof(fankey), key, fan);
1907 if (asmc_key_write(dev, fankey, buf, sizeof(buf)) < 0)
1908 return (-1);
1909
1910 return (0);
1911 }
1912
1913 static int
asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)1914 asmc_mb_sysctl_fanspeed(SYSCTL_HANDLER_ARGS)
1915 {
1916 device_t dev = (device_t)arg1;
1917 int fan = arg2;
1918 int error;
1919 int32_t v;
1920
1921 v = asmc_fan_getvalue(dev, ASMC_KEY_FANSPEED, fan);
1922 error = sysctl_handle_int(oidp, &v, 0, req);
1923
1924 return (error);
1925 }
1926
1927 static int
asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)1928 asmc_mb_sysctl_fanid(SYSCTL_HANDLER_ARGS)
1929 {
1930 uint8_t buf[16];
1931 device_t dev = (device_t)arg1;
1932 int fan = arg2;
1933 int error = true;
1934 char *desc;
1935
1936 desc = asmc_fan_getstring(dev, ASMC_KEY_FANID, fan, buf, sizeof(buf));
1937
1938 if (desc != NULL)
1939 error = sysctl_handle_string(oidp, desc, 0, req);
1940
1941 return (error);
1942 }
1943
1944 static int
asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)1945 asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS)
1946 {
1947 device_t dev = (device_t)arg1;
1948 int fan = arg2;
1949 int error;
1950 int32_t v;
1951
1952 v = asmc_fan_getvalue(dev, ASMC_KEY_FANSAFESPEED, fan);
1953 error = sysctl_handle_int(oidp, &v, 0, req);
1954
1955 return (error);
1956 }
1957
1958 static int
asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)1959 asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS)
1960 {
1961 device_t dev = (device_t)arg1;
1962 int fan = arg2;
1963 int error;
1964 int32_t v;
1965
1966 v = asmc_fan_getvalue(dev, ASMC_KEY_FANMINSPEED, fan);
1967 error = sysctl_handle_int(oidp, &v, 0, req);
1968
1969 if (error == 0 && req->newptr != NULL) {
1970 unsigned int newspeed = v;
1971 asmc_fan_setvalue(dev, ASMC_KEY_FANMINSPEED, fan, newspeed);
1972 }
1973
1974 return (error);
1975 }
1976
1977 static int
asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)1978 asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS)
1979 {
1980 device_t dev = (device_t)arg1;
1981 int fan = arg2;
1982 int error;
1983 int32_t v;
1984
1985 v = asmc_fan_getvalue(dev, ASMC_KEY_FANMAXSPEED, fan);
1986 error = sysctl_handle_int(oidp, &v, 0, req);
1987
1988 if (error == 0 && req->newptr != NULL) {
1989 unsigned int newspeed = v;
1990 asmc_fan_setvalue(dev, ASMC_KEY_FANMAXSPEED, fan, newspeed);
1991 }
1992
1993 return (error);
1994 }
1995
1996 static int
asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)1997 asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
1998 {
1999 device_t dev = (device_t)arg1;
2000 int fan = arg2;
2001 int error;
2002 int32_t v;
2003
2004 v = asmc_fan_getvalue(dev, ASMC_KEY_FANTARGETSPEED, fan);
2005 error = sysctl_handle_int(oidp, &v, 0, req);
2006
2007 if (error == 0 && req->newptr != NULL) {
2008 unsigned int newspeed = v;
2009 asmc_fan_setvalue(dev, ASMC_KEY_FANTARGETSPEED, fan, newspeed);
2010 }
2011
2012 return (error);
2013 }
2014
2015 static int
asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS)2016 asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS)
2017 {
2018 device_t dev = (device_t)arg1;
2019 int fan = arg2;
2020 int error;
2021 int32_t v;
2022 uint8_t buf[2];
2023 uint16_t val;
2024
2025 /* Read current FS! bitmask (asmc_key_read locks internally) */
2026 error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf));
2027 if (error != 0)
2028 return (error);
2029
2030 /* Extract manual bit for this fan (big-endian) */
2031 val = (buf[0] << 8) | buf[1];
2032 v = (val >> fan) & 0x01;
2033
2034 /* Let sysctl handle the value */
2035 error = sysctl_handle_int(oidp, &v, 0, req);
2036
2037 if (error == 0 && req->newptr != NULL) {
2038 /* Validate input (0 = auto, 1 = manual) */
2039 if (v != 0 && v != 1)
2040 return (EINVAL);
2041 /* Read-modify-write of FS! bitmask */
2042 error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf,
2043 sizeof(buf));
2044 if (error == 0) {
2045 val = (buf[0] << 8) | buf[1];
2046
2047 /* Modify single bit */
2048 if (v)
2049 val |= (1 << fan); /* Set to manual */
2050 else
2051 val &= ~(1 << fan); /* Set to auto */
2052
2053 /* Write back */
2054 buf[0] = val >> 8;
2055 buf[1] = val & 0xff;
2056 error = asmc_key_write(dev, ASMC_KEY_FANMANUAL, buf,
2057 sizeof(buf));
2058 }
2059 }
2060
2061 return (error);
2062 }
2063
2064 /*
2065 * Temperature functions.
2066 */
2067 static int
asmc_temp_getvalue(device_t dev,const char * key)2068 asmc_temp_getvalue(device_t dev, const char *key)
2069 {
2070 uint8_t buf[2];
2071
2072 /*
2073 * Check for invalid temperatures.
2074 */
2075 if (asmc_key_read(dev, key, buf, sizeof(buf)) != 0)
2076 return (-1);
2077
2078 return (buf[0]);
2079 }
2080
2081 static int
asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)2082 asmc_temp_sysctl(SYSCTL_HANDLER_ARGS)
2083 {
2084 device_t dev = (device_t)arg1;
2085 struct asmc_softc *sc = device_get_softc(dev);
2086 int error, val;
2087
2088 if (arg2 < 0 || arg2 >= sc->sc_temp_count)
2089 return (EINVAL);
2090
2091 val = asmc_temp_getvalue(dev, sc->sc_temp_sensors[arg2]);
2092 error = sysctl_handle_int(oidp, &val, 0, req);
2093
2094 return (error);
2095 }
2096
2097 /*
2098 * Sudden Motion Sensor functions.
2099 */
2100 static int
asmc_sms_read(device_t dev,const char * key,int16_t * val)2101 asmc_sms_read(device_t dev, const char *key, int16_t *val)
2102 {
2103 uint8_t buf[2];
2104 int error;
2105
2106 /* no need to do locking here as asmc_key_read() already does it */
2107 switch (key[3]) {
2108 case 'X':
2109 case 'Y':
2110 case 'Z':
2111 error = asmc_key_read(dev, key, buf, sizeof(buf));
2112 break;
2113 default:
2114 device_printf(dev, "%s called with invalid argument %s\n",
2115 __func__, key);
2116 error = EINVAL;
2117 goto out;
2118 }
2119 *val = ((int16_t)buf[0] << 8) | buf[1];
2120 out:
2121 return (error);
2122 }
2123
2124 static void
asmc_sms_calibrate(device_t dev)2125 asmc_sms_calibrate(device_t dev)
2126 {
2127 struct asmc_softc *sc = device_get_softc(dev);
2128
2129 asmc_sms_read(dev, ASMC_KEY_SMS_X, &sc->sms_rest_x);
2130 asmc_sms_read(dev, ASMC_KEY_SMS_Y, &sc->sms_rest_y);
2131 asmc_sms_read(dev, ASMC_KEY_SMS_Z, &sc->sms_rest_z);
2132 }
2133
2134 static int
asmc_sms_intrfast(void * arg)2135 asmc_sms_intrfast(void *arg)
2136 {
2137 uint8_t type;
2138 device_t dev = (device_t)arg;
2139 struct asmc_softc *sc = device_get_softc(dev);
2140 if (!sc->sc_sms_intr_works)
2141 return (FILTER_HANDLED);
2142
2143 mtx_lock_spin(&sc->sc_mtx);
2144 type = ASMC_INTPORT_READ(sc);
2145 mtx_unlock_spin(&sc->sc_mtx);
2146
2147 sc->sc_sms_intrtype = type;
2148 asmc_sms_printintr(dev, type);
2149
2150 /* Don't queue SMS task for ambient light interrupts */
2151 if (type == ASMC_ALSL_INT2A && sc->sc_has_alsl)
2152 return (FILTER_HANDLED);
2153
2154 taskqueue_enqueue(sc->sc_sms_tq, &sc->sc_sms_task);
2155 return (FILTER_HANDLED);
2156 }
2157
2158 static void
asmc_sms_printintr(device_t dev,uint8_t type)2159 asmc_sms_printintr(device_t dev, uint8_t type)
2160 {
2161 struct asmc_softc *sc = device_get_softc(dev);
2162
2163 switch (type) {
2164 case ASMC_SMS_INTFF:
2165 device_printf(dev, "WARNING: possible free fall!\n");
2166 break;
2167 case ASMC_SMS_INTHA:
2168 device_printf(dev, "WARNING: high acceleration detected!\n");
2169 break;
2170 case ASMC_SMS_INTSH:
2171 device_printf(dev, "WARNING: possible shock!\n");
2172 break;
2173 case ASMC_ALSL_INT2A:
2174 /*
2175 * This suppresses console and log messages for the ambient
2176 * light sensor interrupt on models that have ALSL.
2177 */
2178 if (sc->sc_has_alsl)
2179 break;
2180 /* FALLTHROUGH */
2181 default:
2182 device_printf(dev, "unknown interrupt: 0x%x\n", type);
2183 }
2184 }
2185
2186 static void
asmc_sms_task(void * arg,int pending)2187 asmc_sms_task(void *arg, int pending)
2188 {
2189 struct asmc_softc *sc = (struct asmc_softc *)arg;
2190 char notify[16];
2191 int type;
2192
2193 switch (sc->sc_sms_intrtype) {
2194 case ASMC_SMS_INTFF:
2195 type = 2;
2196 break;
2197 case ASMC_SMS_INTHA:
2198 type = 1;
2199 break;
2200 case ASMC_SMS_INTSH:
2201 type = 0;
2202 break;
2203 default:
2204 type = 255;
2205 }
2206
2207 snprintf(notify, sizeof(notify), " notify=0x%x", type);
2208 devctl_notify("ACPI", "asmc", "SMS", notify);
2209 }
2210
2211 static int
asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)2212 asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS)
2213 {
2214 device_t dev = (device_t)arg1;
2215 int error;
2216 int16_t val;
2217 int32_t v;
2218
2219 asmc_sms_read(dev, ASMC_KEY_SMS_X, &val);
2220 v = (int32_t)val;
2221 error = sysctl_handle_int(oidp, &v, 0, req);
2222
2223 return (error);
2224 }
2225
2226 static int
asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)2227 asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS)
2228 {
2229 device_t dev = (device_t)arg1;
2230 int error;
2231 int16_t val;
2232 int32_t v;
2233
2234 asmc_sms_read(dev, ASMC_KEY_SMS_Y, &val);
2235 v = (int32_t)val;
2236 error = sysctl_handle_int(oidp, &v, 0, req);
2237
2238 return (error);
2239 }
2240
2241 static int
asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)2242 asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS)
2243 {
2244 device_t dev = (device_t)arg1;
2245 int error;
2246 int16_t val;
2247 int32_t v;
2248
2249 asmc_sms_read(dev, ASMC_KEY_SMS_Z, &val);
2250 v = (int32_t)val;
2251 error = sysctl_handle_int(oidp, &v, 0, req);
2252
2253 return (error);
2254 }
2255
2256 static int
asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)2257 asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
2258 {
2259 device_t dev = (device_t)arg1;
2260 uint8_t buf[6];
2261 int error;
2262 int32_t v;
2263
2264 asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof(buf));
2265 v = buf[2];
2266 error = sysctl_handle_int(oidp, &v, 0, req);
2267
2268 return (error);
2269 }
2270
2271 static int
asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)2272 asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
2273 {
2274 device_t dev = (device_t)arg1;
2275 uint8_t buf[6];
2276 int error;
2277 int32_t v;
2278
2279 asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, sizeof(buf));
2280 v = buf[2];
2281 error = sysctl_handle_int(oidp, &v, 0, req);
2282
2283 return (error);
2284 }
2285
2286 static int
asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)2287 asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
2288 {
2289 device_t dev = (device_t)arg1;
2290 struct asmc_softc *sc = device_get_softc(dev);
2291 uint8_t buf[2];
2292 int error;
2293 int v;
2294
2295 v = light_control;
2296 error = sysctl_handle_int(oidp, &v, 0, req);
2297
2298 if (error == 0 && req->newptr != NULL) {
2299 if (v < 0 || v > 255)
2300 return (EINVAL);
2301 light_control = v;
2302 sc->sc_kbd_bkl_level = v * 100 / 255;
2303 buf[0] = light_control;
2304 buf[1] = 0x00;
2305 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf));
2306 }
2307 return (error);
2308 }
2309
2310 static int
asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS)2311 asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS)
2312 {
2313 device_t dev = (device_t)arg1;
2314 uint8_t buf[10];
2315 int error;
2316 uint32_t v;
2317
2318 asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, sizeof(buf));
2319
2320 /*
2321 * This seems to be a 32 bit big endian value from buf[6] -> buf[9].
2322 *
2323 * Extract it out manually here, then shift/clamp it.
2324 */
2325 v = be32dec(&buf[6]);
2326
2327 /*
2328 * Shift out, clamp at 255; that way it looks like the
2329 * earlier SMC firmware version responses.
2330 */
2331 v = v >> 8;
2332 if (v > 255)
2333 v = 255;
2334
2335 error = sysctl_handle_int(oidp, &v, 0, req);
2336
2337 return (error);
2338 }
2339
2340 /*
2341 * Auto power-on after AC power loss (AUPO key).
2342 * When non-zero the machine boots automatically when AC is restored
2343 * after an unclean power loss. Useful for always-on servers / home labs.
2344 */
2345 static int
asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS)2346 asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS)
2347 {
2348 device_t dev = (device_t)arg1;
2349 uint8_t aupo;
2350 int val, error;
2351
2352 if (asmc_key_read(dev, ASMC_KEY_AUPO, &aupo, 1) != 0)
2353 return (EIO);
2354
2355 val = (aupo != 0) ? 1 : 0;
2356 error = sysctl_handle_int(oidp, &val, 0, req);
2357 if (error != 0 || req->newptr == NULL)
2358 return (error);
2359
2360 aupo = (val != 0) ? 1 : 0;
2361 if (asmc_key_write(dev, ASMC_KEY_AUPO, &aupo, 1) != 0)
2362 return (EIO);
2363
2364 return (0);
2365 }
2366
2367 static int
asmc_backlight_update_status(device_t dev,struct backlight_props * props)2368 asmc_backlight_update_status(device_t dev, struct backlight_props *props)
2369 {
2370 struct asmc_softc *sc = device_get_softc(dev);
2371 uint8_t buf[2];
2372
2373 sc->sc_kbd_bkl_level = props->brightness;
2374 light_control = props->brightness * 255 / 100;
2375 buf[0] = light_control;
2376 buf[1] = 0x00;
2377 asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, sizeof(buf));
2378
2379 return (0);
2380 }
2381
2382 static int
asmc_backlight_get_status(device_t dev,struct backlight_props * props)2383 asmc_backlight_get_status(device_t dev, struct backlight_props *props)
2384 {
2385 struct asmc_softc *sc = device_get_softc(dev);
2386
2387 props->brightness = sc->sc_kbd_bkl_level;
2388 props->nlevels = 0;
2389
2390 return (0);
2391 }
2392
2393 static int
asmc_backlight_get_info(device_t dev,struct backlight_info * info)2394 asmc_backlight_get_info(device_t dev, struct backlight_info *info)
2395 {
2396 info->type = BACKLIGHT_TYPE_KEYBOARD;
2397 strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH);
2398
2399 return (0);
2400 }
2401