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 
  27 
  28 /*
  29  * generic character driver
  30  */
  31 #include <sys/types.h>
  32 #include <sys/param.h>
  33 #include <sys/errno.h>
  34 #include <sys/uio.h>
  35 #include <sys/buf.h>
  36 #include <sys/modctl.h>
  37 #include <sys/open.h>
  38 #include <sys/kmem.h>
  39 #include <sys/conf.h>
  40 #include <sys/cmn_err.h>
  41 #include <sys/stat.h>
  42 #include <sys/ddi.h>
  43 #include <sys/sunddi.h>
  44 #include <sys/sunndi.h>
  45 
  46 
  47 #define NUMEVENTS 6
  48 #define COMPONENTS 2
  49 #define COMP_0_MAXPWR   3
  50 #define COMP_1_MAXPWR   2
  51 #define MINPWR          0
  52 static int maxpwr[] = { COMP_0_MAXPWR, COMP_1_MAXPWR };
  53 
  54 /*
  55  * The state for each generic device.
  56  * NOTE: We save the node_type in the state structure. The node_type string
  57  * (and not a copy) is stashed in a minor node by  ddi_create_minor_node(),
  58  * so ddi_remove_minor_node() must occur prior to state free.
  59  */
  60 typedef struct dstate {
  61         uint_t          flag;
  62         dev_info_t      *dip;                   /* my devinfo handle */
  63         char            *node_type;     /* stable node_type copy */
  64         ddi_callback_id_t gen_cb_ids[NUMEVENTS];
  65         kmutex_t        lock;
  66         char            *nodename;
  67         int             level[COMPONENTS];      /* pm level */
  68         int             busy[COMPONENTS];       /* busy state */
  69 } dstate_t;
  70 
  71 
  72 static void *dstates;
  73 
  74 static int gen_debug = 0;
  75 
  76 #ifdef DEBUG
  77 #define gen_debug gen_debug_on
  78 static int gen_debug_on = 0;
  79 #define GEN_DEBUG(args) if (gen_debug) cmn_err args
  80 #else
  81 #define GEN_DEBUG(args)
  82 #endif
  83 
  84 extern void prom_printf(const char *fmt, ...);
  85 
  86 static int gen_open(dev_t *devp, int flag, int otyp, cred_t *cred);
  87 static int gen_close(dev_t devp, int flag, int otyp, cred_t *cred);
  88 static int gen_read(dev_t dev, struct uio *uiop, cred_t *credp);
  89 static int gen_write(dev_t dev, struct uio *uiop, cred_t *credp);
  90 static int gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
  91     cred_t *credp, int *rvalp);
  92 static int gen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
  93 static int gen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
  94 static void gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
  95         void *arg, void *impl_data);
  96 
  97 static int gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
  98     void **result);
  99 static int gen_create_minor_nodes(dev_info_t *, struct dstate *);
 100 static int gen_power(dev_info_t *, int, int);
 101 
 102 static struct cb_ops gen_cb_ops = {
 103         gen_open,                       /* open */
 104         gen_close,                      /* close */
 105         nodev,                          /* strategy */
 106         nodev,                          /* print */
 107         nodev,                          /* dump */
 108         gen_read,                       /* read */
 109         gen_write,                      /* write */
 110         gen_ioctl,                      /* ioctl */
 111         nodev,                          /* devmap */
 112         nodev,                          /* mmap */
 113         nodev,                          /* segmap */
 114         nochpoll,                       /* poll */
 115         ddi_prop_op,                    /* prop_op */
 116         NULL,                           /* streamtab */
 117         D_NEW | D_MP | D_HOTPLUG,       /* flag */
 118         CB_REV,                         /* cb_rev */
 119         nodev,                          /* aread */
 120         nodev                           /* awrite */
 121 };
 122 
 123 
 124 static struct dev_ops gen_ops = {
 125         DEVO_REV,               /* devo_rev */
 126         0,                      /* refcnt */
 127         gen_info,               /* getinfo */
 128         nulldev,                /* identify */
 129         nulldev,                /* probe */
 130         gen_attach,             /* attach */
 131         gen_detach,             /* detach */
 132         nodev,                  /* reset */
 133         &gen_cb_ops,                /* driver ops */
 134         (struct bus_ops *)0,    /* bus ops */
 135         gen_power,              /* power */
 136         ddi_quiesce_not_supported,      /* devo_quiesce */
 137 };
 138 
 139 /*
 140  * INST_TO_MINOR() gives the starting minor number for a given gen_drv driver
 141  * instance. A shift left by 6 bits allows for each instance to have upto
 142  * 64 (2^6) minor numbers. The maximum minor number allowed by the system
 143  * is L_MAXMIN32 (0x3ffff). This effectively limits the gen_drv instance
 144  * numbers from 0 to 0xfff for a total of 4096 instances.
 145  */
 146 #define INST_TO_MINOR(i)        (i << 6)
 147 #define MINOR_TO_INST(mn)       (mn >> 6)
 148 
 149 static char *mnodetypes[] = {
 150         "ddi_nt",
 151         "ddi_nt:device_type",
 152         "ddi_nt:device_class:bus_class",
 153         "ddi_nt2",
 154         "ddi_nt2:device_type",
 155         "ddi_nt2:device_type:bus_class",
 156 };
 157 #define N_NTYPES        (sizeof (mnodetypes) / sizeof (char *))
 158 
 159 static struct modldrv modldrv = {
 160         &mod_driverops,
 161         "generic test driver",
 162         &gen_ops
 163 };
 164 
 165 static struct modlinkage modlinkage = {
 166         MODREV_1, { &modldrv, NULL }
 167 };
 168 
 169 
 170 /*
 171  * flags
 172  */
 173 #define OPEN_FLAG                       0x001
 174 #define PWR_HAS_CHANGED_ON_RESUME_FLAG  0x002
 175 #define FAIL_SUSPEND_FLAG               0x004
 176 #define PUP_WITH_PWR_HAS_CHANGED_FLAG   0x008
 177 #define POWER_FLAG                      0x010
 178 #define LOWER_POWER_FLAG                0x020
 179 #define NO_INVOL_FLAG                   0x040
 180 #define PM_SUPPORTED_FLAG               0x080
 181 
 182 /*
 183  * ioctl commands (non-devctl ioctl commands)
 184  */
 185 #define GENDRV_IOCTL                            ('P' << 8)
 186 #define GENDRV_IOFAULT_SIMULATE                 (GENDRV_IOCTL | 0)
 187 #define GENDRV_NDI_EVENT_TEST                   (GENDRV_IOCTL | 1)
 188 
 189 int
 190 _init(void)
 191 {
 192         int e;
 193 
 194         if ((e = ddi_soft_state_init(&dstates,
 195             sizeof (struct dstate), 0)) != 0) {
 196                 return (e);
 197         }
 198 
 199         if ((e = mod_install(&modlinkage)) != 0)  {
 200                 ddi_soft_state_fini(&dstates);
 201         }
 202 
 203         return (e);
 204 }
 205 
 206 int
 207 _fini(void)
 208 {
 209         int e;
 210 
 211         if ((e = mod_remove(&modlinkage)) != 0)  {
 212                 return (e);
 213         }
 214         ddi_soft_state_fini(&dstates);
 215         return (e);
 216 }
 217 
 218 int
 219 _info(struct modinfo *modinfop)
 220 {
 221         return (mod_info(&modlinkage, modinfop));
 222 }
 223 
 224 static int
 225 gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 226 {
 227         int instance = ddi_get_instance(devi);
 228         struct dstate *dstatep;
 229         int rval;
 230         int n_devs;
 231         int n_minorcomps;
 232         int isclone;
 233         ddi_eventcookie_t dev_offline_cookie, dev_reset_cookie;
 234         ddi_eventcookie_t bus_reset_cookie, bus_quiesce_cookie;
 235         ddi_eventcookie_t bus_unquiesce_cookie, bus_test_post_cookie;
 236         int i_init = 0;
 237         int level_tmp;
 238 
 239         int i;
 240         char *pm_comp[] = {
 241                 "NAME=leaf0",
 242                 "0=D0",
 243                 "1=D1",
 244                 "2=D2",
 245                 "3=D3",
 246                 "NAME=leaf1",
 247                 "0=off",
 248                 "1=blank",
 249                 "2=on"};
 250         char *pm_hw_state = {"needs-suspend-resume"};
 251 
 252 
 253         switch (cmd) {
 254         case DDI_ATTACH:
 255 
 256                 if (ddi_soft_state_zalloc(dstates, instance) !=
 257                     DDI_SUCCESS) {
 258                         cmn_err(CE_CONT, "%s%d: can't allocate state\n",
 259                             ddi_get_name(devi), instance);
 260 
 261                         return (DDI_FAILURE);
 262                 }
 263 
 264                 dstatep = ddi_get_soft_state(dstates, instance);
 265                 dstatep->dip = devi;
 266                 mutex_init(&dstatep->lock, NULL, MUTEX_DRIVER, NULL);
 267 
 268                 n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
 269                     "ndevs", 1);
 270 
 271                 isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
 272                     "isclone", 0);
 273 
 274                 n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
 275                     "ncomps", 1);
 276 
 277                 GEN_DEBUG((CE_CONT,
 278                     "%s%d attaching: n_devs=%d n_minorcomps=%d isclone=%d",
 279                     ddi_get_name(devi), ddi_get_instance(devi),
 280                     n_devs, n_minorcomps, isclone));
 281 
 282                 if (isclone) {
 283                         if (ddi_create_minor_node(devi, "gen", S_IFCHR,
 284                             INST_TO_MINOR(instance), mnodetypes[0],
 285                             isclone) != DDI_SUCCESS) {
 286                                 ddi_remove_minor_node(devi, NULL);
 287                                 ddi_soft_state_free(dstates, instance);
 288                                 cmn_err(CE_WARN, "%s%d: can't create minor "
 289                                 "node", ddi_get_name(devi), instance);
 290 
 291                                 return (DDI_FAILURE);
 292                         }
 293                         rval = DDI_SUCCESS;
 294                 } else {
 295                         rval = gen_create_minor_nodes(devi, dstatep);
 296                         if (rval != DDI_SUCCESS) {
 297                                 ddi_prop_remove_all(devi);
 298                                 ddi_remove_minor_node(devi, NULL);
 299                                 ddi_soft_state_free(dstates, instance);
 300                                 cmn_err(CE_WARN, "%s%d: can't create minor "
 301                                 "nodes", ddi_get_name(devi), instance);
 302 
 303                                 return (DDI_FAILURE);
 304                         }
 305                 }
 306 
 307                 if (ddi_get_eventcookie(devi, "pshot_dev_offline",
 308                     &dev_offline_cookie) == DDI_SUCCESS) {
 309                         (void) ddi_add_event_handler(devi, dev_offline_cookie,
 310                             gen_event_cb, NULL, &(dstatep->gen_cb_ids[0]));
 311                 }
 312 
 313                 if (ddi_get_eventcookie(devi, "pshot_dev_reset",
 314                     &dev_reset_cookie) == DDI_SUCCESS) {
 315                         (void) ddi_add_event_handler(devi, dev_reset_cookie,
 316                             gen_event_cb, NULL, &(dstatep->gen_cb_ids[1]));
 317                 }
 318 
 319                 if (ddi_get_eventcookie(devi, "pshot_bus_reset",
 320                     &bus_reset_cookie) == DDI_SUCCESS) {
 321                         (void) ddi_add_event_handler(devi, bus_reset_cookie,
 322                             gen_event_cb, NULL, &(dstatep->gen_cb_ids[2]));
 323                 }
 324 
 325                 if (ddi_get_eventcookie(devi, "pshot_bus_quiesce",
 326                     &bus_quiesce_cookie) == DDI_SUCCESS) {
 327                         (void) ddi_add_event_handler(devi, bus_quiesce_cookie,
 328                             gen_event_cb, NULL, &(dstatep->gen_cb_ids[3]));
 329                 }
 330 
 331                 if (ddi_get_eventcookie(devi, "pshot_bus_unquiesce",
 332                     &bus_unquiesce_cookie) == DDI_SUCCESS) {
 333                         (void) ddi_add_event_handler(devi,
 334                             bus_unquiesce_cookie, gen_event_cb,
 335                             NULL, &(dstatep->gen_cb_ids[4]));
 336                 }
 337 
 338                 if (ddi_get_eventcookie(devi, "pshot_bus_test_post",
 339                     &bus_test_post_cookie) == DDI_SUCCESS) {
 340                         (void) ddi_add_event_handler(devi,
 341                             bus_test_post_cookie, gen_event_cb,
 342                             NULL, &(dstatep->gen_cb_ids[5]));
 343                 }
 344 
 345                 /*
 346                  * initialize the devices' pm state
 347                  */
 348                 mutex_enter(&dstatep->lock);
 349                 dstatep->flag &= ~OPEN_FLAG;
 350                 dstatep->flag &= ~PWR_HAS_CHANGED_ON_RESUME_FLAG;
 351                 dstatep->flag &= ~FAIL_SUSPEND_FLAG;
 352                 dstatep->flag &= ~PUP_WITH_PWR_HAS_CHANGED_FLAG;
 353                 dstatep->flag |= LOWER_POWER_FLAG;
 354                 dstatep->flag &= ~NO_INVOL_FLAG;
 355                 dstatep->flag |= PM_SUPPORTED_FLAG;
 356                 dstatep->busy[0] = 0;
 357                 dstatep->busy[1] = 0;
 358                 dstatep->level[0] = -1;
 359                 dstatep->level[1] = -1;
 360                 mutex_exit(&dstatep->lock);
 361 
 362                 /*
 363                  * stash the nodename
 364                  */
 365                 dstatep->nodename = ddi_node_name(devi);
 366 
 367                 /*
 368                  * Check if the no-involuntary-power-cycles property
 369                  * was created. Set NO_INVOL_FLAG if so.
 370                  */
 371                 if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
 372                     (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
 373                     "no-involuntary-power-cycles") == 1) {
 374                         GEN_DEBUG((CE_CONT,
 375                             "%s%d: DDI_ATTACH:\n\tno-involuntary-power-cycles"
 376                             " property was created",
 377                             ddi_node_name(devi), ddi_get_instance(devi)));
 378                         mutex_enter(&dstatep->lock);
 379                         dstatep->flag |= NO_INVOL_FLAG;
 380                         mutex_exit(&dstatep->lock);
 381                 }
 382 
 383                 /*
 384                  * Check if the dependency-property property
 385                  * was created.
 386                  */
 387                 if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
 388                     (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
 389                     "dependency-property") == 1) {
 390                         GEN_DEBUG((CE_CONT,
 391                             "%s%d: DDI_ATTACH:\n\tdependency-property"
 392                             " property was created",
 393                             ddi_node_name(devi), ddi_get_instance(devi)));
 394                 }
 395 
 396                 /*
 397                  * create the pm-components property. two comps:
 398                  * 4 levels on comp0, 3 on comp 1.
 399                  * - skip for a "tape" device, clear PM_SUPPORTED_FLAG
 400                  */
 401                 if (strcmp(ddi_node_name(devi), "tape") != 0) {
 402                         if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi,
 403                             "pm-components", pm_comp, 9) != DDI_PROP_SUCCESS) {
 404                                 cmn_err(CE_WARN, "%s%d: %s\n",
 405                                     ddi_node_name(devi),
 406                                     ddi_get_instance(devi),
 407                                     "unable to create \"pm-components\" "
 408                                     " property.");
 409 
 410                                 return (DDI_FAILURE);
 411                         }
 412                 } else {
 413                         mutex_enter(&dstatep->lock);
 414                         dstatep->flag &= ~PM_SUPPORTED_FLAG;
 415                         mutex_exit(&dstatep->lock);
 416                 }
 417 
 418                 /*
 419                  * Check if the pm-components property was created
 420                  */
 421                 if (dstatep->flag & PM_SUPPORTED_FLAG) {
 422                         if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip,
 423                             (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
 424                             "pm-components") != 1) {
 425                                 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s",
 426                                     ddi_node_name(devi),
 427                                     ddi_get_instance(devi),
 428                                     "\"pm-components\" property does"
 429                                     " not exist");
 430 
 431                                 return (DDI_FAILURE);
 432 
 433                         } else {
 434                                 GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:"
 435                                     " created pm-components property",
 436                                     ddi_node_name(devi),
 437                                     ddi_get_instance(devi)));
 438                         }
 439                 }
 440 
 441                 /*
 442                  * create the pm-hardware-state property.
 443                  * needed to get DDI_SUSPEND and DDI_RESUME calls
 444                  */
 445                 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi,
 446                     "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) {
 447                         cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s\n",
 448                             ddi_node_name(devi), ddi_get_instance(devi),
 449                             "unable to create \"pm-hardware-state\" "
 450                             " property.");
 451 
 452                         return (DDI_FAILURE);
 453                 }
 454 
 455                 /*
 456                  * set power levels to max via pm_raise_power(),
 457                  */
 458                 mutex_enter(&dstatep->lock);
 459                 i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
 460                 mutex_exit(&dstatep->lock);
 461                 for (i = i_init; i < COMPONENTS; i++) {
 462                         GEN_DEBUG((CE_CONT,
 463                             "%s%d: DDI_ATTACH: pm_raise_power comp %d "
 464                             "to level %d", ddi_node_name(devi),
 465                             ddi_get_instance(devi), i, maxpwr[i]));
 466                         if (pm_raise_power(dstatep->dip, i, maxpwr[i]) !=
 467                             DDI_SUCCESS) {
 468                                 cmn_err(CE_WARN,
 469                                     "%s%d: DDI_ATTACH: pm_raise_power failed\n",
 470                                     ddi_node_name(devi),
 471                                     ddi_get_instance(devi));
 472                                 dstatep->level[i] = -1;
 473 
 474                                 return (DDI_FAILURE);
 475                         }
 476                 }
 477 
 478                 if (rval == DDI_SUCCESS) {
 479                         ddi_report_dev(devi);
 480                 }
 481                 return (rval);
 482 
 483 
 484         case DDI_RESUME:
 485                 GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME", ddi_node_name(devi),
 486                     ddi_get_instance(devi)));
 487 
 488                 dstatep = ddi_get_soft_state(dstates, ddi_get_instance(devi));
 489                 if (dstatep == NULL) {
 490 
 491                         return (DDI_FAILURE);
 492                 }
 493 
 494                 /*
 495                  * Call pm_power_has_changed() if flag
 496                  * PWR_HAS_CHANGED_ON_RESUME_FLAG is set,
 497                  * then clear the flag
 498                  */
 499                 mutex_enter(&dstatep->lock);
 500                 i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS;
 501                 mutex_exit(&dstatep->lock);
 502                 if (dstatep->flag & PWR_HAS_CHANGED_ON_RESUME_FLAG) {
 503                         for (i = i_init; i < COMPONENTS; i++) {
 504                                 GEN_DEBUG((CE_CONT,
 505                                     "%s%d: DDI_RESUME: pm_power_has_changed "
 506                                     "comp %d to level %d", ddi_node_name(devi),
 507                                     ddi_get_instance(devi), i, maxpwr[i]));
 508                                 mutex_enter(&dstatep->lock);
 509                                 level_tmp = dstatep->level[i];
 510                                 dstatep->level[i] = maxpwr[i];
 511                                 if (pm_power_has_changed(dstatep->dip, i,
 512                                     maxpwr[i]) != DDI_SUCCESS) {
 513                                         cmn_err(CE_WARN,
 514                                             "%s%d: DDI_RESUME:\n\t"
 515                                             " pm_power_has_changed"
 516                                             " failed: comp %d to level %d\n",
 517                                             ddi_node_name(devi),
 518                                             ddi_get_instance(devi),
 519                                             i, maxpwr[i]);
 520                                         dstatep->level[i] = level_tmp;
 521                                 }
 522                                 mutex_exit(&dstatep->lock);
 523                         }
 524                 } else {
 525                         /*
 526                          * Call pm_raise_power() instead
 527                          */
 528                         for (i = i_init; i < COMPONENTS; i++) {
 529                                 GEN_DEBUG((CE_CONT,
 530                                     "%s%d: DDI_RESUME: pm_raise_power"
 531                                     " comp %d to level %d",
 532                                     ddi_node_name(devi), ddi_get_instance(devi),
 533                                     i, maxpwr[i]));
 534                                 if (pm_raise_power(dstatep->dip, i, maxpwr[i])
 535                                     != DDI_SUCCESS) {
 536                                         cmn_err(CE_WARN,
 537                                             "%s%d: DDI_RESUME:"
 538                                             "\n\tpm_raise_power"
 539                                             "failed: comp %d to level %d\n",
 540                                             ddi_node_name(devi),
 541                                             ddi_get_instance(devi),
 542                                             i, maxpwr[i]);
 543                                 }
 544                         }
 545                 }
 546 
 547                 return (DDI_SUCCESS);
 548 
 549         default:
 550                 GEN_DEBUG((CE_WARN, "attach: default"));
 551                 return (DDI_FAILURE);
 552         }
 553 }
 554 
 555 static int
 556 gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 557 {
 558         struct dstate *dstatep;
 559         int instance;
 560         int i;
 561         int rv;
 562         int rm_power;
 563         int level_tmp;
 564 
 565 #ifdef DEBUG
 566         int n_devs;
 567         int n_minorcomps;
 568         int isclone;
 569 #endif
 570 
 571         switch (cmd) {
 572         case DDI_DETACH:
 573                 GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH", ddi_node_name(devi),
 574                     ddi_get_instance(devi)));
 575 
 576                 instance = ddi_get_instance(devi);
 577                 dstatep = ddi_get_soft_state(dstates, instance);
 578                 if (dstatep == NULL) {
 579 
 580                         return (DDI_FAILURE);
 581 }
 582 
 583 #ifdef DEBUG
 584                 n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
 585                     "ndevs", 1);
 586 
 587                 isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
 588                     "isclone", 0);
 589 
 590                 n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0,
 591                     "ncomps", 1);
 592 #endif /* DEBUG */
 593 
 594                 /*
 595                  * power off component 1.
 596                  */
 597                 if (dstatep->flag & PM_SUPPORTED_FLAG) {
 598                         GEN_DEBUG((CE_CONT,
 599                             "%s%d: DDI_DETACH: pm_lower_power comp 1 level %d",
 600                             ddi_node_name(devi), ddi_get_instance(devi),
 601                             MINPWR));
 602                         if (pm_lower_power(dstatep->dip, 1, MINPWR)
 603                             != DDI_SUCCESS) {
 604                                 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
 605                                     "pm_lower_power failed for comp 1 to"
 606                                     " level %d\n", ddi_node_name(devi),
 607                                     ddi_get_instance(devi), MINPWR);
 608 
 609                                 return (DDI_FAILURE);
 610                         }
 611 
 612                         /*
 613                          * check power level. Issue pm_power_has_changed
 614                          * if not at MINPWR.
 615                          */
 616                         mutex_enter(&dstatep->lock);
 617                         level_tmp = dstatep->level[1];
 618                         dstatep->level[1] = MINPWR;
 619                         if (dstatep->level[1] != MINPWR) {
 620                                 GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
 621                                     " power off via pm_power_has_changed"
 622                                     " instead", ddi_node_name(devi),
 623                                     ddi_get_instance(devi)));
 624                                 if (pm_power_has_changed(dstatep->dip,
 625                                     1, MINPWR) != DDI_SUCCESS) {
 626                                         GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
 627                                             " pm_power_has_changed failed for"
 628                                             " comp 1 to level %d",
 629                                             ddi_node_name(devi),
 630                                             ddi_get_instance(devi),
 631                                             MINPWR));
 632                                         dstatep->level[1] = level_tmp;
 633                                         mutex_exit(&dstatep->lock);
 634 
 635                                         return (DDI_FAILURE);
 636                                 }
 637                         }
 638                         mutex_exit(&dstatep->lock);
 639                 }
 640 
 641                 /*
 642                  * If the LOWER_POWER_FLAG flag is not set,
 643                  * don't call pm_lowr_power() for comp 0.
 644                  * This should be used only for the XXXXX@XX,no_invol
 645                  * devices that export the
 646                  * no-involuntary-power-cycles property
 647                  */
 648                 if (!(dstatep->flag & LOWER_POWER_FLAG) &&
 649                     dstatep->flag & PM_SUPPORTED_FLAG) {
 650                         cmn_err(CE_NOTE, "%s%d: DDI_DETACH:\n\t"
 651                             " NOT CALLING PM_LOWER_POWER():"
 652                             " LOWER_POWER_FLAG NOT SET\n",
 653                             ddi_node_name(devi), ddi_get_instance(devi));
 654                 } else if (dstatep->flag & PM_SUPPORTED_FLAG) {
 655                         GEN_DEBUG((CE_CONT,
 656                             "%s%d: DDI_DETACH: pm_lower_power comp 0 level %d",
 657                             ddi_node_name(devi), ddi_get_instance(devi),
 658                             MINPWR));
 659                         if (pm_lower_power(dstatep->dip, 0, MINPWR)
 660                             != DDI_SUCCESS) {
 661                                 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t"
 662                                     "pm_lower_power failed for comp 0 to"
 663                                     " level %d\n", ddi_node_name(devi),
 664                                     ddi_get_instance(devi), MINPWR);
 665 
 666                                 return (DDI_FAILURE);
 667                         }
 668 
 669                         /*
 670                          * check power level. Issue pm_power_has_changed
 671                          * if not at MINPWR.
 672                          */
 673                         mutex_enter(&dstatep->lock);
 674                         level_tmp = dstatep->level[0];
 675                         dstatep->level[0] = MINPWR;
 676                         if (dstatep->level[0] != MINPWR) {
 677                                 GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
 678                                     " power off via pm_power_has_changed"
 679                                     " instead", ddi_node_name(devi),
 680                                     ddi_get_instance(devi)));
 681                                 if (pm_power_has_changed(dstatep->dip,
 682                                     0, MINPWR) != DDI_SUCCESS) {
 683                                         GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:"
 684                                             " pm_power_has_changed failed for"
 685                                             " comp 0 to level %d",
 686                                             ddi_node_name(devi),
 687                                             ddi_get_instance(devi),
 688                                             MINPWR));
 689                                         dstatep->level[0] = level_tmp;
 690                                         mutex_exit(&dstatep->lock);
 691 
 692                                         return (DDI_FAILURE);
 693                                 }
 694                         }
 695                         mutex_exit(&dstatep->lock);
 696                 }
 697 
 698                 GEN_DEBUG((CE_CONT,
 699                     "%s%d detaching: n_devs=%d n_minorcomps=%d isclone=%d",
 700                     ddi_node_name(devi), ddi_get_instance(devi),
 701                     n_devs, n_minorcomps, isclone));
 702 
 703                 for (i = 0; i < NUMEVENTS; i++) {
 704                         if (dstatep->gen_cb_ids[i]) {
 705                 (void) ddi_remove_event_handler(dstatep->gen_cb_ids[i]);
 706                                 dstatep->gen_cb_ids[i] = NULL;
 707                         }
 708                 }
 709 
 710                 ddi_prop_remove_all(devi);
 711                 ddi_remove_minor_node(devi, NULL);
 712                 if (dstatep->node_type)
 713                         kmem_free(dstatep->node_type,
 714                             strlen(dstatep->node_type) + 1);
 715                 ddi_soft_state_free(dstates, instance);
 716                 return (DDI_SUCCESS);
 717 
 718         case DDI_SUSPEND:
 719                 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND",
 720                     ddi_node_name(devi), ddi_get_instance(devi)));
 721 
 722                 instance = ddi_get_instance(devi);
 723                 dstatep = ddi_get_soft_state(dstates, instance);
 724                 if (dstatep == NULL) {
 725 
 726                         return (DDI_FAILURE);
 727                 }
 728 
 729                 /*
 730                  * fail the suspend if FAIL_SUSPEND_FLAG is set.
 731                  * clear the FAIL_SUSPEND_FLAG flag
 732                  */
 733                 mutex_enter(&dstatep->lock);
 734                 if (dstatep->flag & FAIL_SUSPEND_FLAG) {
 735                         GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
 736                             " FAIL_SUSPEND_FLAG is set,"
 737                             " fail suspend",
 738                             ddi_node_name(devi), ddi_get_instance(devi)));
 739                         dstatep->flag &= ~FAIL_SUSPEND_FLAG;
 740                         rv = DDI_FAILURE;
 741                 } else {
 742                         rv = DDI_SUCCESS;
 743                 }
 744                 mutex_exit(&dstatep->lock);
 745 
 746                 /*
 747                  * Issue ddi_removing_power() to determine if the suspend
 748                  * was initiated by either CPR or DR. If CPR, the system
 749                  * will be powered OFF; if this driver has set the
 750                  * NO_INVOL_FLAG, then refuse to suspend. If DR, power
 751                  * will not be removed, thus allow the suspend.
 752                  */
 753                 if (dstatep->flag & NO_INVOL_FLAG &&
 754                     dstatep->flag & PM_SUPPORTED_FLAG) {
 755                         GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:"
 756                             " check via ddi_removing_power()",
 757                             ddi_node_name(devi), ddi_get_instance(devi)));
 758 
 759                         rm_power = ddi_removing_power(dstatep->dip);
 760 
 761                         if (rm_power < 0) {
 762                                 cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:"
 763                                     " ddi_removing_power() failed\n",
 764                                     ddi_node_name(devi),
 765                                     ddi_get_instance(devi));
 766                         } else if (rm_power == 1) {
 767                                 /*
 768                                  * CPR: power will be removed
 769                                  */
 770                                 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
 771                                     " CPR: POWER WILL BE REMOVED, THEREFORE"
 772                                     " REFUSE TO SUSPEND", ddi_node_name(devi),
 773                                     ddi_get_instance(devi)));
 774                                 rv = DDI_FAILURE;
 775                         } else if (rm_power == 0) {
 776                                 /*
 777                                  * DR: power will not be removed
 778                                  */
 779                                 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t"
 780                                     " DR: POWER WILL NOT BE REMOVED, THEREFORE"
 781                                     " ALLOW THE SUSPEND", ddi_node_name(devi),
 782                                     ddi_get_instance(devi)));
 783                                 rv = DDI_SUCCESS;
 784                         }
 785                 }
 786 
 787                 /*
 788                  * power OFF via pm_power_has_changed()
 789                  */
 790                 mutex_enter(&dstatep->lock);
 791                 if (dstatep->flag & PM_SUPPORTED_FLAG &&
 792                     !(dstatep->flag & NO_INVOL_FLAG)) {
 793                         level_tmp = dstatep->level[0];
 794                         dstatep->level[0] = MINPWR;
 795                         GEN_DEBUG((CE_CONT,
 796                             "%s%d: DDI_SUSPEND: pm_power_has_changed comp 0"
 797                             " level %d", ddi_node_name(devi),
 798                             ddi_get_instance(devi), MINPWR));
 799                         if (pm_power_has_changed(dstatep->dip, 0, MINPWR)
 800                             != DDI_SUCCESS) {
 801                                 cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:\n\t"
 802                                     "pm_power_has_changed failed for comp 0 to"
 803                                     " level %d\n", ddi_node_name(devi),
 804                                     ddi_get_instance(devi), MINPWR);
 805                                 dstatep->level[0] = level_tmp;
 806                                 mutex_exit(&dstatep->lock);
 807 
 808                                 return (DDI_FAILURE);
 809                         }
 810                 }
 811                 mutex_exit(&dstatep->lock);
 812 
 813                 return (rv);
 814 
 815         default:
 816 
 817                 return (DDI_FAILURE);
 818         }
 819 }
 820 
 821 /* ARGSUSED */
 822 static int
 823 gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 824 {
 825         dev_t   dev;
 826         int     instance;
 827 
 828         if (infocmd != DDI_INFO_DEVT2INSTANCE)
 829                 return (DDI_FAILURE);
 830 
 831         dev = (dev_t)arg;
 832         instance = MINOR_TO_INST(getminor(dev));
 833         *result = (void *)(uintptr_t)instance;
 834         return (DDI_SUCCESS);
 835 }
 836 
 837 
 838 /*ARGSUSED*/
 839 static int
 840 gen_open(dev_t *devp, int flag, int otyp, cred_t *cred)
 841 {
 842         minor_t minor;
 843         struct dstate *dstatep;
 844 
 845         if (otyp != OTYP_BLK && otyp != OTYP_CHR)
 846                 return (EINVAL);
 847 
 848         minor = getminor(*devp);
 849         if ((dstatep = ddi_get_soft_state(dstates,
 850             MINOR_TO_INST(minor))) == NULL)
 851                 return (ENXIO);
 852 
 853         mutex_enter(&dstatep->lock);
 854         dstatep->flag |= OPEN_FLAG;
 855         mutex_exit(&dstatep->lock);
 856 
 857         GEN_DEBUG((CE_CONT,
 858             "%s%d open",
 859             dstatep->nodename, MINOR_TO_INST(minor)));
 860 
 861         return (0);
 862 }
 863 
 864 /*ARGSUSED*/
 865 static int
 866 gen_close(dev_t dev, int flag, int otyp, cred_t *cred)
 867 {
 868         struct dstate *dstatep;
 869         minor_t minor = getminor(dev);
 870 
 871         if (otyp != OTYP_BLK && otyp != OTYP_CHR)
 872                 return (EINVAL);
 873 
 874         dstatep = ddi_get_soft_state(dstates, MINOR_TO_INST(minor));
 875 
 876         if (dstatep == NULL)
 877                 return (ENXIO);
 878 
 879         mutex_enter(&dstatep->lock);
 880         dstatep->flag &= ~OPEN_FLAG;
 881         mutex_exit(&dstatep->lock);
 882 
 883         GEN_DEBUG((CE_CONT,
 884             "%s%d close",
 885             dstatep->nodename, MINOR_TO_INST(minor)));
 886 
 887         return (0);
 888 }
 889 
 890 /*ARGSUSED*/
 891 static int
 892 gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
 893 {
 894         struct dstate *dstatep;
 895         ddi_eventcookie_t cookie;
 896         int instance;
 897         int rval = 0;
 898         char *nodename;
 899         int i;
 900         struct devctl_iocdata *dcp;
 901         uint_t state;
 902         int ret;
 903         int level_tmp;
 904 
 905         instance = MINOR_TO_INST(getminor(dev));
 906         dstatep = ddi_get_soft_state(dstates, instance);
 907         nodename = dstatep->nodename;
 908 
 909         if (dstatep == NULL)
 910                 return (ENXIO);
 911 
 912         /*
 913          * read devctl ioctl data
 914          */
 915         if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
 916                 return (EFAULT);
 917 
 918         switch (cmd) {
 919         case GENDRV_IOFAULT_SIMULATE:
 920                 if (ddi_get_eventcookie(dstatep->dip, DDI_DEVI_FAULT_EVENT,
 921                     &(cookie)) != NDI_SUCCESS)
 922                         return (DDI_FAILURE);
 923 
 924                 return (ndi_post_event(dstatep->dip, dstatep->dip, cookie,
 925                     NULL));
 926 
 927         case GENDRV_NDI_EVENT_TEST:
 928                 if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_offline",
 929                     &cookie) == NDI_SUCCESS) {
 930                         (void) ndi_post_event(dstatep->dip, dstatep->dip,
 931                             cookie, NULL);
 932                 }
 933 
 934                 if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_reset",
 935                     &cookie) == NDI_SUCCESS) {
 936                         (void) ndi_post_event(dstatep->dip, dstatep->dip,
 937                             cookie, NULL);
 938                 }
 939 
 940                 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_reset",
 941                     &cookie) == NDI_SUCCESS) {
 942                         (void) ndi_post_event(dstatep->dip, dstatep->dip,
 943                             cookie, NULL);
 944                 }
 945 
 946                 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_quiesce",
 947                     &cookie) == NDI_SUCCESS) {
 948                         (void) ndi_post_event(dstatep->dip, dstatep->dip,
 949                             cookie, NULL);
 950                 }
 951 
 952                 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_unquiesce",
 953                     &cookie) == NDI_SUCCESS) {
 954                         (void) ndi_post_event(dstatep->dip, dstatep->dip,
 955                             cookie, NULL);
 956                 }
 957 
 958                 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_test_post",
 959                     &cookie) == NDI_SUCCESS) {
 960                         (void) ndi_post_event(dstatep->dip, dstatep->dip,
 961                             cookie, NULL);
 962                 }
 963 
 964                 break;
 965 
 966         case DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME:
 967                 /*
 968                  * Issue pm_power_has_changed() call on DDI_RESUME
 969                  */
 970                 mutex_enter(&dstatep->lock);
 971                 dstatep->flag |= PWR_HAS_CHANGED_ON_RESUME_FLAG;
 972                 mutex_exit(&dstatep->lock);
 973                 GEN_DEBUG((CE_CONT, "%s%d:"
 974                     " DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME", nodename,
 975                     instance));
 976 
 977                 break;
 978 
 979         case DEVCTL_PM_FAIL_SUSPEND:
 980                 /*
 981                  * Fail the suspend attempt in DDI_SUSPEND
 982                  */
 983                 mutex_enter(&dstatep->lock);
 984                 dstatep->flag |= FAIL_SUSPEND_FLAG;
 985                 mutex_exit(&dstatep->lock);
 986                 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_FAIL_SUSPEND",
 987                     nodename, instance));
 988 
 989                 break;
 990 
 991         case DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED:
 992                 /*
 993                  * Use pm_power_has_changed() to power up comp 0 when
 994                  * enforcing the comp 0 vs comp-not 0 dependency:
 995                  * Power up comp 0 first, if request for comp-not-0
 996                  * comes in.
 997                  * Else, default to pm_raise_power().
 998                  */
 999                 mutex_enter(&dstatep->lock);
1000                 dstatep->flag |= PUP_WITH_PWR_HAS_CHANGED_FLAG;
1001                 mutex_exit(&dstatep->lock);
1002                 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED",
1003                     nodename, instance));
1004 
1005                 break;
1006 
1007         case DEVCTL_PM_BUSY_COMP:
1008                 /*
1009                  * mark component 0 busy via a pm_busy_component() call.
1010                  * update the busy[] array.
1011                  */
1012                 mutex_enter(&dstatep->lock);
1013                 ++dstatep->busy[0];
1014                 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP: comp 0:"
1015                     " busy=%d", nodename, instance, dstatep->busy[0]));
1016                 mutex_exit(&dstatep->lock);
1017                 ret = pm_busy_component(dstatep->dip, 0);
1018                 ASSERT(ret == DDI_SUCCESS);
1019 
1020                 break;
1021 
1022         case DEVCTL_PM_BUSY_COMP_TEST:
1023                 /*
1024                  * test busy state on component 0
1025                  */
1026                 mutex_enter(&dstatep->lock);
1027                 state = dstatep->busy[0];
1028                 if (copyout(&state, dcp->cpyout_buf,
1029                     sizeof (uint_t)) != 0) {
1030                         cmn_err(CE_WARN, "%s%d:"
1031                             " DEVCTL_PM_BUSY_COMP_TEST: copyout failed\n",
1032                             nodename, instance);
1033                         rval = EINVAL;
1034                 }
1035                 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP_TEST:"
1036                     " comp 0 busy %d",
1037                     nodename, instance, state));
1038                 mutex_exit(&dstatep->lock);
1039 
1040                 break;
1041 
1042         case DEVCTL_PM_IDLE_COMP:
1043                 /*
1044                  * mark component 0 idle via a pm_idle_component() call.
1045                  * NOP if dstatep->busy[0] == 0.
1046                  */
1047                 mutex_enter(&dstatep->lock);
1048                 if (dstatep->busy[0] > 0) {
1049                         --dstatep->busy[0];
1050                         GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_IDLE_COMP:"
1051                             " comp 0: busy=%d", nodename, instance,
1052                             dstatep->busy[0]));
1053                         mutex_exit(&dstatep->lock);
1054                         ret = pm_idle_component(dstatep->dip, 0);
1055                         ASSERT(ret == DDI_SUCCESS);
1056                 } else {
1057                         mutex_exit(&dstatep->lock);
1058                 }
1059 
1060                 break;
1061 
1062         case DEVCTL_PM_PROM_PRINTF:
1063                 (void) prom_printf("%s%d: PROM_PRINTF FROM GEN_DRV\n",
1064                     nodename, instance);
1065 
1066                 break;
1067 
1068         case DEVCTL_PM_RAISE_PWR:
1069                 /*
1070                  * power up both components to MAXPWR via
1071                  * pm_raise_power() calls. this ioctl() cmd
1072                  * assumes that the current level is 0
1073                  */
1074                 for (i = 0; i < COMPONENTS; i++) {
1075                         GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_RAISE_PWR:"
1076                             " comp %d old 0 new %d",
1077                             nodename, instance, i, maxpwr[i]));
1078                         if (pm_raise_power(dstatep->dip, 0, maxpwr[i])
1079                             != DDI_SUCCESS) {
1080                                 rval = EINVAL;
1081                         }
1082                 }
1083 
1084                 break;
1085 
1086         case DEVCTL_PM_CHANGE_PWR_LOW:
1087                 /*
1088                  * power off both components via pm_power_has_changed() calls
1089                  */
1090                 for (i = (COMPONENTS - 1); i >= 0; --i) {
1091                         GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_LOW:"
1092                             " comp %d new 0",
1093                             nodename, instance, i));
1094                         mutex_enter(&dstatep->lock);
1095                         level_tmp = dstatep->level[i];
1096                         dstatep->level[i] = 0;
1097                         if (pm_power_has_changed(dstatep->dip, i, 0)
1098                             != DDI_SUCCESS) {
1099                                 dstatep->level[i] = level_tmp;
1100                                 rval = EINVAL;
1101                         }
1102                         mutex_exit(&dstatep->lock);
1103                 }
1104 
1105                 break;
1106 
1107         case DEVCTL_PM_CHANGE_PWR_HIGH:
1108                 /*
1109                  * power up both components to MAXPWR via
1110                  * pm_power_has_changed() calls
1111                  */
1112                 for (i = 0; i < COMPONENTS; i++) {
1113                         GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_HIGH:"
1114                             " comp %d new %d",
1115                             nodename, instance, i, maxpwr[i]));
1116                         mutex_enter(&dstatep->lock);
1117                         level_tmp = dstatep->level[i];
1118                         dstatep->level[i] = maxpwr[i];
1119                         if (pm_power_has_changed(dstatep->dip, i, maxpwr[i])
1120                             != DDI_SUCCESS) {
1121                                 dstatep->level[i] = level_tmp;
1122                                 rval = EINVAL;
1123                         }
1124                         mutex_exit(&dstatep->lock);
1125                 }
1126 
1127                 break;
1128 
1129         case DEVCTL_PM_POWER:
1130                 /*
1131                  * test if the gen_drv_power() routine has been called,
1132                  * then clear
1133                  */
1134                 mutex_enter(&dstatep->lock);
1135                 state = (dstatep->flag & POWER_FLAG) ? 1 : 0;
1136                 if (copyout(&state, dcp->cpyout_buf,
1137                     sizeof (uint_t)) != 0) {
1138                         cmn_err(CE_WARN, "%s%d: DEVCTL_PM_POWER:"
1139                             " copyout failed\n", nodename, instance);
1140                         rval = EINVAL;
1141                 }
1142                 GEN_DEBUG((CE_CONT, "%s%d: %s POWER_FLAG: %d",
1143                     nodename, instance, "DEVCTL_PM_POWER", state));
1144                 dstatep->flag &= ~POWER_FLAG;
1145                 mutex_exit(&dstatep->lock);
1146                 break;
1147 
1148         case DEVCTL_PM_NO_LOWER_POWER:
1149                 /*
1150                  * issue to not invoke pm_lower_power() on detach
1151                  */
1152                 mutex_enter(&dstatep->lock);
1153                 dstatep->flag &= ~LOWER_POWER_FLAG;
1154                 mutex_exit(&dstatep->lock);
1155                 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_NO_LOWER_POWER",
1156                     nodename, instance));
1157                 break;
1158 
1159         default:
1160                 return (ENOTTY);
1161         }
1162 
1163         return (rval);
1164 }
1165 
1166 /*ARGSUSED*/
1167 static int
1168 gen_read(dev_t dev, struct uio *uiop, cred_t *credp)
1169 {
1170         return (0);
1171 }
1172 
1173 /*ARGSUSED*/
1174 static int
1175 gen_write(dev_t dev, struct uio *uiop, cred_t *credp)
1176 {
1177         return (0);
1178 }
1179 
1180 /*ARGSUSED0*/
1181 static int
1182 gen_power(dev_info_t *dip, int cmpt, int level)
1183 {
1184         struct dstate *dstatep;
1185         int instance = ddi_get_instance(dip);
1186         char *nodename = ddi_node_name(dip);
1187         int level_tmp;
1188 
1189         GEN_DEBUG((CE_CONT, "%s%d: power: cmpt %d to level %d",
1190             nodename, instance, cmpt, level));
1191 
1192         dstatep = ddi_get_soft_state(dstates, instance);
1193         if (dstatep == NULL) {
1194 
1195                 return (DDI_FAILURE);
1196         }
1197 
1198         /*
1199          * Keep track of the power levels for both components
1200          * in the dstatep->comp[] array.
1201          * Set comp 0 to full level if non-zero comps
1202          * are being set to a higher, non-zero level.
1203          */
1204         if (cmpt == 0) {
1205                 mutex_enter(&dstatep->lock);
1206                 dstatep->level[cmpt] = level;
1207                 mutex_exit(&dstatep->lock);
1208         } else if (level > dstatep->level[cmpt] && level != 0 &&
1209             dstatep->level[0] != COMP_0_MAXPWR) {
1210                 /*
1211                  * If component 0 is not at COMP_0_MAXPWR, and component 1
1212                  * is being powered ON, invoke pm_raise_power() or
1213                  * pm_power_has_changed() based on the
1214                  * PUP_WITH_PWR_HAS_CHANGED_FLAG flag.
1215                  * PUP_WITH_PWR_HAS_CHANGED_FLAG = FALSE by default, invoking
1216                  * pm_raise_power().
1217                  */
1218                 if (!(dstatep->flag & PUP_WITH_PWR_HAS_CHANGED_FLAG)) {
1219                         /*
1220                          * first set comp 0 to level COMP_0_MAXPWR
1221                          */
1222                         GEN_DEBUG((CE_CONT, "%s%d: power:  "
1223                             "pm_raise_power: comp 0 to level %d",
1224                             nodename, instance, COMP_0_MAXPWR));
1225                         if (pm_raise_power(dip, 0, COMP_0_MAXPWR) !=
1226                             DDI_SUCCESS) {
1227                                 cmn_err(CE_WARN,
1228                                     "%s%d: power: pm_raise_power() "
1229                                     "failed: comp 0 to level %d\n",
1230                                     nodename, instance, COMP_0_MAXPWR);
1231 
1232                                 return (DDI_FAILURE);
1233 
1234                         } else {
1235                                 mutex_enter(&dstatep->lock);
1236                                 dstatep->level[0] = COMP_0_MAXPWR;
1237                                 /*
1238                                  * now set the level on the non-zero comp
1239                                  */
1240                                 dstatep->level[cmpt] = level;
1241                                 mutex_exit(&dstatep->lock);
1242                                 GEN_DEBUG((CE_CONT, "%s%d: power: "
1243                                     "comp %d to level %d",
1244                                     nodename, instance, cmpt, level));
1245                         }
1246                 } else {
1247                         GEN_DEBUG((CE_CONT, "%s%d: power: "
1248                             "pm_power_has_changed: comp 0 to level %d",
1249                             nodename, instance, COMP_0_MAXPWR));
1250                         mutex_enter(&dstatep->lock);
1251                         level_tmp = dstatep->level[0];
1252                         dstatep->level[0] = COMP_0_MAXPWR;
1253                         if (pm_power_has_changed(dip, 0, COMP_0_MAXPWR) !=
1254                             DDI_SUCCESS) {
1255                                 cmn_err(CE_WARN,
1256                                     "%s%d: power: pm_power_has_changed() "
1257                                     "failed: comp 0 to level %d\n",
1258                                     nodename, instance, COMP_0_MAXPWR);
1259                                 dstatep->level[0] = level_tmp;
1260                         } else {
1261                                 /*
1262                                  * now set the level on the non-zero comp
1263                                  */
1264                                 GEN_DEBUG((CE_CONT, "%s%d: power:"
1265                                     " pm_power_has_changed: comp %d"
1266                                     " to level %d", nodename, instance,
1267                                     cmpt, level));
1268                                 dstatep->level[cmpt] = level;
1269                         }
1270                         mutex_exit(&dstatep->lock);
1271                 }
1272         } else {
1273                 mutex_enter(&dstatep->lock);
1274                 dstatep->level[cmpt] = level;
1275                 mutex_exit(&dstatep->lock);
1276         }
1277 
1278         return (DDI_SUCCESS);
1279 }
1280 
1281 
1282 /*
1283  * Create properties of various data types for testing devfs events.
1284  */
1285 static int
1286 gen_create_properties(dev_info_t *devi)
1287 {
1288         int int_val = 3023;
1289         int int_array[] = { 3, 10, 304, 230, 4};
1290         int64_t int64_val = 20;
1291         int64_t int64_array[] = { 12, 24, 36, 48};
1292         char *string_val = "Dev_node_prop";
1293         char *string_array[] = {"Dev_node_prop:0",
1294             "Dev_node_prop:1", "Dev_node_prop:2", "Dev_node_prop:3"};
1295         uchar_t byte_array[] = { (uchar_t)0xaa, (uchar_t)0x55,
1296             (uchar_t)0x12, (uchar_t)0xcd };
1297         char bytes[] = { (char)0x00, (char)0xef, (char)0xff };
1298 
1299         if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, "int", int_val)
1300             != DDI_PROP_SUCCESS)
1301                 return (DDI_FAILURE);
1302 
1303         if (ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, "int-array",
1304             int_array, sizeof (int_array) / sizeof (int)) != DDI_PROP_SUCCESS)
1305                 return (DDI_FAILURE);
1306 
1307         if (ddi_prop_update_int64(DDI_DEV_T_NONE, devi, "int64", int64_val)
1308             != DDI_PROP_SUCCESS)
1309                 return (DDI_FAILURE);
1310 
1311         if (ddi_prop_update_int64_array(DDI_DEV_T_NONE, devi, "int64-array",
1312             int64_array, sizeof (int64_array) / sizeof (int64_t))
1313             != DDI_PROP_SUCCESS)
1314                 return (DDI_FAILURE);
1315 
1316         if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "string", string_val)
1317             != DDI_PROP_SUCCESS)
1318                 return (DDI_FAILURE);
1319 
1320         if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "string-array",
1321             string_array, sizeof (string_array) / sizeof (char *))
1322             != DDI_PROP_SUCCESS)
1323                 return (DDI_FAILURE);
1324 
1325         if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1326             "boolean", NULL, 0) != DDI_PROP_SUCCESS)
1327                 return (DDI_FAILURE);
1328 
1329         if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, devi, "byte-array",
1330             byte_array, sizeof (byte_array)) != DDI_PROP_SUCCESS)
1331                 return (DDI_FAILURE);
1332 
1333         /* untyped property */
1334         if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "untyped",
1335             (caddr_t)bytes, sizeof (bytes)) != DDI_PROP_SUCCESS)
1336                 return (DDI_FAILURE);
1337 
1338         return (DDI_SUCCESS);
1339 }
1340 
1341 static struct driver_minor_data {
1342         char    *name;
1343         minor_t minor;
1344         int     type;
1345 } disk_minor_data[] = {
1346         {"a", 0, S_IFBLK},
1347         {"b", 1, S_IFBLK},
1348         {"c", 2, S_IFBLK},
1349         {"d", 3, S_IFBLK},
1350         {"e", 4, S_IFBLK},
1351         {"f", 5, S_IFBLK},
1352         {"g", 6, S_IFBLK},
1353         {"h", 7, S_IFBLK},
1354         {"a,raw", 0, S_IFCHR},
1355         {"b,raw", 1, S_IFCHR},
1356         {"c,raw", 2, S_IFCHR},
1357         {"d,raw", 3, S_IFCHR},
1358         {"e,raw", 4, S_IFCHR},
1359         {"f,raw", 5, S_IFCHR},
1360         {"g,raw", 6, S_IFCHR},
1361         {"h,raw", 7, S_IFCHR},
1362         {0}
1363 };
1364 
1365 
1366 static struct driver_serial_minor_data {
1367         char    *name;
1368         minor_t minor;
1369         int     type;
1370         char    *node_type;
1371 }  serial_minor_data[] = {
1372         {"0", 0, S_IFCHR, "ddi_serial"},
1373         {"1", 1, S_IFCHR, "ddi_serial"},
1374         {"0,cu", 2, S_IFCHR, "ddi_serial:dialout"},
1375         {"1,cu", 3, S_IFCHR, "ddi_serial:dialout"},
1376         {0}
1377 };
1378 
1379 
1380 static int
1381 gen_create_display(dev_info_t *devi)
1382 {
1383 
1384         int instance = ddi_get_instance(devi);
1385         char minor_name[15];
1386 
1387         (void) sprintf(minor_name, "cgtwenty%d", instance);
1388 
1389         return (ddi_create_minor_node(devi, minor_name, S_IFCHR,
1390             INST_TO_MINOR(instance), DDI_NT_DISPLAY, NULL));
1391 }
1392 
1393 static int
1394 gen_create_mn_disk_chan(dev_info_t *devi)
1395 {
1396         struct driver_minor_data *dmdp;
1397         int instance = ddi_get_instance(devi);
1398 
1399         if (gen_create_properties(devi) != DDI_SUCCESS)
1400                 return (DDI_FAILURE);
1401 
1402         for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1403                 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1404                     (INST_TO_MINOR(instance)) | dmdp->minor,
1405                     DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
1406 
1407                         return (DDI_FAILURE);
1408                 }
1409         }
1410         return (DDI_SUCCESS);
1411 }
1412 
1413 static uint_t
1414 atod(char *s)
1415 {
1416         uint_t val = 0;
1417         uint_t digit;
1418 
1419         while (*s) {
1420                 if (*s >= '0' && *s <= '9')
1421                         digit = *s++ - '0';
1422                 else
1423                         break;
1424                 val = (val * 10) + digit;
1425         }
1426         return (val);
1427 }
1428 
1429 
1430 static int
1431 gen_create_mn_disk_wwn(dev_info_t *devi)
1432 {
1433         struct driver_minor_data *dmdp;
1434         int instance = ddi_get_instance(devi);
1435         char *address = ddi_get_name_addr(devi);
1436         int target, lun;
1437 
1438         if (address[0] >= '0' && address[0] <= '9' &&
1439             strchr(address, ',')) {
1440                 target = atod(address);
1441                 address = strchr(address, ',');
1442                 lun = atod(++address);
1443         } else { /* this hack is for rm_stale_link() testing */
1444                 target = 10;
1445                 lun = 5;
1446         }
1447 
1448         if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1449             "target", (caddr_t)&target, sizeof (int))
1450             != DDI_PROP_SUCCESS) {
1451                 return (DDI_FAILURE);
1452         }
1453         if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
1454             "lun", (caddr_t)&lun, sizeof (int))
1455             != DDI_PROP_SUCCESS) {
1456                 return (DDI_FAILURE);
1457         }
1458 
1459         for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1460                 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1461                     (INST_TO_MINOR(instance)) | dmdp->minor,
1462                     DDI_NT_BLOCK_WWN, NULL) != DDI_SUCCESS) {
1463 
1464                         return (DDI_FAILURE);
1465                 }
1466         }
1467         return (DDI_SUCCESS);
1468 }
1469 
1470 static int
1471 gen_create_mn_disk_cdrom(dev_info_t *devi)
1472 {
1473         struct driver_minor_data *dmdp;
1474         int instance = ddi_get_instance(devi);
1475 
1476         for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1477                 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1478                     (INST_TO_MINOR(instance)) | dmdp->minor,
1479                     DDI_NT_CD_CHAN, NULL) != DDI_SUCCESS) {
1480 
1481                         return (DDI_FAILURE);
1482                 }
1483         }
1484         return (DDI_SUCCESS);
1485 }
1486 
1487 static int
1488 gen_create_mn_disk_fd(dev_info_t *devi)
1489 {
1490         struct driver_minor_data *dmdp;
1491         int instance = ddi_get_instance(devi);
1492 
1493         for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) {
1494                 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1495                     (INST_TO_MINOR(instance)) | dmdp->minor,
1496                     DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) {
1497 
1498                         return (DDI_FAILURE);
1499                 }
1500         }
1501         return (DDI_SUCCESS);
1502 }
1503 
1504 static int
1505 gen_create_serial(dev_info_t *devi)
1506 {
1507         struct driver_serial_minor_data *dmdp;
1508         int instance = ddi_get_instance(devi);
1509 
1510         for (dmdp = serial_minor_data; dmdp->name != NULL; dmdp++) {
1511                 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type,
1512                     (INST_TO_MINOR(instance)) | dmdp->minor,
1513                     dmdp->node_type, NULL) != DDI_SUCCESS) {
1514 
1515                         return (DDI_FAILURE);
1516                 }
1517         }
1518         return (DDI_SUCCESS);
1519 }
1520 
1521 static int
1522 gen_create_net(dev_info_t *devi)
1523 {
1524         int instance = ddi_get_instance(devi);
1525         char minorname[32];
1526 
1527         if (gen_create_properties(devi) != DDI_SUCCESS)
1528                 return (DDI_FAILURE);
1529 
1530         (void) snprintf(minorname, sizeof (minorname), "gen_drv%d", instance);
1531         return (ddi_create_minor_node(devi, minorname, S_IFCHR,
1532             INST_TO_MINOR(instance), DDI_NT_NET, 0));
1533 }
1534 
1535 static int
1536 gen_create_minor_nodes(dev_info_t *devi, struct dstate *dstatep)
1537 {
1538         int rval = DDI_SUCCESS;
1539         char *node_name;
1540 
1541         node_name = ddi_node_name(devi);
1542 
1543         if (strcmp(node_name, "disk_chan") == 0) {
1544                 rval = gen_create_mn_disk_chan(devi);
1545         } else if (strcmp(node_name, "disk_wwn") == 0) {
1546                 rval = gen_create_mn_disk_wwn(devi);
1547         } else if (strcmp(node_name, "disk_cdrom") == 0) {
1548                 rval = gen_create_mn_disk_cdrom(devi);
1549         } else if (strcmp(node_name, "disk_fd") == 0) {
1550                 rval = gen_create_mn_disk_fd(devi);
1551         } else if (strcmp(node_name, "cgtwenty") == 0) {
1552                 rval = gen_create_display(devi);
1553         } else if (strcmp(node_name, "genzs") == 0) {
1554                 rval = gen_create_serial(devi);
1555         } else if (strcmp(node_name, "net") == 0) {
1556                 rval = gen_create_net(devi);
1557         } else {
1558                 int instance = ddi_get_instance(devi);
1559                 char *node_type;
1560 
1561                 /*
1562                  * Solaris may directly hang the node_type off the minor node
1563                  * (without making a copy).  Since we free the node_type
1564                  * property below we need to make a private copy to pass
1565                  * to ddi_create_minor_node to avoid devinfo snapshot panics.
1566                  * We store a pointer to our copy in dstate and free it in
1567                  * gen_detach after the minor nodes have been deleted by
1568                  * ddi_remove_minor_node.
1569                  */
1570                 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
1571                     DDI_PROP_DONTPASS, "node-type", &node_type) != 0) {
1572                         cmn_err(CE_WARN, "couldn't get node-type\n");
1573                         return (DDI_FAILURE);
1574                 }
1575                 if (node_type) {
1576                         dstatep->node_type = kmem_alloc(
1577                             strlen(node_type) + 1, KM_SLEEP);
1578                         (void) strcpy(dstatep->node_type, node_type);
1579                 }
1580                 ddi_prop_free(node_type);
1581 
1582                 /* the minor name is the same as the node name */
1583                 if (ddi_create_minor_node(devi, node_name, S_IFCHR,
1584                     (INST_TO_MINOR(instance)), dstatep->node_type, NULL) !=
1585                     DDI_SUCCESS) {
1586                         if (dstatep->node_type) {
1587                                 kmem_free(dstatep->node_type,
1588                                     strlen(dstatep->node_type) + 1);
1589                                 dstatep->node_type = NULL;
1590                         }
1591                         return (DDI_FAILURE);
1592                 }
1593                 return (DDI_SUCCESS);
1594         }
1595 
1596         if (rval != DDI_SUCCESS) {
1597                 ddi_prop_remove_all(devi);
1598                 ddi_remove_minor_node(devi, NULL);
1599         }
1600 
1601         return (rval);
1602 }
1603 
1604 /*ARGSUSED*/
1605 static void
1606 gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg,
1607     void *impl_data)
1608 {
1609         if (gen_debug)
1610                 cmn_err(CE_NOTE, "gen_event_cb invoked");
1611 
1612 }