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