xref: /freebsd/sys/x86/acpica/acpi_apm.c (revision 36138969847b231cd98f48272e2bdf88a8dc08dd)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2001 Mitsuru IWASAKI
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/bus.h>
31 #include <sys/condvar.h>
32 #include <sys/conf.h>
33 #include <sys/fcntl.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/poll.h>
37 #include <sys/uio.h>
38 
39 #include <contrib/dev/acpica/include/acpi.h>
40 
41 #include <dev/acpica/acpivar.h>
42 #include <dev/acpica/acpiio.h>
43 
44 #include <machine/apm_bios.h>
45 
46 /*
47  * APM driver emulation
48  */
49 
50 #define	APM_UNKNOWN	0xff
51 
52 static int apm_active;
53 
54 static MALLOC_DEFINE(M_APMDEV, "apmdev", "APM device emulation");
55 
56 static d_open_t		apmopen;
57 static d_write_t	apmwrite;
58 static d_ioctl_t	apmioctl;
59 static d_poll_t		apmpoll;
60 static d_kqfilter_t	apmkqfilter;
61 static void		apmreadfiltdetach(struct knote *kn);
62 static int		apmreadfilt(struct knote *kn, long hint);
63 static const struct filterops apm_readfiltops = {
64 	.f_isfd = 1,
65 	.f_detach = apmreadfiltdetach,
66 	.f_event = apmreadfilt,
67 	.f_copy = knote_triv_copy,
68 };
69 
70 static struct cdevsw apm_cdevsw = {
71 	.d_version =	D_VERSION,
72 	.d_open =	apmopen,
73 	.d_write =	apmwrite,
74 	.d_ioctl =	apmioctl,
75 	.d_poll =	apmpoll,
76 	.d_name =	"apm",
77 	.d_kqfilter =	apmkqfilter
78 };
79 
80 static int
acpi_capm_convert_battstate(struct acpi_battinfo * battp)81 acpi_capm_convert_battstate(struct  acpi_battinfo *battp)
82 {
83 	int	state;
84 
85 	state = APM_UNKNOWN;
86 
87 	if (battp->state & ACPI_BATT_STAT_DISCHARG) {
88 		if (battp->cap >= 50)
89 			state = 0;	/* high */
90 		else
91 			state = 1;	/* low */
92 	}
93 	if (battp->state & ACPI_BATT_STAT_CRITICAL)
94 		state = 2;		/* critical */
95 	if (battp->state & ACPI_BATT_STAT_CHARGING)
96 		state = 3;		/* charging */
97 
98 	/* If still unknown, determine it based on the battery capacity. */
99 	if (state == APM_UNKNOWN) {
100 		if (battp->cap >= 50)
101 			state = 0;	/* high */
102 		else
103 			state = 1;	/* low */
104 	}
105 
106 	return (state);
107 }
108 
109 static int
acpi_capm_convert_battflags(struct acpi_battinfo * battp)110 acpi_capm_convert_battflags(struct  acpi_battinfo *battp)
111 {
112 	int	flags;
113 
114 	flags = 0;
115 
116 	if (battp->cap >= 50)
117 		flags |= APM_BATT_HIGH;
118 	else {
119 		if (battp->state & ACPI_BATT_STAT_CRITICAL)
120 			flags |= APM_BATT_CRITICAL;
121 		else
122 			flags |= APM_BATT_LOW;
123 	}
124 	if (battp->state & ACPI_BATT_STAT_CHARGING)
125 		flags |= APM_BATT_CHARGING;
126 	if (battp->state == ACPI_BATT_STAT_NOT_PRESENT)
127 		flags = APM_BATT_NOT_PRESENT;
128 
129 	return (flags);
130 }
131 
132 static int
acpi_capm_get_info(apm_info_t aip)133 acpi_capm_get_info(apm_info_t aip)
134 {
135 	int	acline;
136 	struct	acpi_battinfo batt;
137 
138 	aip->ai_infoversion = 1;
139 	aip->ai_major       = 1;
140 	aip->ai_minor       = 2;
141 	aip->ai_status      = apm_active;
142 	aip->ai_capabilities= 0xff00;	/* unknown */
143 
144 	if (acpi_acad_get_acline(&acline))
145 		aip->ai_acline = 1;		/* no info -- on-line best guess */
146 	else
147 		aip->ai_acline = acline;	/* on/off */
148 
149 	if (acpi_battery_get_battinfo(NULL, &batt) != 0) {
150 		aip->ai_batt_stat = 0;		/* "high" old I/F has no unknown state */
151 		aip->ai_batt_life = 255;	/* N/A, not -1 */
152 		aip->ai_batt_time = -1;		/* unknown */
153 		aip->ai_batteries = ~0U;	/* unknown */
154 	} else {
155 		aip->ai_batt_stat = acpi_capm_convert_battstate(&batt);
156 		aip->ai_batt_life = (batt.cap == -1) ? 255 : batt.cap;
157 		aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
158 		aip->ai_batteries = acpi_battery_get_units();
159 	}
160 
161 	return (0);
162 }
163 
164 static int
acpi_capm_get_pwstatus(apm_pwstatus_t app)165 acpi_capm_get_pwstatus(apm_pwstatus_t app)
166 {
167 	device_t dev;
168 	int	acline, unit, error;
169 	struct	acpi_battinfo batt;
170 
171 	if (app->ap_device != PMDV_ALLDEV &&
172 	    (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL))
173 		return (1);
174 
175 	if (app->ap_device == PMDV_ALLDEV)
176 		error = acpi_battery_get_battinfo(NULL, &batt);
177 	else {
178 		unit = app->ap_device - PMDV_BATT0;
179 		dev = devclass_get_device(devclass_find("battery"), unit);
180 		if (dev != NULL)
181 			error = acpi_battery_get_battinfo(dev, &batt);
182 		else
183 			error = ENXIO;
184 	}
185 	if (error)
186 		return (1);
187 
188 	app->ap_batt_stat = acpi_capm_convert_battstate(&batt);
189 	app->ap_batt_flag = acpi_capm_convert_battflags(&batt);
190 	app->ap_batt_life = (batt.cap == -1) ? 255 : batt.cap;
191 	app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
192 
193 	if (acpi_acad_get_acline(&acline))
194 		app->ap_acline = 1;		/* no info -- on-line best guess */
195 	else
196 		app->ap_acline = acline;	/* on/off */
197 
198 	return (0);
199 }
200 
201 /* Create a struct for tracking per-device suspend notification. */
202 static struct apm_clone_data *
apm_create_clone(struct cdev * dev,struct acpi_softc * acpi_sc)203 apm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc)
204 {
205 	struct apm_clone_data *clone;
206 
207 	clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK);
208 	clone->cdev = dev;
209 	clone->acpi_sc = acpi_sc;
210 	clone->notify_status = APM_EV_NONE;
211 	bzero(&clone->sel_read, sizeof(clone->sel_read));
212 	knlist_init_mtx(&clone->sel_read.si_note, &acpi_mutex);
213 
214 	/*
215 	 * The acpi device is always managed by devd(8) and is considered
216 	 * writable (i.e., ack is required to allow suspend to proceed.)
217 	 */
218 	if (strcmp("acpi", devtoname(dev)) == 0)
219 		clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE;
220 	else
221 		clone->flags = ACPI_EVF_NONE;
222 
223 	ACPI_LOCK(acpi);
224 	STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries);
225 	ACPI_UNLOCK(acpi);
226 	return (clone);
227 }
228 
229 static void
apmdtor(void * data)230 apmdtor(void *data)
231 {
232 	struct	apm_clone_data *clone;
233 	struct	acpi_softc *acpi_sc;
234 
235 	clone = data;
236 	acpi_sc = clone->acpi_sc;
237 
238 	/* We are about to lose a reference so check if suspend should occur */
239 	if (acpi_sc->acpi_next_stype != POWER_STYPE_AWAKE &&
240 	    clone->notify_status != APM_EV_ACKED)
241 		acpi_AckSleepState(clone, 0);
242 
243 	/* Remove this clone's data from the list and free it. */
244 	ACPI_LOCK(acpi);
245 	STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries);
246 	seldrain(&clone->sel_read);
247 	knlist_destroy(&clone->sel_read.si_note);
248 	ACPI_UNLOCK(acpi);
249 	free(clone, M_APMDEV);
250 }
251 
252 static int
apmopen(struct cdev * dev,int flag,int fmt,struct thread * td)253 apmopen(struct cdev *dev, int flag, int fmt, struct thread *td)
254 {
255 	struct	acpi_softc *acpi_sc;
256 	struct 	apm_clone_data *clone;
257 
258 	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
259 	clone = apm_create_clone(dev, acpi_sc);
260 	devfs_set_cdevpriv(clone, apmdtor);
261 
262 	/* If the device is opened for write, record that. */
263 	if ((flag & FWRITE) != 0)
264 		clone->flags |= ACPI_EVF_WRITE;
265 
266 	return (0);
267 }
268 
269 static int
apmioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flag,struct thread * td)270 apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
271 {
272 	int	error;
273 	struct	apm_clone_data *clone;
274 	struct	acpi_softc *acpi_sc;
275 	struct	apm_info info;
276 	struct 	apm_event_info *ev_info;
277 	apm_info_old_t aiop;
278 
279 	error = 0;
280 	devfs_get_cdevpriv((void **)&clone);
281 	acpi_sc = clone->acpi_sc;
282 
283 	switch (cmd) {
284 	case APMIO_SUSPEND:
285 		if ((flag & FWRITE) == 0)
286 			return (EPERM);
287 		if (acpi_sc->acpi_next_stype == POWER_STYPE_AWAKE) {
288 			if (power_suspend_stype != POWER_STYPE_POWEROFF) {
289 				error = acpi_ReqSleepState(acpi_sc,
290 				    power_suspend_stype);
291 			} else {
292 				printf(
293 			"power off via apm suspend not supported\n");
294 				error = ENXIO;
295 			}
296 		} else
297 			error = acpi_AckSleepState(clone, 0);
298 		break;
299 	case APMIO_STANDBY:
300 		if ((flag & FWRITE) == 0)
301 			return (EPERM);
302 		if (acpi_sc->acpi_next_stype == POWER_STYPE_AWAKE) {
303 			if (power_standby_stype != POWER_STYPE_POWEROFF) {
304 				error = acpi_ReqSleepState(acpi_sc,
305 				    power_standby_stype);
306 			} else {
307 				printf(
308 			"power off via apm standby not supported\n");
309 				error = ENXIO;
310 			}
311 		} else
312 			error = acpi_AckSleepState(clone, 0);
313 		break;
314 	case APMIO_NEXTEVENT:
315 		printf("apm nextevent start\n");
316 		ACPI_LOCK(acpi);
317 		if (acpi_sc->acpi_next_stype != POWER_STYPE_AWAKE &&
318 		    clone->notify_status == APM_EV_NONE) {
319 			ev_info = (struct apm_event_info *)addr;
320 			/* XXX Check this. */
321 			if (acpi_sc->acpi_next_stype == POWER_STYPE_STANDBY)
322 				ev_info->type = PMEV_STANDBYREQ;
323 			else
324 				ev_info->type = PMEV_SUSPENDREQ;
325 			ev_info->index = 0;
326 			clone->notify_status = APM_EV_NOTIFIED;
327 			printf("apm event returning %d\n", ev_info->type);
328 		} else
329 			error = EAGAIN;
330 		ACPI_UNLOCK(acpi);
331 		break;
332 	case APMIO_GETINFO_OLD:
333 		if (acpi_capm_get_info(&info))
334 			error = ENXIO;
335 		aiop = (apm_info_old_t)addr;
336 		aiop->ai_major = info.ai_major;
337 		aiop->ai_minor = info.ai_minor;
338 		aiop->ai_acline = info.ai_acline;
339 		aiop->ai_batt_stat = info.ai_batt_stat;
340 		aiop->ai_batt_life = info.ai_batt_life;
341 		aiop->ai_status = info.ai_status;
342 		break;
343 	case APMIO_GETINFO:
344 		if (acpi_capm_get_info((apm_info_t)addr))
345 			error = ENXIO;
346 		break;
347 	case APMIO_GETPWSTATUS:
348 		if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr))
349 			error = ENXIO;
350 		break;
351 	case APMIO_ENABLE:
352 		if ((flag & FWRITE) == 0)
353 			return (EPERM);
354 		apm_active = 1;
355 		break;
356 	case APMIO_DISABLE:
357 		if ((flag & FWRITE) == 0)
358 			return (EPERM);
359 		apm_active = 0;
360 		break;
361 	case APMIO_HALTCPU:
362 		break;
363 	case APMIO_NOTHALTCPU:
364 		break;
365 	case APMIO_DISPLAY:
366 		if ((flag & FWRITE) == 0)
367 			return (EPERM);
368 		break;
369 	case APMIO_BIOS:
370 		if ((flag & FWRITE) == 0)
371 			return (EPERM);
372 		bzero(addr, sizeof(struct apm_bios_arg));
373 		break;
374 	default:
375 		error = EINVAL;
376 		break;
377 	}
378 
379 	return (error);
380 }
381 
382 static int
apmwrite(struct cdev * dev,struct uio * uio,int ioflag)383 apmwrite(struct cdev *dev, struct uio *uio, int ioflag)
384 {
385 	return (uio->uio_resid);
386 }
387 
388 static int
apmpoll(struct cdev * dev,int events,struct thread * td)389 apmpoll(struct cdev *dev, int events, struct thread *td)
390 {
391 	struct	apm_clone_data *clone;
392 	int revents;
393 
394 	revents = 0;
395 	devfs_get_cdevpriv((void **)&clone);
396 	ACPI_LOCK(acpi);
397 	if (clone->acpi_sc->acpi_next_stype != POWER_STYPE_AWAKE)
398 		revents |= events & (POLLIN | POLLRDNORM);
399 	else
400 		selrecord(td, &clone->sel_read);
401 	ACPI_UNLOCK(acpi);
402 	return (revents);
403 }
404 
405 static int
apmkqfilter(struct cdev * dev,struct knote * kn)406 apmkqfilter(struct cdev *dev, struct knote *kn)
407 {
408 	struct	apm_clone_data *clone;
409 
410 	devfs_get_cdevpriv((void **)&clone);
411 	ACPI_LOCK(acpi);
412 	kn->kn_hook = clone;
413 	kn->kn_fop = &apm_readfiltops;
414 	knlist_add(&clone->sel_read.si_note, kn, 0);
415 	ACPI_UNLOCK(acpi);
416 	return (0);
417 }
418 
419 static void
apmreadfiltdetach(struct knote * kn)420 apmreadfiltdetach(struct knote *kn)
421 {
422 	struct	apm_clone_data *clone;
423 
424 	ACPI_LOCK(acpi);
425 	clone = kn->kn_hook;
426 	knlist_remove(&clone->sel_read.si_note, kn, 0);
427 	ACPI_UNLOCK(acpi);
428 }
429 
430 static int
apmreadfilt(struct knote * kn,long hint)431 apmreadfilt(struct knote *kn, long hint)
432 {
433 	struct	apm_clone_data *clone;
434 	int	sleeping;
435 
436 	ACPI_LOCK(acpi);
437 	clone = kn->kn_hook;
438 	sleeping = clone->acpi_sc->acpi_next_stype != POWER_STYPE_AWAKE;
439 	ACPI_UNLOCK(acpi);
440 	return (sleeping);
441 }
442 
443 void
acpi_apm_init(struct acpi_softc * sc)444 acpi_apm_init(struct acpi_softc *sc)
445 {
446 
447 	/* Create a clone for /dev/acpi also. */
448 	STAILQ_INIT(&sc->apm_cdevs);
449 	sc->acpi_clone = apm_create_clone(sc->acpi_dev_t, sc);
450 
451 	make_dev(&apm_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0660, "apmctl");
452 	make_dev(&apm_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664, "apm");
453 }
454