root/arch/i386/i386/apm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. apm_get_event
  2. apm_err_translate
  3. apm_perror
  4. apm_power_print
  5. apm_suspend
  6. apm_standby
  7. apm_resume
  8. apm_record_event
  9. apm_handle_event
  10. apm_periodic_check
  11. apm_powmgt_enable
  12. apm_powmgt_engage
  13. apm_devpowmgt_enable
  14. apm_set_powstate
  15. apm_cpu_busy
  16. apm_cpu_idle
  17. apm_set_ver
  18. apm_disconnect
  19. apmprobe
  20. apmattach
  21. apm_thread_create
  22. apm_thread
  23. apmopen
  24. apmclose
  25. apmioctl
  26. filt_apmrdetach
  27. filt_apmread
  28. apmkqfilter

    1 /*      $OpenBSD: apm.c,v 1.76 2007/07/02 17:11:29 thib Exp $   */
    2 
    3 /*-
    4  * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
    5  * Copyright (c) 1995 John T. Kohl.  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  * 3. All advertising materials mentioning features or use of this software
   16  *    must display the following acknowledgement:
   17  *      This product includes software developed by the University of
   18  *      California, Berkeley and its contributors.
   19  * 4. Neither the name of the University nor the names of its contributors
   20  *    may be used to endorse or promote products derived from this software
   21  *    without specific prior written permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  *
   35  */
   36 
   37 #include "apm.h"
   38 
   39 #if NAPM > 1
   40 #error only one APM device may be configured
   41 #endif
   42 
   43 #include <sys/param.h>
   44 #include <sys/systm.h>
   45 #include <sys/signalvar.h>
   46 #include <sys/kernel.h>
   47 #include <sys/kthread.h>
   48 #include <sys/rwlock.h>
   49 #include <sys/proc.h>
   50 #include <sys/user.h>
   51 #include <sys/malloc.h>
   52 #include <sys/device.h>
   53 #include <sys/fcntl.h>
   54 #include <sys/ioctl.h>
   55 #include <sys/event.h>
   56 #include <sys/mount.h>  /* for vfs_syncwait() proto */
   57 
   58 #include <machine/conf.h>
   59 #include <machine/cpu.h>
   60 #include <machine/cpufunc.h>
   61 #include <machine/gdt.h>
   62 #include <machine/psl.h>
   63 
   64 #include <dev/isa/isareg.h>
   65 #include <i386/isa/isa_machdep.h>
   66 #include <i386/isa/nvram.h>
   67 #include <dev/isa/isavar.h>
   68 
   69 #include <machine/biosvar.h>
   70 #include <machine/apmvar.h>
   71 
   72 #if defined(APMDEBUG)
   73 #define DPRINTF(x)      printf x
   74 #else
   75 #define DPRINTF(x)      /**/
   76 #endif
   77 
   78 struct cfdriver apm_cd = {
   79         NULL, "apm", DV_DULL
   80 };
   81 
   82 struct apm_softc {
   83         struct device sc_dev;
   84         struct klist sc_note;
   85         int     sc_flags;
   86         int     batt_life;
   87         struct proc *sc_thread;
   88         struct rwlock sc_lock;
   89 };
   90 #define SCFLAG_OREAD    0x0000001
   91 #define SCFLAG_OWRITE   0x0000002
   92 #define SCFLAG_OPEN     (SCFLAG_OREAD|SCFLAG_OWRITE)
   93 
   94 int     apmprobe(struct device *, void *, void *);
   95 void    apmattach(struct device *, struct device *, void *);
   96 
   97 struct cfattach apm_ca = {
   98         sizeof(struct apm_softc), apmprobe, apmattach
   99 };
  100 
  101 void    filt_apmrdetach(struct knote *kn);
  102 int     filt_apmread(struct knote *kn, long hint);
  103 
  104 struct filterops apmread_filtops = {
  105         1, NULL, filt_apmrdetach, filt_apmread
  106 };
  107 
  108 /* battery percentage at where we get verbose in our warnings.  This
  109  * value can be changed using sysctl(8), value machdep.apmwarn.
  110  * Setting it to zero kills all warnings
  111  */
  112 int     cpu_apmwarn = 10;
  113 
  114 #define APM_RESUME_HOLDOFF      3
  115 
  116 /*
  117  * Flags to control kernel display
  118  *      SCFLAG_NOPRINT:         do not output APM power messages due to
  119  *                              a power change event.
  120  *
  121  *      SCFLAG_PCTPRINT:        do not output APM power messages due to
  122  *                              to a power change event unless the battery
  123  *                              percentage changes.
  124  */
  125 #define SCFLAG_NOPRINT  0x0008000
  126 #define SCFLAG_PCTPRINT 0x0004000
  127 #define SCFLAG_PRINT    (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
  128 
  129 #define APMUNIT(dev)    (minor(dev)&0xf0)
  130 #define APMDEV(dev)     (minor(dev)&0x0f)
  131 #define APMDEV_NORMAL   0
  132 #define APMDEV_CTL      8
  133 
  134 int     apm_standbys;
  135 int     apm_userstandbys;
  136 int     apm_suspends;
  137 int     apm_resumes;
  138 int     apm_battlow;
  139 int     apm_evindex;
  140 int     apm_error;
  141 int     apm_op_inprog;
  142 
  143 u_int   apm_flags;
  144 u_char  apm_majver;
  145 u_char  apm_minver;
  146 int     apm_dobusy = 0;
  147 int     apm_doidle = 0;
  148 int     apm_bebatt = 0;
  149 int     apm_idle_called = 0;
  150 int     apm_attached = 0;
  151 
  152 struct {
  153         u_int32_t entry;
  154         u_int16_t seg;
  155         u_int16_t pad;
  156 } apm_ep;
  157 
  158 struct apmregs {
  159         u_int32_t       ax;
  160         u_int32_t       bx;
  161         u_int32_t       cx;
  162         u_int32_t       dx;
  163 };
  164 
  165 int  apmcall(u_int, u_int, struct apmregs *);
  166 void apm_power_print(struct apm_softc *, struct apmregs *);
  167 int  apm_handle_event(struct apm_softc *, struct apmregs *);
  168 void apm_set_ver(struct apm_softc *);
  169 int  apm_periodic_check(struct apm_softc *);
  170 void apm_thread_create(void *v);
  171 void apm_thread(void *);
  172 void apm_disconnect(struct apm_softc *);
  173 void apm_perror(const char *, struct apmregs *);
  174 void apm_powmgt_enable(int onoff);
  175 void apm_powmgt_engage(int onoff, u_int devid);
  176 /* void apm_devpowmgt_enable(int onoff, u_int devid); */
  177 int  apm_record_event(struct apm_softc *sc, u_int type);
  178 const char *apm_err_translate(int code);
  179 
  180 #define apm_get_powstat(r) apmcall(APM_POWER_STATUS, APM_DEV_ALLDEVS, r)
  181 void    apm_standby(void);
  182 void    apm_suspend(void);
  183 void    apm_resume(struct apm_softc *, struct apmregs *);
  184 
  185 static int __inline
  186 apm_get_event(struct apmregs *r)
  187 {
  188         int rv;
  189 
  190         bzero(r, sizeof(*r));
  191         rv = apmcall(APM_GET_PM_EVENT, 0, r);
  192         return rv;
  193 }
  194 
  195 const char *
  196 apm_err_translate(int code)
  197 {
  198         switch (code) {
  199         case APM_ERR_PM_DISABLED:
  200                 return "power management disabled";
  201         case APM_ERR_REALALREADY:
  202                 return "real mode interface already connected";
  203         case APM_ERR_NOTCONN:
  204                 return "interface not connected";
  205         case APM_ERR_16ALREADY:
  206                 return "16-bit interface already connected";
  207         case APM_ERR_16NOTSUPP:
  208                 return "16-bit interface not supported";
  209         case APM_ERR_32ALREADY:
  210                 return "32-bit interface already connected";
  211         case APM_ERR_32NOTSUPP:
  212                 return "32-bit interface not supported";
  213         case APM_ERR_UNRECOG_DEV:
  214                 return "unrecognized device ID";
  215         case APM_ERR_ERANGE:
  216                 return "parameter out of range";
  217         case APM_ERR_NOTENGAGED:
  218                 return "interface not engaged";
  219         case APM_ERR_UNABLE:
  220                 return "unable to enter requested state";
  221         case APM_ERR_NOEVENTS:
  222                 return "No pending events";
  223         case APM_ERR_NOT_PRESENT:
  224                 return "No APM present";
  225         default:
  226                 return "unknown error code?";
  227         }
  228 }
  229 
  230 int apmerrors = 0;
  231 
  232 void
  233 apm_perror(const char *str, struct apmregs *regs)
  234 {
  235         printf("apm0: APM %s: %s (%d)\n", str,
  236             apm_err_translate(APM_ERR_CODE(regs)),
  237             APM_ERR_CODE(regs));
  238         delay(1000000);
  239 
  240         apmerrors++;
  241 }
  242 
  243 void
  244 apm_power_print (struct apm_softc *sc, struct apmregs *regs)
  245 {
  246 #if !defined(APM_NOPRINT)
  247         sc->batt_life = BATT_LIFE(regs);
  248         if (BATT_LIFE(regs) != APM_BATT_LIFE_UNKNOWN) {
  249                 printf("%s: battery life expectancy %d%%\n",
  250                     sc->sc_dev.dv_xname,
  251                     BATT_LIFE(regs));
  252         }
  253         printf("%s: AC ", sc->sc_dev.dv_xname);
  254         switch (AC_STATE(regs)) {
  255         case APM_AC_OFF:
  256                 printf("off,");
  257                 break;
  258         case APM_AC_ON:
  259                 printf("on,");
  260                 break;
  261         case APM_AC_BACKUP:
  262                 printf("backup power,");
  263                 break;
  264         default:
  265         case APM_AC_UNKNOWN:
  266                 printf("unknown,");
  267                 break;
  268         }
  269         if (apm_minver == 0) {
  270                 printf(" battery is ");
  271                 switch (BATT_STATE(regs)) {
  272                 case APM_BATT_HIGH:
  273                         printf("high");
  274                         break;
  275                 case APM_BATT_LOW:
  276                         printf("low");
  277                         break;
  278                 case APM_BATT_CRITICAL:
  279                         printf("CRITICAL");
  280                         break;
  281                 case APM_BATT_CHARGING:
  282                         printf("charging");
  283                         break;
  284                 case APM_BATT_UNKNOWN:
  285                         printf("unknown");
  286                         break;
  287                 default:
  288                         printf("undecoded (%x)", BATT_STATE(regs));
  289                         break;
  290                 }
  291         } else if (apm_minver >= 1) {
  292                 if (BATT_FLAGS(regs) & APM_BATT_FLAG_NOBATTERY)
  293                         printf(" no battery");
  294                 else {
  295                         printf(" battery charge ");
  296                         if (BATT_FLAGS(regs) & APM_BATT_FLAG_HIGH)
  297                                 printf("high");
  298                         else if (BATT_FLAGS(regs) & APM_BATT_FLAG_LOW)
  299                                 printf("low");
  300                         else if (BATT_FLAGS(regs) & APM_BATT_FLAG_CRITICAL)
  301                                 printf("critical");
  302                         else
  303                                 printf("unknown");
  304                         if (BATT_FLAGS(regs) & APM_BATT_FLAG_CHARGING)
  305                                 printf(", charging");
  306                         if (BATT_REM_VALID(regs)) {
  307                                 int life = BATT_REMAINING(regs);
  308                                 if (apm_bebatt)
  309                                         life = swap16(life);
  310                                 printf(", estimated %d:%02d hours",
  311                                     life / 60, life % 60);
  312                         }
  313                 }
  314         }
  315 
  316         printf("\n");
  317 #endif
  318 }
  319 
  320 void
  321 apm_suspend()
  322 {
  323         dopowerhooks(PWR_SUSPEND);
  324 
  325         if (cold)
  326                 vfs_syncwait(0);
  327 
  328         (void)apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_SUSPEND);
  329 }
  330 
  331 void
  332 apm_standby()
  333 {
  334         dopowerhooks(PWR_STANDBY);
  335 
  336         if (cold)
  337                 vfs_syncwait(0);
  338 
  339         (void)apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_STANDBY);
  340 }
  341 
  342 void
  343 apm_resume(struct apm_softc *sc, struct apmregs *regs)
  344 {
  345         extern int perflevel;
  346 
  347         apm_resumes = APM_RESUME_HOLDOFF;
  348 
  349         /* they say that some machines may require reinitializing the clock */
  350         initrtclock();
  351 
  352         inittodr(time_second);
  353         /* lower bit in cx means pccard was powered down */
  354         dopowerhooks(PWR_RESUME);
  355         apm_record_event(sc, regs->bx);
  356 
  357         /* acknowledge any rtc interrupt we may have missed */
  358         rtcdrain(NULL);
  359 
  360         /* restore hw.setperf */
  361         if (cpu_setperf != NULL)
  362                 cpu_setperf(perflevel);
  363 }
  364 
  365 int
  366 apm_record_event(struct apm_softc *sc, u_int type)
  367 {
  368         if (!apm_error && (sc->sc_flags & SCFLAG_OPEN) == 0) {
  369                 DPRINTF(("apm_record_event: no user waiting\n"));
  370                 apm_error++;
  371                 return 1;
  372         }
  373 
  374         apm_evindex++;
  375         KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, apm_evindex));
  376         return (0);
  377 }
  378 
  379 int
  380 apm_handle_event(struct apm_softc *sc, struct apmregs *regs)
  381 {
  382         struct apmregs nregs;
  383         int ret = 0;
  384 
  385         switch (regs->bx) {
  386         case APM_NOEVENT:
  387                 ret++;
  388                 break;
  389 
  390         case APM_USER_STANDBY_REQ:
  391                 if (apm_resumes || apm_op_inprog)
  392                         break;
  393                 DPRINTF(("user wants STANDBY--fat chance\n"));
  394                 apm_op_inprog++;
  395                 if (apm_record_event(sc, regs->bx)) {
  396                         DPRINTF(("standby ourselves\n"));
  397                         apm_userstandbys++;
  398                 }
  399                 break;
  400         case APM_STANDBY_REQ:
  401                 if (apm_resumes || apm_op_inprog)
  402                         break;
  403                 DPRINTF(("standby requested\n"));
  404                 if (apm_standbys || apm_suspends) {
  405                         DPRINTF(("premature standby\n"));
  406                         apm_error++;
  407                         ret++;
  408                 }
  409                 apm_op_inprog++;
  410                 if (apm_record_event(sc, regs->bx)) {
  411                         DPRINTF(("standby ourselves\n"));
  412                         apm_standbys++;
  413                 }
  414                 break;
  415         case APM_USER_SUSPEND_REQ:
  416                 if (apm_resumes || apm_op_inprog)
  417                         break;
  418                 DPRINTF(("user wants suspend--fat chance!\n"));
  419                 apm_op_inprog++;
  420                 if (apm_record_event(sc, regs->bx)) {
  421                         DPRINTF(("suspend ourselves\n"));
  422                         apm_suspends++;
  423                 }
  424                 break;
  425         case APM_SUSPEND_REQ:
  426                 if (apm_resumes || apm_op_inprog)
  427                         break;
  428                 DPRINTF(("suspend requested\n"));
  429                 if (apm_standbys || apm_suspends) {
  430                         DPRINTF(("premature suspend\n"));
  431                         apm_error++;
  432                         ret++;
  433                 }
  434                 apm_op_inprog++;
  435                 if (apm_record_event(sc, regs->bx)) {
  436                         DPRINTF(("suspend ourselves\n"));
  437                         apm_suspends++;
  438                 }
  439                 break;
  440         case APM_POWER_CHANGE:
  441                 DPRINTF(("power status change\n"));
  442                 if (apm_get_powstat(&nregs) == 0 &&
  443                     BATT_LIFE(&nregs) != APM_BATT_LIFE_UNKNOWN &&
  444                     BATT_LIFE(&nregs) < cpu_apmwarn &&
  445                     (sc->sc_flags & SCFLAG_PRINT) != SCFLAG_NOPRINT &&
  446                     ((sc->sc_flags & SCFLAG_PRINT) != SCFLAG_PCTPRINT ||
  447                      sc->batt_life != BATT_LIFE(&nregs)))
  448                         apm_power_print(sc, &nregs);
  449                 apm_record_event(sc, regs->bx);
  450                 break;
  451         case APM_NORMAL_RESUME:
  452                 DPRINTF(("system resumed\n"));
  453                 apm_resume(sc, regs);
  454                 break;
  455         case APM_CRIT_RESUME:
  456                 DPRINTF(("system resumed without us!\n"));
  457                 apm_resume(sc, regs);
  458                 break;
  459         case APM_SYS_STANDBY_RESUME:
  460                 DPRINTF(("system standby resume\n"));
  461                 apm_resume(sc, regs);
  462                 break;
  463         case APM_UPDATE_TIME:
  464                 DPRINTF(("update time, please\n"));
  465                 inittodr(time_second);
  466                 apm_record_event(sc, regs->bx);
  467                 break;
  468         case APM_CRIT_SUSPEND_REQ:
  469                 DPRINTF(("suspend required immediately\n"));
  470                 apm_record_event(sc, regs->bx);
  471                 apm_suspend();
  472                 break;
  473         case APM_BATTERY_LOW:
  474                 DPRINTF(("Battery low!\n"));
  475                 apm_battlow++;
  476                 apm_record_event(sc, regs->bx);
  477                 break;
  478         case APM_CAPABILITY_CHANGE:
  479                 DPRINTF(("capability change\n"));
  480                 if (apm_minver < 2) {
  481                         DPRINTF(("adult event\n"));
  482                 } else {
  483                         if (apmcall(APM_GET_CAPABILITIES, APM_DEV_APM_BIOS,
  484                             &nregs) != 0) {
  485                                 apm_perror("get capabilities", &nregs);
  486                         } else {
  487                                 apm_get_powstat(&nregs);
  488                         }
  489                 }
  490                 break;
  491         default: {
  492 #ifdef APMDEBUG
  493                 char *p;
  494                 switch (regs->bx >> 8) {
  495                 case 0: p = "reserved system";  break;
  496                 case 1: p = "reserved device";  break;
  497                 case 2: p = "OEM defined";      break;
  498                 default:p = "reserved";         break;
  499                 }
  500 #endif
  501                 DPRINTF(("apm_handle_event: %s event, code %d\n", p, regs->bx));
  502             }
  503         }
  504         return ret;
  505 }
  506 
  507 int
  508 apm_periodic_check(struct apm_softc *sc)
  509 {
  510         struct apmregs regs;
  511         int ret = 0;
  512 
  513         if (apm_op_inprog)
  514                 apm_set_powstate(APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
  515 
  516         while (1) {
  517                 if (apm_get_event(&regs) != 0) {
  518                         /* i think some bioses combine the error codes */
  519                         if (!(APM_ERR_CODE(&regs) & APM_ERR_NOEVENTS))
  520                                 apm_perror("get event", &regs);
  521                         break;
  522                 }
  523 
  524                 if (apm_handle_event(sc, &regs))
  525                         break;
  526         }
  527 
  528         if (apm_error || APM_ERR_CODE(&regs) == APM_ERR_NOTCONN)
  529                 ret = -1;
  530 
  531         if (apm_suspends /*|| (apm_battlow && apm_userstandbys)*/) {
  532                 apm_op_inprog = 0;
  533                 apm_suspend();
  534         } else if (apm_standbys || apm_userstandbys) {
  535                 apm_op_inprog = 0;
  536                 apm_standby();
  537         }
  538         apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
  539         apm_error = 0;
  540 
  541         if (apm_resumes)
  542                 apm_resumes--;
  543         return (ret);
  544 }
  545 
  546 void
  547 apm_powmgt_enable(int onoff)
  548 {
  549         struct apmregs regs;
  550 
  551         bzero(&regs, sizeof(regs));
  552         regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
  553         if (apmcall(APM_PWR_MGT_ENABLE,
  554             (apm_minver? APM_DEV_APM_BIOS : APM_MGT_ALL), &regs) != 0)
  555                 apm_perror("power management enable", &regs);
  556 }
  557 
  558 void
  559 apm_powmgt_engage(int onoff, u_int dev)
  560 {
  561         struct apmregs regs;
  562 
  563         if (apm_minver == 0)
  564                 return;
  565         bzero(&regs, sizeof(regs));
  566         regs.cx = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE;
  567         if (apmcall(APM_PWR_MGT_ENGAGE, dev, &regs) != 0)
  568                 printf("apm0: APM engage (device %x): %s (%d)\n",
  569                     dev, apm_err_translate(APM_ERR_CODE(&regs)),
  570                     APM_ERR_CODE(&regs));
  571 }
  572 
  573 #ifdef notused
  574 void
  575 apm_devpowmgt_enable(int onoff, u_int dev)
  576 {
  577         struct apmregs regs;
  578 
  579         if (apm_minver == 0)
  580                 return;
  581         /* enable is auto BIOS management.
  582          * disable is program control.
  583          */
  584         bzero(&regs, sizeof(regs));
  585         regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
  586         if (apmcall(APM_DEVICE_MGMT_ENABLE, dev, &regs) != 0)
  587                 printf("APM device engage (device %x): %s (%d)\n",
  588                     dev, apm_err_translate(APM_ERR_CODE(&regs)),
  589                     APM_ERR_CODE(&regs));
  590 }
  591 #endif
  592 
  593 int
  594 apm_set_powstate(u_int dev, u_int state)
  595 {
  596         struct apmregs regs;
  597 
  598         if (!apm_cd.cd_ndevs || (apm_minver == 0 && state > APM_SYS_OFF))
  599                 return EINVAL;
  600         bzero(&regs, sizeof(regs));
  601         regs.cx = state;
  602         if (apmcall(APM_SET_PWR_STATE, dev, &regs) != 0) {
  603                 apm_perror("set power state", &regs);
  604                 if (APM_ERR_CODE(&regs) == APM_ERR_UNRECOG_DEV)
  605                         return ENXIO;
  606                 else
  607                         return EIO;
  608         }
  609         return 0;
  610 }
  611 
  612 void
  613 apm_cpu_busy(void)
  614 {
  615         struct apmregs regs;
  616 
  617         if (!apm_cd.cd_ndevs)   /* No APM device, punt */
  618                 return;
  619         if (!apm_dobusy)
  620                 return;
  621         if (!apm_idle_called)
  622                 return;
  623 
  624         if (apm_flags & APM_IDLE_SLOWS) {
  625                 bzero(&regs, sizeof(regs));
  626                 if (apmcall(APM_CPU_BUSY, 0, &regs) != 0) {
  627 #ifdef DIAGNOSTIC
  628                         apm_perror("set CPU busy", &regs);
  629 #endif
  630                 }
  631                 apm_idle_called = 0;
  632         }
  633 }
  634 
  635 void
  636 apm_cpu_idle(void)
  637 {
  638         struct apmregs regs;
  639         static u_int64_t call_apm = 0;
  640 
  641         if (!apm_cd.cd_ndevs) { /* No APM device, wait for next interrupt */
  642                 __asm __volatile("sti;hlt");
  643                 return;
  644         }
  645 
  646         if (!apm_doidle) {
  647                 __asm __volatile("sti;hlt");
  648                 return;
  649         }
  650                 
  651         /*
  652          * We call the bios APM_IDLE routine here only when we
  653          * have been idle for some time - otherwise we just hlt.
  654          */
  655 
  656         if  (call_apm != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
  657                 /* Always call BIOS halt/idle stuff */
  658                 bzero(&regs, sizeof(regs));
  659                 if (apmcall(APM_CPU_IDLE, 0, &regs) != 0) {
  660 #ifdef APMDEBUG
  661                         apm_perror("set CPU idle", &regs);
  662 #endif
  663                 }
  664                 apm_idle_called = 1;
  665                 /* If BIOS did halt, don't do it again! */
  666                 if (apm_flags & APM_IDLE_SLOWS) {
  667                         __asm __volatile("sti;hlt");
  668                 }
  669                 call_apm = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
  670         } else {
  671                 __asm __volatile("sti;hlt");
  672         }
  673 }
  674 
  675 void
  676 apm_set_ver(struct apm_softc *self)
  677 {
  678         struct apmregs regs;
  679         int rv = 0;
  680 
  681         bzero(&regs, sizeof(regs));
  682         regs.cx = APM_VERSION;
  683 
  684         if (APM_MAJOR(apm_flags) == 1 && APM_MINOR(apm_flags) == 2 &&
  685             (rv = apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, &regs)) == 0) {
  686                 apm_majver = APM_CONN_MAJOR(&regs);
  687                 apm_minver = APM_CONN_MINOR(&regs);
  688         } else {
  689 #ifdef APMDEBUG
  690                 if (rv)
  691                         apm_perror("set version 1.2", &regs);
  692 #endif
  693                 /* try downgrading to 1.1 */
  694                 bzero(&regs, sizeof(regs));
  695                 regs.cx = 0x0101;
  696 
  697                 if (apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, &regs) == 0) {
  698                         apm_majver = 1;
  699                         apm_minver = 1;
  700                 } else {
  701 #ifdef APMDEBUG
  702                         apm_perror("set version 1.1", &regs);
  703 #endif
  704                         /* stay w/ flags then */
  705                         apm_majver = APM_MAJOR(apm_flags);
  706                         apm_minver = APM_MINOR(apm_flags);
  707 
  708                         /* fix version for some endianess-challenged compaqs */
  709                         if (!apm_majver) {
  710                                 apm_majver = 1;
  711                                 apm_minver = 0;
  712                         }
  713                 }
  714         }
  715         printf(": Power Management spec V%d.%d", apm_majver, apm_minver);
  716         if (apm_flags & APM_IDLE_SLOWS) {
  717                 DPRINTF((" (slowidle)"));
  718                 apm_dobusy = 1;
  719                 apm_doidle = 1;
  720         } else {
  721                 apm_dobusy = 0;
  722                 apm_doidle = 1;
  723         }
  724 #ifdef DIAGNOSTIC
  725         if (apm_flags & APM_BIOS_PM_DISABLED)
  726                 printf(" (BIOS mgmt disabled)");
  727         if (apm_flags & APM_BIOS_PM_DISENGAGED)
  728                 printf(" (BIOS managing devices)");
  729 #endif
  730         printf("\n");
  731 }
  732 
  733 void
  734 apm_disconnect(struct apm_softc *sc)
  735 {
  736         struct apmregs regs;
  737 
  738         bzero(&regs, sizeof(regs));
  739         if (apmcall(APM_SYSTEM_DEFAULTS,
  740             (apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL), &regs))
  741                 apm_perror("system defaults failed", &regs);
  742 
  743         if (apmcall(APM_DISCONNECT, APM_DEV_APM_BIOS, &regs))
  744                 apm_perror("disconnect failed", &regs);
  745         else
  746                 printf("%s: disconnected\n", sc->sc_dev.dv_xname);
  747         apm_flags |= APM_BIOS_PM_DISABLED;
  748 }
  749 
  750 int
  751 apmprobe(struct device *parent, void *match, void *aux)
  752 {
  753         struct bios_attach_args *ba = aux;
  754         bios_apminfo_t *ap = ba->bios_apmp;
  755         bus_space_handle_t ch, dh;
  756 
  757         if (apm_cd.cd_ndevs || strcmp(ba->bios_dev, "apm") ||
  758             !(ba->bios_apmp->apm_detail & APM_32BIT_SUPPORTED)) {
  759                 DPRINTF(("%s: %x\n", ba->bios_dev, ba->bios_apmp->apm_detail));
  760                 return 0;
  761         }
  762 
  763         /* addresses check
  764            since pc* console and vga* probes much later
  765            we cannot check for video memory being mapped
  766            for apm stuff w/ bus_space_map() */
  767         if (ap->apm_code_len == 0 ||
  768             (ap->apm_code32_base < IOM_BEGIN &&
  769              ap->apm_code32_base + ap->apm_code_len > IOM_BEGIN) ||
  770             (ap->apm_code16_base < IOM_BEGIN &&
  771              ap->apm_code16_base + ap->apm_code16_len > IOM_BEGIN) ||
  772             (ap->apm_data_base < IOM_BEGIN &&
  773              ap->apm_data_base + ap->apm_data_len > IOM_BEGIN))
  774                 return 0;
  775 
  776         if (bus_space_map(ba->bios_memt, ap->apm_code32_base,
  777             ap->apm_code_len, 1, &ch) != 0) {
  778                 DPRINTF(("apm0: can't map code\n"));
  779                 return 0;
  780         }
  781         bus_space_unmap(ba->bios_memt, ch, ap->apm_code_len);
  782 
  783         if (bus_space_map(ba->bios_memt, ap->apm_data_base,
  784             ap->apm_data_len, 1, &dh) != 0) {
  785                 DPRINTF(("apm0: can't map data\n"));
  786                 return 0;
  787         }
  788         bus_space_unmap(ba->bios_memt, dh, ap->apm_data_len);
  789         return 1;
  790 }
  791 
  792 void
  793 apmattach(struct device *parent, struct device *self, void *aux)
  794 {
  795         struct bios_attach_args *ba = aux;
  796         bios_apminfo_t *ap = ba->bios_apmp;
  797         struct apm_softc *sc = (void *)self;
  798         struct apmregs regs;
  799         u_int cbase, clen, l;
  800         bus_space_handle_t ch16, ch32, dh;
  801 
  802         apm_flags = ap->apm_detail;
  803         /*
  804          * set up GDT descriptors for APM
  805          */
  806         if (apm_flags & APM_32BIT_SUPPORTED) {
  807 
  808                 /* truncate segments' limits to a page */
  809                 ap->apm_code_len -= (ap->apm_code32_base +
  810                     ap->apm_code_len + 1) & 0xfff;
  811                 ap->apm_code16_len -= (ap->apm_code16_base +
  812                     ap->apm_code16_len + 1) & 0xfff;
  813                 ap->apm_data_len -= (ap->apm_data_base +
  814                     ap->apm_data_len + 1) & 0xfff;
  815 
  816                 /* adjust version */
  817                 if ((sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK) &&
  818                     (apm_flags & APM_VERMASK) !=
  819                     (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK))
  820                         apm_flags = (apm_flags & ~APM_VERMASK) |
  821                             (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK);
  822                 if (sc->sc_dev.dv_cfdata->cf_flags & APM_NOCLI) {
  823                         extern int apm_cli; /* from apmcall.S */
  824                         apm_cli = 0;
  825                 }
  826                 if (sc->sc_dev.dv_cfdata->cf_flags & APM_BEBATT)
  827                         apm_bebatt = 1;
  828                 apm_ep.seg = GSEL(GAPM32CODE_SEL,SEL_KPL);
  829                 apm_ep.entry = ap->apm_entry;
  830                 cbase = min(ap->apm_code32_base, ap->apm_code16_base);
  831                 clen = max(ap->apm_code32_base + ap->apm_code_len,
  832                            ap->apm_code16_base + ap->apm_code16_len) - cbase;
  833                 if ((cbase <= ap->apm_data_base &&
  834                      cbase + clen >= ap->apm_data_base) ||
  835                     (ap->apm_data_base <= cbase &&
  836                      ap->apm_data_base + ap->apm_data_len >= cbase)) {
  837                         l = max(ap->apm_data_base + ap->apm_data_len + 1,
  838                                 cbase + clen + 1) -
  839                             min(ap->apm_data_base, cbase);
  840                         bus_space_map(ba->bios_memt,
  841                                 min(ap->apm_data_base, cbase),
  842                                 l, 1, &dh);
  843                         ch16 = dh;
  844                         if (ap->apm_data_base < cbase)
  845                                 ch16 += cbase - ap->apm_data_base;
  846                         else
  847                                 dh += ap->apm_data_base - cbase;
  848                 } else {
  849 
  850                         bus_space_map(ba->bios_memt, cbase, clen + 1, 1, &ch16);
  851                         bus_space_map(ba->bios_memt, ap->apm_data_base,
  852                             ap->apm_data_len + 1, 1, &dh);
  853                 }
  854                 ch32 = ch16;
  855                 if (ap->apm_code16_base == cbase)
  856                         ch32 += ap->apm_code32_base - cbase;
  857                 else
  858                         ch16 += ap->apm_code16_base - cbase;
  859 
  860                 setgdt(GAPM32CODE_SEL, (void *)ch32, ap->apm_code_len,
  861                     SDT_MEMERA, SEL_KPL, 1, 0);
  862                 setgdt(GAPM16CODE_SEL, (void *)ch16, ap->apm_code16_len,
  863                     SDT_MEMERA, SEL_KPL, 0, 0);
  864                 setgdt(GAPMDATA_SEL, (void *)dh, ap->apm_data_len, SDT_MEMRWA,
  865                     SEL_KPL, 1, 0);
  866                 DPRINTF((": flags %x code 32:%x/%x[%x] 16:%x/%x[%x] "
  867                     "data %x/%x/%x ep %x (%x:%x)\n%s", apm_flags,
  868                     ap->apm_code32_base, ch32, ap->apm_code_len,
  869                     ap->apm_code16_base, ch16, ap->apm_code16_len,
  870                     ap->apm_data_base, dh, ap->apm_data_len,
  871                     ap->apm_entry, apm_ep.seg, ap->apm_entry+ch32,
  872                     sc->sc_dev.dv_xname));
  873 
  874                 apm_set_ver(sc);
  875 
  876                 if (apm_flags & APM_BIOS_PM_DISABLED)
  877                         apm_powmgt_enable(1);
  878                 /*
  879                  * Engage cooperative power mgt (we get to do it)
  880                  * on all devices (v1.1).
  881                  */
  882                 apm_powmgt_engage(1, APM_DEV_ALLDEVS);
  883 
  884                 bzero(&regs, sizeof(regs));
  885                 if (apm_get_powstat(&regs) == 0)
  886                         apm_power_print(sc, &regs);
  887                 else
  888                         apm_perror("get power status", &regs);
  889                 apm_cpu_busy();
  890 
  891                 rw_init(&sc->sc_lock, "apmlk");
  892 
  893                 /*
  894                  * Do a check once, ignoring any errors. This avoids
  895                  * gratuitous APM disconnects on laptops where the first
  896                  * event in the queue (after a boot) is non-recognizable.
  897                  * The IBM ThinkPad 770Z is one of those.
  898                  */
  899                 apm_periodic_check(sc);
  900 
  901                 if (apm_periodic_check(sc) == -1) {
  902                         apm_disconnect(sc);
  903                         apm_dobusy = apm_doidle = 0;
  904                 } else {
  905                         kthread_create_deferred(apm_thread_create, sc);
  906                         apm_attached = 1;
  907                 }
  908         } else {
  909                 setgdt(GAPM32CODE_SEL, NULL, 0, 0, 0, 0, 0);
  910                 setgdt(GAPM16CODE_SEL, NULL, 0, 0, 0, 0, 0);
  911                 setgdt(GAPMDATA_SEL, NULL, 0, 0, 0, 0, 0);
  912         }
  913         /* XXX - To go away */
  914         printf("apm0: flags %x dobusy %d doidle %d\n",
  915             apm_flags, apm_dobusy, apm_doidle);
  916 }
  917 
  918 void
  919 apm_thread_create(void *v)
  920 {
  921         struct apm_softc *sc = v;
  922 
  923 #ifdef MULTIPROCESSOR
  924         if (ncpus > 1) {
  925                 apm_disconnect(sc);
  926                 apm_dobusy = apm_doidle = 0;
  927                 return;
  928         }
  929 #endif
  930 
  931         if (kthread_create(apm_thread, sc, &sc->sc_thread, "%s",
  932             sc->sc_dev.dv_xname)) {
  933                 apm_disconnect(sc);
  934                 printf("%s: failed to create kernel thread, disabled",
  935                     sc->sc_dev.dv_xname);
  936                 apm_dobusy = apm_doidle = 0;
  937         }
  938 }
  939 
  940 void
  941 apm_thread(void *v)
  942 {
  943         struct apm_softc *sc = v;
  944 
  945         for (;;) {
  946                 rw_enter_write(&sc->sc_lock);
  947                 (void) apm_periodic_check(sc);
  948                 rw_exit_write(&sc->sc_lock);
  949                 tsleep(&lbolt, PWAIT, "apmev", 0);
  950         }
  951 }
  952 
  953 int
  954 apmopen(dev_t dev, int flag, int mode, struct proc *p)
  955 {
  956         struct apm_softc *sc;
  957         int error = 0;
  958 
  959         /* apm0 only */
  960         if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
  961             !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
  962                 return ENXIO;
  963 
  964         if (apm_flags & APM_BIOS_PM_DISABLED)
  965                 return ENXIO;
  966 
  967         DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
  968             APMDEV(dev), p->p_pid, flag, mode));
  969 
  970         rw_enter_write(&sc->sc_lock);
  971         switch (APMDEV(dev)) {
  972         case APMDEV_CTL:
  973                 if (!(flag & FWRITE)) {
  974                         error = EINVAL;
  975                         break;
  976                 }
  977                 if (sc->sc_flags & SCFLAG_OWRITE) {
  978                         error = EBUSY;
  979                         break;
  980                 }
  981                 sc->sc_flags |= SCFLAG_OWRITE;
  982                 break;
  983         case APMDEV_NORMAL:
  984                 if (!(flag & FREAD) || (flag & FWRITE)) {
  985                         error = EINVAL;
  986                         break;
  987                 }
  988                 sc->sc_flags |= SCFLAG_OREAD;
  989                 break;
  990         default:
  991                 error = ENXIO;
  992                 break;
  993         }
  994         rw_exit_write(&sc->sc_lock);
  995         return error;
  996 }
  997 
  998 int
  999 apmclose(dev_t dev, int flag, int mode, struct proc *p)
 1000 {
 1001         struct apm_softc *sc;
 1002 
 1003         /* apm0 only */
 1004         if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
 1005             !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
 1006                 return ENXIO;
 1007 
 1008         DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
 1009 
 1010         rw_enter_write(&sc->sc_lock);
 1011         switch (APMDEV(dev)) {
 1012         case APMDEV_CTL:
 1013                 sc->sc_flags &= ~SCFLAG_OWRITE;
 1014                 break;
 1015         case APMDEV_NORMAL:
 1016                 sc->sc_flags &= ~SCFLAG_OREAD;
 1017                 break;
 1018         }
 1019         rw_exit_write(&sc->sc_lock);
 1020         return 0;
 1021 }
 1022 
 1023 int
 1024 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
 1025 {
 1026         struct apm_softc *sc;
 1027         struct apmregs regs;
 1028         int error = 0;
 1029 
 1030         /* apm0 only */
 1031         if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
 1032             !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
 1033                 return ENXIO;
 1034 
 1035         rw_enter_write(&sc->sc_lock);
 1036         switch (cmd) {
 1037                 /* some ioctl names from linux */
 1038         case APM_IOC_STANDBY:
 1039                 if ((flag & FWRITE) == 0)
 1040                         error = EBADF;
 1041                 else
 1042                         apm_userstandbys++;
 1043                 break;
 1044         case APM_IOC_SUSPEND:
 1045                 if ((flag & FWRITE) == 0)
 1046                         error = EBADF;
 1047                 else
 1048                         apm_suspends++;
 1049                 break;
 1050         case APM_IOC_PRN_CTL:
 1051                 if ((flag & FWRITE) == 0)
 1052                         error = EBADF;
 1053                 else {
 1054                         int flag = *(int *)data;
 1055                         DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
 1056                         switch (flag) {
 1057                         case APM_PRINT_ON:      /* enable printing */
 1058                                 sc->sc_flags &= ~SCFLAG_PRINT;
 1059                                 break;
 1060                         case APM_PRINT_OFF: /* disable printing */
 1061                                 sc->sc_flags &= ~SCFLAG_PRINT;
 1062                                 sc->sc_flags |= SCFLAG_NOPRINT;
 1063                                 break;
 1064                         case APM_PRINT_PCT: /* disable some printing */
 1065                                 sc->sc_flags &= ~SCFLAG_PRINT;
 1066                                 sc->sc_flags |= SCFLAG_PCTPRINT;
 1067                                 break;
 1068                         default:
 1069                                 error = EINVAL;
 1070                                 break;
 1071                         }
 1072                 }
 1073                 break;
 1074         case APM_IOC_DEV_CTL:
 1075                 if ((flag & FWRITE) == 0)
 1076                         error = EBADF;
 1077                 else {
 1078                         struct apm_ctl *actl = (struct apm_ctl *)data;
 1079 
 1080                         bzero(&regs, sizeof(regs));
 1081                         if (!apmcall(APM_GET_POWER_STATE, actl->dev, &regs))
 1082                                 printf("%s: dev %04x state %04x\n",
 1083                                     sc->sc_dev.dv_xname, dev, regs.cx);
 1084 
 1085                         error = apm_set_powstate(actl->dev, actl->mode);
 1086                 }
 1087                 break;
 1088         case APM_IOC_GETPOWER:
 1089                 if (apm_get_powstat(&regs) == 0) {
 1090                         struct apm_power_info *powerp =
 1091                             (struct apm_power_info *)data;
 1092 
 1093                         bzero(powerp, sizeof(*powerp));
 1094                         if (BATT_LIFE(&regs) != APM_BATT_LIFE_UNKNOWN)
 1095                                 powerp->battery_life = BATT_LIFE(&regs);
 1096                         powerp->ac_state = AC_STATE(&regs);
 1097                         switch (apm_minver) {
 1098                         case 0:
 1099                                 if (!(BATT_FLAGS(&regs) & APM_BATT_FLAG_NOBATTERY))
 1100                                         powerp->battery_state = BATT_STATE(&regs);
 1101                                 break;
 1102                         case 1:
 1103                         default:
 1104                                 if (BATT_FLAGS(&regs) & APM_BATT_FLAG_HIGH)
 1105                                         powerp->battery_state = APM_BATT_HIGH;
 1106                                 else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_LOW)
 1107                                         powerp->battery_state = APM_BATT_LOW;
 1108                                 else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_CRITICAL)
 1109                                         powerp->battery_state = APM_BATT_CRITICAL;
 1110                                 else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_CHARGING)
 1111                                         powerp->battery_state = APM_BATT_CHARGING;
 1112                                 else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_NOBATTERY)
 1113                                         powerp->battery_state = APM_BATTERY_ABSENT;
 1114                                 else
 1115                                         powerp->battery_state = APM_BATT_UNKNOWN;
 1116                                 if (BATT_REM_VALID(&regs)) {
 1117                                         powerp->minutes_left = BATT_REMAINING(&regs);
 1118                                         if (apm_bebatt)
 1119                                                 powerp->minutes_left =
 1120                                                     swap16(powerp->minutes_left);
 1121                                 }
 1122                         }
 1123                 } else {
 1124                         apm_perror("ioctl get power status", &regs);
 1125                         error = EIO;
 1126                 }
 1127                 break;
 1128 
 1129         default:
 1130                 error = ENOTTY;
 1131         }
 1132 
 1133         rw_exit_write(&sc->sc_lock);
 1134         return error;
 1135 }
 1136 
 1137 void
 1138 filt_apmrdetach(struct knote *kn)
 1139 {
 1140         struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
 1141 
 1142         rw_enter_write(&sc->sc_lock);
 1143         SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
 1144         rw_exit_write(&sc->sc_lock);
 1145 }
 1146 
 1147 int
 1148 filt_apmread(struct knote *kn, long hint)
 1149 {
 1150         /* XXX weird kqueue_scan() semantics */
 1151         if (hint && !kn->kn_data)
 1152                 kn->kn_data = (int)hint;
 1153         return (1);
 1154 }
 1155 
 1156 int
 1157 apmkqfilter(dev_t dev, struct knote *kn)
 1158 {
 1159         struct apm_softc *sc;
 1160 
 1161         /* apm0 only */
 1162         if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
 1163             !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
 1164                 return ENXIO;
 1165 
 1166         switch (kn->kn_filter) {
 1167         case EVFILT_READ:
 1168                 kn->kn_fop = &apmread_filtops;
 1169                 break;
 1170         default:
 1171                 return (1);
 1172         }
 1173 
 1174         kn->kn_hook = (caddr_t)sc;
 1175 
 1176         rw_enter_write(&sc->sc_lock);
 1177         SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
 1178         rw_exit_write(&sc->sc_lock);
 1179         return (0);
 1180 }

/* [<][>][^][v][top][bottom][index][help] */