185556Siwasaki/*-
285556Siwasaki * Copyright (c) 2001 Mitsuru IWASAKI
385556Siwasaki * All rights reserved.
485556Siwasaki *
585556Siwasaki * Redistribution and use in source and binary forms, with or without
685556Siwasaki * modification, are permitted provided that the following conditions
785556Siwasaki * are met:
885556Siwasaki * 1. Redistributions of source code must retain the above copyright
985556Siwasaki *    notice, this list of conditions and the following disclaimer.
1085556Siwasaki * 2. Redistributions in binary form must reproduce the above copyright
1185556Siwasaki *    notice, this list of conditions and the following disclaimer in the
1285556Siwasaki *    documentation and/or other materials provided with the distribution.
1385556Siwasaki *
1485556Siwasaki * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1585556Siwasaki * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1685556Siwasaki * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1785556Siwasaki * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1885556Siwasaki * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1985556Siwasaki * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2085556Siwasaki * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2185556Siwasaki * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2285556Siwasaki * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2385556Siwasaki * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2485556Siwasaki * SUCH DAMAGE.
2585556Siwasaki */
2685556Siwasaki
27115681Sobrien#include <sys/cdefs.h>
28115681Sobrien__FBSDID("$FreeBSD$");
29115681Sobrien
3085556Siwasaki#include <sys/param.h>
3185556Siwasaki#include <sys/bus.h>
32168191Sjhb#include <sys/condvar.h>
3385556Siwasaki#include <sys/conf.h>
3485556Siwasaki#include <sys/fcntl.h>
35215097Sjkim#include <sys/kernel.h>
36170976Snjl#include <sys/malloc.h>
37170976Snjl#include <sys/poll.h>
3885556Siwasaki#include <sys/uio.h>
3985556Siwasaki
40193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
41193530Sjkim
4285556Siwasaki#include <dev/acpica/acpivar.h>
4385556Siwasaki#include <dev/acpica/acpiio.h>
4485556Siwasaki
45215072Sjkim#include <machine/apm_bios.h>
46177157Sjhb
4785556Siwasaki/*
4885556Siwasaki * APM driver emulation
4985556Siwasaki */
5085556Siwasaki
51215072Sjkim#define	APM_UNKNOWN	0xff
5285556Siwasaki
53132619Snjlstatic int apm_active;
5485556Siwasaki
55227293Sedstatic MALLOC_DEFINE(M_APMDEV, "apmdev", "APM device emulation");
5685556Siwasaki
57170976Snjlstatic d_open_t		apmopen;
58170976Snjlstatic d_write_t	apmwrite;
59170976Snjlstatic d_ioctl_t	apmioctl;
60170976Snjlstatic d_poll_t		apmpoll;
61170976Snjlstatic d_kqfilter_t	apmkqfilter;
62170976Snjlstatic void		apmreadfiltdetach(struct knote *kn);
63170976Snjlstatic int		apmreadfilt(struct knote *kn, long hint);
64197134Srwatsonstatic struct filterops	apm_readfiltops = {
65197134Srwatson	.f_isfd = 1,
66197134Srwatson	.f_detach = apmreadfiltdetach,
67197134Srwatson	.f_event = apmreadfilt,
68197134Srwatson};
69170976Snjl
7085556Siwasakistatic struct cdevsw apm_cdevsw = {
71126080Sphk	.d_version =	D_VERSION,
72111815Sphk	.d_open =	apmopen,
73111815Sphk	.d_write =	apmwrite,
74111815Sphk	.d_ioctl =	apmioctl,
75111815Sphk	.d_poll =	apmpoll,
76111815Sphk	.d_name =	"apm",
77170976Snjl	.d_kqfilter =	apmkqfilter
7885556Siwasaki};
7985556Siwasaki
8085556Siwasakistatic int
8185556Siwasakiacpi_capm_convert_battstate(struct  acpi_battinfo *battp)
8285556Siwasaki{
8385556Siwasaki	int	state;
8485556Siwasaki
85131218Simp	state = APM_UNKNOWN;
8685556Siwasaki
8785556Siwasaki	if (battp->state & ACPI_BATT_STAT_DISCHARG) {
88119530Snjl		if (battp->cap >= 50)
8985556Siwasaki			state = 0;	/* high */
90119530Snjl		else
9185556Siwasaki			state = 1;	/* low */
9285556Siwasaki	}
93119530Snjl	if (battp->state & ACPI_BATT_STAT_CRITICAL)
9485556Siwasaki		state = 2;		/* critical */
95119530Snjl	if (battp->state & ACPI_BATT_STAT_CHARGING)
9685556Siwasaki		state = 3;		/* charging */
97119530Snjl
98120156Siwasaki	/* If still unknown, determine it based on the battery capacity. */
99131218Simp	if (state == APM_UNKNOWN) {
100128975Snjl		if (battp->cap >= 50)
101120156Siwasaki			state = 0;	/* high */
102128975Snjl		else
103120156Siwasaki			state = 1;	/* low */
104120156Siwasaki	}
105120156Siwasaki
10685556Siwasaki	return (state);
10785556Siwasaki}
10885556Siwasaki
10985556Siwasakistatic int
11085556Siwasakiacpi_capm_convert_battflags(struct  acpi_battinfo *battp)
11185556Siwasaki{
11285556Siwasaki	int	flags;
11385556Siwasaki
11485556Siwasaki	flags = 0;
11585556Siwasaki
116128975Snjl	if (battp->cap >= 50)
11785556Siwasaki		flags |= APM_BATT_HIGH;
118128975Snjl	else {
119119530Snjl		if (battp->state & ACPI_BATT_STAT_CRITICAL)
12085556Siwasaki			flags |= APM_BATT_CRITICAL;
121119530Snjl		else
12285556Siwasaki			flags |= APM_BATT_LOW;
12385556Siwasaki	}
124119530Snjl	if (battp->state & ACPI_BATT_STAT_CHARGING)
12585556Siwasaki		flags |= APM_BATT_CHARGING;
126119530Snjl	if (battp->state == ACPI_BATT_STAT_NOT_PRESENT)
12785556Siwasaki		flags = APM_BATT_NOT_PRESENT;
12885556Siwasaki
12985556Siwasaki	return (flags);
13085556Siwasaki}
13185556Siwasaki
13285556Siwasakistatic int
13385556Siwasakiacpi_capm_get_info(apm_info_t aip)
13485556Siwasaki{
13585556Siwasaki	int	acline;
13685556Siwasaki	struct	acpi_battinfo batt;
13785556Siwasaki
13885556Siwasaki	aip->ai_infoversion = 1;
13985556Siwasaki	aip->ai_major       = 1;
14085556Siwasaki	aip->ai_minor       = 2;
141132619Snjl	aip->ai_status      = apm_active;
142131218Simp	aip->ai_capabilities= 0xff00;	/* unknown */
14385556Siwasaki
144119530Snjl	if (acpi_acad_get_acline(&acline))
145131218Simp		aip->ai_acline = APM_UNKNOWN;	/* unknown */
146119530Snjl	else
14785556Siwasaki		aip->ai_acline = acline;	/* on/off */
14885556Siwasaki
149148352Snjl	if (acpi_battery_get_battinfo(NULL, &batt) != 0) {
150131218Simp		aip->ai_batt_stat = APM_UNKNOWN;
151131218Simp		aip->ai_batt_life = APM_UNKNOWN;
152131218Simp		aip->ai_batt_time = -1;		 /* unknown */
153131218Simp		aip->ai_batteries = ~0U;	 /* unknown */
15485556Siwasaki	} else {
15585556Siwasaki		aip->ai_batt_stat = acpi_capm_convert_battstate(&batt);
15685556Siwasaki		aip->ai_batt_life = batt.cap;
15785556Siwasaki		aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
15885556Siwasaki		aip->ai_batteries = acpi_battery_get_units();
15985556Siwasaki	}
16085556Siwasaki
16185556Siwasaki	return (0);
16285556Siwasaki}
16385556Siwasaki
16485556Siwasakistatic int
16585556Siwasakiacpi_capm_get_pwstatus(apm_pwstatus_t app)
16685556Siwasaki{
167148352Snjl	device_t dev;
168148352Snjl	int	acline, unit, error;
16985556Siwasaki	struct	acpi_battinfo batt;
17085556Siwasaki
17185556Siwasaki	if (app->ap_device != PMDV_ALLDEV &&
172128975Snjl	    (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL))
17385556Siwasaki		return (1);
17485556Siwasaki
175119530Snjl	if (app->ap_device == PMDV_ALLDEV)
176148352Snjl		error = acpi_battery_get_battinfo(NULL, &batt);
177148352Snjl	else {
178148352Snjl		unit = app->ap_device - PMDV_BATT0;
179148352Snjl		dev = devclass_get_device(devclass_find("battery"), unit);
180148352Snjl		if (dev != NULL)
181148352Snjl			error = acpi_battery_get_battinfo(dev, &batt);
182148352Snjl		else
183148352Snjl			error = ENXIO;
184148352Snjl	}
185148352Snjl	if (error)
18685556Siwasaki		return (1);
18785556Siwasaki
18885556Siwasaki	app->ap_batt_stat = acpi_capm_convert_battstate(&batt);
18985556Siwasaki	app->ap_batt_flag = acpi_capm_convert_battflags(&batt);
19085556Siwasaki	app->ap_batt_life = batt.cap;
19185556Siwasaki	app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
19285556Siwasaki
193119530Snjl	if (acpi_acad_get_acline(&acline))
194131218Simp		app->ap_acline = APM_UNKNOWN;
195119530Snjl	else
19685556Siwasaki		app->ap_acline = acline;	/* on/off */
19785556Siwasaki
19885556Siwasaki	return (0);
19985556Siwasaki}
20085556Siwasaki
201170976Snjl/* Create a struct for tracking per-device suspend notification. */
202170976Snjlstatic struct apm_clone_data *
203170976Snjlapm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc)
204170976Snjl{
205170976Snjl	struct apm_clone_data *clone;
206170976Snjl
207170976Snjl	clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK);
208170976Snjl	clone->cdev = dev;
209170976Snjl	clone->acpi_sc = acpi_sc;
210170976Snjl	clone->notify_status = APM_EV_NONE;
211170976Snjl	bzero(&clone->sel_read, sizeof(clone->sel_read));
212193951Skib	knlist_init_mtx(&clone->sel_read.si_note, &acpi_mutex);
213170976Snjl
214170976Snjl	/*
215170976Snjl	 * The acpi device is always managed by devd(8) and is considered
216170976Snjl	 * writable (i.e., ack is required to allow suspend to proceed.)
217170976Snjl	 */
218170976Snjl	if (strcmp("acpi", devtoname(dev)) == 0)
219170976Snjl		clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE;
220170976Snjl	else
221170976Snjl		clone->flags = ACPI_EVF_NONE;
222170976Snjl
223170976Snjl	ACPI_LOCK(acpi);
224170976Snjl	STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries);
225170976Snjl	ACPI_UNLOCK(acpi);
226170976Snjl	return (clone);
227170976Snjl}
228170976Snjl
229228283Sedstatic void
230228283Sedapmdtor(void *data)
23185556Siwasaki{
232170976Snjl	struct	apm_clone_data *clone;
233170976Snjl	struct	acpi_softc *acpi_sc;
234170976Snjl
235228283Sed	clone = data;
236170976Snjl	acpi_sc = clone->acpi_sc;
237170976Snjl
238170976Snjl	/* We are about to lose a reference so check if suspend should occur */
239170976Snjl	if (acpi_sc->acpi_next_sstate != 0 &&
240170976Snjl	    clone->notify_status != APM_EV_ACKED)
241170976Snjl		acpi_AckSleepState(clone, 0);
242170976Snjl
243170976Snjl	/* Remove this clone's data from the list and free it. */
244170976Snjl	ACPI_LOCK(acpi);
245170976Snjl	STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries);
246225177Sattilio	seldrain(&clone->sel_read);
247170976Snjl	knlist_destroy(&clone->sel_read.si_note);
248170976Snjl	ACPI_UNLOCK(acpi);
249170976Snjl	free(clone, M_APMDEV);
250228283Sed}
251228283Sed
252228283Sedstatic int
253228283Sedapmopen(struct cdev *dev, int flag, int fmt, struct thread *td)
254228283Sed{
255228283Sed	struct	acpi_softc *acpi_sc;
256228283Sed	struct 	apm_clone_data *clone;
257228283Sed
258228283Sed	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
259228283Sed	clone = apm_create_clone(dev, acpi_sc);
260228283Sed	devfs_set_cdevpriv(clone, apmdtor);
261228283Sed
262228283Sed	/* If the device is opened for write, record that. */
263228283Sed	if ((flag & FWRITE) != 0)
264228283Sed		clone->flags |= ACPI_EVF_WRITE;
265228283Sed
26685556Siwasaki	return (0);
26785556Siwasaki}
26885556Siwasaki
26985556Siwasakistatic int
270192442Simpapmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
27185556Siwasaki{
272170976Snjl	int	error;
273170976Snjl	struct	apm_clone_data *clone;
27485556Siwasaki	struct	acpi_softc *acpi_sc;
275170976Snjl	struct	apm_info info;
276170976Snjl	struct 	apm_event_info *ev_info;
27785556Siwasaki	apm_info_old_t aiop;
27885556Siwasaki
279170976Snjl	error = 0;
280228283Sed	devfs_get_cdevpriv((void **)&clone);
281170976Snjl	acpi_sc = clone->acpi_sc;
28285556Siwasaki
28385556Siwasaki	switch (cmd) {
28485556Siwasaki	case APMIO_SUSPEND:
285119530Snjl		if ((flag & FWRITE) == 0)
28685556Siwasaki			return (EPERM);
287170976Snjl		if (acpi_sc->acpi_next_sstate == 0) {
288170976Snjl			if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) {
289170976Snjl				error = acpi_ReqSleepState(acpi_sc,
290170976Snjl				    acpi_sc->acpi_suspend_sx);
291170976Snjl			} else {
292170976Snjl				printf(
293170976Snjl			"power off via apm suspend not supported\n");
294170976Snjl				error = ENXIO;
295170976Snjl			}
296170976Snjl		} else
297170976Snjl			error = acpi_AckSleepState(clone, 0);
29885556Siwasaki		break;
29985556Siwasaki	case APMIO_STANDBY:
300119530Snjl		if ((flag & FWRITE) == 0)
30185556Siwasaki			return (EPERM);
302170976Snjl		if (acpi_sc->acpi_next_sstate == 0) {
303170976Snjl			if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) {
304170976Snjl				error = acpi_ReqSleepState(acpi_sc,
305170976Snjl				    acpi_sc->acpi_standby_sx);
306170976Snjl			} else {
307170976Snjl				printf(
308170976Snjl			"power off via apm standby not supported\n");
309170976Snjl				error = ENXIO;
310170976Snjl			}
311170976Snjl		} else
312170976Snjl			error = acpi_AckSleepState(clone, 0);
31385556Siwasaki		break;
314170976Snjl	case APMIO_NEXTEVENT:
315170976Snjl		printf("apm nextevent start\n");
316170976Snjl		ACPI_LOCK(acpi);
317170976Snjl		if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status ==
318170976Snjl		    APM_EV_NONE) {
319170976Snjl			ev_info = (struct apm_event_info *)addr;
320170976Snjl			if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3)
321170976Snjl				ev_info->type = PMEV_STANDBYREQ;
322170976Snjl			else
323170976Snjl				ev_info->type = PMEV_SUSPENDREQ;
324170976Snjl			ev_info->index = 0;
325170976Snjl			clone->notify_status = APM_EV_NOTIFIED;
326170976Snjl			printf("apm event returning %d\n", ev_info->type);
327170976Snjl		} else
328170976Snjl			error = EAGAIN;
329170976Snjl		ACPI_UNLOCK(acpi);
330170976Snjl		break;
33185556Siwasaki	case APMIO_GETINFO_OLD:
33285556Siwasaki		if (acpi_capm_get_info(&info))
33385556Siwasaki			error = ENXIO;
33485556Siwasaki		aiop = (apm_info_old_t)addr;
33585556Siwasaki		aiop->ai_major = info.ai_major;
33685556Siwasaki		aiop->ai_minor = info.ai_minor;
33785556Siwasaki		aiop->ai_acline = info.ai_acline;
33885556Siwasaki		aiop->ai_batt_stat = info.ai_batt_stat;
33985556Siwasaki		aiop->ai_batt_life = info.ai_batt_life;
34085556Siwasaki		aiop->ai_status = info.ai_status;
34185556Siwasaki		break;
34285556Siwasaki	case APMIO_GETINFO:
34385556Siwasaki		if (acpi_capm_get_info((apm_info_t)addr))
34485556Siwasaki			error = ENXIO;
34585556Siwasaki		break;
34685556Siwasaki	case APMIO_GETPWSTATUS:
34785556Siwasaki		if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr))
34885556Siwasaki			error = ENXIO;
34985556Siwasaki		break;
35085556Siwasaki	case APMIO_ENABLE:
351119530Snjl		if ((flag & FWRITE) == 0)
35285556Siwasaki			return (EPERM);
353132619Snjl		apm_active = 1;
35485556Siwasaki		break;
35585556Siwasaki	case APMIO_DISABLE:
356119530Snjl		if ((flag & FWRITE) == 0)
35785556Siwasaki			return (EPERM);
358132619Snjl		apm_active = 0;
35985556Siwasaki		break;
36085556Siwasaki	case APMIO_HALTCPU:
36185556Siwasaki		break;
36285556Siwasaki	case APMIO_NOTHALTCPU:
36385556Siwasaki		break;
36485556Siwasaki	case APMIO_DISPLAY:
365119530Snjl		if ((flag & FWRITE) == 0)
36685556Siwasaki			return (EPERM);
36785556Siwasaki		break;
36885556Siwasaki	case APMIO_BIOS:
369119530Snjl		if ((flag & FWRITE) == 0)
37085556Siwasaki			return (EPERM);
37185556Siwasaki		bzero(addr, sizeof(struct apm_bios_arg));
37285556Siwasaki		break;
37385556Siwasaki	default:
37485556Siwasaki		error = EINVAL;
37585556Siwasaki		break;
37685556Siwasaki	}
37785556Siwasaki
37885556Siwasaki	return (error);
37985556Siwasaki}
38085556Siwasaki
38185556Siwasakistatic int
382130585Sphkapmwrite(struct cdev *dev, struct uio *uio, int ioflag)
38385556Siwasaki{
38485556Siwasaki	return (uio->uio_resid);
38585556Siwasaki}
38685556Siwasaki
38785556Siwasakistatic int
388192442Simpapmpoll(struct cdev *dev, int events, struct thread *td)
38985556Siwasaki{
390170976Snjl	struct	apm_clone_data *clone;
391170976Snjl	int revents;
392170976Snjl
393170976Snjl	revents = 0;
394228283Sed	devfs_get_cdevpriv((void **)&clone);
395170976Snjl	ACPI_LOCK(acpi);
396170976Snjl	if (clone->acpi_sc->acpi_next_sstate)
397170976Snjl		revents |= events & (POLLIN | POLLRDNORM);
398170976Snjl	else
399170976Snjl		selrecord(td, &clone->sel_read);
400170976Snjl	ACPI_UNLOCK(acpi);
401170976Snjl	return (revents);
402170976Snjl}
403170976Snjl
404170976Snjlstatic int
405170976Snjlapmkqfilter(struct cdev *dev, struct knote *kn)
406170976Snjl{
407170976Snjl	struct	apm_clone_data *clone;
408170976Snjl
409228283Sed	devfs_get_cdevpriv((void **)&clone);
410170976Snjl	ACPI_LOCK(acpi);
411170976Snjl	kn->kn_hook = clone;
412170976Snjl	kn->kn_fop = &apm_readfiltops;
413170976Snjl	knlist_add(&clone->sel_read.si_note, kn, 0);
414170976Snjl	ACPI_UNLOCK(acpi);
41585556Siwasaki	return (0);
41685556Siwasaki}
41785556Siwasaki
41885556Siwasakistatic void
419170976Snjlapmreadfiltdetach(struct knote *kn)
42085556Siwasaki{
421170976Snjl	struct	apm_clone_data *clone;
422170976Snjl
423170976Snjl	ACPI_LOCK(acpi);
424170976Snjl	clone = kn->kn_hook;
425170976Snjl	knlist_remove(&clone->sel_read.si_note, kn, 0);
426170976Snjl	ACPI_UNLOCK(acpi);
42785556Siwasaki}
42885556Siwasaki
429170976Snjlstatic int
430170976Snjlapmreadfilt(struct knote *kn, long hint)
431170976Snjl{
432170976Snjl	struct	apm_clone_data *clone;
433170976Snjl	int	sleeping;
434170976Snjl
435170976Snjl	ACPI_LOCK(acpi);
436170976Snjl	clone = kn->kn_hook;
437170976Snjl	sleeping = clone->acpi_sc->acpi_next_sstate ? 1 : 0;
438170976Snjl	ACPI_UNLOCK(acpi);
439170976Snjl	return (sleeping);
440170976Snjl}
441170976Snjl
442215097Sjkimvoid
443215097Sjkimacpi_apm_init(struct acpi_softc *sc)
44485556Siwasaki{
44585556Siwasaki
446215097Sjkim	/* Create a clone for /dev/acpi also. */
447215097Sjkim	STAILQ_INIT(&sc->apm_cdevs);
448215097Sjkim	sc->acpi_clone = apm_create_clone(sc->acpi_dev_t, sc);
449228283Sed
450228283Sed	make_dev(&apm_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0660, "apmctl");
451228283Sed	make_dev(&apm_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664, "apm");
45285556Siwasaki}
453