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

Cache object: ace188925d0cff3061ba2249f4078871


[ 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.