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 struct filterops apm_readfiltops = { 64 .f_isfd = 1, 65 .f_detach = apmreadfiltdetach, 66 .f_event = apmreadfilt, 67 }; 68 69 static struct cdevsw apm_cdevsw = { 70 .d_version = D_VERSION, 71 .d_open = apmopen, 72 .d_write = apmwrite, 73 .d_ioctl = apmioctl, 74 .d_poll = apmpoll, 75 .d_name = "apm", 76 .d_kqfilter = apmkqfilter 77 }; 78 79 static int 80 acpi_capm_convert_battstate(struct acpi_battinfo *battp) 81 { 82 int state; 83 84 state = APM_UNKNOWN; 85 86 if (battp->state & ACPI_BATT_STAT_DISCHARG) { 87 if (battp->cap >= 50) 88 state = 0; /* high */ 89 else 90 state = 1; /* low */ 91 } 92 if (battp->state & ACPI_BATT_STAT_CRITICAL) 93 state = 2; /* critical */ 94 if (battp->state & ACPI_BATT_STAT_CHARGING) 95 state = 3; /* charging */ 96 97 /* If still unknown, determine it based on the battery capacity. */ 98 if (state == APM_UNKNOWN) { 99 if (battp->cap >= 50) 100 state = 0; /* high */ 101 else 102 state = 1; /* low */ 103 } 104 105 return (state); 106 } 107 108 static int 109 acpi_capm_convert_battflags(struct acpi_battinfo *battp) 110 { 111 int flags; 112 113 flags = 0; 114 115 if (battp->cap >= 50) 116 flags |= APM_BATT_HIGH; 117 else { 118 if (battp->state & ACPI_BATT_STAT_CRITICAL) 119 flags |= APM_BATT_CRITICAL; 120 else 121 flags |= APM_BATT_LOW; 122 } 123 if (battp->state & ACPI_BATT_STAT_CHARGING) 124 flags |= APM_BATT_CHARGING; 125 if (battp->state == ACPI_BATT_STAT_NOT_PRESENT) 126 flags = APM_BATT_NOT_PRESENT; 127 128 return (flags); 129 } 130 131 static int 132 acpi_capm_get_info(apm_info_t aip) 133 { 134 int acline; 135 struct acpi_battinfo batt; 136 137 aip->ai_infoversion = 1; 138 aip->ai_major = 1; 139 aip->ai_minor = 2; 140 aip->ai_status = apm_active; 141 aip->ai_capabilities= 0xff00; /* unknown */ 142 143 if (acpi_acad_get_acline(&acline)) 144 aip->ai_acline = 1; /* no info -- on-line best guess */ 145 else 146 aip->ai_acline = acline; /* on/off */ 147 148 if (acpi_battery_get_battinfo(NULL, &batt) != 0) { 149 aip->ai_batt_stat = 0; /* "high" old I/F has no unknown state */ 150 aip->ai_batt_life = 255; /* N/A, not -1 */ 151 aip->ai_batt_time = -1; /* unknown */ 152 aip->ai_batteries = ~0U; /* unknown */ 153 } else { 154 aip->ai_batt_stat = acpi_capm_convert_battstate(&batt); 155 aip->ai_batt_life = (batt.cap == -1) ? 255 : batt.cap; 156 aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60; 157 aip->ai_batteries = acpi_battery_get_units(); 158 } 159 160 return (0); 161 } 162 163 static int 164 acpi_capm_get_pwstatus(apm_pwstatus_t app) 165 { 166 device_t dev; 167 int acline, unit, error; 168 struct acpi_battinfo batt; 169 170 if (app->ap_device != PMDV_ALLDEV && 171 (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL)) 172 return (1); 173 174 if (app->ap_device == PMDV_ALLDEV) 175 error = acpi_battery_get_battinfo(NULL, &batt); 176 else { 177 unit = app->ap_device - PMDV_BATT0; 178 dev = devclass_get_device(devclass_find("battery"), unit); 179 if (dev != NULL) 180 error = acpi_battery_get_battinfo(dev, &batt); 181 else 182 error = ENXIO; 183 } 184 if (error) 185 return (1); 186 187 app->ap_batt_stat = acpi_capm_convert_battstate(&batt); 188 app->ap_batt_flag = acpi_capm_convert_battflags(&batt); 189 app->ap_batt_life = (batt.cap == -1) ? 255 : batt.cap; 190 app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60; 191 192 if (acpi_acad_get_acline(&acline)) 193 app->ap_acline = 1; /* no info -- on-line best guess */ 194 else 195 app->ap_acline = acline; /* on/off */ 196 197 return (0); 198 } 199 200 /* Create a struct for tracking per-device suspend notification. */ 201 static struct apm_clone_data * 202 apm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc) 203 { 204 struct apm_clone_data *clone; 205 206 clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK); 207 clone->cdev = dev; 208 clone->acpi_sc = acpi_sc; 209 clone->notify_status = APM_EV_NONE; 210 bzero(&clone->sel_read, sizeof(clone->sel_read)); 211 knlist_init_mtx(&clone->sel_read.si_note, &acpi_mutex); 212 213 /* 214 * The acpi device is always managed by devd(8) and is considered 215 * writable (i.e., ack is required to allow suspend to proceed.) 216 */ 217 if (strcmp("acpi", devtoname(dev)) == 0) 218 clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE; 219 else 220 clone->flags = ACPI_EVF_NONE; 221 222 ACPI_LOCK(acpi); 223 STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries); 224 ACPI_UNLOCK(acpi); 225 return (clone); 226 } 227 228 static void 229 apmdtor(void *data) 230 { 231 struct apm_clone_data *clone; 232 struct acpi_softc *acpi_sc; 233 234 clone = data; 235 acpi_sc = clone->acpi_sc; 236 237 /* We are about to lose a reference so check if suspend should occur */ 238 if (acpi_sc->acpi_next_sstate != 0 && 239 clone->notify_status != APM_EV_ACKED) 240 acpi_AckSleepState(clone, 0); 241 242 /* Remove this clone's data from the list and free it. */ 243 ACPI_LOCK(acpi); 244 STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries); 245 seldrain(&clone->sel_read); 246 knlist_destroy(&clone->sel_read.si_note); 247 ACPI_UNLOCK(acpi); 248 free(clone, M_APMDEV); 249 } 250 251 static int 252 apmopen(struct cdev *dev, int flag, int fmt, struct thread *td) 253 { 254 struct acpi_softc *acpi_sc; 255 struct apm_clone_data *clone; 256 257 acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 258 clone = apm_create_clone(dev, acpi_sc); 259 devfs_set_cdevpriv(clone, apmdtor); 260 261 /* If the device is opened for write, record that. */ 262 if ((flag & FWRITE) != 0) 263 clone->flags |= ACPI_EVF_WRITE; 264 265 return (0); 266 } 267 268 static int 269 apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 270 { 271 int error; 272 struct apm_clone_data *clone; 273 struct acpi_softc *acpi_sc; 274 struct apm_info info; 275 struct apm_event_info *ev_info; 276 apm_info_old_t aiop; 277 278 error = 0; 279 devfs_get_cdevpriv((void **)&clone); 280 acpi_sc = clone->acpi_sc; 281 282 switch (cmd) { 283 case APMIO_SUSPEND: 284 if ((flag & FWRITE) == 0) 285 return (EPERM); 286 if (acpi_sc->acpi_next_sstate == 0) { 287 if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) { 288 error = acpi_ReqSleepState(acpi_sc, 289 acpi_sc->acpi_suspend_sx); 290 } else { 291 printf( 292 "power off via apm suspend not supported\n"); 293 error = ENXIO; 294 } 295 } else 296 error = acpi_AckSleepState(clone, 0); 297 break; 298 case APMIO_STANDBY: 299 if ((flag & FWRITE) == 0) 300 return (EPERM); 301 if (acpi_sc->acpi_next_sstate == 0) { 302 if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) { 303 error = acpi_ReqSleepState(acpi_sc, 304 acpi_sc->acpi_standby_sx); 305 } else { 306 printf( 307 "power off via apm standby not supported\n"); 308 error = ENXIO; 309 } 310 } else 311 error = acpi_AckSleepState(clone, 0); 312 break; 313 case APMIO_NEXTEVENT: 314 printf("apm nextevent start\n"); 315 ACPI_LOCK(acpi); 316 if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status == 317 APM_EV_NONE) { 318 ev_info = (struct apm_event_info *)addr; 319 if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3) 320 ev_info->type = PMEV_STANDBYREQ; 321 else 322 ev_info->type = PMEV_SUSPENDREQ; 323 ev_info->index = 0; 324 clone->notify_status = APM_EV_NOTIFIED; 325 printf("apm event returning %d\n", ev_info->type); 326 } else 327 error = EAGAIN; 328 ACPI_UNLOCK(acpi); 329 break; 330 case APMIO_GETINFO_OLD: 331 if (acpi_capm_get_info(&info)) 332 error = ENXIO; 333 aiop = (apm_info_old_t)addr; 334 aiop->ai_major = info.ai_major; 335 aiop->ai_minor = info.ai_minor; 336 aiop->ai_acline = info.ai_acline; 337 aiop->ai_batt_stat = info.ai_batt_stat; 338 aiop->ai_batt_life = info.ai_batt_life; 339 aiop->ai_status = info.ai_status; 340 break; 341 case APMIO_GETINFO: 342 if (acpi_capm_get_info((apm_info_t)addr)) 343 error = ENXIO; 344 break; 345 case APMIO_GETPWSTATUS: 346 if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr)) 347 error = ENXIO; 348 break; 349 case APMIO_ENABLE: 350 if ((flag & FWRITE) == 0) 351 return (EPERM); 352 apm_active = 1; 353 break; 354 case APMIO_DISABLE: 355 if ((flag & FWRITE) == 0) 356 return (EPERM); 357 apm_active = 0; 358 break; 359 case APMIO_HALTCPU: 360 break; 361 case APMIO_NOTHALTCPU: 362 break; 363 case APMIO_DISPLAY: 364 if ((flag & FWRITE) == 0) 365 return (EPERM); 366 break; 367 case APMIO_BIOS: 368 if ((flag & FWRITE) == 0) 369 return (EPERM); 370 bzero(addr, sizeof(struct apm_bios_arg)); 371 break; 372 default: 373 error = EINVAL; 374 break; 375 } 376 377 return (error); 378 } 379 380 static int 381 apmwrite(struct cdev *dev, struct uio *uio, int ioflag) 382 { 383 return (uio->uio_resid); 384 } 385 386 static int 387 apmpoll(struct cdev *dev, int events, struct thread *td) 388 { 389 struct apm_clone_data *clone; 390 int revents; 391 392 revents = 0; 393 devfs_get_cdevpriv((void **)&clone); 394 ACPI_LOCK(acpi); 395 if (clone->acpi_sc->acpi_next_sstate) 396 revents |= events & (POLLIN | POLLRDNORM); 397 else 398 selrecord(td, &clone->sel_read); 399 ACPI_UNLOCK(acpi); 400 return (revents); 401 } 402 403 static int 404 apmkqfilter(struct cdev *dev, struct knote *kn) 405 { 406 struct apm_clone_data *clone; 407 408 devfs_get_cdevpriv((void **)&clone); 409 ACPI_LOCK(acpi); 410 kn->kn_hook = clone; 411 kn->kn_fop = &apm_readfiltops; 412 knlist_add(&clone->sel_read.si_note, kn, 0); 413 ACPI_UNLOCK(acpi); 414 return (0); 415 } 416 417 static void 418 apmreadfiltdetach(struct knote *kn) 419 { 420 struct apm_clone_data *clone; 421 422 ACPI_LOCK(acpi); 423 clone = kn->kn_hook; 424 knlist_remove(&clone->sel_read.si_note, kn, 0); 425 ACPI_UNLOCK(acpi); 426 } 427 428 static int 429 apmreadfilt(struct knote *kn, long hint) 430 { 431 struct apm_clone_data *clone; 432 int sleeping; 433 434 ACPI_LOCK(acpi); 435 clone = kn->kn_hook; 436 sleeping = clone->acpi_sc->acpi_next_sstate ? 1 : 0; 437 ACPI_UNLOCK(acpi); 438 return (sleeping); 439 } 440 441 void 442 acpi_apm_init(struct acpi_softc *sc) 443 { 444 445 /* Create a clone for /dev/acpi also. */ 446 STAILQ_INIT(&sc->apm_cdevs); 447 sc->acpi_clone = apm_create_clone(sc->acpi_dev_t, sc); 448 449 make_dev(&apm_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0660, "apmctl"); 450 make_dev(&apm_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664, "apm"); 451 } 452