The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/apm/apm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * APM (Advanced Power Management) BIOS Device Driver
    3  *
    4  * Copyright (c) 1994 UKAI, Fumitoshi.
    5  * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>
    6  * Copyright (c) 1996 Nate Williams <nate@FreeBSD.org>
    7  * Copyright (c) 1997 Poul-Henning Kamp <phk@FreeBSD.org>
    8  *
    9  * This software may be used, modified, copied, and distributed, in
   10  * both source and binary form provided that the above copyright and
   11  * these terms are retained. Under no circumstances is the author
   12  * responsible for the proper functioning of this software, nor does
   13  * the author assume any responsibility for damages incurred with its
   14  * use.
   15  *
   16  * Sep, 1994    Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
   17  *
   18  * $FreeBSD$
   19  */
   20 
   21 #include "opt_devfs.h"
   22 #include "opt_vm86.h"
   23 
   24 #include <sys/param.h>
   25 #include <sys/systm.h>
   26 #include <sys/conf.h>
   27 #include <sys/kernel.h>
   28 #ifdef DEVFS
   29 #include <sys/devfsext.h>
   30 #endif /*DEVFS*/
   31 #include <sys/time.h>
   32 #include <sys/reboot.h>
   33 #include <sys/select.h>
   34 #include <sys/poll.h>
   35 #include <sys/fcntl.h>
   36 #include <sys/proc.h>
   37 #include <sys/uio.h>
   38 #include <sys/signalvar.h>
   39 #include <sys/sysctl.h>
   40 #include <i386/isa/isa_device.h>
   41 #include <machine/apm_bios.h>
   42 #include <machine/segments.h>
   43 #include <machine/clock.h>
   44 #include <vm/vm.h>
   45 #include <vm/vm_param.h>
   46 #include <vm/pmap.h>
   47 #include <sys/syslog.h>
   48 #include <i386/apm/apm_setup.h>
   49 #include <i386/apm/apm.h>
   50 
   51 #ifdef VM86
   52 #include <machine/psl.h>
   53 #include <machine/vm86.h>
   54 #endif
   55 
   56 /* Used by the apm_saver screen saver module */
   57 int apm_display __P((int newstate));
   58 struct apm_softc apm_softc;
   59 
   60 static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx));
   61 static void apm_resume __P((void));
   62 static int apm_check_function_supported __P((u_int version, u_int func));
   63 
   64 int     apm_evindex;
   65 
   66 #define SCFLAG_ONORMAL  0x0000001
   67 #define SCFLAG_OCTL     0x0000002
   68 #define SCFLAG_OPEN     (SCFLAG_ONORMAL|SCFLAG_OCTL)
   69 
   70 #define APMDEV(dev)     (minor(dev)&0x0f)
   71 #define APMDEV_NORMAL   0
   72 #define APMDEV_CTL      8
   73 
   74 static struct apmhook   *hook[NAPM_HOOK];               /* XXX */
   75 
   76 #define is_enabled(foo) ((foo) ? "enabled" : "disabled")
   77 
   78 /* Map version number to integer (keeps ordering of version numbers) */
   79 #define INTVERSION(major, minor)        ((major)*100 + (minor))
   80 
   81 static struct callout_handle apm_timeout_ch = 
   82     CALLOUT_HANDLE_INITIALIZER(&apm_timeout_ch);
   83 
   84 static timeout_t apm_timeout;
   85 static d_open_t apmopen;
   86 static d_close_t apmclose;
   87 static d_write_t apmwrite;
   88 static d_ioctl_t apmioctl;
   89 static d_poll_t apmpoll;
   90 
   91 #define CDEV_MAJOR 39
   92 static struct cdevsw apm_cdevsw = 
   93         { apmopen,      apmclose,       noread,         apmwrite,       /*39*/
   94           apmioctl,     nostop,         nullreset,      nodevtotty,/* APM */
   95           apmpoll,      nommap,         NULL ,  "apm"   ,NULL,  -1};
   96 
   97 /* setup APM GDT discriptors */
   98 static void
   99 setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code32_limit, u_int code16_limit, u_int data_limit)
  100 {
  101         /* setup 32bit code segment */
  102         gdt_segs[GAPMCODE32_SEL].ssd_base  = code32_base;
  103         gdt_segs[GAPMCODE32_SEL].ssd_limit = code32_limit;
  104 
  105         /* setup 16bit code segment */
  106         gdt_segs[GAPMCODE16_SEL].ssd_base  = code16_base;
  107         gdt_segs[GAPMCODE16_SEL].ssd_limit = code16_limit;
  108 
  109         /* setup data segment */
  110         gdt_segs[GAPMDATA_SEL  ].ssd_base  = data_base;
  111         gdt_segs[GAPMDATA_SEL  ].ssd_limit = data_limit;
  112 
  113         /* reflect these changes on physical GDT */
  114         ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd);
  115         ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd);
  116         ssdtosd(gdt_segs + GAPMDATA_SEL  , &gdt[GAPMDATA_SEL  ].sd);
  117 }
  118 
  119 /* 48bit far pointer. Do not staticize - used from apm_setup.s */
  120 struct addr48 {
  121         u_long          offset;
  122         u_short         segment;
  123 } apm_addr;
  124 
  125 static int apm_errno;
  126 
  127 static int apm_suspend_delay = 1;
  128 static int apm_standby_delay = 1;
  129 
  130 SYSCTL_INT(_machdep, OID_AUTO, apm_suspend_delay, CTLFLAG_RW, &apm_suspend_delay, 1, "");
  131 SYSCTL_INT(_machdep, OID_AUTO, apm_standby_delay, CTLFLAG_RW, &apm_standby_delay, 1, "");
  132 
  133 /*
  134  * return  0 if the function successfull,
  135  * return  1 if the function unsuccessfull,
  136  * return -1 if the function unsupported.
  137  */
  138 static int
  139 apm_do_int(struct apm_bios_arg *apap)
  140 {
  141         struct apm_softc *sc = &apm_softc;
  142         int errno = 0;
  143         u_long apm_func = apap->eax & 0x00ff;
  144 
  145         if (!apm_check_function_supported(sc->intversion, apm_func)) {
  146 #ifdef APM_DEBUG
  147                 printf("apm_bioscall: function 0x%x is not supported in v%d.%d\n
  148 ",
  149                         apm_func, sc->majorversion, sc->minorversion);
  150 
  151 #endif
  152                 return (-1);
  153         }
  154 
  155         sc->bios_busy = 1;
  156         errno = apm_bios_call(apap);
  157         sc->bios_busy = 0;
  158         return errno;
  159 }
  160 
  161 static int
  162 apm_int(u_long *eax, u_long *ebx, u_long *ecx, u_long *edx)
  163 {
  164         struct apm_bios_arg apa;
  165         int cf;
  166 
  167         apa.eax = *eax;
  168         apa.ebx = *ebx;
  169         apa.ecx = *ecx;
  170         apa.edx = *edx;
  171         apa.esi = 0;    /* clear register */
  172         apa.edi = 0;    /* clear register */
  173         cf = apm_do_int(&apa);
  174         *eax = apa.eax;
  175         *ebx = apa.ebx;
  176         *ecx = apa.ecx;
  177         *edx = apa.edx;
  178         apm_errno = ((*eax) >> 8) & 0xff;
  179         return cf;
  180 }
  181 
  182 
  183 /* check whether APM function is supported (1)  or not (0). */
  184 static int
  185 apm_check_function_supported(u_int version, u_int func)
  186 {
  187         /* except driver version */
  188         if (func == APM_DRVVERSION) {
  189                 return (1);
  190         }
  191 
  192         switch (version) {
  193         case INTVERSION(1, 0):
  194                 if (func > APM_GETPMEVENT) {
  195                         return (0); /* not supported */
  196                 }
  197                 break;
  198         case INTVERSION(1, 1):
  199                 if (func > APM_ENGAGEDISENGAGEPM &&
  200                     func < APM_OEMFUNC) {
  201                         return (0); /* not supported */
  202                 }
  203                 break;
  204         case INTVERSION(1, 2):
  205                 break;
  206         }
  207 
  208         return (1); /* supported */
  209 }
  210 
  211 /* enable/disable power management */
  212 static int
  213 apm_enable_disable_pm(int enable)
  214 {
  215         struct apm_softc *sc = &apm_softc;
  216 
  217         u_long eax, ebx, ecx, edx;
  218 
  219         eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM;
  220 
  221         if (sc->intversion >= INTVERSION(1, 1))
  222                 ebx  = PMDV_ALLDEV;
  223         else
  224                 ebx  = 0xffff;  /* APM version 1.0 only */
  225         ecx  = enable;
  226         edx = 0;
  227         return apm_int(&eax, &ebx, &ecx, &edx);
  228 }
  229 
  230 /* register driver version (APM 1.1 or later) */ 
  231 static void
  232 apm_driver_version(int version)
  233 {
  234         u_long eax, ebx, ecx, edx;
  235 
  236         /* First try APM 1.2 */
  237         eax = (APM_BIOS << 8) | APM_DRVVERSION;
  238         ebx  = 0x0;
  239         ecx  = version;
  240         edx = 0;
  241         if (!apm_int(&eax, &ebx, &ecx, &edx)) {
  242                 /*
  243                  * Some old BIOSes don't return
  244                  * the connection version in %ax.
  245                  */
  246                 if (eax == ((APM_BIOS << 8) | APM_DRVVERSION)) {
  247                         apm_version = version;
  248                 } else {
  249                         apm_version = eax & 0xffff;
  250                 }
  251         }
  252 }
  253 
  254 /* engage/disengage power management (APM 1.1 or later) */
  255 static int
  256 apm_engage_disengage_pm(int engage)
  257 {
  258         u_long eax, ebx, ecx, edx;
  259 
  260         eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM;
  261         ebx = PMDV_ALLDEV;
  262         ecx = engage;
  263         edx = 0;
  264         return(apm_int(&eax, &ebx, &ecx, &edx));
  265 }
  266 
  267 /* get PM event */
  268 static u_int
  269 apm_getevent(void)
  270 {
  271         u_long eax, ebx, ecx, edx;
  272 
  273         eax = (APM_BIOS << 8) | APM_GETPMEVENT;
  274 
  275         ebx = 0;
  276         ecx = 0;
  277         edx = 0;
  278         if (apm_int(&eax, &ebx, &ecx, &edx))
  279                 return PMEV_NOEVENT;
  280 
  281         return ebx & 0xffff;
  282 }
  283 
  284 /* suspend entire system */
  285 static int
  286 apm_suspend_system(int state)
  287 {
  288         u_long eax, ebx, ecx, edx;
  289 
  290         eax = (APM_BIOS << 8) | APM_SETPWSTATE;
  291         ebx = PMDV_ALLDEV;
  292         ecx = state;
  293         edx = 0;
  294 
  295         if (apm_int(&eax, &ebx, &ecx, &edx)) {
  296                 printf("Entire system suspend failure: errcode = %ld\n",
  297                         0xff & (eax >> 8));
  298                 return 1;
  299         }
  300         return 0;
  301 }
  302 
  303 /* Display control */
  304 /*
  305  * Experimental implementation: My laptop machine can't handle this function
  306  * If your laptop can control the display via APM, please inform me.
  307  *                            HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>
  308  */
  309 int
  310 apm_display(int newstate)
  311 {
  312         u_long eax, ebx, ecx, edx;
  313 
  314         eax = (APM_BIOS << 8) | APM_SETPWSTATE;
  315         ebx = PMDV_DISP0;
  316         ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND;
  317         edx = 0;
  318         if (apm_int(&eax, &ebx, &ecx, &edx)) {
  319                 printf("Display off failure: errcode = %ld\n",
  320                         0xff & (eax >> 8));
  321                 return 1;
  322         }
  323         return 0;
  324 }
  325 
  326 /*
  327  * Turn off the entire system.
  328  */
  329 static void
  330 apm_power_off(int howto, void *junk)
  331 {
  332         u_long eax, ebx, ecx, edx;
  333 
  334         /* Not halting powering off, or not active */
  335         if (!(howto & RB_POWEROFF) || !apm_softc.active)
  336                 return;
  337         eax = (APM_BIOS << 8) | APM_SETPWSTATE;
  338         ebx = PMDV_ALLDEV;
  339         ecx = PMST_OFF;
  340         edx = 0;
  341         apm_int(&eax, &ebx, &ecx, &edx);
  342 }
  343 
  344 /* APM Battery low handler */
  345 static void
  346 apm_battery_low(void)
  347 {
  348         printf("\007\007 * * * BATTERY IS LOW * * * \007\007");
  349 }
  350 
  351 /* APM hook manager */
  352 static struct apmhook *
  353 apm_add_hook(struct apmhook **list, struct apmhook *ah)
  354 {
  355         int s;
  356         struct apmhook *p, *prev;
  357 
  358 #ifdef APM_DEBUG
  359         printf("Add hook \"%s\"\n", ah->ah_name);
  360 #endif
  361 
  362         s = splhigh();
  363         if (ah == NULL)
  364                 panic("illegal apm_hook!");
  365         prev = NULL;
  366         for (p = *list; p != NULL; prev = p, p = p->ah_next)
  367                 if (p->ah_order > ah->ah_order)
  368                         break;
  369 
  370         if (prev == NULL) {
  371                 ah->ah_next = *list;
  372                 *list = ah;
  373         } else {
  374                 ah->ah_next = prev->ah_next;
  375                 prev->ah_next = ah;
  376         }
  377         splx(s);
  378         return ah;
  379 }
  380 
  381 static void
  382 apm_del_hook(struct apmhook **list, struct apmhook *ah)
  383 {
  384         int s;
  385         struct apmhook *p, *prev;
  386 
  387         s = splhigh();
  388         prev = NULL;
  389         for (p = *list; p != NULL; prev = p, p = p->ah_next)
  390                 if (p == ah)
  391                         goto deleteit;
  392         panic("Tried to delete unregistered apm_hook.");
  393         goto nosuchnode;
  394 deleteit:
  395         if (prev != NULL)
  396                 prev->ah_next = p->ah_next;
  397         else
  398                 *list = p->ah_next;
  399 nosuchnode:
  400         splx(s);
  401 }
  402 
  403 
  404 /* APM driver calls some functions automatically */
  405 static void
  406 apm_execute_hook(struct apmhook *list)
  407 {
  408         struct apmhook *p;
  409 
  410         for (p = list; p != NULL; p = p->ah_next) {
  411 #ifdef APM_DEBUG
  412                 printf("Execute APM hook \"%s.\"\n", p->ah_name);
  413 #endif
  414                 if ((*(p->ah_fun))(p->ah_arg))
  415                         printf("Warning: APM hook \"%s\" failed", p->ah_name);
  416         }
  417 }
  418 
  419 
  420 /* establish an apm hook */
  421 struct apmhook *
  422 apm_hook_establish(int apmh, struct apmhook *ah)
  423 {
  424         if (apmh < 0 || apmh >= NAPM_HOOK)
  425                 return NULL;
  426 
  427         return apm_add_hook(&hook[apmh], ah);
  428 }
  429 
  430 /* disestablish an apm hook */
  431 void
  432 apm_hook_disestablish(int apmh, struct apmhook *ah)
  433 {
  434         if (apmh < 0 || apmh >= NAPM_HOOK)
  435                 return;
  436 
  437         apm_del_hook(&hook[apmh], ah);
  438 }
  439 
  440 
  441 static struct timeval suspend_time;
  442 static struct timeval diff_time;
  443 
  444 static int
  445 apm_default_resume(void *arg)
  446 {
  447         int pl;
  448         u_int second, minute, hour;
  449         struct timeval resume_time, tmp_time;
  450 
  451         /* modified for adjkerntz */
  452         pl = splsoftclock();
  453         i8254_restore();                /* restore timer_freq and hz */
  454         inittodr(0);                    /* adjust time to RTC */
  455         microtime(&resume_time);
  456         getmicrotime(&tmp_time);
  457         timevaladd(&tmp_time, &diff_time);
  458 
  459 #ifdef FIXME
  460         /* XXX THIS DOESN'T WORK!!! */
  461         time = tmp_time;
  462 #endif
  463 
  464 #ifdef APM_FIXUP_CALLTODO
  465         /* Calculate the delta time suspended */
  466         timevalsub(&resume_time, &suspend_time);
  467         /* Fixup the calltodo list with the delta time. */
  468         adjust_timeout_calltodo(&resume_time);
  469 #endif /* APM_FIXUP_CALLTODOK */
  470         splx(pl);
  471 #ifndef APM_FIXUP_CALLTODO
  472         second = resume_time.tv_sec - suspend_time.tv_sec; 
  473 #else /* APM_FIXUP_CALLTODO */
  474         /* 
  475          * We've already calculated resume_time to be the delta between 
  476          * the suspend and the resume. 
  477          */
  478         second = resume_time.tv_sec; 
  479 #endif /* APM_FIXUP_CALLTODO */
  480         hour = second / 3600;
  481         second %= 3600;
  482         minute = second / 60;
  483         second %= 60;
  484         log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
  485                 hour, minute, second);
  486         return 0;
  487 }
  488 
  489 static int
  490 apm_default_suspend(void *arg)
  491 {
  492         int     pl;
  493 
  494         pl = splsoftclock();
  495         microtime(&diff_time);
  496         inittodr(0);
  497         microtime(&suspend_time);
  498         timevalsub(&diff_time, &suspend_time);
  499         splx(pl);
  500         return 0;
  501 }
  502 
  503 static int apm_record_event __P((struct apm_softc *, u_int));
  504 static void apm_processevent(void);
  505 
  506 static u_int apm_op_inprog = 0;
  507 
  508 static void
  509 apm_do_suspend(void)
  510 {
  511         struct apm_softc *sc = &apm_softc;
  512 
  513         if (!sc)
  514                 return;
  515 
  516         apm_op_inprog = 0;
  517         sc->suspends = sc->suspend_countdown = 0;
  518 
  519         if (sc->initialized) {
  520                 apm_execute_hook(hook[APM_HOOK_SUSPEND]);
  521                 if (apm_suspend_system(PMST_SUSPEND) == 0)
  522                         apm_processevent();
  523                 else
  524                         /* Failure, 'resume' the system again */
  525                         apm_execute_hook(hook[APM_HOOK_RESUME]);
  526         }
  527 }
  528 
  529 static void
  530 apm_do_standby(void)
  531 {
  532         struct apm_softc *sc = &apm_softc;
  533 
  534         if (!sc)
  535                 return;
  536 
  537         apm_op_inprog = 0;
  538         sc->standbys = sc->standby_countdown = 0;
  539 
  540         if (sc->initialized) {
  541                 /*
  542                  * As far as standby, we don't need to execute 
  543                  * all of suspend hooks.
  544                  */
  545                 apm_default_suspend(&apm_softc);
  546                 if (apm_suspend_system(PMST_STANDBY) == 0)
  547                         apm_processevent();
  548         }
  549 }
  550 
  551 static void
  552 apm_lastreq_notify(void)
  553 {
  554         u_long eax, ebx, ecx, edx;
  555 
  556         eax = (APM_BIOS << 8) | APM_SETPWSTATE;
  557         ebx = PMDV_ALLDEV;
  558         ecx = PMST_LASTREQNOTIFY;
  559         edx = 0;
  560 
  561         apm_int(&eax, &ebx, &ecx, &edx);
  562 }
  563 
  564 static int
  565 apm_lastreq_rejected(void)
  566 {
  567         u_long eax, ebx, ecx, edx;
  568 
  569         if (apm_op_inprog == 0) {
  570                 return 1;       /* no operation in progress */
  571         }
  572 
  573         eax = (APM_BIOS << 8) | APM_SETPWSTATE;
  574         ebx = PMDV_ALLDEV;
  575         ecx = PMST_LASTREQREJECT;
  576         edx = 0;
  577 
  578         if (apm_int(&eax, &ebx, &ecx, &edx)) {
  579 #ifdef APM_DEBUG
  580                 printf("apm_lastreq_rejected: failed\n");
  581 #endif
  582                 return 1;
  583         }
  584 
  585         apm_op_inprog = 0;
  586 
  587         return 0;
  588 }
  589 
  590 /*
  591  * Public interface to the suspend/resume:
  592  *
  593  * Execute suspend and resume hook before and after sleep, respectively.
  594  *
  595  */
  596 
  597 void
  598 apm_suspend(int state)
  599 {
  600         struct apm_softc *sc = &apm_softc;
  601 
  602         if (!sc->initialized)
  603                 return;
  604 
  605         switch (state) {
  606         case PMST_SUSPEND:
  607                 if (sc->suspends)
  608                         return;
  609                 sc->suspends++;
  610                 sc->suspend_countdown = apm_suspend_delay;
  611                 break;
  612         case PMST_STANDBY:
  613                 if (sc->standbys)
  614                         return;
  615                 sc->standbys++;
  616                 sc->standby_countdown = apm_standby_delay;
  617                 break;
  618         default:
  619                 printf("apm_suspend: Unknown Suspend state 0x%x\n", state);
  620                 return;
  621         }
  622 
  623         apm_op_inprog++;
  624         apm_lastreq_notify();
  625 }
  626 
  627 void
  628 apm_resume(void)
  629 {
  630         struct apm_softc *sc = &apm_softc;
  631 
  632         if (!sc)
  633                 return;
  634 
  635         if (sc->initialized)
  636                 apm_execute_hook(hook[APM_HOOK_RESUME]);
  637 }
  638 
  639 
  640 /* get APM information */
  641 static int
  642 apm_get_info(apm_info_t aip)
  643 {
  644         struct apm_softc *sc = &apm_softc;
  645         u_long eax, ebx, ecx, edx;
  646 
  647         eax = (APM_BIOS << 8) | APM_GETPWSTATUS;
  648         ebx = PMDV_ALLDEV;
  649         ecx = 0;
  650         edx = 0xffff;                   /* default to unknown battery time */
  651 
  652         if (apm_int(&eax, &ebx, &ecx, &edx))
  653                 return 1;
  654 
  655         aip->ai_infoversion = 1;
  656         aip->ai_acline      = (ebx >> 8) & 0xff;
  657         aip->ai_batt_stat   = ebx & 0xff;
  658         aip->ai_batt_life   = ecx & 0xff;
  659         aip->ai_major       = (u_int)sc->majorversion;
  660         aip->ai_minor       = (u_int)sc->minorversion;
  661         aip->ai_status      = (u_int)sc->active;
  662         edx &= 0xffff;
  663         if (edx == 0xffff)      /* Time is unknown */
  664                 aip->ai_batt_time = -1;
  665         else if (edx & 0x8000)  /* Time is in minutes */
  666                 aip->ai_batt_time = (edx & 0x7fff) * 60;
  667         else                    /* Time is in seconds */
  668                 aip->ai_batt_time = edx;
  669 
  670         eax = (APM_BIOS << 8) | APM_GETCAPABILITIES;
  671         ebx = 0;
  672         ecx = 0;
  673         edx = 0;
  674         if (apm_int(&eax, &ebx, &ecx, &edx)) {
  675                 aip->ai_batteries = -1; /* Unknown */
  676                 aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */
  677         } else {
  678                 aip->ai_batteries = ebx & 0xff;
  679                 aip->ai_capabilities = ecx & 0xf;
  680         }
  681 
  682         bzero(aip->ai_spare, sizeof aip->ai_spare);
  683 
  684         return 0;
  685 }
  686 
  687 
  688 /* inform APM BIOS that CPU is idle */
  689 void
  690 apm_cpu_idle(void)
  691 {
  692         struct apm_softc *sc = &apm_softc;
  693 
  694         if (sc->active) {
  695                 u_long eax, ebx, ecx, edx;
  696 
  697                 eax = (APM_BIOS <<8) | APM_CPUIDLE;
  698                 edx = ecx = ebx = 0;
  699                 apm_int(&eax, &ebx, &ecx, &edx);
  700         }
  701         /*
  702          * Some APM implementation halts CPU in BIOS, whenever
  703          * "CPU-idle" function are invoked, but swtch() of
  704          * FreeBSD halts CPU, therefore, CPU is halted twice
  705          * in the sched loop. It makes the interrupt latency
  706          * terribly long and be able to cause a serious problem
  707          * in interrupt processing. We prevent it by removing
  708          * "hlt" operation from swtch() and managed it under
  709          * APM driver.
  710          */
  711         if (!sc->active || sc->always_halt_cpu)
  712                 __asm("hlt");   /* wait for interrupt */
  713 }
  714 
  715 /* inform APM BIOS that CPU is busy */
  716 void
  717 apm_cpu_busy(void)
  718 {
  719         struct apm_softc *sc = &apm_softc;
  720 
  721         /*
  722          * The APM specification says this is only necessary if your BIOS
  723          * slows down the processor in the idle task, otherwise it's not
  724          * necessary.
  725          */
  726         if (sc->slow_idle_cpu && sc->active) {
  727                 u_long eax, ebx, ecx, edx;
  728 
  729                 eax = (APM_BIOS <<8) | APM_CPUBUSY;
  730                 edx = ecx = ebx = 0;
  731                 apm_int(&eax, &ebx, &ecx, &edx);
  732         }
  733 }
  734 
  735 
  736 /*
  737  * APM timeout routine:
  738  *
  739  * This routine is automatically called by timer once per second.
  740  */
  741 
  742 static void
  743 apm_timeout(void *dummy)
  744 {
  745         struct apm_softc *sc = &apm_softc;
  746 
  747         if (apm_op_inprog)
  748                 apm_lastreq_notify();
  749 
  750         if (sc->standbys && sc->standby_countdown-- <= 0)
  751                 apm_do_standby();
  752 
  753         if (sc->suspends && sc->suspend_countdown-- <= 0)
  754                 apm_do_suspend();
  755 
  756         if (!sc->bios_busy)
  757                 apm_processevent();
  758   
  759         if (sc->active == 1)
  760                 /* Run slightly more oftan than 1 Hz */
  761                 apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 );
  762 }
  763 
  764 /* enable APM BIOS */
  765 static void
  766 apm_event_enable(void)
  767 {
  768         struct apm_softc *sc = &apm_softc;
  769 
  770 #ifdef APM_DEBUG
  771         printf("called apm_event_enable()\n");
  772 #endif
  773         if (sc->initialized) {
  774                 sc->active = 1;
  775                 apm_timeout(sc);
  776         }
  777 }
  778 
  779 /* disable APM BIOS */
  780 static void
  781 apm_event_disable(void)
  782 {
  783         struct apm_softc *sc = &apm_softc;
  784 
  785 #ifdef APM_DEBUG
  786         printf("called apm_event_disable()\n");
  787 #endif
  788         if (sc->initialized) {
  789                 untimeout(apm_timeout, NULL, apm_timeout_ch);
  790                 sc->active = 0;
  791         }
  792 }
  793 
  794 /* halt CPU in scheduling loop */
  795 static void
  796 apm_halt_cpu(void)
  797 {
  798         struct apm_softc *sc = &apm_softc;
  799 
  800         if (sc->initialized)
  801                 sc->always_halt_cpu = 1;
  802 }
  803 
  804 /* don't halt CPU in scheduling loop */
  805 static void
  806 apm_not_halt_cpu(void)
  807 {
  808         struct apm_softc *sc = &apm_softc;
  809 
  810         if (sc->initialized)
  811                 sc->always_halt_cpu = 0;
  812 }
  813 
  814 /* device driver definitions */
  815 static int apmprobe (struct isa_device *);
  816 static int apmattach(struct isa_device *);
  817 struct isa_driver apmdriver = { apmprobe, apmattach, "apm" };
  818 
  819 /*
  820  * probe APM (dummy):
  821  *
  822  * APM probing routine is placed on locore.s and apm_init.S because
  823  * this process forces the CPU to turn to real mode or V86 mode.
  824  * Current version uses real mode, but in a future version, we want
  825  * to use V86 mode in APM initialization.
  826  * 
  827  * XXX If VM86 is defined, we do.
  828  */
  829 
  830 static int
  831 apmprobe(struct isa_device *dvp)
  832 {
  833 #ifdef VM86
  834         struct vm86frame        vmf;
  835         int                     i;
  836 #endif
  837 
  838         if ( dvp->id_unit > 0 ) {
  839                 printf("apm: Only one APM driver supported.\n");
  840                 return 0;
  841         }
  842 
  843 #ifdef VM86
  844         bzero(&vmf, sizeof(struct vm86frame));          /* safety */
  845         vmf.vmf_ax = (APM_BIOS << 8) | APM_INSTCHECK;
  846         vmf.vmf_bx = 0;
  847         if (((i = vm86_intcall(SYSTEM_BIOS, &vmf)) == 0) &&
  848             !(vmf.vmf_eflags & PSL_C) && 
  849             (vmf.vmf_bx == 0x504d)) {
  850 
  851                 apm_version   = vmf.vmf_ax;
  852                 apm_flags     = vmf.vmf_cx;
  853 
  854                 vmf.vmf_ax = (APM_BIOS << 8) | APM_PROT32CONNECT;
  855                 vmf.vmf_bx = 0;
  856                 if (((i = vm86_intcall(SYSTEM_BIOS, &vmf)) == 0) &&
  857                     !(vmf.vmf_eflags & PSL_C)) {
  858 
  859                         apm_cs32_base = vmf.vmf_ax;
  860                         apm_cs_entry  = vmf.vmf_ebx;
  861                         apm_cs16_base = vmf.vmf_cx;
  862                         apm_ds_base   = vmf.vmf_dx;
  863                         apm_cs32_limit  = vmf.vmf_si;
  864                         if (apm_version >= 0x0102)
  865                                 apm_cs16_limit = (vmf.esi.r_ex >> 16);
  866                         apm_ds_limit  = vmf.vmf_di;
  867 #ifdef APM_DEBUG
  868                         printf("apm: BIOS probe/32-bit connect successful\n");
  869 #endif
  870                 } else {
  871                         /* XXX constant typo! */
  872                         if (vmf.vmf_ah == APME_PROT32NOTDUPPORTED) {
  873                                 apm_version = APMINI_NOT32BIT;
  874                         } else {
  875                                 apm_version = APMINI_CONNECTERR;
  876                         }
  877 #ifdef APM_DEBUG
  878                         printf("apm: BIOS 32-bit connect failed: error 0x%x  carry %d  ah 0x%x\n",
  879                                i, (vmf.vmf_eflags & PSL_C) ? 1 : 0, vmf.vmf_ah);
  880 #endif
  881                 }
  882         } else {
  883                 apm_version = APMINI_CANTFIND;
  884 #ifdef APM_DEBUG
  885                 printf("apm: BIOS probe failed: error 0x%x  carry %d  bx 0x%x\n",
  886                        i, (vmf.vmf_eflags & PSL_C) ? 1 : 0, vmf.vmf_bx);
  887 #endif
  888         }
  889 #endif
  890 
  891         bzero(&apm_softc, sizeof(apm_softc));
  892 
  893         switch (apm_version) {
  894         case APMINI_CANTFIND:
  895                 /* silent */
  896                 return ENXIO;
  897         case APMINI_NOT32BIT:
  898                 printf("apm: 32bit connection is not supported.\n");
  899                 return ENXIO;
  900         case APMINI_CONNECTERR:
  901                 printf("apm: 32-bit connection error.\n");
  902                 return ENXIO;
  903         }
  904         if (dvp->id_flags & 0x20)
  905                 statclock_disable = 1;
  906         return -1;
  907 }
  908 
  909 /*
  910  * return 0 if the user will notice and handle the event,
  911  * return 1 if the kernel driver should do so.
  912  */
  913 static int
  914 apm_record_event(struct apm_softc *sc, u_int event_type)
  915 {
  916         struct apm_event_info *evp;
  917 
  918         if ((sc->sc_flags & SCFLAG_OPEN) == 0)
  919                 return 1;               /* no user waiting */
  920         if (sc->event_count == APM_NEVENTS)
  921                 return 1;                       /* overflow */
  922         if (sc->event_filter[event_type] == 0)
  923                 return 1;               /* not registered */
  924         evp = &sc->event_list[sc->event_ptr];
  925         sc->event_count++;
  926         sc->event_ptr++;
  927         sc->event_ptr %= APM_NEVENTS;
  928         evp->type = event_type;
  929         evp->index = ++apm_evindex;
  930         selwakeup(&sc->sc_rsel);
  931         return (sc->sc_flags & SCFLAG_OCTL) ? 0 : 1; /* user may handle */
  932 }
  933 
  934 /* Process APM event */
  935 static void
  936 apm_processevent(void)
  937 {
  938         int apm_event;
  939         struct apm_softc *sc = &apm_softc;
  940 
  941 #ifdef APM_DEBUG
  942 #  define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
  943         printf("Received APM Event: " #symbol "\n");
  944 #else
  945 #  define OPMEV_DEBUGMESSAGE(symbol) case symbol:
  946 #endif
  947         do {
  948                 apm_event = apm_getevent();
  949                 switch (apm_event) {
  950                     OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
  951                         if (apm_op_inprog == 0) {
  952                             apm_op_inprog++;
  953                             if (apm_record_event(sc, apm_event)) {
  954                                 apm_suspend(PMST_STANDBY);
  955                             }
  956                         }
  957                         break;
  958                     OPMEV_DEBUGMESSAGE(PMEV_USERSTANDBYREQ);
  959                         if (apm_op_inprog == 0) {
  960                             apm_op_inprog++;
  961                             if (apm_record_event(sc, apm_event)) {
  962                                 apm_suspend(PMST_STANDBY);
  963                             }
  964                         }
  965                         break;
  966                     OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
  967                         apm_lastreq_notify();
  968                         if (apm_op_inprog == 0) {
  969                             apm_op_inprog++;
  970                             if (apm_record_event(sc, apm_event)) {
  971                                 apm_do_suspend();
  972                             }
  973                         }
  974                         return; /* XXX skip the rest */
  975                     OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
  976                         apm_lastreq_notify();
  977                         if (apm_op_inprog == 0) {
  978                             apm_op_inprog++;
  979                             if (apm_record_event(sc, apm_event)) {
  980                                 apm_do_suspend();
  981                             }
  982                         }
  983                         return; /* XXX skip the rest */
  984                     OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
  985                         apm_do_suspend();
  986                         break;
  987                     OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
  988                         apm_record_event(sc, apm_event);
  989                         apm_resume();
  990                         break;
  991                     OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
  992                         apm_record_event(sc, apm_event);
  993                         apm_resume();
  994                         break;
  995                     OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
  996                         apm_record_event(sc, apm_event);
  997                         apm_resume();
  998                         break;
  999                     OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
 1000                         if (apm_record_event(sc, apm_event)) {
 1001                             apm_battery_low();
 1002                             apm_suspend(PMST_SUSPEND);
 1003                         }
 1004                         break;
 1005                     OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
 1006                         apm_record_event(sc, apm_event);
 1007                         break;
 1008                     OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
 1009                         apm_record_event(sc, apm_event);
 1010                         inittodr(0);    /* adjust time to RTC */
 1011                         break;
 1012                     OPMEV_DEBUGMESSAGE(PMEV_CAPABILITIESCHANGE);
 1013                         apm_record_event(sc, apm_event);
 1014                         break;
 1015                     case PMEV_NOEVENT:
 1016                         break;
 1017                     default:
 1018                         printf("Unknown Original APM Event 0x%x\n", apm_event);
 1019                             break;
 1020                 }
 1021         } while (apm_event != PMEV_NOEVENT);
 1022 }
 1023 
 1024 /*
 1025  * Attach APM:
 1026  *
 1027  * Initialize APM driver (APM BIOS itself has been initialized in locore.s)
 1028  */
 1029 
 1030 static int
 1031 apmattach(struct isa_device *dvp)
 1032 {
 1033 #define APM_KERNBASE    KERNBASE
 1034         struct apm_softc        *sc = &apm_softc;
 1035 
 1036         sc->initialized = 0;
 1037 
 1038         /* Must be externally enabled */
 1039         sc->active = 0;
 1040 
 1041         /* setup APM parameters */
 1042         sc->cs16_base = (apm_cs16_base << 4) + APM_KERNBASE;
 1043         sc->cs32_base = (apm_cs32_base << 4) + APM_KERNBASE;
 1044         sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE;
 1045         sc->cs32_limit = apm_cs32_limit - 1;
 1046         if (apm_cs16_limit == 0)
 1047             apm_cs16_limit = apm_cs32_limit;
 1048         sc->cs16_limit = apm_cs16_limit - 1;
 1049         sc->ds_limit = apm_ds_limit - 1;
 1050         sc->cs_entry = apm_cs_entry;
 1051 
 1052         if (!(dvp->id_flags & 0x40)) {
 1053                 /* Don't trust the segment limits that the BIOS reports. */
 1054                 sc->cs32_limit = 0xffff;
 1055                 sc->cs16_limit = 0xffff;
 1056                 sc->ds_limit   = 0xffff;
 1057         }
 1058 
 1059         /* Always call HLT in idle loop */
 1060         sc->always_halt_cpu = 1;
 1061 
 1062         sc->slow_idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0);
 1063         sc->disabled = ((apm_flags & APM_DISABLED) != 0);
 1064         sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0);
 1065 
 1066         /* print bootstrap messages */
 1067 #ifdef APM_DEBUG
 1068         printf("apm: APM BIOS version %04x\n",  apm_version);
 1069         printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n",
 1070                 sc->cs32_base, sc->cs16_base, sc->ds_base);
 1071         printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n",
 1072                 sc->cs_entry, is_enabled(sc->slow_idle_cpu),
 1073                 is_enabled(!sc->disabled));
 1074         printf("apm: CS32_limit=0x%x, CS16_limit=0x%x, DS_limit=0x%x\n",
 1075                 (u_short)sc->cs32_limit, (u_short)sc->cs16_limit, (u_short)sc->ds_limit);
 1076 #endif /* APM_DEBUG */
 1077 
 1078 #if 0
 1079         /* Workaround for some buggy APM BIOS implementations */
 1080         sc->cs_limit = 0xffff;
 1081         sc->ds_limit = 0xffff;
 1082 #endif
 1083 
 1084         /* setup GDT */
 1085         setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base,
 1086                         sc->cs32_limit, sc->cs16_limit, sc->ds_limit);
 1087 
 1088         /* setup entry point 48bit pointer */
 1089         apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
 1090         apm_addr.offset  = sc->cs_entry;
 1091 
 1092         if ((dvp->id_flags & 0x10)) {
 1093                 if ((dvp->id_flags & 0xf) >= 0x2) {
 1094                         apm_driver_version(0x102);
 1095                 } 
 1096                 if (!apm_version && (dvp->id_flags & 0xf) >= 0x1) {
 1097                         apm_driver_version(0x101);
 1098                 }
 1099         } else {
 1100                 apm_driver_version(0x102);
 1101                 if (!apm_version)
 1102                         apm_driver_version(0x101);
 1103         } 
 1104         if (!apm_version)
 1105                 apm_version = 0x100;
 1106 
 1107         sc->minorversion = ((apm_version & 0x00f0) >>  4) * 10 +
 1108                         ((apm_version & 0x000f) >> 0);
 1109         sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 +
 1110                         ((apm_version & 0x0f00) >> 8);
 1111 
 1112         sc->intversion = INTVERSION(sc->majorversion, sc->minorversion);
 1113 
 1114 #ifdef APM_DEBUG
 1115         if (sc->intversion >= INTVERSION(1, 1))
 1116                 printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged));
 1117 #endif
 1118 
 1119         printf("apm: found APM BIOS version %d.%d\n",
 1120                 sc->majorversion, sc->minorversion);
 1121 
 1122 #ifdef APM_DEBUG
 1123         printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu));
 1124 #endif
 1125 
 1126         /* enable power management */
 1127         if (sc->disabled) {
 1128                 if (apm_enable_disable_pm(1)) {
 1129 #ifdef APM_DEBUG
 1130                         printf("apm: *Warning* enable function failed! [%x]\n",
 1131                                 apm_errno);
 1132 #endif
 1133                 }
 1134         }
 1135 
 1136         /* engage power managment (APM 1.1 or later) */
 1137         if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) {
 1138                 if (apm_engage_disengage_pm(1)) {
 1139 #ifdef APM_DEBUG
 1140                         printf("apm: *Warning* engage function failed err=[%x]",
 1141                                 apm_errno);
 1142                         printf(" (Docked or using external power?).\n");
 1143 #endif
 1144                 }
 1145         }
 1146 
 1147         /* default suspend hook */
 1148         sc->sc_suspend.ah_fun = apm_default_suspend;
 1149         sc->sc_suspend.ah_arg = sc;
 1150         sc->sc_suspend.ah_name = "default suspend";
 1151         sc->sc_suspend.ah_order = APM_MAX_ORDER;
 1152 
 1153         /* default resume hook */
 1154         sc->sc_resume.ah_fun = apm_default_resume;
 1155         sc->sc_resume.ah_arg = sc;
 1156         sc->sc_resume.ah_name = "default resume";
 1157         sc->sc_resume.ah_order = APM_MIN_ORDER;
 1158 
 1159         apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend);
 1160         apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume);
 1161 
 1162         /* Power the system off using APM */
 1163         at_shutdown_pri(apm_power_off, NULL, SHUTDOWN_FINAL, SHUTDOWN_PRI_LAST);
 1164 
 1165         sc->initialized = 1;
 1166 
 1167 #ifdef DEVFS
 1168         sc->sc_devfs_token = 
 1169                 devfs_add_devswf(&apm_cdevsw, 0, DV_CHR, 0, 0, 0600, "apm");
 1170 #endif
 1171         return 0;
 1172 }
 1173 
 1174 static int
 1175 apmopen(dev_t dev, int flag, int fmt, struct proc *p)
 1176 {
 1177         struct apm_softc *sc = &apm_softc;
 1178         int ctl = APMDEV(dev);
 1179 
 1180         if (!sc->initialized)
 1181                 return (ENXIO);
 1182 
 1183         switch (ctl) {
 1184         case APMDEV_CTL:
 1185                 if (!(flag & FWRITE))
 1186                         return EINVAL;
 1187                 if (sc->sc_flags & SCFLAG_OCTL)
 1188                         return EBUSY;
 1189                 sc->sc_flags |= SCFLAG_OCTL;
 1190                 bzero(sc->event_filter, sizeof sc->event_filter);
 1191                 break;
 1192         case APMDEV_NORMAL:
 1193                 sc->sc_flags |= SCFLAG_ONORMAL;
 1194                 break;
 1195         default:
 1196                 return ENXIO;
 1197                 break;
 1198         }
 1199         return 0;
 1200 }
 1201 
 1202 static int
 1203 apmclose(dev_t dev, int flag, int fmt, struct proc *p)
 1204 {
 1205         struct apm_softc *sc = &apm_softc;
 1206         int ctl = APMDEV(dev);
 1207 
 1208         switch (ctl) {
 1209         case APMDEV_CTL:
 1210                 apm_lastreq_rejected();
 1211                 sc->sc_flags &= ~SCFLAG_OCTL;
 1212                 bzero(sc->event_filter, sizeof sc->event_filter);
 1213                 break;
 1214         case APMDEV_NORMAL:
 1215                 sc->sc_flags &= ~SCFLAG_ONORMAL;
 1216                 break;
 1217         }
 1218         if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
 1219                 sc->event_count = 0;
 1220                 sc->event_ptr = 0;
 1221         }
 1222         return 0;
 1223 }
 1224 
 1225 static int
 1226 apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
 1227 {
 1228         struct apm_softc *sc = &apm_softc;
 1229         int error = 0;
 1230         int ret;
 1231         int newstate;
 1232 
 1233         if (!sc->initialized)
 1234                 return (ENXIO);
 1235 #ifdef APM_DEBUG
 1236         printf("APM ioctl: cmd = 0x%x\n", cmd);
 1237 #endif
 1238         switch (cmd) {
 1239         case APMIO_SUSPEND:
 1240                 if (sc->active)
 1241                         apm_suspend(PMST_SUSPEND);
 1242                 else
 1243                         error = EINVAL;
 1244                 break;
 1245 
 1246         case APMIO_STANDBY:
 1247                 if (sc->active)
 1248                         apm_suspend(PMST_STANDBY);
 1249                 else
 1250                         error = EINVAL;
 1251                 break;
 1252 
 1253         case APMIO_GETINFO_OLD:
 1254                 {
 1255                         struct apm_info info;
 1256                         apm_info_old_t aiop;
 1257 
 1258                         if (apm_get_info(&info))
 1259                                 error = ENXIO;
 1260                         aiop = (apm_info_old_t)addr;
 1261                         aiop->ai_major = info.ai_major;
 1262                         aiop->ai_minor = info.ai_minor;
 1263                         aiop->ai_acline = info.ai_acline;
 1264                         aiop->ai_batt_stat = info.ai_batt_stat;
 1265                         aiop->ai_batt_life = info.ai_batt_life;
 1266                         aiop->ai_status = info.ai_status;
 1267                 }
 1268                 break;
 1269         case APMIO_GETINFO:
 1270                 if (apm_get_info((apm_info_t)addr))
 1271                         error = ENXIO;
 1272                 break;
 1273         case APMIO_ENABLE:
 1274                 apm_event_enable();
 1275                 break;
 1276         case APMIO_DISABLE:
 1277                 apm_event_disable();
 1278                 break;
 1279         case APMIO_HALTCPU:
 1280                 apm_halt_cpu();
 1281                 break;
 1282         case APMIO_NOTHALTCPU:
 1283                 apm_not_halt_cpu();
 1284                 break;
 1285         case APMIO_DISPLAY:
 1286                 newstate = *(int *)addr;
 1287                 if (apm_display(newstate))
 1288                         error = ENXIO;
 1289                 break;
 1290         case APMIO_BIOS:
 1291                 if ((ret = apm_do_int((struct apm_bios_arg*)addr))) {
 1292                         /*
 1293                          * Return code 1 means bios call was unsuccessful.
 1294                          * Error code is stored in %ah.
 1295                          * Return code -1 means bios call was unsupported
 1296                          * in the APM BIOS version.
 1297                          */
 1298                         if (ret == -1) {
 1299                                 error = EINVAL;
 1300                         }
 1301                 } else {
 1302                         /*
 1303                          * Return code 0 means bios call was successful.
 1304                          * We need only %al and can discard %ah.
 1305                          */
 1306                         ((struct apm_bios_arg*)addr)->eax &= 0xff;
 1307                 }
 1308                 break;
 1309         default:
 1310                 error = EINVAL;
 1311                 break;
 1312         }
 1313 
 1314         /* for /dev/apmctl */
 1315         if (APMDEV(dev) == APMDEV_CTL) {
 1316                 struct apm_event_info *evp;
 1317                 int i;
 1318 
 1319                 error = 0;
 1320                 switch (cmd) {
 1321                 case APMIO_NEXTEVENT:
 1322                         if (!sc->event_count) {
 1323                                 error = EAGAIN;
 1324                         } else {
 1325                                 evp = (struct apm_event_info *)addr;
 1326                                 i = sc->event_ptr + APM_NEVENTS - sc->event_count;
 1327                                 i %= APM_NEVENTS;
 1328                                 *evp = sc->event_list[i];
 1329                                 sc->event_count--;
 1330                         }
 1331                         break;
 1332                 case APMIO_REJECTLASTREQ:
 1333                         if (apm_lastreq_rejected()) {
 1334                                 error = EINVAL;
 1335                         }
 1336                         break;
 1337                 default:
 1338                         error = EINVAL;
 1339                         break;
 1340                 }
 1341         }
 1342 
 1343         return error;
 1344 }
 1345 
 1346 static int
 1347 apmwrite(dev_t dev, struct uio *uio, int ioflag)
 1348 {
 1349         struct apm_softc *sc = &apm_softc;
 1350         u_int event_type;
 1351         int error;
 1352         u_char enabled;
 1353 
 1354         if (APMDEV(dev) != APMDEV_CTL)
 1355                 return(ENODEV);
 1356         if (uio->uio_resid != sizeof(u_int))
 1357                 return(E2BIG);
 1358 
 1359         if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio)))
 1360                 return(error);
 1361 
 1362         if (event_type < 0 || event_type >= APM_NPMEV)
 1363                 return(EINVAL);
 1364 
 1365         if (sc->event_filter[event_type] == 0) {
 1366                 enabled = 1;
 1367         } else {
 1368                 enabled = 0;
 1369         }
 1370         sc->event_filter[event_type] = enabled;
 1371 #ifdef APM_DEBUG
 1372         printf("apmwrite: event 0x%x %s\n", event_type, is_enabled(enabled));
 1373 #endif
 1374 
 1375         return uio->uio_resid;
 1376 }
 1377 
 1378 static int
 1379 apmpoll(dev_t dev, int events, struct proc *p)
 1380 {
 1381         struct apm_softc *sc = &apm_softc;
 1382         int revents = 0;
 1383 
 1384         if (events & (POLLIN | POLLRDNORM)) {
 1385                 if (sc->event_count) {
 1386                         revents |= events & (POLLIN | POLLRDNORM);
 1387                 } else {
 1388                         selrecord(p, &sc->sc_rsel);
 1389                 }
 1390         }
 1391 
 1392         return (revents);
 1393 }
 1394 
 1395 
 1396 static int apm_devsw_installed = 0;
 1397 
 1398 static void
 1399 apm_drvinit(void *unused)
 1400 {
 1401         dev_t dev;
 1402 
 1403         if( ! apm_devsw_installed ) {
 1404                 dev = makedev(CDEV_MAJOR,0);
 1405                 cdevsw_add(&dev,&apm_cdevsw,NULL);
 1406                 apm_devsw_installed = 1;
 1407         }
 1408 }
 1409 
 1410 SYSINIT(apmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,apm_drvinit,NULL)

Cache object: dbd8c5d2b2e8aef08cb00132f59b526f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.