code:
/*	$OpenBSD: acpivar.h,v 1.79 2014/12/08 07:12:37 mlarkin Exp $	*/
/*
 * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _DEV_ACPI_ACPIVAR_H_
#define _DEV_ACPI_ACPIVAR_H_

#define ACPI_TRAMPOLINE		(19 * NBPG)
#define ACPI_TRAMP_DATA		(20 * NBPG)

#ifndef _ACPI_WAKECODE

#include <sys/timeout.h>
#include <sys/rwlock.h>
#include <machine/biosvar.h>

#include "acpipwrres.h"

/* #define ACPI_DEBUG */
#ifdef ACPI_DEBUG
extern int acpi_debug;
#define dprintf(x...)	  do { if (acpi_debug) printf(x); } while (0)
#define dnprintf(n,x...)  do { if (acpi_debug > (n)) printf(x); } while (0)
#else
#define dprintf(x...)
#define dnprintf(n,x...)
#endif

extern int acpi_hasprocfvs;

#define LAPIC_MAP_SIZE	256
extern u_int8_t acpi_lapic_flags[LAPIC_MAP_SIZE];

struct klist;
struct acpiec_softc;
struct acpipwrres_softc;

struct acpivideo_softc {
	struct device sc_dev;

	struct acpi_softc *sc_acpi;
	struct aml_node	*sc_devnode;
};

struct acpi_attach_args {
	char		*aaa_name;
	bus_space_tag_t	 aaa_iot;
	bus_space_tag_t	 aaa_memt;
	void		*aaa_table;
	struct aml_node *aaa_node;
	const char	*aaa_dev;
};

struct acpi_mem_map {
	vaddr_t		 baseva;
	u_int8_t	*va;
	size_t		 vsize;
	paddr_t		 pa;
};

struct acpi_q {
	SIMPLEQ_ENTRY(acpi_q)	 q_next;
	int			 q_id;
	void			*q_table;
	u_int8_t		 q_data[0];
};

struct acpi_taskq {
	SIMPLEQ_ENTRY(acpi_taskq)	next;
	void 				(*handler)(void *, int);
	void				*arg0;
	int				arg1;
};

struct acpi_wakeq {
	SIMPLEQ_ENTRY(acpi_wakeq)	 q_next;
	struct aml_node			*q_node;
	struct aml_value		*q_wakepkg;
	int				 q_gpe;
	int				 q_state;
};

#if NACPIPWRRES > 0
struct acpi_pwrres {
	SIMPLEQ_ENTRY(acpi_pwrres)	 p_next;
	struct aml_node			*p_node;	/* device's node */
	int				 p_state;	/* current state */

	int				 p_res_state;
	struct acpipwrres_softc		*p_res_sc;
};

typedef SIMPLEQ_HEAD(, acpi_pwrres) acpi_pwrreshead_t;
#endif /* NACPIPWRRES > 0 */

typedef SIMPLEQ_HEAD(, acpi_q) acpi_qhead_t;
typedef SIMPLEQ_HEAD(, acpi_wakeq) acpi_wakeqhead_t;

#define ACPIREG_PM1A_STS	0x00
#define ACPIREG_PM1A_EN		0x01
#define ACPIREG_PM1A_CNT	0x02
#define ACPIREG_PM1B_STS	0x03
#define ACPIREG_PM1B_EN		0x04
#define ACPIREG_PM1B_CNT	0x05
#define ACPIREG_PM2_CNT		0x06
#define ACPIREG_PM_TMR		0x07
#define ACPIREG_GPE0_STS	0x08
#define ACPIREG_GPE0_EN		0x09
#define ACPIREG_GPE1_STS	0x0A
#define ACPIREG_GPE1_EN		0x0B
#define ACPIREG_SMICMD		0x0C
#define ACPIREG_MAXREG		0x0D

/* Special registers */
#define ACPIREG_PM1_STS		0x0E
#define ACPIREG_PM1_EN		0x0F
#define ACPIREG_PM1_CNT		0x10
#define ACPIREG_GPE_STS		0x11
#define ACPIREG_GPE_EN		0x12

/* System status (_SST) codes */
#define ACPI_SST_INDICATOR_OFF	0
#define ACPI_SST_WORKING	1
#define ACPI_SST_WAKING		2
#define ACPI_SST_SLEEPING	3
#define ACPI_SST_SLEEP_CONTEXT	4

struct acpi_parsestate {
	u_int8_t		*start;
	u_int8_t		*end;
	u_int8_t		*pos;
};

struct acpi_reg_map {
	bus_space_handle_t	ioh;
	int			addr;
	int			size;
	int			access;
	const char		*name;
};

struct acpi_thread {
	struct acpi_softc   *sc;
	volatile int	    running;
};

struct acpi_mutex {
	struct rwlock		amt_lock;
#define ACPI_MTX_MAXNAME	5
	char			amt_name[ACPI_MTX_MAXNAME + 3]; /* only 4 used */
	int			amt_ref_count;
	int			amt_timeout;
	int			amt_synclevel;
};

struct gpe_block {
	int  (*handler)(struct acpi_softc *, int, void *);
	void *arg;
	int   active;
	int   edge;
};

struct acpi_devlist {
	struct aml_node			*dev_node;
	TAILQ_ENTRY(acpi_devlist)	dev_link;
};

TAILQ_HEAD(acpi_devlist_head, acpi_devlist);

struct acpi_ac {
	struct acpiac_softc	*aac_softc;
	SLIST_ENTRY(acpi_ac)	aac_link;
};

SLIST_HEAD(acpi_ac_head, acpi_ac);

struct acpi_bat {
	struct acpibat_softc	*aba_softc;
	SLIST_ENTRY(acpi_bat)	aba_link;
};

SLIST_HEAD(acpi_bat_head, acpi_bat);

struct acpi_softc {
	struct device		sc_dev;

	bus_space_tag_t		sc_iot;
	bus_space_tag_t		sc_memt;
#if 0
	bus_space_tag_t		sc_pcit;
	bus_space_tag_t		sc_smbust;
#endif

	/*
	 * First-level ACPI tables
	 */
	struct acpi_fadt	*sc_fadt;
	acpi_qhead_t		 sc_tables;
	acpi_wakeqhead_t	 sc_wakedevs;
#if NACPIPWRRES > 0
	acpi_pwrreshead_t	 sc_pwrresdevs;
#endif /* NACPIPWRRES > 0 */

	/*
	 * Second-level information from FADT
	 */
	struct acpi_facs	*sc_facs;	/* Shared with firmware! */

	struct klist		*sc_note;
	struct acpi_reg_map	sc_pmregs[ACPIREG_MAXREG];
	bus_space_handle_t	sc_ioh_pm1a_evt;

	void			*sc_interrupt;

	struct rwlock		sc_lck;

	struct {
		int slp_typa;
		int slp_typb;
	}			sc_sleeptype[6];
	int			sc_maxgpe;
	int			sc_lastgpe;

	struct gpe_block	*gpe_table;

	int			sc_threadwaiting;
	u_int32_t		sc_gpe_sts;
	u_int32_t		sc_gpe_en;
	struct acpi_thread	*sc_thread;

	struct aml_node		*sc_tts;
	struct aml_node		*sc_pts;
	struct aml_node		*sc_bfs;
	struct aml_node		*sc_gts;
	struct aml_node		*sc_sst;
	struct aml_node		*sc_wak;
	int			sc_state;
	struct acpiec_softc	*sc_ec;		/* XXX assume single EC */

	struct acpi_ac_head	sc_ac;
	struct acpi_bat_head	sc_bat;

	struct timeout		sc_dev_timeout;

	int			sc_revision;

	int			sc_pse;		/* passive cooling enabled */

	int			sc_flags;
};

extern struct acpi_softc *acpi_softc;

#define	SCFLAG_OREAD	0x0000001
#define	SCFLAG_OWRITE	0x0000002
#define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)

#define GPE_NONE  0x00
#define GPE_LEVEL 0x01
#define GPE_EDGE  0x02

struct acpi_table {
	int	offset;
	size_t	size;
	void	*table;
};

struct acpi_dev_rank {
	struct device	*dev;
	int		rank;
	TAILQ_ENTRY(acpi_dev_rank) link;
};

#define	ACPI_IOC_GETFACS	_IOR('A', 0, struct acpi_facs)
#define	ACPI_IOC_GETTABLE	_IOWR('A', 1, struct acpi_table)
#define ACPI_IOC_SETSLEEPSTATE	_IOW('A', 2, int)

#if defined(_KERNEL)
struct   acpi_gas;
int	 acpi_map_address(struct acpi_softc *, struct acpi_gas *, bus_addr_t, bus_size_t,
			  bus_space_handle_t *, bus_space_tag_t *);

int	 acpi_map(paddr_t, size_t, struct acpi_mem_map *);
void	 acpi_unmap(struct acpi_mem_map *);
int	 acpi_probe(struct device *, struct cfdata *, struct bios_attach_args *);
u_int	 acpi_checksum(const void *, size_t);
void	 acpi_attach_machdep(struct acpi_softc *);
int	 acpi_interrupt(void *);
void	 acpi_powerdown(void);
void	 acpi_reset(void);
int	 acpi_sleep_state(struct acpi_softc *, int);
void	 acpi_sleep_clocks(struct acpi_softc *, int);
int	 acpi_sleep_cpu(struct acpi_softc *, int);
void	 acpi_sleep_mp(void);
void	 acpi_sleep_pm(struct acpi_softc *, int);
void	 acpi_resume_pm(struct acpi_softc *, int);
void	 acpi_resume_clocks(struct acpi_softc *);
void	 acpi_resume_cpu(struct acpi_softc *);
void	 acpi_resume_mp(void);
void	 acpi_sleep_walk(struct acpi_softc *, int);

#define ACPI_IOREAD 0
#define ACPI_IOWRITE 1

void acpi_wakeup(void *);

int acpi_gasio(struct acpi_softc *, int, int, uint64_t, int, int, void *);

int	acpi_set_gpehandler(struct acpi_softc *, int,
	    int (*)(struct acpi_softc *, int, void *), void *, int);
void	acpi_enable_gpe(struct acpi_softc *, u_int32_t);

int	acpiec_intr(struct acpiec_softc *);
void	acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *);
void	acpiec_write(struct acpiec_softc *, u_int8_t, int, u_int8_t *);
void	acpiec_handle_events(struct acpiec_softc *);

#if NACPIPWRRES > 0
int	acpipwrres_ref_incr(struct acpipwrres_softc *, struct aml_node *);
int	acpipwrres_ref_decr(struct acpipwrres_softc *, struct aml_node *);
#endif /* NACPIPWRRES > 0 */

int	acpi_read_pmreg(struct acpi_softc *, int, int);
void	acpi_write_pmreg(struct acpi_softc *, int, int, int);

void	acpi_poll(void *);
void	acpi_sleep(int, char *);

int acpi_matchhids(struct acpi_attach_args *, const char *[], const char *);

int	acpi_record_event(struct acpi_softc *, u_int);

void	acpi_addtask(struct acpi_softc *, void (*)(void *, int), void *, int);
int	acpi_dotask(struct acpi_softc *);

void	acpi_powerdown_task(void *, int);
void	acpi_sleep_task(void *, int);

/* Section 5.2.10.1: global lock acquire/release functions */
#define	GL_BIT_PENDING	0x01
#define	GL_BIT_OWNED	0x02
int	acpi_acquire_glk(uint32_t *);
int	acpi_release_glk(uint32_t *);

void	acpi_pciroots_attach(struct device *, void *, cfprint_t);

#endif

#endif /* !_ACPI_WAKECODE */
#endif	/* !_DEV_ACPI_ACPIVAR_H_ */


code:
/*	$OpenBSD: acpivideo.c,v 1.10 2015/03/14 03:38:47 jsg Exp $	*/
/*
 * Copyright (c) 2008 Federico G. Schwindt <fgsch@openbsd.org>
 * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <machine/bus.h>

#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpidev.h>
#include <dev/acpi/amltypes.h>
#include <dev/acpi/dsdt.h>

#ifdef ACPIVIDEO_DEBUG
#define DPRINTF(x)	printf x
#else
#define DPRINTF(x)
#endif

/* _DOS Enable/Disable Output Switching */
#define DOS_SWITCH_BY_OSPM		0
#define DOS_SWITCH_BY_BIOS		1
#define DOS_SWITCH_LOCKED		2
#define DOS_SWITCH_BY_OSPM_EXT		3
#define DOS_BRIGHTNESS_BY_OSPM		4

/* Notifications for Displays Devices */
#define NOTIFY_OUTPUT_SWITCHED		0x80
#define NOTIFY_OUTPUT_CHANGED		0x81
#define NOTIFY_OUTPUT_CYCLE_KEY		0x82
#define NOTIFY_OUTPUT_NEXT_KEY		0x83
#define NOTIFY_OUTPUT_PREV_KEY		0x84

int	acpivideo_match(struct device *, void *, void *);
void	acpivideo_attach(struct device *, struct device *, void *);
int	acpivideo_notify(struct aml_node *, int, void *);

void	acpivideo_set_policy(struct acpivideo_softc *, int);
int	acpi_foundvout(struct aml_node *, void *);
int	acpivideo_print(void *, const char *);

int	acpivideo_getpcibus(struct acpivideo_softc *, struct aml_node *);

struct cfattach acpivideo_ca = {
	sizeof(struct acpivideo_softc), acpivideo_match, acpivideo_attach
};

struct cfdriver acpivideo_cd = {
	NULL, "acpivideo", DV_DULL
};

int
acpivideo_match(struct device *parent, void *match, void *aux)
{
	struct acpi_attach_args	*aaa = aux;
	struct cfdata *cf = match;

	if (aaa->aaa_name == NULL || strcmp(aaa->aaa_name,
	    cf->cf_driver->cd_name) != 0 || aaa->aaa_table != NULL)
		return (0);

	return (1);
}

void
acpivideo_attach(struct device *parent, struct device *self, void *aux)
{
	struct acpivideo_softc *sc = (struct acpivideo_softc *)self;
	struct acpi_attach_args *aaa = aux;

	sc->sc_acpi = (struct acpi_softc *)parent;
	sc->sc_devnode = aaa->aaa_node;

	printf(": %s\n", sc->sc_devnode->name);

	if (acpivideo_getpcibus(sc, sc->sc_devnode) == -1)
		return;

	aml_register_notify(sc->sc_devnode, aaa->aaa_dev,
	    acpivideo_notify, sc, ACPIDEV_NOPOLL);

	acpivideo_set_policy(sc,
	    DOS_SWITCH_BY_OSPM | DOS_BRIGHTNESS_BY_OSPM);

	aml_find_node(aaa->aaa_node, "_BCL", acpi_foundvout, sc);
}

int
acpivideo_notify(struct aml_node *node, int notify, void *arg)
{
	struct acpivideo_softc *sc = arg;

	switch (notify) {
	case NOTIFY_OUTPUT_SWITCHED:
	case NOTIFY_OUTPUT_CHANGED:
	case NOTIFY_OUTPUT_CYCLE_KEY:
	case NOTIFY_OUTPUT_NEXT_KEY:
	case NOTIFY_OUTPUT_PREV_KEY:
		DPRINTF(("%s: event 0x%02x\n", DEVNAME(sc), notify));
		break;
	default:
		printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
		break;
	}

	return (0);
}

void
acpivideo_set_policy(struct acpivideo_softc *sc, int policy)
{
	struct aml_value args, res;

	memset(&args, 0, sizeof(args));
	args.v_integer = policy;
	args.type = AML_OBJTYPE_INTEGER;

	aml_evalname(sc->sc_acpi, sc->sc_devnode, "_DOS", 1, &args, &res);
	DPRINTF(("%s: set policy to %X\n", DEVNAME(sc), aml_val2int(&res)));

	aml_freevalue(&res);
}

int
acpi_foundvout(struct aml_node *node, void *arg)
{
	struct acpivideo_softc *sc = (struct acpivideo_softc *)arg;
	struct device *self = (struct device *)arg;
	struct acpi_attach_args aaa;
	node = node->parent;

	DPRINTF(("Inside acpi_foundvout()\n"));
	if (node->parent != sc->sc_devnode)
		return (0);

	if (aml_searchname(node, "_BCM") && aml_searchname(node, "_BQC")) {
		memset(&aaa, 0, sizeof(aaa));
		aaa.aaa_iot = sc->sc_acpi->sc_iot;
		aaa.aaa_memt = sc->sc_acpi->sc_memt;
		aaa.aaa_node = node;
		aaa.aaa_name = "acpivout";

		config_found(self, &aaa, acpivideo_print);
	}

	return (0);
}

int
acpivideo_print(void *aux, const char *pnp)
{
	struct acpi_attach_args *aa = aux;

	if (pnp) {
		if (aa->aaa_name)
			printf("%s at %s", aa->aaa_name, pnp);
		else
			return (QUIET);
	}

	return (UNCONF);
}

int
acpivideo_getpcibus(struct acpivideo_softc *sc, struct aml_node *node)
{
	/* Check if parent device has PCI mapping */
	return (node->parent && node->parent->pci) ?
		node->parent->pci->sub : -1;
}

license: unknown


code:
/*	$OpenBSD: acpivout.c,v 1.11 2015/03/14 03:38:47 jsg Exp $	*/
/*
 * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/bus.h>

#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpidev.h>
#include <dev/acpi/amltypes.h>
#include <dev/acpi/dsdt.h>

#include <dev/wscons/wsconsio.h>

int	acpivout_match(struct device *, void *, void *);
void	acpivout_attach(struct device *, struct device *, void *);
int	acpivout_notify(struct aml_node *, int, void *);

#ifdef ACPIVIDEO_DEBUG
#define DPRINTF(x)	printf x
#else
#define DPRINTF(x)
#endif

/* Notifications for Output Devices */
#define NOTIFY_BRIGHTNESS_CYCLE		0x85
#define NOTIFY_BRIGHTNESS_UP		0x86
#define NOTIFY_BRIGHTNESS_DOWN		0x87
#define NOTIFY_BRIGHTNESS_ZERO		0x88
#define NOTIFY_DISPLAY_OFF		0x89

struct acpivout_softc {
	struct device		sc_dev;

	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;

	struct acpi_softc	*sc_acpi;
	struct aml_node		*sc_devnode;

	int	*sc_bcl;
	size_t	sc_bcl_len;
};

void	acpivout_brightness_cycle(struct acpivout_softc *);
void	acpivout_brightness_up(struct acpivout_softc *);
void	acpivout_brightness_down(struct acpivout_softc *);
void	acpivout_brightness_zero(struct acpivout_softc *);
int	acpivout_get_brightness(struct acpivout_softc *);
int	acpivout_find_brightness(struct acpivout_softc *, int);
void	acpivout_set_brightness(struct acpivout_softc *, int);
void	acpivout_get_bcl(struct acpivout_softc *);

/* wconsole hook functions */
int	acpivout_get_param(struct wsdisplay_param *);
int	acpivout_set_param(struct wsdisplay_param *);

extern int (*ws_get_param)(struct wsdisplay_param *);
extern int (*ws_set_param)(struct wsdisplay_param *);

struct cfattach acpivout_ca = {
	sizeof(struct acpivout_softc), acpivout_match, acpivout_attach
};

struct cfdriver acpivout_cd = {
	NULL, "acpivout", DV_DULL
};

int
acpivout_match(struct device *parent, void *match, void *aux)
{
	struct acpi_attach_args	*aaa = aux;
	struct cfdata		*cf = match;

	if (aaa->aaa_name == NULL ||
	    strcmp(aaa->aaa_name, cf->cf_driver->cd_name) != 0 ||
	    aaa->aaa_table != NULL)
		return (0);

	return (1);
}

void
acpivout_attach(struct device *parent, struct device *self, void *aux)
{
	struct acpivout_softc	*sc = (struct acpivout_softc *)self;
	struct acpi_attach_args	*aaa = aux;

	sc->sc_acpi = ((struct acpivideo_softc *)parent)->sc_acpi;
	sc->sc_devnode = aaa->aaa_node;

	printf(": %s\n", sc->sc_devnode->name);

	aml_register_notify(sc->sc_devnode, aaa->aaa_dev,
	    acpivout_notify, sc, ACPIDEV_NOPOLL);

	ws_get_param = acpivout_get_param;
	ws_set_param = acpivout_set_param;

	acpivout_get_bcl(sc);
}

int
acpivout_notify(struct aml_node *node, int notify, void *arg)
{
	struct acpivout_softc *sc = arg;

	switch (notify) {
	case NOTIFY_BRIGHTNESS_CYCLE:
		acpivout_brightness_cycle(sc);
		break;
	case NOTIFY_BRIGHTNESS_UP:
		acpivout_brightness_up(sc);
		break;
	case NOTIFY_BRIGHTNESS_DOWN:
		acpivout_brightness_down(sc);
		break;
	case NOTIFY_BRIGHTNESS_ZERO:
		acpivout_brightness_zero(sc);
		break;
	case NOTIFY_DISPLAY_OFF:
		/* TODO: D3 state change */
		break;
	default:
		printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
		break;
	}

	return (0);
}

void
acpivout_brightness_cycle(struct acpivout_softc *sc)
{
	int	cur_level;

	if (sc->sc_bcl_len == 0)
		return;
	cur_level = acpivout_get_brightness(sc);
	if (cur_level == sc->sc_bcl[sc->sc_bcl_len - 1])
		acpivout_brightness_zero(sc);
	else
		acpivout_brightness_up(sc);
}

void
acpivout_brightness_up(struct acpivout_softc *sc)
{
	int i, cur_level;

	if (sc->sc_bcl_len == 0)
		return;
	cur_level = acpivout_get_brightness(sc);
	if (cur_level == -1)
		return;

	/* check for max brightness level */
	if (cur_level == sc->sc_bcl[sc->sc_bcl_len - 1])
		return;

	for (i = 0; i < sc->sc_bcl_len && cur_level != sc->sc_bcl[i]; i++);
	acpivout_set_brightness(sc, sc->sc_bcl[i + 1]);
}

void
acpivout_brightness_down(struct acpivout_softc *sc)
{
	int i, cur_level;

	if (sc->sc_bcl_len == 0)
		return;
	cur_level = acpivout_get_brightness(sc);
	if (cur_level == -1)
		return;

	/* check for min brightness level */
	if (cur_level == sc->sc_bcl[0])
		return;

	for (i = 0; i < sc->sc_bcl_len && cur_level != sc->sc_bcl[i]; i++);
	acpivout_set_brightness(sc, sc->sc_bcl[i - 1]);
}

void
acpivout_brightness_zero(struct acpivout_softc *sc)
{
	if (sc->sc_bcl_len == 0)
		return;
	acpivout_set_brightness(sc, sc->sc_bcl[0]);
}

int
acpivout_get_brightness(struct acpivout_softc *sc)
{
	struct aml_value res;
	int level;

	aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BQC", 0, NULL, &res);
	level = aml_val2int(&res);
	aml_freevalue(&res);
	DPRINTF(("%s: BQC = %d\n", DEVNAME(sc), level));

	if (level < sc->sc_bcl[0] || level > sc->sc_bcl[sc->sc_bcl_len -1])
		level = -1;

	return (level);
}

int
acpivout_find_brightness(struct acpivout_softc *sc, int level)
{
	int i, mid;

	for (i = 0; i < sc->sc_bcl_len - 1; i++) {
		mid = sc->sc_bcl[i] + (sc->sc_bcl[i + 1] - sc->sc_bcl[i]) / 2;
		if (sc->sc_bcl[i] <= level && level <=  mid)
			return sc->sc_bcl[i];
		if  (mid < level && level <= sc->sc_bcl[i + 1])
			return sc->sc_bcl[i + 1];
	}
	if (level < sc->sc_bcl[0])
		return sc->sc_bcl[0];
	else
		return sc->sc_bcl[i];
}

void
acpivout_set_brightness(struct acpivout_softc *sc, int level)
{
	struct aml_value args, res;

	memset(&args, 0, sizeof(args));
	args.v_integer = level;
	args.type = AML_OBJTYPE_INTEGER;

	DPRINTF(("%s: BCM = %d\n", DEVNAME(sc), level));
	aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BCM", 1, &args, &res);

	aml_freevalue(&res);
}

void
acpivout_get_bcl(struct acpivout_softc *sc)
{
	int	i, j, value;
	struct aml_value res;

	DPRINTF(("Getting _BCL!"));
	aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BCL", 0, NULL, &res);
	if (res.type != AML_OBJTYPE_PACKAGE) {
		sc->sc_bcl_len = 0;
		goto err;
	}
	/*
	 * Per the ACPI spec section B.6.2 the _BCL method returns a package.
	 * The first integer in the package is the brightness level
	 * when the computer has full power, and the second is the
	 * brightness level when the computer is on batteries.
	 * All other levels may be used by OSPM.
	 * So we skip the first two integers in the package.
	 */
	if (res.length <= 2) {
		sc->sc_bcl_len = 0;
		goto err;
	}
	sc->sc_bcl_len = res.length - 2;

	sc->sc_bcl = mallocarray(sc->sc_bcl_len, sizeof(int), M_DEVBUF,
	    M_WAITOK | M_ZERO);

	for (i = 0; i < sc->sc_bcl_len; i++) {
		/* Sort darkest to brightest */
		value = aml_val2int(res.v_package[i + 2]);
		for (j = i; j > 0 && sc->sc_bcl[j - 1] > value; j--)
			sc->sc_bcl[j] = sc->sc_bcl[j - 1];
		 sc->sc_bcl[j] = value;
	}

err:
	aml_freevalue(&res);
}

int
acpivout_get_param(struct wsdisplay_param *dp)
{
	struct acpivout_softc	*sc = NULL;
	int i;

	switch (dp->param) {
	case WSDISPLAYIO_PARAM_BRIGHTNESS:
		for (i = 0; i < acpivout_cd.cd_ndevs; i++) {
			if (acpivout_cd.cd_devs[i] == NULL)
				continue;
			sc = (struct acpivout_softc *)acpivout_cd.cd_devs[i];
			/* Ignore device if not connected. */
			if (sc->sc_bcl_len != 0)
				break;
		}
		if (sc != NULL && sc->sc_bcl_len != 0) {
			dp->min = 0;
			dp->max =  sc->sc_bcl[sc->sc_bcl_len - 1];
			rw_enter_write(&sc->sc_acpi->sc_lck);
			dp->curval = acpivout_get_brightness(sc);
			rw_exit_write(&sc->sc_acpi->sc_lck);
			if (dp->curval != -1)
				return 0;
		}
		return -1;
	default:
		return -1;
	}
}

int
acpivout_set_param(struct wsdisplay_param *dp)
{
	struct acpivout_softc	*sc = NULL;
	int i, exact;

	switch (dp->param) {
	case WSDISPLAYIO_PARAM_BRIGHTNESS:
		for (i = 0; i < acpivout_cd.cd_ndevs; i++) {
			if (acpivout_cd.cd_devs[i] == NULL)
				continue;
			sc = (struct acpivout_softc *)acpivout_cd.cd_devs[i];
			/* Ignore device if not connected. */
			if (sc->sc_bcl_len != 0)
				break;
		}
		if (sc != NULL && sc->sc_bcl_len != 0) {
			rw_enter_write(&sc->sc_acpi->sc_lck);
			exact = acpivout_find_brightness(sc, dp->curval);
			acpivout_set_brightness(sc, exact);
			rw_exit_write(&sc->sc_acpi->sc_lck);
			return 0;
		}
		return -1;
	default:
		return -1;
	}
}


code:
/* $OpenBSD: amltypes.h,v 1.40 2012/09/07 19:19:59 kettenis Exp $ */
/*
 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef __DEV_ACPI_AMLTYPES_H__
#define __DEV_ACPI_AMLTYPES_H__

/* AML Opcodes */
#define AMLOP_ZERO		0x00
#define AMLOP_ONE		0x01
#define AMLOP_ALIAS		0x06
#define AMLOP_NAME		0x08
#define AMLOP_BYTEPREFIX	0x0A
#define AMLOP_WORDPREFIX	0x0B
#define AMLOP_DWORDPREFIX	0x0C
#define AMLOP_STRINGPREFIX	0x0D
#define AMLOP_QWORDPREFIX	0x0E
#define AMLOP_SCOPE		0x10
#define AMLOP_BUFFER		0x11
#define AMLOP_PACKAGE		0x12
#define AMLOP_VARPACKAGE	0x13
#define AMLOP_METHOD		0x14
#define AMLOP_DUALNAMEPREFIX	0x2E
#define AMLOP_MULTINAMEPREFIX	0x2F
#define AMLOP_EXTPREFIX		0x5B
#define AMLOP_MUTEX		0x5B01
#define AMLOP_EVENT		0x5B02
#define AMLOP_CONDREFOF		0x5B12
#define AMLOP_CREATEFIELD	0x5B13
#define AMLOP_LOADTABLE		0x5B1F
#define AMLOP_LOAD		0x5B20
#define AMLOP_STALL		0x5B21
#define AMLOP_SLEEP		0x5B22
#define AMLOP_ACQUIRE		0x5B23
#define AMLOP_SIGNAL		0x5B24
#define AMLOP_WAIT		0x5B25
#define AMLOP_RESET		0x5B26
#define AMLOP_RELEASE		0x5B27
#define AMLOP_FROMBCD		0x5B28
#define AMLOP_TOBCD		0x5B29
#define AMLOP_UNLOAD		0x5B2A
#define AMLOP_REVISION		0x5B30
#define AMLOP_DEBUG		0x5B31
#define AMLOP_FATAL		0x5B32
#define AMLOP_TIMER		0x5B33
#define AMLOP_OPREGION		0x5B80
#define AMLOP_FIELD		0x5B81
#define AMLOP_DEVICE		0x5B82
#define AMLOP_PROCESSOR		0x5B83
#define AMLOP_POWERRSRC		0x5B84
#define AMLOP_THERMALZONE	0x5B85
#define AMLOP_INDEXFIELD	0x5B86
#define AMLOP_BANKFIELD		0x5B87
#define AMLOP_DATAREGION	0x5B88
#define AMLOP_ROOTCHAR		0x5C
#define AMLOP_PARENTPREFIX	0x5E
#define AMLOP_NAMECHAR		0x5F
#define AMLOP_LOCAL0		0x60
#define AMLOP_LOCAL1		0x61
#define AMLOP_LOCAL2		0x62
#define AMLOP_LOCAL3		0x63
#define AMLOP_LOCAL4		0x64
#define AMLOP_LOCAL5		0x65
#define AMLOP_LOCAL6		0x66
#define AMLOP_LOCAL7		0x67
#define AMLOP_ARG0		0x68
#define AMLOP_ARG1		0x69
#define AMLOP_ARG2		0x6A
#define AMLOP_ARG3		0x6B
#define AMLOP_ARG4		0x6C
#define AMLOP_ARG5		0x6D
#define AMLOP_ARG6		0x6E
#define AMLOP_STORE		0x70
#define AMLOP_REFOF		0x71
#define AMLOP_ADD		0x72
#define AMLOP_CONCAT		0x73
#define AMLOP_SUBTRACT		0x74
#define AMLOP_INCREMENT		0x75
#define AMLOP_DECREMENT		0x76
#define AMLOP_MULTIPLY		0x77
#define AMLOP_DIVIDE		0x78
#define AMLOP_SHL		0x79
#define AMLOP_SHR		0x7A
#define AMLOP_AND		0x7B
#define AMLOP_NAND		0x7C
#define AMLOP_OR		0x7D
#define AMLOP_NOR		0x7E
#define AMLOP_XOR		0x7F
#define AMLOP_NOT		0x80
#define AMLOP_FINDSETLEFTBIT	0x81
#define AMLOP_FINDSETRIGHTBIT	0x82
#define AMLOP_DEREFOF		0x83
#define AMLOP_CONCATRES		0x84
#define AMLOP_MOD		0x85
#define AMLOP_NOTIFY		0x86
#define AMLOP_SIZEOF		0x87
#define AMLOP_INDEX		0x88
#define AMLOP_MATCH		0x89
#define AMLOP_CREATEDWORDFIELD	0x8A
#define AMLOP_CREATEWORDFIELD	0x8B
#define AMLOP_CREATEBYTEFIELD	0x8C
#define AMLOP_CREATEBITFIELD	0x8D
#define AMLOP_OBJECTTYPE	0x8E
#define AMLOP_CREATEQWORDFIELD	0x8F
#define AMLOP_LAND		0x90
#define AMLOP_LOR		0x91
#define AMLOP_LNOT		0x92
#define AMLOP_LNOTEQUAL		0x9293
#define AMLOP_LLESSEQUAL	0x9294
#define AMLOP_LGREATEREQUAL	0x9295
#define AMLOP_LEQUAL		0x93
#define AMLOP_LGREATER		0x94
#define AMLOP_LLESS		0x95
#define AMLOP_TOBUFFER		0x96
#define AMLOP_TODECSTRING	0x97
#define AMLOP_TOHEXSTRING	0x98
#define AMLOP_TOINTEGER		0x99
#define AMLOP_TOSTRING		0x9C
#define AMLOP_COPYOBJECT	0x9D
#define AMLOP_MID		0x9E
#define AMLOP_CONTINUE		0x9F
#define AMLOP_IF		0xA0
#define AMLOP_ELSE		0xA1
#define AMLOP_WHILE		0xA2
#define AMLOP_NOP		0xA3
#define AMLOP_RETURN		0xA4
#define AMLOP_BREAK		0xA5
#define AMLOP_BREAKPOINT	0xCC
#define AMLOP_ONES		0xFF

#define AMLOP_FIELDUNIT		0xFE00
#define AML_ANYINT		0xFF00

/*
 * Comparison types for Match()
 *
 *  true,==,<=,<,>=,>
 */
#define AML_MATCH_TR		0
#define AML_MATCH_EQ		1
#define AML_MATCH_LE		2
#define AML_MATCH_LT		3
#define AML_MATCH_GE		4
#define AML_MATCH_GT		5

/* Defined types for ObjectType() */
enum aml_objecttype {
	AML_OBJTYPE_UNINITIALIZED = 0,
	AML_OBJTYPE_INTEGER,
	AML_OBJTYPE_STRING,
	AML_OBJTYPE_BUFFER,
	AML_OBJTYPE_PACKAGE,
	AML_OBJTYPE_FIELDUNIT,
	AML_OBJTYPE_DEVICE,
	AML_OBJTYPE_EVENT,
	AML_OBJTYPE_METHOD,
	AML_OBJTYPE_MUTEX,
	AML_OBJTYPE_OPREGION,
	AML_OBJTYPE_POWERRSRC,
	AML_OBJTYPE_PROCESSOR,
	AML_OBJTYPE_THERMZONE,
	AML_OBJTYPE_BUFFERFIELD,
	AML_OBJTYPE_DDBHANDLE,
	AML_OBJTYPE_DEBUGOBJ,

	AML_OBJTYPE_NAMEREF = 0x100,
	AML_OBJTYPE_OBJREF,
	AML_OBJTYPE_SCOPE,
	AML_OBJTYPE_NOTARGET,
	AML_OBJTYPE_HEXSTRING,
	AML_OBJTYPE_DECSTRING,
};

/* AML Opcode Arguments */
#define AML_ARG_INTEGER		'i'
#define AML_ARG_BYTE		'b'
#define AML_ARG_WORD		'w'
#define AML_ARG_DWORD		'd'
#define AML_ARG_QWORD		'q'
#define AML_ARG_IMPBYTE		'!'
#define AML_ARG_OBJLEN		'p'
#define AML_ARG_STRING		'a'
#define AML_ARG_BYTELIST	'B'
#define AML_ARG_REVISION	'R'

#define AML_ARG_METHOD		'M'
#define AML_ARG_NAMESTRING	'N'
#define AML_ARG_NAMEREF		'n'
#define AML_ARG_FIELDLIST	'F'
#define AML_ARG_FLAG		'f'

#define AML_ARG_DATAOBJLIST	'O'
#define AML_ARG_DATAOBJ		'o'

#define AML_ARG_SIMPLENAME	's'
#define AML_ARG_SUPERNAME	'S'

#define AML_ARG_TERMOBJLIST	'T'
#define AML_ARG_TERMOBJ		't'

#define AML_ARG_IFELSE          'I'
#define AML_ARG_BUFFER          'B'
#define AML_ARG_SEARCHNAME      'n'
#define AML_ARG_CREATENAME      'N'
#define AML_ARG_STKARG          'A'
#define AML_ARG_STKLOCAL        'L'
#define AML_ARG_DEBUG           'D'
#define AML_ARG_CONST           'c'
#define AML_ARG_TARGET          'r'

#define AML_METHOD_ARGCOUNT(v)	 (((v) >> 0) & 0x7)
#define AML_METHOD_SERIALIZED(v) (((v) >> 3) & 0x1)
#define AML_METHOD_SYNCLEVEL(v)	 (((v) >> 4) & 0xF)

#define AML_FIELD_ACCESSMASK	0x0F
#define AML_FIELD_SETATTR(f,t,a) (((f) & 0xF0) | ((t) & 0xF) | ((a)<<8))
#define AML_FIELD_ACCESS(v)	(((v) >> 0) & 0xF)
# define AML_FIELD_ANYACC	0x0
# define AML_FIELD_BYTEACC	0x1
# define AML_FIELD_WORDACC	0x2
# define AML_FIELD_DWORDACC	0x3
# define AML_FIELD_QWORDACC	0x4
# define AML_FIELD_BUFFERACC	0x5
#define AML_FIELD_LOCK(v)	(((v) >> 4) & 0x1)
# define AML_FIELD_LOCK_OFF	0x0
# define AML_FIELD_LOCK_ON	0x1
#define AML_FIELD_UPDATE(v)	(((v) >> 5) & 0x3)
# define AML_FIELD_PRESERVE	0x0
# define AML_FIELD_WRITEASONES	0x1
# define AML_FIELD_WRITEASZEROES 0x2
#define AML_FIELD_ATTR(v)	((v) >> 8)
#define AML_FIELD_RESERVED	0x00
/* XXX fix this name */
#define AML_FIELD_ATTR__		0x01

struct aml_scope;
struct aml_node;

struct aml_waitq {
	struct aml_scope          *scope;
	SIMPLEQ_ENTRY(aml_waitq)   link;
};
SIMPLEQ_HEAD(aml_waitq_head, aml_waitq);

/* AML Object Value */
struct aml_value {
	int	type;
	int	length;
	int	refcnt;
	int	stack;
	struct aml_node *node;
	union {
		int64_t		vinteger;
		char		*vstring;
		u_int8_t	*vbuffer;
		struct aml_value **vpackage;
		struct {
			u_int8_t	iospace;
			u_int64_t	iobase;
			u_int32_t	iolen;
			int		flag;
		} vopregion;
		struct {
			int		flags;
			u_int8_t	*start;
			u_int8_t	*end;
			struct aml_value *(*fneval)(struct aml_scope *, struct aml_value *);
			u_int8_t        *base;
		} vmethod;
		struct {
			u_int16_t	 type;
			u_int16_t	 flags;
			u_int32_t	 bitpos;
			u_int32_t	 bitlen;
			struct aml_value *ref1;
			struct aml_value *ref2;
			int		 ref3;
		} vfield;
		struct {
			u_int8_t	proc_id;
			u_int32_t	proc_addr;
			u_int8_t	proc_len;
		} vprocessor;
		struct {
			int		type;
			int		index;
			struct aml_value *ref;
		} vobjref;
		struct {
			u_int8_t	pwr_level;
			u_int16_t	pwr_order;
		} vpowerrsrc;
		struct acpi_mutex	*vmutex;
		struct {
			u_int8_t         *name;
			struct aml_node  *node;
		} vnameref;
		struct {
			int               synclvl;
			int               savelvl;
			int               count;
			char              ownername[5];
			struct aml_scope *owner;
			struct aml_waitq_head    waiters;
		} Vmutex;
		struct {
			int               state;
			struct aml_waitq_head    waiters;
		} Vevent;
	} _;
};

#define v_nameref		_.vbuffer
#define v_objref		_.vobjref
#define v_integer		_.vinteger
#define v_string		_.vstring
#define v_buffer		_.vbuffer
#define v_package		_.vpackage
#define v_field			_.vfield
#define v_opregion		_.vopregion
#define v_method		_.vmethod
#define v_processor		_.vprocessor
#define v_powerrsrc		_.vpowerrsrc
#define v_mutex			_.vmutex
#define v_mtx                   _.Vmutex
#define v_evt                   _.Vevent

#define xaml_intval(v)		((v)->v_integer)
#define aml_strlen(v)		((v)->length)
#define aml_strval(v)		((v)->v_string ? (v)->v_string : "bad string")
#define aml_buflen(v)		((v)->length)
#define aml_bufval(v)		((v)->v_buffer)
#define aml_pkglen(v)		((v)->length)
#define aml_pkgval(v,i)		(&(v)->v_package[(i)])

struct acpi_pci {
	TAILQ_ENTRY(acpi_pci)		next;

	struct aml_node			*node;
	struct device			*device;

	int				sub;
	int				seg;
	int				bus;
	int				dev;
	int				fun;

	int				_s3d;
	int				_s3w;
	int				_s4d;
	int				_s4w;
};

struct aml_node {
	struct aml_node *parent;

	SIMPLEQ_HEAD(,aml_node)	son;
	SIMPLEQ_ENTRY(aml_node)	sib;

	char		name[5];
	u_int16_t	opcode;
	u_int8_t	*start;
	u_int8_t	*end;

	struct aml_value *value;
	struct acpi_pci  *pci;
};

#define aml_bitmask(n)		(1L << ((n) & 0x7))
#define aml_bitpos(n)		((n)&0x7)
#define aml_bytepos(n)		((n)>>3)
#define aml_bytelen(n)		(((n)+7)>>3)
#define aml_bytealigned(x)	!((x)&0x7)

#endif /* __DEV_ACPI_AMLTYPES_H__ */


code:
/*	$OpenBSD: atk0110.c,v 1.12 2014/12/19 07:42:49 deraadt Exp $	*/

/*
 * Copyright (c) 2009 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/sensors.h>

#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpidev.h>
#include <dev/acpi/amltypes.h>
#include <dev/acpi/dsdt.h>

/*
 * ASUSTeK AI Booster (ACPI ATK0110).
 *
 * The driver was inspired by Takanori Watanabe's acpi_aiboost driver.
 * http://cvsweb.freebsd.org/src/sys/dev/acpi_support/acpi_aiboost.c
 *
 * Special thanks goes to Sam Fourman Jr. for providing access to several
 * ASUS boxes where the driver could be tested.
 *
 *							-- cnst.su.
 */

#define ATK_ID_MUX_HWMON	0x00000006

#define ATK_CLASS(x)		(((x) >> 24) & 0xff)
#define ATK_CLASS_FREQ_CTL	3
#define ATK_CLASS_FAN_CTL	4
#define ATK_CLASS_HWMON		6
#define ATK_CLASS_MGMT		17

#define ATK_TYPE(x)		(((x) >> 16) & 0xff)
#define ATK_TYPE_VOLT		2
#define ATK_TYPE_TEMP		3
#define ATK_TYPE_FAN		4

#define AIBS_MORE_SENSORS
/* #define AIBS_VERBOSE */

struct aibs_sensor {
	struct ksensor	s;
	int64_t		i;
	int64_t		l;
	int64_t		h;
	SIMPLEQ_ENTRY(aibs_sensor)	entry;
};

struct aibs_softc {
	struct device		sc_dev;

	struct acpi_softc	*sc_acpi;
	struct aml_node		*sc_devnode;

	struct aml_node		*sc_ggrpnode;
	struct aml_node		*sc_gitmnode;
	struct aml_node		*sc_sitmnode;
	struct aml_node		*sc_rtmpnode;
	struct aml_node		*sc_rvltnode;
	struct aml_node		*sc_rfannode;

	SIMPLEQ_HEAD(, aibs_sensor)	sc_sensorlist;
	struct ksensordev	sc_sensordev;

	int			sc_mode;	/* 1 = new, 0 = old */
};

/* Command buffer used for GITM and SITM methods */
struct aibs_cmd_buffer {
	u_int32_t	id;
	u_int32_t	param1;
	u_int32_t	param2;
};

/* Return buffer used by the GITM and SITM mehtods */
struct aibs_ret_buffer {
	u_int32_t	flags;
	u_int32_t	value;
	/* there is more stuff that is unknown */
};

int	aibs_match(struct device *, void *, void *);
void	aibs_attach(struct device *, struct device *, void *);
int	aibs_notify(struct aml_node *, int, void *);
void	aibs_refresh(void *);

void	aibs_attach_sif(struct aibs_softc *, enum sensor_type);
void	aibs_attach_new(struct aibs_softc *);
void	aibs_add_sensor(struct aibs_softc *, char *);
void	aibs_refresh_r(struct aibs_softc *, struct aibs_sensor *);
int	aibs_getvalue(struct aibs_softc *, int64_t, int64_t *);
int	aibs_getpack(struct aibs_softc *, struct aml_node *, int64_t,
	    struct aml_value *);
void	aibs_probe(struct aibs_softc *);
int	aibs_find_cb(struct aml_node *, void *);

struct cfattach aibs_ca = {
	sizeof(struct aibs_softc), aibs_match, aibs_attach
};

struct cfdriver aibs_cd = {
	NULL, "aibs", DV_DULL
};

static const char* aibs_hids[] = {
	ACPI_DEV_ASUSAIBOOSTER,
	NULL
};

int
aibs_match(struct device *parent, void *match, void *aux)
{
	struct acpi_attach_args	*aa = aux;
	struct cfdata		*cf = match;

	return acpi_matchhids(aa, aibs_hids, cf->cf_driver->cd_name);
}

void
aibs_attach(struct device *parent, struct device *self, void *aux)
{
	struct aibs_softc	*sc = (struct aibs_softc *)self;
	struct acpi_attach_args	*aa = aux;

	sc->sc_acpi = (struct acpi_softc *)parent;
	sc->sc_devnode = aa->aaa_node;

	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));
	SIMPLEQ_INIT(&sc->sc_sensorlist);

	aibs_probe(sc);
	printf("\n");

	if (sc->sc_mode)
		aibs_attach_new(sc);
	else {
		aibs_attach_sif(sc, SENSOR_TEMP);
		aibs_attach_sif(sc, SENSOR_FANRPM);
		aibs_attach_sif(sc, SENSOR_VOLTS_DC);
	}

	if (sc->sc_sensordev.sensors_count == 0) {
		printf("%s: no sensors found\n", DEVNAME(sc));
		return;
	}

	sensordev_install(&sc->sc_sensordev);

	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
	    aibs_notify, sc, ACPIDEV_POLL);
}

void
aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
{
	struct aml_value	res;
	struct aml_value	**v;
	int			i, n;
	char			name[] = "?SIF";

	switch (st) {
	case SENSOR_TEMP:
		name[0] = 'T';
		break;
	case SENSOR_FANRPM:
		name[0] = 'F';
		break;
	case SENSOR_VOLTS_DC:
		name[0] = 'V';
		break;
	default:
		return;
	}

	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) {
		printf("%s: %s not found\n", DEVNAME(sc), name);
		aml_freevalue(&res);
		return;
	}
	if (res.type != AML_OBJTYPE_PACKAGE) {
		printf("%s: %s: not a package\n", DEVNAME(sc), name);
		aml_freevalue(&res);
		return;
	}
	v = res.v_package;
	if (v[0]->type != AML_OBJTYPE_INTEGER) {
		printf("%s: %s[0]: invalid type\n", DEVNAME(sc), name);
		aml_freevalue(&res);
		return;
	}

	n = v[0]->v_integer;
	if (res.length - 1 < n) {
		printf("%s: %s: invalid package\n", DEVNAME(sc), name);
		aml_freevalue(&res);
		return;
	} else if (res.length - 1 > n) {
		printf("%s: %s: misformed package: %i/%i",
		    DEVNAME(sc), name, n, res.length - 1);
#ifdef AIBS_MORE_SENSORS
		n = res.length - 1;
#endif
		printf(", assume %i\n", n);
	}
	if (n < 1) {
		printf("%s: %s: no members in the package\n",
		    DEVNAME(sc), name);
		aml_freevalue(&res);
		return;
	}

	for (i = 0, v++; i < n; i++, v++) {
		if(v[0]->type != AML_OBJTYPE_STRING) {
			printf("%s: %s: %i: not a string: %i type\n",
			    DEVNAME(sc), name, i, v[0]->type);
			continue;
		}
		aibs_add_sensor(sc, v[0]->v_string);
	}

	aml_freevalue(&res);
}

void
aibs_attach_new(struct aibs_softc *sc)
{
	struct aml_value	res;
	int			i;

	if (aibs_getpack(sc, sc->sc_ggrpnode, ATK_ID_MUX_HWMON, &res)) {
		printf("%s: GGRP: sensor enumeration failed\n", DEVNAME(sc));
		return;
	}

	for (i = 0; i < res.length; i++) {
		struct aml_value	*r;
		r = res.v_package[i];
		if (r->type != AML_OBJTYPE_STRING) {
			printf("%s: %s: %i: not a string (type %i)\n",
			    DEVNAME(sc), "GGRP", i, r->type);
			continue;
		}
		aibs_add_sensor(sc, r->v_string);
	}
	aml_freevalue(&res);
}

void
aibs_add_sensor(struct aibs_softc *sc, char *name)
{
	struct aml_value	 ri;
	struct aibs_sensor	*as;
	int			 len, lim1, lim2, ena;

	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
	    0, NULL, &ri)) {
		printf("%s: aibs_add_sensor: %s not found\n",
		    DEVNAME(sc), name);
		aml_freevalue(&ri);
		return;
	}
	if (ri.type != AML_OBJTYPE_PACKAGE) {
		printf("%s: aibs_add_sensor: %s: not a package\n",
		    DEVNAME(sc), name);
		aml_freevalue(&ri);
		return;
	}
	if (sc->sc_mode) {
		len = 7;
		lim1 = 4;
		lim2 = 5;
		ena = 6;
	} else {
		len = 5;
		lim1 = 2;
		lim2 = 3;
		ena = 4;
	}

	if (ri.length != len ||
	    ri.v_package[0]->type != AML_OBJTYPE_INTEGER ||
	    ri.v_package[1]->type != AML_OBJTYPE_STRING ||
	    ri.v_package[lim1]->type != AML_OBJTYPE_INTEGER ||
	    ri.v_package[lim2]->type != AML_OBJTYPE_INTEGER ||
	    ri.v_package[ena]->type != AML_OBJTYPE_INTEGER) {
		printf("%s: aibs_add_sensor: %s: invalid package\n",
		    DEVNAME(sc), name);
		aml_freevalue(&ri);
		return;
	}
	as = malloc(sizeof(*as), M_DEVBUF, M_NOWAIT | M_ZERO);
	if (!as) {
		printf("%s: aibs_add_sensor: %s: failed to allocate sensor\n",
		    DEVNAME(sc), name);
		aml_freevalue(&ri);
		return;
	}
	as->i = ri.v_package[0]->v_integer;
	switch (ATK_TYPE(as->i)) {
	case ATK_TYPE_VOLT:
		as->s.type = SENSOR_VOLTS_DC;
		break;
	case ATK_TYPE_TEMP:
		as->s.type = SENSOR_TEMP;
		break;
	case ATK_TYPE_FAN:
		as->s.type = SENSOR_FANRPM;
		break;
	default:
		printf("%s: aibs_add_sensor: %s: unknown sensor type %llx\n",
		    DEVNAME(sc), name, ri.v_package[0]->v_integer);
		aml_freevalue(&ri);
		free(as, M_DEVBUF, 0);
		return;
	}
	strlcpy(as->s.desc, ri.v_package[1]->v_string,
	    sizeof(as->s.desc));
	as->l = ri.v_package[lim1]->v_integer;
	if (sc->sc_mode)
		/* the second limit is a actually a range */
		as->h = as->l + ri.v_package[lim2]->v_integer;
	else
		as->h = ri.v_package[lim2]->v_integer;
#ifdef AIBS_VERBOSE
	printf("%s: %4s: %s 0x%08llx %5lli / %5lli  0x%llx\n",
	    DEVNAME(sc), name, as->s.desc, as->i, as->l, as->h,
	    ri.v_package[ena]->v_integer);
#endif
	SIMPLEQ_INSERT_TAIL(&sc->sc_sensorlist, as, entry);
	sensor_attach(&sc->sc_sensordev, &as->s);
	aml_freevalue(&ri);
	return;
}

void
aibs_refresh(void *arg)
{
	struct aibs_softc	*sc = arg;
	struct aibs_sensor	*as;

	SIMPLEQ_FOREACH(as, &sc->sc_sensorlist, entry)
		aibs_refresh_r(sc, as);
}

void
aibs_refresh_r(struct aibs_softc *sc, struct aibs_sensor *as)
{
	struct ksensor		*s = &as->s;
	int64_t			v;
	const int64_t		l = as->l, h = as->h;

	if (aibs_getvalue(sc, as->i, &v)) {
		s->flags |= SENSOR_FINVALID;
		return;
	}
	switch (s->type) {
	case SENSOR_TEMP:
		s->value = v * 100 * 1000 + 273150000;
		if (v == 0) {
			s->status = SENSOR_S_UNKNOWN;
			s->flags |= SENSOR_FINVALID;
		} else {
			if (v > h)
				s->status = SENSOR_S_CRIT;
			else if (v > l)
				s->status = SENSOR_S_WARN;
			else
				s->status = SENSOR_S_OK;
			s->flags &= ~SENSOR_FINVALID;
		}
		break;
	case SENSOR_FANRPM:
		s->value = v;
		/* some boards have strange limits for fans */
		if ((l != 0 && l < v && v < h) ||
		    (l == 0 && v > h))
			s->status = SENSOR_S_OK;
		else
			s->status = SENSOR_S_WARN;
		s->flags &= ~SENSOR_FINVALID;
		break;
	case SENSOR_VOLTS_DC:
		s->value = v * 1000;
		if (l < v && v < h)
			s->status = SENSOR_S_OK;
		else
			s->status = SENSOR_S_WARN;
		s->flags &= ~SENSOR_FINVALID;
		break;
	default:
		/* NOTREACHED */
		break;
	}
}

int
aibs_getvalue(struct aibs_softc *sc, int64_t i, int64_t *v)
{
	struct aml_node		*n = sc->sc_gitmnode;
	struct aml_value	req, res;
	struct aibs_cmd_buffer	cmd;
	struct aibs_ret_buffer	ret;
	enum aml_objecttype	type;

	if (sc->sc_mode) {
		cmd.id = i;
		cmd.param1 = 0;
		cmd.param2 = 0;
		type = req.type = AML_OBJTYPE_BUFFER;
		req.v_buffer = (uint8_t *)&cmd;
		req.length = sizeof(cmd);
	} else {
		switch (ATK_TYPE(i)) {
		case ATK_TYPE_TEMP:
			n = sc->sc_rtmpnode;
			break;
		case ATK_TYPE_FAN:
			n = sc->sc_rfannode;
			break;
		case ATK_TYPE_VOLT:
			n = sc->sc_rvltnode;
			break;
		default:
			return (-1);
		}
		type = req.type = AML_OBJTYPE_INTEGER;
		req.v_integer = i;
	}

	if (aml_evalnode(sc->sc_acpi, n, 1, &req, &res)) {
		dprintf("%s: %s: %lld: evaluation failed\n",
		    DEVNAME(sc), n->name, i);
		aml_freevalue(&res);
		return (-1);
	}
	if (res.type != type) {
		dprintf("%s: %s: %lld: not an integer: type %i\n",
		    DEVNAME(sc), n->name, i, res.type);
		aml_freevalue(&res);
		return (-1);
	}

	if (sc->sc_mode) {
		if (res.length < sizeof(ret)) {
			dprintf("%s: %s: %lld: result buffer too small\n",
			    DEVNAME(sc), n->name, i);
			aml_freevalue(&res);
			return (-1);
		}
		memcpy(&ret, res.v_buffer, sizeof(ret));
		if (ret.flags == 0) {
			dprintf("%s: %s: %lld: bad flags in result\n",
			    DEVNAME(sc), n->name, i);
			aml_freevalue(&res);
			return (-1);
		}
		*v = ret.value;
	} else {
		*v = res.v_integer;
	}
	aml_freevalue(&res);

	return (0);
}

int
aibs_getpack(struct aibs_softc *sc, struct aml_node *n, int64_t i,
    struct aml_value *res)
{
	struct aml_value	req;

	req.type = AML_OBJTYPE_INTEGER;
	req.v_integer = i;

	if (aml_evalnode(sc->sc_acpi, n, 1, &req, res)) {
		dprintf("%s: %s: %lld: evaluation failed\n",
		    DEVNAME(sc), n->name, i);
		aml_freevalue(res);
		return (-1);
	}
	if (res->type != AML_OBJTYPE_PACKAGE) {
		dprintf("%s: %s: %lld: not a package: type %i\n",
		    DEVNAME(sc), n->name, i, res->type);
		aml_freevalue(res);
		return (-1);
	}

	return (0);
}

void
aibs_probe(struct aibs_softc *sc)
{
	/*
	 * Old mode uses TSIF, VSIF, and FSIF to enumerate sensors and
	 * RTMP, RVLT, and RFAN are used to get the values.
	 * New mode uses GGRP for enumeration and GITM and SITM as accessor.
	 * If the new methods are available use them else default to old mode.
	 */
	aml_find_node(sc->sc_devnode, "RTMP", aibs_find_cb, &sc->sc_rtmpnode);
	aml_find_node(sc->sc_devnode, "RVLT", aibs_find_cb, &sc->sc_rvltnode);
	aml_find_node(sc->sc_devnode, "RFAN", aibs_find_cb, &sc->sc_rfannode);

	aml_find_node(sc->sc_devnode, "GGRP", aibs_find_cb, &sc->sc_ggrpnode);
	aml_find_node(sc->sc_devnode, "GITM", aibs_find_cb, &sc->sc_gitmnode);
	aml_find_node(sc->sc_devnode, "SITM", aibs_find_cb, &sc->sc_sitmnode);

	if (sc->sc_ggrpnode && sc->sc_gitmnode && sc->sc_sitmnode &&
	    !sc->sc_rtmpnode && !sc->sc_rvltnode && !sc->sc_rfannode)
		sc->sc_mode = 1;
}

int
aibs_find_cb(struct aml_node *node, void *arg)
{
	struct aml_node	**np = arg;

	printf(" %s", node->name);
	*np = node;
	return (1);
}

int
aibs_notify(struct aml_node *node, int notify_type, void *arg)
{
	struct aibs_softc *sc = arg;

	if (notify_type == 0x00) {
		/* Poll sensors */
		aibs_refresh(sc);
	}
	return (0);
}