1 /* $NetBSD: acpi_acad.c,v 1.35 2009/08/25 10:34:08 jmcneill Exp $ */
4 * Copyright 2001 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 #define ACPI_ACAD_DEBUG
43 * ACPI AC Adapter driver.
46 #include <sys/cdefs.h>
47 __KERNEL_RCSID(0, "$NetBSD: acpi_acad.c,v 1.35 2009/08/25 10:34:08 jmcneill Exp $");
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/device.h>
52 #include <sys/mutex.h>
54 #include <dev/acpi/acpica.h>
55 #include <dev/acpi/acpireg.h>
56 #include <dev/acpi/acpivar.h>
58 #include <dev/sysmon/sysmonvar.h>
60 struct acpiacad_softc
{
61 struct acpi_devnode
*sc_node
; /* our ACPI devnode */
62 int sc_flags
; /* see below */
63 int sc_status
; /* status changed/not changed */
64 int sc_notifysent
; /* notify message sent */
66 struct sysmon_envsys
*sc_sme
;
67 struct sysmon_pswitch sc_smpsw
; /* our sysmon glue */
68 envsys_data_t sc_sensor
;
73 static const char * const acad_hid
[] = {
78 #define AACAD_F_VERBOSE 0x01 /* verbose events */
79 #define AACAD_F_AVAILABLE 0x02 /* information is available */
80 #define AACAD_F_STCHANGED 0x04 /* status changed */
82 #define AACAD_SET(sc, f) (void)((sc)->sc_flags |= (f))
83 #define AACAD_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f))
84 #define AACAD_ISSET(sc, f) ((sc)->sc_flags & (f))
86 static int acpiacad_match(device_t
, cfdata_t
, void *);
87 static void acpiacad_attach(device_t
, device_t
, void *);
89 CFATTACH_DECL_NEW(acpiacad
, sizeof(struct acpiacad_softc
),
90 acpiacad_match
, acpiacad_attach
, NULL
, NULL
);
92 static void acpiacad_get_status(void *);
93 static void acpiacad_clear_status(struct acpiacad_softc
*);
94 static void acpiacad_notify_handler(ACPI_HANDLE
, UINT32
, void *);
95 static void acpiacad_init_envsys(device_t
);
96 static void acpiacad_refresh(struct sysmon_envsys
*, envsys_data_t
*);
97 static bool acpiacad_resume(device_t
, pmf_qual_t
);
102 * Autoconfiguration `match' routine.
105 acpiacad_match(device_t parent
, cfdata_t match
, void *aux
)
107 struct acpi_attach_args
*aa
= aux
;
109 if (aa
->aa_node
->ad_type
!= ACPI_TYPE_DEVICE
)
112 return acpi_match_hid(aa
->aa_node
->ad_devinfo
, acad_hid
);
118 * Autoconfiguration `attach' routine.
121 acpiacad_attach(device_t parent
, device_t self
, void *aux
)
123 struct acpiacad_softc
*sc
= device_private(self
);
124 struct acpi_attach_args
*aa
= aux
;
127 aprint_naive(": ACPI AC Adapter\n");
128 aprint_normal(": ACPI AC Adapter\n");
130 sc
->sc_node
= aa
->aa_node
;
131 mutex_init(&sc
->sc_mtx
, MUTEX_DEFAULT
, IPL_NONE
);
133 sc
->sc_smpsw
.smpsw_name
= device_xname(self
);
134 sc
->sc_smpsw
.smpsw_type
= PSWITCH_TYPE_ACADAPTER
;
135 if (sysmon_pswitch_register(&sc
->sc_smpsw
) != 0) {
136 aprint_error_dev(self
, "unable to register with sysmon\n");
142 rv
= AcpiInstallNotifyHandler(sc
->sc_node
->ad_handle
,
143 ACPI_ALL_NOTIFY
, acpiacad_notify_handler
, self
);
144 if (ACPI_FAILURE(rv
)) {
145 aprint_error_dev(self
, "unable to register DEVICE and SYSTEM "
146 "NOTIFY handler: %s\n", AcpiFormatException(rv
));
150 #ifdef ACPI_ACAD_DEBUG
151 /* Display the current state. */
152 sc
->sc_flags
= AACAD_F_VERBOSE
;
155 if (!pmf_device_register(self
, NULL
, acpiacad_resume
))
156 aprint_error_dev(self
, "couldn't establish power handler\n");
158 acpiacad_init_envsys(self
);
164 * Clear status after resuming to fetch new status.
167 acpiacad_resume(device_t dv
, pmf_qual_t qual
)
169 struct acpiacad_softc
*sc
= device_private(dv
);
171 acpiacad_clear_status(sc
);
176 * acpiacad_get_status:
178 * Get, and possibly display, the current AC line status.
181 acpiacad_get_status(void *arg
)
184 struct acpiacad_softc
*sc
= device_private(dv
);
188 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
, "_PSR", &status
);
189 if (ACPI_FAILURE(rv
))
192 mutex_enter(&sc
->sc_mtx
);
193 sc
->sc_notifysent
= 0;
194 if (sc
->sc_status
!= status
) {
195 sc
->sc_status
= status
;
197 sc
->sc_sensor
.value_cur
= 1;
199 sc
->sc_sensor
.value_cur
= 0;
200 AACAD_SET(sc
, AACAD_F_STCHANGED
);
203 sc
->sc_sensor
.state
= ENVSYS_SVALID
;
204 AACAD_SET(sc
, AACAD_F_AVAILABLE
);
206 * If status has changed, send the event.
208 * PSWITCH_EVENT_RELEASED : AC offline
209 * PSWITCH_EVENT_PRESSED : AC online
211 if (AACAD_ISSET(sc
, AACAD_F_STCHANGED
)) {
212 sysmon_pswitch_event(&sc
->sc_smpsw
, status
?
213 PSWITCH_EVENT_PRESSED
: PSWITCH_EVENT_RELEASED
);
214 if (AACAD_ISSET(sc
, AACAD_F_VERBOSE
))
215 aprint_verbose_dev(dv
, "AC adapter %sconnected\n",
216 status
== 0 ? "not " : "");
218 mutex_exit(&sc
->sc_mtx
);
225 acpiacad_clear_status(struct acpiacad_softc
*sc
)
227 sc
->sc_sensor
.state
= ENVSYS_SINVALID
;
228 AACAD_CLEAR(sc
, AACAD_F_AVAILABLE
);
229 AACAD_CLEAR(sc
, AACAD_F_STCHANGED
);
233 * acpiacad_notify_handler:
235 * Callback from ACPI interrupt handler to notify us of an event.
238 acpiacad_notify_handler(ACPI_HANDLE handle
, UINT32 notify
, void *context
)
240 device_t dv
= context
;
241 struct acpiacad_softc
*sc
= device_private(dv
);
246 * XXX So, BusCheck is not exactly what I would expect,
247 * but at least my IBM T21 sends it on AC adapter status
248 * change. --thorpej@wasabisystems.com
251 * XXX My Acer TravelMate 291 sends DeviceCheck on AC
252 * adapter status change.
253 * --rpaulo@NetBSD.org
256 * XXX Sony VAIO VGN-N250E sends BatteryInformationChanged on AC
257 * adapter status change.
258 * --jmcneill@NetBSD.org
260 case ACPI_NOTIFY_BusCheck
:
261 case ACPI_NOTIFY_DeviceCheck
:
262 case ACPI_NOTIFY_PowerSourceStatusChanged
:
263 case ACPI_NOTIFY_BatteryInformationChanged
:
264 mutex_enter(&sc
->sc_mtx
);
265 acpiacad_clear_status(sc
);
266 mutex_exit(&sc
->sc_mtx
);
267 if (sc
->sc_status
== -1 || !sc
->sc_notifysent
) {
268 rv
= AcpiOsExecute(OSL_NOTIFY_HANDLER
,
269 acpiacad_get_status
, dv
);
270 if (ACPI_FAILURE(rv
))
272 "unable to queue status check: %s\n",
273 AcpiFormatException(rv
));
274 sc
->sc_notifysent
= 1;
275 #ifdef ACPI_ACAD_DEBUG
276 aprint_debug_dev(dv
, "received notify message: 0x%x\n",
283 aprint_error_dev(dv
, "received unknown notify message: 0x%x\n",
289 acpiacad_init_envsys(device_t dv
)
291 struct acpiacad_softc
*sc
= device_private(dv
);
293 sc
->sc_sme
= sysmon_envsys_create();
294 sc
->sc_sensor
.state
= ENVSYS_SVALID
;
295 sc
->sc_sensor
.units
= ENVSYS_INDICATOR
;
296 strlcpy(sc
->sc_sensor
.desc
, "connected", sizeof(sc
->sc_sensor
.desc
));
298 if (sysmon_envsys_sensor_attach(sc
->sc_sme
, &sc
->sc_sensor
)) {
299 aprint_error_dev(dv
, "unable to add sensor\n");
300 sysmon_envsys_destroy(sc
->sc_sme
);
304 sc
->sc_sme
->sme_name
= device_xname(dv
);
305 sc
->sc_sme
->sme_cookie
= dv
;
306 sc
->sc_sme
->sme_refresh
= acpiacad_refresh
;
307 sc
->sc_sme
->sme_class
= SME_CLASS_ACADAPTER
;
308 sc
->sc_sme
->sme_flags
= SME_INIT_REFRESH
;
310 if (sysmon_envsys_register(sc
->sc_sme
)) {
311 aprint_error_dev(dv
, "unable to register with sysmon\n");
312 sysmon_envsys_destroy(sc
->sc_sme
);
317 acpiacad_refresh(struct sysmon_envsys
*sme
, envsys_data_t
*edata
)
319 device_t dv
= sme
->sme_cookie
;
320 struct acpiacad_softc
*sc
= device_private(dv
);
322 if (!AACAD_ISSET(sc
, AACAD_F_AVAILABLE
))
323 acpiacad_get_status(dv
);