1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/ksynch.h>
  28 #include <sys/kmem.h>
  29 #include <sys/file.h>
  30 #include <sys/errno.h>
  31 #include <sys/open.h>
  32 #include <sys/cred.h>
  33 #include <sys/conf.h>
  34 #include <sys/uio.h>
  35 #include <sys/cmn_err.h>
  36 
  37 #define __NSC_GEN__
  38 #include "nsc_dev.h"
  39 #include "nsc_ioctl.h"
  40 #include "nsc_power.h"
  41 #include "../nsctl.h"
  42 
  43 extern nsc_mem_t *_nsc_local_mem;
  44 static  int null_power(void);
  45 
  46 
  47 typedef struct _nsc_power_s {
  48         struct _nsc_power_s *next;      /* chain */
  49         char *name;                     /* module name */
  50         void (*pw_power_lost)(int);     /* callback power lost(rideout) */
  51         void (*pw_power_ok)(void);      /* callback power ok */
  52         void (*pw_power_down)(void);
  53                                 /* callback power down (shutdown imminent) */
  54 } _nsc_power_t;
  55 
  56 #define _P(x)   (((long)(&((_nsc_power_t *)0)->x))/sizeof (long))
  57 
  58 static nsc_def_t _nsc_power_def[] = {
  59         { "Power_Lost", (uintptr_t)null_power,  _P(pw_power_lost) },
  60         { "Power_OK",   (uintptr_t)null_power,  _P(pw_power_ok) },
  61         { "Power_Down", (uintptr_t)null_power,  _P(pw_power_down) },
  62         { NULL,         (uintptr_t)NULL,        0}
  63 };
  64 
  65 static _nsc_power_t *_power_clients;
  66 static kmutex_t _power_mutex;
  67 
  68 
  69 static int null_power(void)
  70 /*
  71  * init null_power - dummy power routine for clients that choose not
  72  * to implement all the power hooks.
  73  *
  74  */
  75 {
  76         return (0);
  77 }
  78 
  79 /*
  80  * int
  81  * _nsc_power
  82  *      Call registered clients of the generic power ioctls.
  83  *
  84  * Calling/Exit State:
  85  *      Calls all the registered clients with a message describing the
  86  *      current state of the power for the system.
  87  */
  88 int
  89 _nsc_power(blind_t argp, int *rvp)
  90 {
  91         nsc_power_ctl_t opc;
  92         _nsc_power_t *pp;
  93 
  94         *rvp = 0;
  95         if (copyin((void *) argp, &opc, sizeof (nsc_power_ctl_t)))
  96                 return (EFAULT);
  97         mutex_enter(&_power_mutex);
  98 
  99         pp = _power_clients;
 100         while (pp) {
 101                 switch ((nsc_power_ops_t)opc.msg) {
 102 
 103         case Power_OK:
 104                         (*pp->pw_power_ok)();
 105                         break;
 106 
 107         case Power_Down:
 108                         (*pp->pw_power_down)();
 109                         break;
 110 
 111         case Power_Lost:
 112                         (*pp->pw_power_lost)(opc.arg1);
 113                         break;
 114 
 115         default:
 116                         mutex_exit(&_power_mutex);
 117                         return (EINVAL);
 118                 }
 119 
 120                 pp = pp->next;
 121         }
 122         mutex_exit(&_power_mutex);
 123         return (0);
 124 }
 125 
 126 /*
 127  * int
 128  * _nsc_init_power (void)
 129  *      Initialise power ioctl subsystem.
 130  *
 131  * Calling/Exit State:
 132  *      Called at driver initialisation time to allocate necessary
 133  *      data structures.
 134  */
 135 int
 136 _nsc_init_power(void)
 137 {
 138         mutex_init(&_power_mutex, NULL, MUTEX_DRIVER, NULL);
 139         return (0);
 140 }
 141 
 142 /*
 143  * int
 144  * _nsc_deinit_power (void)
 145  *      Initialise power ioctl subsystem.
 146  *
 147  * Calling/Exit State:
 148  *      Called at driver initialisation time to allocate necessary
 149  *      data structures.
 150  */
 151 int
 152 _nsc_deinit_power(void)
 153 {
 154         _nsc_power_t *pp, *npp;
 155 
 156         mutex_enter(&_power_mutex);
 157         pp = _power_clients;
 158         while (pp) {
 159                 npp = pp->next;
 160                 nsc_kmem_free(pp, sizeof (_nsc_power_t));
 161                 pp = npp;
 162         }
 163         _power_clients = NULL;
 164         mutex_exit(&_power_mutex);
 165         mutex_destroy(&_power_mutex);
 166         return (0);
 167 }
 168 
 169 /*
 170  * blind_t
 171  * nsc_register_power (char *name, nsc_def_t *def)
 172  *      Register an power ioctl client.
 173  *
 174  * Calling/Exit State:
 175  *      Returns a token for use in future calls to nsc_unregister_power.
 176  *      If a client with the same name is already registered then NULL
 177  *      is return to indicate failure.
 178  *      If registration fails NULL is returned.
 179  *
 180  * Description:
 181  *      Registers an power ioctl client for notifications during subsequent
 182  *      ioctl from UPS/PCU management.
 183  */
 184 blind_t
 185 nsc_register_power(char *name, nsc_def_t *def)
 186 {
 187         _nsc_power_t *entry, *pp;
 188 
 189 
 190         entry = nsc_kmem_alloc(sizeof (_nsc_power_t), 0, _nsc_local_mem);
 191 
 192         if (entry == NULL)
 193                 return (NULL);
 194         nsc_decode_param(def, _nsc_power_def, (long *)entry);
 195 
 196         mutex_enter(&_power_mutex);
 197 
 198         for (pp = _power_clients; pp; pp = pp->next) {
 199                 if (strcmp(pp->name, name) == 0) {
 200                         mutex_exit(&_power_mutex);
 201                         nsc_kmem_free(entry, sizeof (_nsc_power_t));
 202                         return (NULL);
 203                 }
 204         }
 205         entry->name = name;
 206 
 207         entry->next = _power_clients;
 208         _power_clients = entry;
 209         mutex_exit(&_power_mutex);
 210         return ((blind_t)entry);
 211 }
 212 
 213 /*
 214  * int
 215  * nsc_unregister_power (blind_t powerp)
 216  *      Un-register a power ioctl client.
 217  *
 218  * Calling/Exit State:
 219  *      Returns 0 on success, otherwise returns an error code.
 220  *
 221  * Description:
 222  *      The specified power ioctl client is un-registered if possible.
 223  *      Zero is returned on success otherwise an error code.
 224  */
 225 int
 226 nsc_unregister_power(blind_t powerp)
 227 {
 228         _nsc_power_t **xpp, *entry;
 229 
 230         entry = (_nsc_power_t *)powerp;
 231         if (entry == NULL)
 232                 return (EINVAL);
 233 
 234         mutex_enter(&_power_mutex);
 235 
 236         for (xpp = &_power_clients; *xpp; xpp = &(*xpp)->next)
 237                 if (*xpp == entry)
 238                         break;
 239 
 240         if (*xpp == NULL) {
 241                 mutex_exit(&_power_mutex);
 242                 return (EALREADY);
 243         }
 244         *xpp = entry->next;
 245         mutex_exit(&_power_mutex);
 246         nsc_kmem_free(entry, sizeof (_nsc_power_t));
 247 
 248         return (0);
 249 }