2000-12-22 14:41:55 +00:00
|
|
|
/*-
|
2005-07-23 19:36:00 +00:00
|
|
|
* Copyright (c) 2005 Nate Lawson
|
2001-06-23 10:38:25 +00:00
|
|
|
* Copyright (c) 2000 Munehiro Matsuda
|
2000-12-22 14:41:55 +00:00
|
|
|
* Copyright (c) 2000 Takanori Watanabe
|
|
|
|
* Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2000-12-22 14:41:55 +00:00
|
|
|
#include "opt_acpi.h"
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/kernel.h>
|
2004-05-30 20:08:47 +00:00
|
|
|
#include <sys/module.h>
|
2000-12-22 14:41:55 +00:00
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/ioccom.h>
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <sys/rman.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
|
2009-06-05 18:44:36 +00:00
|
|
|
#include <contrib/dev/acpica/include/acpi.h>
|
|
|
|
|
2000-12-22 14:41:55 +00:00
|
|
|
#include <dev/acpica/acpivar.h>
|
|
|
|
#include <dev/acpica/acpiio.h>
|
|
|
|
|
2011-11-07 06:44:47 +00:00
|
|
|
static MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat",
|
|
|
|
"ACPI control method battery data");
|
2001-10-30 14:24:26 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
/* Number of times to retry initialization before giving up. */
|
|
|
|
#define ACPI_CMBAT_RETRY_MAX 6
|
|
|
|
|
|
|
|
/* Check the battery once a minute. */
|
2004-08-13 06:21:44 +00:00
|
|
|
#define CMBAT_POLLRATE (60 * hz)
|
2001-10-22 18:01:37 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
/* Hooks for the ACPI CA debugging infrastructure */
|
2001-10-30 15:51:52 +00:00
|
|
|
#define _COMPONENT ACPI_BATTERY
|
2002-02-23 05:24:14 +00:00
|
|
|
ACPI_MODULE_NAME("BATTERY")
|
2001-05-29 20:13:42 +00:00
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
#define ACPI_BATTERY_BST_CHANGE 0x80
|
|
|
|
#define ACPI_BATTERY_BIF_CHANGE 0x81
|
2020-02-19 06:28:55 +00:00
|
|
|
#define ACPI_BATTERY_BIX_CHANGE ACPI_BATTERY_BIF_CHANGE
|
2000-12-24 19:12:10 +00:00
|
|
|
|
2001-10-30 15:51:52 +00:00
|
|
|
struct acpi_cmbat_softc {
|
2003-08-11 15:34:43 +00:00
|
|
|
device_t dev;
|
2005-07-23 19:36:00 +00:00
|
|
|
int flags;
|
2003-08-11 15:34:43 +00:00
|
|
|
|
2020-02-19 06:28:55 +00:00
|
|
|
struct acpi_bix bix;
|
2003-08-11 15:34:43 +00:00
|
|
|
struct acpi_bst bst;
|
|
|
|
struct timespec bst_lastupdated;
|
2001-10-30 15:51:52 +00:00
|
|
|
};
|
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_DECL(cmbat, "ACPI cmbat");
|
2003-08-11 15:34:43 +00:00
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
static int acpi_cmbat_probe(device_t dev);
|
|
|
|
static int acpi_cmbat_attach(device_t dev);
|
|
|
|
static int acpi_cmbat_detach(device_t dev);
|
|
|
|
static int acpi_cmbat_resume(device_t dev);
|
|
|
|
static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
|
|
|
|
void *context);
|
|
|
|
static int acpi_cmbat_info_expired(struct timespec *lastupdated);
|
|
|
|
static void acpi_cmbat_info_updated(struct timespec *lastupdated);
|
2005-11-23 00:57:51 +00:00
|
|
|
static void acpi_cmbat_get_bst(void *arg);
|
2020-02-19 06:28:55 +00:00
|
|
|
static void acpi_cmbat_get_bix_task(void *arg);
|
|
|
|
static void acpi_cmbat_get_bix(void *arg);
|
|
|
|
static int acpi_cmbat_bst(device_t, struct acpi_bst *);
|
|
|
|
static int acpi_cmbat_bix(device_t, void *, size_t);
|
2005-07-23 19:36:00 +00:00
|
|
|
static void acpi_cmbat_init_battery(void *arg);
|
2001-10-30 15:51:52 +00:00
|
|
|
|
2003-08-15 02:18:15 +00:00
|
|
|
static device_method_t acpi_cmbat_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_probe, acpi_cmbat_probe),
|
|
|
|
DEVMETHOD(device_attach, acpi_cmbat_attach),
|
2004-07-12 20:53:04 +00:00
|
|
|
DEVMETHOD(device_detach, acpi_cmbat_detach),
|
2003-08-15 02:18:15 +00:00
|
|
|
DEVMETHOD(device_resume, acpi_cmbat_resume),
|
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
/* ACPI battery interface */
|
2020-02-19 06:28:55 +00:00
|
|
|
DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bix),
|
2005-07-23 19:36:00 +00:00
|
|
|
DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
|
|
|
|
|
2013-01-30 18:01:20 +00:00
|
|
|
DEVMETHOD_END
|
2003-08-15 02:18:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t acpi_cmbat_driver = {
|
2005-07-23 19:36:00 +00:00
|
|
|
"battery",
|
2003-08-15 02:18:15 +00:00
|
|
|
acpi_cmbat_methods,
|
|
|
|
sizeof(struct acpi_cmbat_softc),
|
|
|
|
};
|
|
|
|
|
|
|
|
static devclass_t acpi_cmbat_devclass;
|
|
|
|
DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
|
2004-04-09 18:14:32 +00:00
|
|
|
MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1);
|
2003-08-15 02:18:15 +00:00
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
static int
|
|
|
|
acpi_cmbat_probe(device_t dev)
|
|
|
|
{
|
|
|
|
static char *cmbat_ids[] = { "PNP0C0A", NULL };
|
2018-10-26 00:05:46 +00:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (acpi_disabled("cmbat"))
|
2005-07-23 19:36:00 +00:00
|
|
|
return (ENXIO);
|
2018-10-26 00:05:46 +00:00
|
|
|
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids, NULL);
|
|
|
|
if (rv <= 0)
|
|
|
|
device_set_desc(dev, "ACPI Control Method Battery");
|
|
|
|
return (rv);
|
2005-07-23 19:36:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_cmbat_attach(device_t dev)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
ACPI_HANDLE handle;
|
|
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
handle = acpi_get_handle(dev);
|
|
|
|
sc->dev = dev;
|
|
|
|
|
|
|
|
timespecclear(&sc->bst_lastupdated);
|
|
|
|
|
|
|
|
error = acpi_battery_register(dev);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(dev, "registering battery failed\n");
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Install a system notify handler in addition to the device notify.
|
|
|
|
* Toshiba notebook uses this alternate notify for its battery.
|
|
|
|
*/
|
|
|
|
AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
|
|
|
|
acpi_cmbat_notify_handler, dev);
|
|
|
|
|
2007-03-22 18:16:43 +00:00
|
|
|
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
|
2005-07-23 19:36:00 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_cmbat_detach(device_t dev)
|
|
|
|
{
|
2006-04-15 16:10:53 +00:00
|
|
|
ACPI_HANDLE handle;
|
2005-07-23 19:36:00 +00:00
|
|
|
|
2006-04-15 16:10:53 +00:00
|
|
|
handle = acpi_get_handle(dev);
|
2006-04-15 12:31:34 +00:00
|
|
|
AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler);
|
2006-04-15 16:10:53 +00:00
|
|
|
acpi_battery_remove(dev);
|
2017-02-27 08:36:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Force any pending notification handler calls to complete by
|
|
|
|
* requesting cmbat serialisation while freeing and clearing the
|
|
|
|
* softc pointer:
|
|
|
|
*/
|
|
|
|
ACPI_SERIAL_BEGIN(cmbat);
|
|
|
|
device_set_softc(dev, NULL);
|
|
|
|
ACPI_SERIAL_END(cmbat);
|
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
acpi_cmbat_resume(device_t dev)
|
|
|
|
{
|
|
|
|
|
2007-03-22 18:16:43 +00:00
|
|
|
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
|
2005-07-23 19:36:00 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
|
|
|
|
{
|
|
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
device_t dev;
|
|
|
|
|
|
|
|
dev = (device_t)context;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
switch (notify) {
|
|
|
|
case ACPI_NOTIFY_DEVICE_CHECK:
|
|
|
|
case ACPI_BATTERY_BST_CHANGE:
|
2005-11-23 00:57:51 +00:00
|
|
|
/*
|
|
|
|
* Clear the last updated time. The next call to retrieve the
|
|
|
|
* battery status will get the new value for us.
|
|
|
|
*/
|
2005-07-23 19:36:00 +00:00
|
|
|
timespecclear(&sc->bst_lastupdated);
|
|
|
|
break;
|
|
|
|
case ACPI_NOTIFY_BUS_CHECK:
|
2020-02-19 06:28:55 +00:00
|
|
|
case ACPI_BATTERY_BIX_CHANGE:
|
2005-11-23 00:57:51 +00:00
|
|
|
/*
|
|
|
|
* Queue a callback to get the current battery info from thread
|
|
|
|
* context. It's not safe to block in a notify handler.
|
|
|
|
*/
|
2020-02-19 06:28:55 +00:00
|
|
|
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bix_task, dev);
|
2005-07-23 19:36:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
acpi_UserNotify("CMBAT", h, notify);
|
|
|
|
}
|
|
|
|
|
2003-08-15 02:18:15 +00:00
|
|
|
static int
|
2001-06-23 10:38:25 +00:00
|
|
|
acpi_cmbat_info_expired(struct timespec *lastupdated)
|
|
|
|
{
|
2003-08-11 15:34:43 +00:00
|
|
|
struct timespec curtime;
|
2001-06-23 10:38:25 +00:00
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_ASSERT(cmbat);
|
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
if (lastupdated == NULL)
|
2004-08-13 06:21:44 +00:00
|
|
|
return (TRUE);
|
2003-08-11 15:34:43 +00:00
|
|
|
if (!timespecisset(lastupdated))
|
2004-08-13 06:21:44 +00:00
|
|
|
return (TRUE);
|
2001-06-24 02:39:08 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
getnanotime(&curtime);
|
Make timespecadd(3) and friends public
The timespecadd(3) family of macros were imported from NetBSD back in
r35029. However, they were initially guarded by #ifdef _KERNEL. In the
meantime, we have grown at least 28 syscalls that use timespecs in some
way, leading many programs both inside and outside of the base system to
redefine those macros. It's better just to make the definitions public.
Our kernel currently defines two-argument versions of timespecadd and
timespecsub. NetBSD, OpenBSD, and FreeDesktop.org's libbsd, however, define
three-argument versions. Solaris also defines a three-argument version, but
only in its kernel. This revision changes our definition to match the
common three-argument version.
Bump _FreeBSD_version due to the breaking KPI change.
Discussed with: cem, jilles, ian, bde
Differential Revision: https://reviews.freebsd.org/D14725
2018-07-30 15:46:40 +00:00
|
|
|
timespecsub(&curtime, lastupdated, &curtime);
|
2003-08-11 15:34:43 +00:00
|
|
|
return (curtime.tv_sec < 0 ||
|
|
|
|
curtime.tv_sec > acpi_battery_get_info_expire());
|
2001-06-23 10:38:25 +00:00
|
|
|
}
|
|
|
|
|
2003-08-15 02:18:15 +00:00
|
|
|
static void
|
2001-06-23 10:38:25 +00:00
|
|
|
acpi_cmbat_info_updated(struct timespec *lastupdated)
|
|
|
|
{
|
2004-08-13 06:21:44 +00:00
|
|
|
|
|
|
|
ACPI_SERIAL_ASSERT(cmbat);
|
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
if (lastupdated != NULL)
|
|
|
|
getnanotime(lastupdated);
|
2001-06-23 10:38:25 +00:00
|
|
|
}
|
|
|
|
|
2000-12-22 14:41:55 +00:00
|
|
|
static void
|
2005-11-23 00:57:51 +00:00
|
|
|
acpi_cmbat_get_bst(void *arg)
|
2000-12-22 14:41:55 +00:00
|
|
|
{
|
2003-08-11 15:34:43 +00:00
|
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
ACPI_STATUS as;
|
2003-12-23 18:27:35 +00:00
|
|
|
ACPI_OBJECT *res;
|
2003-08-11 15:34:43 +00:00
|
|
|
ACPI_HANDLE h;
|
|
|
|
ACPI_BUFFER bst_buffer;
|
2005-11-23 00:57:51 +00:00
|
|
|
device_t dev;
|
2003-08-11 15:34:43 +00:00
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_ASSERT(cmbat);
|
|
|
|
|
2005-11-23 00:57:51 +00:00
|
|
|
dev = arg;
|
2003-08-11 15:34:43 +00:00
|
|
|
sc = device_get_softc(dev);
|
|
|
|
h = acpi_get_handle(dev);
|
2004-08-13 06:21:44 +00:00
|
|
|
bst_buffer.Pointer = NULL;
|
|
|
|
bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
|
2003-08-11 15:34:43 +00:00
|
|
|
|
|
|
|
if (!acpi_cmbat_info_expired(&sc->bst_lastupdated))
|
2004-08-13 06:21:44 +00:00
|
|
|
goto end;
|
2003-08-11 15:34:43 +00:00
|
|
|
|
|
|
|
as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer);
|
|
|
|
if (ACPI_FAILURE(as)) {
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2020-02-19 06:28:55 +00:00
|
|
|
"error fetching current battery status -- %s\n",
|
|
|
|
AcpiFormatException(as));
|
2003-08-11 15:34:43 +00:00
|
|
|
goto end;
|
|
|
|
}
|
2000-12-22 14:41:55 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
res = (ACPI_OBJECT *)bst_buffer.Pointer;
|
2003-12-23 18:27:35 +00:00
|
|
|
if (!ACPI_PKG_VALID(res, 4)) {
|
2003-08-11 15:34:43 +00:00
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2020-02-19 06:28:55 +00:00
|
|
|
"battery status corrupted\n");
|
2003-08-11 15:34:43 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2003-12-23 18:27:35 +00:00
|
|
|
if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0)
|
|
|
|
goto end;
|
|
|
|
if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0)
|
|
|
|
goto end;
|
|
|
|
if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0)
|
|
|
|
goto end;
|
|
|
|
if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0)
|
|
|
|
goto end;
|
2003-08-11 15:34:43 +00:00
|
|
|
acpi_cmbat_info_updated(&sc->bst_lastupdated);
|
2000-12-22 14:41:55 +00:00
|
|
|
|
2010-12-17 16:21:30 +00:00
|
|
|
/* Clear out undefined/extended bits that might be set by hardware. */
|
|
|
|
sc->bst.state &= ACPI_BATT_STAT_BST_MASK;
|
|
|
|
if ((sc->bst.state & ACPI_BATT_STAT_INVALID) == ACPI_BATT_STAT_INVALID)
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"battery reports simultaneous charging and discharging\n");
|
|
|
|
|
2004-12-20 05:03:41 +00:00
|
|
|
/* XXX If all batteries are critical, perhaps we should suspend. */
|
|
|
|
if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
|
|
|
|
if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
|
|
|
|
sc->flags |= ACPI_BATT_STAT_CRITICAL;
|
|
|
|
device_printf(dev, "critically low charge!\n");
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sc->flags &= ~ACPI_BATT_STAT_CRITICAL;
|
2004-10-11 06:18:07 +00:00
|
|
|
|
2000-12-22 14:41:55 +00:00
|
|
|
end:
|
2020-02-19 06:28:55 +00:00
|
|
|
AcpiOsFree(bst_buffer.Pointer);
|
2000-12-22 14:41:55 +00:00
|
|
|
}
|
|
|
|
|
2005-11-26 07:36:53 +00:00
|
|
|
/* XXX There should be a cleaner way to do this locking. */
|
|
|
|
static void
|
2020-02-19 06:28:55 +00:00
|
|
|
acpi_cmbat_get_bix_task(void *arg)
|
2005-11-26 07:36:53 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
ACPI_SERIAL_BEGIN(cmbat);
|
2020-02-19 06:28:55 +00:00
|
|
|
acpi_cmbat_get_bix(arg);
|
2005-11-26 07:36:53 +00:00
|
|
|
ACPI_SERIAL_END(cmbat);
|
|
|
|
}
|
|
|
|
|
2000-12-22 14:41:55 +00:00
|
|
|
static void
|
2020-02-19 06:28:55 +00:00
|
|
|
acpi_cmbat_get_bix(void *arg)
|
2000-12-22 14:41:55 +00:00
|
|
|
{
|
2003-08-11 15:34:43 +00:00
|
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
ACPI_STATUS as;
|
2003-12-23 18:27:35 +00:00
|
|
|
ACPI_OBJECT *res;
|
2003-08-11 15:34:43 +00:00
|
|
|
ACPI_HANDLE h;
|
2020-02-19 06:28:55 +00:00
|
|
|
ACPI_BUFFER bix_buffer;
|
2005-11-23 00:57:51 +00:00
|
|
|
device_t dev;
|
2020-02-19 06:28:55 +00:00
|
|
|
int i, n;
|
|
|
|
const struct {
|
|
|
|
enum { _BIX, _BIF } type;
|
|
|
|
char *name;
|
|
|
|
} bobjs[] = {
|
|
|
|
{ _BIX, "_BIX"},
|
|
|
|
{ _BIF, "_BIF"},
|
|
|
|
};
|
2003-08-11 15:34:43 +00:00
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_ASSERT(cmbat);
|
|
|
|
|
2005-11-23 00:57:51 +00:00
|
|
|
dev = arg;
|
2003-08-11 15:34:43 +00:00
|
|
|
sc = device_get_softc(dev);
|
|
|
|
h = acpi_get_handle(dev);
|
2020-02-19 06:28:55 +00:00
|
|
|
bix_buffer.Pointer = NULL;
|
|
|
|
bix_buffer.Length = ACPI_ALLOCATE_BUFFER;
|
2003-08-11 15:34:43 +00:00
|
|
|
|
2020-02-19 06:28:55 +00:00
|
|
|
for (n = 0; n < sizeof(bobjs); n++) {
|
|
|
|
as = AcpiEvaluateObject(h, bobjs[n].name, NULL, &bix_buffer);
|
|
|
|
if (!ACPI_FAILURE(as)) {
|
|
|
|
res = (ACPI_OBJECT *)bix_buffer.Pointer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
AcpiOsFree(bix_buffer.Pointer);
|
|
|
|
bix_buffer.Pointer = NULL;
|
|
|
|
bix_buffer.Length = ACPI_ALLOCATE_BUFFER;
|
2003-08-11 15:34:43 +00:00
|
|
|
}
|
2020-02-19 06:28:55 +00:00
|
|
|
/* Both _BIF and _BIX were not found. */
|
|
|
|
if (n == sizeof(bobjs)) {
|
2003-08-11 15:34:43 +00:00
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2020-02-19 06:28:55 +00:00
|
|
|
"error fetching current battery info -- %s\n",
|
|
|
|
AcpiFormatException(as));
|
2003-08-11 15:34:43 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2020-02-19 06:28:55 +00:00
|
|
|
/*
|
|
|
|
* ACPI _BIX and _BIF revision mismatch check:
|
|
|
|
*
|
|
|
|
* 1. _BIF has no revision field. The number of fields must be 13.
|
|
|
|
*
|
|
|
|
* 2. _BIX has a revision field. As of ACPI 6.3 it must be "0" or
|
|
|
|
* "1". The number of fields will be checked---20 and 21,
|
|
|
|
* respectively.
|
|
|
|
*
|
|
|
|
* If the revision number is grater than "1" and the number of
|
|
|
|
* fields is grater than 21, it will be treated as compatible with
|
|
|
|
* ACPI 6.0 _BIX. If not, it will be ignored.
|
|
|
|
*/
|
|
|
|
i = 0;
|
|
|
|
switch (bobjs[n].type) {
|
|
|
|
case _BIX:
|
|
|
|
if (acpi_PkgInt16(res, i++, &sc->bix.rev) != 0) {
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"_BIX revision error\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
#define ACPI_BIX_REV_MISMATCH_ERR(x, r) do { \
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
|
|
|
|
"_BIX revision mismatch (%u != %u)\n", x, r); \
|
|
|
|
goto end; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
if (ACPI_PKG_VALID_EQ(res, 21)) { /* ACPI 6.0 _BIX */
|
2020-02-27 17:13:57 +00:00
|
|
|
/*
|
|
|
|
* Some models have rev.0 _BIX with 21 members.
|
|
|
|
* In that case, treat the first 20 members as rev.0 _BIX.
|
|
|
|
*/
|
|
|
|
if (sc->bix.rev != ACPI_BIX_REV_0 &&
|
|
|
|
sc->bix.rev != ACPI_BIX_REV_1)
|
2020-02-19 06:28:55 +00:00
|
|
|
ACPI_BIX_REV_MISMATCH_ERR(sc->bix.rev, ACPI_BIX_REV_1);
|
|
|
|
} else if (ACPI_PKG_VALID_EQ(res, 20)) {/* ACPI 4.0 _BIX */
|
|
|
|
if (sc->bix.rev != ACPI_BIX_REV_0)
|
|
|
|
ACPI_BIX_REV_MISMATCH_ERR(sc->bix.rev, ACPI_BIX_REV_0);
|
2020-02-27 17:13:57 +00:00
|
|
|
} else if (ACPI_PKG_VALID(res, 22)) {
|
|
|
|
/* _BIX with 22 or more members. */
|
|
|
|
if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_1 + 1)) {
|
2020-02-19 06:28:55 +00:00
|
|
|
/*
|
2020-02-27 17:13:57 +00:00
|
|
|
* Unknown revision number.
|
2020-02-19 06:28:55 +00:00
|
|
|
* Assume 21 members are compatible with 6.0 _BIX.
|
|
|
|
*/
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"Unknown _BIX revision(%u). "
|
|
|
|
"Assuming compatible with revision %u.\n",
|
|
|
|
sc->bix.rev, ACPI_BIX_REV_1);
|
2020-02-27 17:13:57 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Known revision number. Ignore the extra members.
|
|
|
|
*/
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"Extra objects found in _BIX were ignored.\n");
|
|
|
|
}
|
2020-02-19 06:28:55 +00:00
|
|
|
} else {
|
|
|
|
/* Invalid _BIX. Ignore it. */
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"Invalid _BIX found (rev=%u, count=%u). Ignored.\n",
|
|
|
|
sc->bix.rev, res->Package.Count);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#undef ACPI_BIX_REV_MISMATCH_ERR
|
|
|
|
case _BIF:
|
|
|
|
if (ACPI_PKG_VALID_EQ(res, 13)) /* _BIF */
|
|
|
|
sc->bix.rev = ACPI_BIX_REV_BIF;
|
|
|
|
else {
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"Invalid _BIF found (count=%u). Ignored.\n",
|
|
|
|
res->Package.Count);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2000-12-22 14:41:55 +00:00
|
|
|
|
2020-02-19 06:28:55 +00:00
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
|
|
"rev = %04x\n", sc->bix.rev);
|
|
|
|
#define BIX_GETU32(NAME) do { \
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
|
|
|
|
#NAME " = %u\n", sc->bix.NAME); \
|
|
|
|
if (acpi_PkgInt32(res, i++, &sc->bix.NAME) != 0) \
|
|
|
|
goto end; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
BIX_GETU32(units);
|
|
|
|
BIX_GETU32(dcap);
|
|
|
|
BIX_GETU32(lfcap);
|
|
|
|
BIX_GETU32(btech);
|
|
|
|
BIX_GETU32(dvol);
|
|
|
|
BIX_GETU32(wcap);
|
|
|
|
BIX_GETU32(lcap);
|
|
|
|
if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_0)) {
|
|
|
|
BIX_GETU32(cycles);
|
|
|
|
BIX_GETU32(accuracy);
|
|
|
|
BIX_GETU32(stmax);
|
|
|
|
BIX_GETU32(stmin);
|
|
|
|
BIX_GETU32(aimax);
|
|
|
|
BIX_GETU32(aimin);
|
|
|
|
}
|
|
|
|
BIX_GETU32(gra1);
|
|
|
|
BIX_GETU32(gra2);
|
|
|
|
if (acpi_PkgStr(res, i++, sc->bix.model, ACPI_CMBAT_MAXSTRLEN) != 0)
|
|
|
|
goto end;
|
|
|
|
if (acpi_PkgStr(res, i++, sc->bix.serial, ACPI_CMBAT_MAXSTRLEN) != 0)
|
|
|
|
goto end;
|
|
|
|
if (acpi_PkgStr(res, i++, sc->bix.type, ACPI_CMBAT_MAXSTRLEN) != 0)
|
|
|
|
goto end;
|
|
|
|
if (acpi_PkgStr(res, i++, sc->bix.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
|
|
|
|
goto end;
|
|
|
|
if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_1))
|
|
|
|
BIX_GETU32(scap);
|
|
|
|
#undef BIX_GETU32
|
2000-12-22 14:41:55 +00:00
|
|
|
end:
|
2020-02-19 06:28:55 +00:00
|
|
|
AcpiOsFree(bix_buffer.Pointer);
|
2000-12-22 14:41:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-02-19 06:28:55 +00:00
|
|
|
acpi_cmbat_bix(device_t dev, void *bix, size_t len)
|
2000-12-22 14:41:55 +00:00
|
|
|
{
|
2003-08-11 15:34:43 +00:00
|
|
|
struct acpi_cmbat_softc *sc;
|
2001-10-30 15:51:52 +00:00
|
|
|
|
2020-02-19 06:28:55 +00:00
|
|
|
if (len != sizeof(struct acpi_bix) &&
|
|
|
|
len != sizeof(struct acpi_bif))
|
|
|
|
return (-1);
|
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
sc = device_get_softc(dev);
|
2001-06-23 10:38:25 +00:00
|
|
|
|
2005-11-23 00:57:51 +00:00
|
|
|
/*
|
|
|
|
* Just copy the data. The only value that should change is the
|
|
|
|
* last-full capacity, so we only update when we get a notify that says
|
|
|
|
* the info has changed. Many systems apparently take a long time to
|
2020-02-19 06:28:55 +00:00
|
|
|
* process a _BI[FX] call so we avoid it if possible.
|
2005-11-23 00:57:51 +00:00
|
|
|
*/
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_BEGIN(cmbat);
|
2020-02-19 06:28:55 +00:00
|
|
|
memcpy(bix, &sc->bix, len);
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_END(cmbat);
|
2004-07-12 20:53:04 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
return (0);
|
2001-10-22 18:01:37 +00:00
|
|
|
}
|
2001-10-30 15:51:52 +00:00
|
|
|
|
2000-12-24 19:12:10 +00:00
|
|
|
static int
|
2020-02-19 06:28:55 +00:00
|
|
|
acpi_cmbat_bst(device_t dev, struct acpi_bst *bst)
|
2000-12-24 19:12:10 +00:00
|
|
|
{
|
2003-08-11 15:34:43 +00:00
|
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2000-12-24 19:12:10 +00:00
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_BEGIN(cmbat);
|
2005-07-23 19:36:00 +00:00
|
|
|
if (acpi_BatteryIsPresent(dev)) {
|
|
|
|
acpi_cmbat_get_bst(dev);
|
2020-02-19 06:28:55 +00:00
|
|
|
memcpy(bst, &sc->bst, sizeof(*bst));
|
2005-07-23 19:36:00 +00:00
|
|
|
} else
|
2020-02-19 06:28:55 +00:00
|
|
|
bst->state = ACPI_BATT_STAT_NOT_PRESENT;
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_END(cmbat);
|
2000-12-24 19:12:10 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
return (0);
|
2000-12-24 19:12:10 +00:00
|
|
|
}
|
2001-06-23 10:38:25 +00:00
|
|
|
|
2002-11-03 10:49:24 +00:00
|
|
|
static void
|
|
|
|
acpi_cmbat_init_battery(void *arg)
|
|
|
|
{
|
2004-08-13 06:21:44 +00:00
|
|
|
struct acpi_cmbat_softc *sc;
|
2005-07-23 19:36:00 +00:00
|
|
|
int retry, valid;
|
2004-08-13 06:21:44 +00:00
|
|
|
device_t dev;
|
2002-11-03 10:49:24 +00:00
|
|
|
|
2004-08-13 06:21:44 +00:00
|
|
|
dev = (device_t)arg;
|
2003-08-11 15:34:43 +00:00
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2020-02-19 06:28:55 +00:00
|
|
|
"battery enitialization start\n");
|
2002-11-03 10:49:24 +00:00
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
/*
|
|
|
|
* Try repeatedly to get valid data from the battery. Since the
|
|
|
|
* embedded controller isn't always ready just after boot, we may have
|
|
|
|
* to wait a while.
|
|
|
|
*/
|
2004-12-02 00:25:35 +00:00
|
|
|
for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
|
2017-02-27 08:36:51 +00:00
|
|
|
/*
|
|
|
|
* Batteries on DOCK can be ejected w/ DOCK during retrying.
|
|
|
|
*
|
|
|
|
* If there is a valid softc pointer the device may be in
|
|
|
|
* attaching, attached or detaching state. If the state is
|
|
|
|
* different from attached retry getting the device state
|
|
|
|
* until it becomes stable. This solves a race if the ACPI
|
|
|
|
* notification handler is called during attach, because
|
|
|
|
* device_is_attached() doesn't return non-zero until after
|
|
|
|
* the attach code has been executed.
|
|
|
|
*/
|
|
|
|
ACPI_SERIAL_BEGIN(cmbat);
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
if (sc == NULL) {
|
|
|
|
ACPI_SERIAL_END(cmbat);
|
2006-04-15 12:31:34 +00:00
|
|
|
return;
|
2017-02-27 08:36:51 +00:00
|
|
|
}
|
2006-04-15 12:31:34 +00:00
|
|
|
|
2017-02-27 08:36:51 +00:00
|
|
|
if (!acpi_BatteryIsPresent(dev) || !device_is_attached(dev)) {
|
|
|
|
ACPI_SERIAL_END(cmbat);
|
2003-08-11 15:34:43 +00:00
|
|
|
continue;
|
2017-02-27 08:36:51 +00:00
|
|
|
}
|
2002-11-03 10:49:24 +00:00
|
|
|
|
2005-11-23 00:57:51 +00:00
|
|
|
/*
|
|
|
|
* Only query the battery if this is the first try or the specific
|
|
|
|
* type of info is still invalid.
|
|
|
|
*/
|
|
|
|
if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) {
|
|
|
|
timespecclear(&sc->bst_lastupdated);
|
|
|
|
acpi_cmbat_get_bst(dev);
|
|
|
|
}
|
2020-02-19 06:28:55 +00:00
|
|
|
if (retry == 0 || !acpi_battery_bix_valid(&sc->bix))
|
|
|
|
acpi_cmbat_get_bix(dev);
|
2005-11-23 00:57:51 +00:00
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
valid = acpi_battery_bst_valid(&sc->bst) &&
|
2020-02-19 06:28:55 +00:00
|
|
|
acpi_battery_bix_valid(&sc->bix);
|
2004-08-13 06:21:44 +00:00
|
|
|
ACPI_SERIAL_END(cmbat);
|
|
|
|
|
2005-07-23 19:36:00 +00:00
|
|
|
if (valid)
|
2004-08-13 06:21:44 +00:00
|
|
|
break;
|
2003-08-11 15:34:43 +00:00
|
|
|
}
|
2002-11-03 10:49:24 +00:00
|
|
|
|
2003-08-11 15:34:43 +00:00
|
|
|
if (retry == ACPI_CMBAT_RETRY_MAX) {
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2020-02-19 06:28:55 +00:00
|
|
|
"battery initialization failed, giving up\n");
|
2003-08-11 15:34:43 +00:00
|
|
|
} else {
|
|
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
2020-02-19 06:28:55 +00:00
|
|
|
"battery initialization done, tried %d times\n", retry + 1);
|
2003-08-11 15:34:43 +00:00
|
|
|
}
|
2002-11-03 10:49:24 +00:00
|
|
|
}
|