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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * The tvhci driver can be used to exercise the mpxio framework together
35 #include <sys/sunddi.h>
36 #include <sys/scsi/scsi.h>
37 #include <sys/scsi/impl/scsi_reset_notify.h>
38 #include <sys/sunmdi.h>
39 #include <sys/mdi_impldefs.h>
42 /* cb_ops entry points */
43 static int tvhci_open(dev_t
*, int, int, cred_t
*);
44 static int tvhci_close(dev_t
, int, int, cred_t
*);
45 static int tvhci_ioctl(dev_t
, int, intptr_t, int, cred_t
*, int *);
46 static int tvhci_attach(dev_info_t
*, ddi_attach_cmd_t
);
47 static int tvhci_detach(dev_info_t
*, ddi_detach_cmd_t
);
48 static int tvhci_getinfo(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
50 /* bus_ops entry points */
51 static int tvhci_ctl(dev_info_t
*, dev_info_t
*, ddi_ctl_enum_t
, void *,
53 static int tvhci_initchild(dev_info_t
*, dev_info_t
*);
54 static int tvhci_uninitchild(dev_info_t
*, dev_info_t
*);
55 static int tvhci_bus_config(dev_info_t
*, uint_t
, ddi_bus_config_op_t
, void *,
57 static int tvhci_bus_unconfig(dev_info_t
*, uint_t
, ddi_bus_config_op_t
,
59 static int tvhci_intr_op(dev_info_t
*dip
, dev_info_t
*rdip
,
60 ddi_intr_op_t op
, ddi_intr_handle_impl_t
*hdlp
, void *result
);
63 static int tvhci_pi_init(dev_info_t
*, mdi_pathinfo_t
*, int);
64 static int tvhci_pi_uninit(dev_info_t
*, mdi_pathinfo_t
*, int);
65 static int tvhci_pi_state_change(dev_info_t
*, mdi_pathinfo_t
*,
66 mdi_pathinfo_state_t
, uint32_t, int);
67 static int tvhci_failover(dev_info_t
*, dev_info_t
*, int);
69 static void *tvhci_state
;
74 static mdi_vhci_ops_t tvhci_opinfo
= {
78 tvhci_pi_state_change
,
82 static struct cb_ops tvhci_cb_ops
= {
83 tvhci_open
, /* open */
84 tvhci_close
, /* close */
90 tvhci_ioctl
, /* ioctl */
94 nochpoll
, /* chpoll */
95 ddi_prop_op
, /* cb_prop_op */
97 D_NEW
| D_MP
, /* cb_flag */
103 static struct bus_ops tvhci_bus_ops
= {
104 BUSO_REV
, /* busops_rev */
105 nullbusmap
, /* bus_map */
106 NULL
, /* bus_get_intrspec */
107 NULL
, /* bus_add_interspec */
108 NULL
, /* bus_remove_interspec */
109 i_ddi_map_fault
, /* bus_map_fault */
110 ddi_no_dma_map
, /* bus_dma_map */
111 ddi_no_dma_allochdl
, /* bus_dma_allochdl */
112 NULL
, /* bus_dma_freehdl */
113 NULL
, /* bus_dma_bindhdl */
114 NULL
, /* bus_dma_unbindhdl */
115 NULL
, /* bus_dma_flush */
116 NULL
, /* bus_dma_win */
117 NULL
, /* bus_dma_ctl */
118 tvhci_ctl
, /* bus_ctl */
119 ddi_bus_prop_op
, /* bus_prop_op */
120 NULL
, /* bus_get_eventcookie */
121 NULL
, /* bus_add_eventcall */
122 NULL
, /* bus_remove_event */
123 NULL
, /* bus_post_event */
124 NULL
, /* bus_intr_ctl */
125 tvhci_bus_config
, /* bus_config */
126 tvhci_bus_unconfig
, /* bus_unconfig */
127 NULL
, /* bus_fm_init */
128 NULL
, /* bus_fm_fini */
129 NULL
, /* bus_fm_access_enter */
130 NULL
, /* bus_fm_access_exit */
131 NULL
, /* bus_power */
132 tvhci_intr_op
/* bus_intr_op */
135 static struct dev_ops tvhci_ops
= {
139 nulldev
, /* identify */
141 tvhci_attach
, /* attach and detach are mandatory */
144 &tvhci_cb_ops
, /* cb_ops */
145 &tvhci_bus_ops
, /* bus_ops */
147 ddi_quiesce_not_needed
, /* quiesce */
150 extern struct mod_ops mod_driverops
;
152 static struct modldrv modldrv
= {
158 static struct modlinkage modlinkage
= {
169 if ((rval
= ddi_soft_state_init(&tvhci_state
,
170 sizeof (struct tvhci_state
), 2)) != 0) {
174 if ((rval
= mod_install(&modlinkage
)) != 0) {
175 ddi_soft_state_fini(&tvhci_state
);
187 * don't start cleaning up until we know that the module remove
188 * has worked -- if this works, then we know that each instance
189 * has successfully been detached
191 if ((rval
= mod_remove(&modlinkage
)) != 0) {
195 ddi_soft_state_fini(&tvhci_state
);
201 _info(struct modinfo
*modinfop
)
203 return (mod_info(&modlinkage
, modinfop
));
208 tvhci_open(dev_t
*devp
, int flag
, int otype
, cred_t
*credp
)
210 struct tvhci_state
*vhci
;
212 if (otype
!= OTYP_CHR
) {
216 vhci
= ddi_get_soft_state(tvhci_state
, getminor(*devp
));
227 tvhci_close(dev_t dev
, int flag
, int otype
, cred_t
*credp
)
229 struct tvhci_state
*vhci
;
230 if (otype
!= OTYP_CHR
) {
234 vhci
= ddi_get_soft_state(tvhci_state
, getminor(dev
));
244 tvhci_ioctl(dev_t dev
, int cmd
, intptr_t data
, int mode
,
245 cred_t
*credp
, int *rval
)
254 tvhci_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
257 int instance
, vhci_regis
= 0;
258 struct tvhci_state
*vhci
= NULL
;
261 instance
= ddi_get_instance(dip
);
269 return (0); /* nothing to do */
272 return (DDI_FAILURE
);
276 * Allocate vhci data structure.
278 if (ddi_soft_state_zalloc(tvhci_state
, instance
) != DDI_SUCCESS
) {
279 return (DDI_FAILURE
);
282 vhci
= ddi_get_soft_state(tvhci_state
, instance
);
283 ASSERT(vhci
!= NULL
);
286 /* parent must be /pshot */
287 pdip
= ddi_get_parent(dip
);
288 if (strcmp(ddi_driver_name(pdip
), "pshot") != 0 ||
289 ddi_get_parent(pdip
) != ddi_root_node()) {
290 cmn_err(CE_NOTE
, "tvhci must be under /pshot/");
295 * XXX add mpxio-disable property. need to remove the check
298 (void) ddi_prop_update_string(DDI_DEV_T_NONE
, dip
,
299 "mpxio-disable", "no");
301 /* bus_addr is the <vhci_class> */
302 vclass
= ddi_get_name_addr(dip
);
303 if (vclass
== NULL
|| vclass
[1] == '\0') {
304 cmn_err(CE_NOTE
, "tvhci invalid vhci class");
309 * Attach this instance with the mpxio framework
311 if (mdi_vhci_register(vclass
, dip
, &tvhci_opinfo
, 0) != MDI_SUCCESS
) {
312 cmn_err(CE_WARN
, "%s mdi_vhci_register failed",
318 if (ddi_create_minor_node(dip
, "devctl", S_IFCHR
,
319 instance
, DDI_NT_SCSI_NEXUS
, 0) != DDI_SUCCESS
) {
320 cmn_err(CE_NOTE
, "%s ddi_create_minor_node failed",
325 (void) ddi_prop_update_int(DDI_DEV_T_NONE
, dip
, DDI_NO_AUTODETACH
, 1);
327 return (DDI_SUCCESS
);
331 (void) mdi_vhci_unregister(dip
, 0);
333 ddi_soft_state_free(tvhci_state
, instance
);
334 return (DDI_FAILURE
);
340 tvhci_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
342 int instance
= ddi_get_instance(dip
);
350 return (0); /* nothing to do */
353 return (DDI_FAILURE
);
356 if (mdi_vhci_unregister(dip
, 0) != MDI_SUCCESS
)
357 return (DDI_FAILURE
);
359 ddi_remove_minor_node(dip
, NULL
);
360 ddi_soft_state_free(tvhci_state
, instance
);
362 return (DDI_SUCCESS
);
367 * Given the device number, return the devinfo pointer or the
369 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
374 tvhci_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **result
)
376 struct tvhci_state
*vhci
;
377 int instance
= getminor((dev_t
)arg
);
380 case DDI_INFO_DEVT2DEVINFO
:
381 vhci
= ddi_get_soft_state(tvhci_state
, instance
);
386 return (DDI_FAILURE
);
390 case DDI_INFO_DEVT2INSTANCE
:
391 *result
= (void *)(uintptr_t)instance
;
395 return (DDI_FAILURE
);
398 return (DDI_SUCCESS
);
403 tvhci_pi_init(dev_info_t
*vdip
, mdi_pathinfo_t
*pip
, int flags
)
405 return (MDI_SUCCESS
);
410 tvhci_pi_uninit(dev_info_t
*vdip
, mdi_pathinfo_t
*pip
, int flags
)
412 return (MDI_SUCCESS
);
417 tvhci_pi_state_change(dev_info_t
*vdip
, mdi_pathinfo_t
*pip
,
418 mdi_pathinfo_state_t state
, uint32_t ext_state
, int flags
)
420 return (MDI_SUCCESS
);
425 tvhci_failover(dev_info_t
*vdip
, dev_info_t
*cdip
, int flags
)
427 return (MDI_SUCCESS
);
431 * Interrupt stuff. NO OP for pseudo drivers.
435 tvhci_intr_op(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_intr_op_t op
,
436 ddi_intr_handle_impl_t
*hdlp
, void *result
)
438 return (DDI_FAILURE
);
443 tvhci_ctl(dev_info_t
*dip
, dev_info_t
*rdip
,
444 ddi_ctl_enum_t ctlop
, void *arg
, void *result
)
447 case DDI_CTLOPS_REPORTDEV
:
448 if (rdip
== (dev_info_t
*)0)
449 return (DDI_FAILURE
);
450 cmn_err(CE_CONT
, "?tvhci-device: %s%d\n",
451 ddi_get_name(rdip
), ddi_get_instance(rdip
));
452 return (DDI_SUCCESS
);
454 case DDI_CTLOPS_INITCHILD
:
456 dev_info_t
*child
= (dev_info_t
*)arg
;
457 return (tvhci_initchild(dip
, child
));
460 case DDI_CTLOPS_UNINITCHILD
:
462 dev_info_t
*child
= (dev_info_t
*)arg
;
463 return (tvhci_uninitchild(dip
, child
));
466 case DDI_CTLOPS_DMAPMAPC
:
467 case DDI_CTLOPS_REPORTINT
:
468 case DDI_CTLOPS_REGSIZE
:
469 case DDI_CTLOPS_NREGS
:
470 case DDI_CTLOPS_SIDDEV
:
471 case DDI_CTLOPS_SLAVEONLY
:
472 case DDI_CTLOPS_AFFINITY
:
473 case DDI_CTLOPS_POKE
:
474 case DDI_CTLOPS_PEEK
:
476 * These ops correspond to functions that "shouldn't" be called
477 * by a pseudo driver. So we whine when we're called.
479 cmn_err(CE_CONT
, "%s%d: invalid op (%d) from %s%d\n",
480 ddi_get_name(dip
), ddi_get_instance(dip
),
481 ctlop
, ddi_get_name(rdip
), ddi_get_instance(rdip
));
482 return (DDI_FAILURE
);
484 case DDI_CTLOPS_ATTACH
:
485 case DDI_CTLOPS_BTOP
:
486 case DDI_CTLOPS_BTOPR
:
487 case DDI_CTLOPS_DETACH
:
488 case DDI_CTLOPS_DVMAPAGESIZE
:
489 case DDI_CTLOPS_IOMIN
:
490 case DDI_CTLOPS_POWER
:
491 case DDI_CTLOPS_PTOB
:
494 * The ops that we pass up (default). We pass up memory
495 * allocation oriented ops that we receive - these may be
496 * associated with pseudo HBA drivers below us with target
497 * drivers below them that use ddi memory allocation
498 * interfaces like scsi_alloc_consistent_buf.
500 return (ddi_ctlops(dip
, rdip
, ctlop
, arg
, result
));
504 /* set devi_addr to "g<guid>" */
506 tvhci_initchild(dev_info_t
*dip
, dev_info_t
*child
)
508 _NOTE(ARGUNUSED(dip
))
511 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, child
, DDI_PROP_DONTPASS
,
512 MDI_CLIENT_GUID_PROP
, &guid
) != DDI_SUCCESS
) {
513 cmn_err(CE_NOTE
, "tvhci_initchild - no guid property");
514 return (DDI_FAILURE
);
517 addr
= kmem_alloc(MAXNAMELEN
, KM_SLEEP
);
518 (void) snprintf(addr
, MAXNAMELEN
, "g%s", guid
);
519 ddi_set_name_addr(child
, addr
);
521 kmem_free(addr
, MAXNAMELEN
);
523 return (DDI_SUCCESS
);
528 tvhci_uninitchild(dev_info_t
*dip
, dev_info_t
*child
)
530 ddi_set_name_addr(child
, NULL
);
531 return (DDI_SUCCESS
);
534 /* form paddr by cname@<phci_inst>,<guid> */
536 tvh_get_phci_devname(char *cname
, char *guid
,
537 dev_info_t
*pdip
, char *pname
, int len
)
539 (void) snprintf(pname
, len
, "%s@%d,%s",
540 cname
, ddi_get_instance(pdip
), guid
);
545 * Return a pointer to the guid part of the devnm.
546 * devnm format is "nodename@busaddr", busaddr format is "gGUID".
549 tvhci_devnm_to_guid(char *devnm
)
556 while (*cp
!= '\0' && *cp
!= '@')
558 if (*cp
== '@' && *(cp
+ 1) == 'g')
564 tvhci_bus_config(dev_info_t
*pdip
, uint_t flags
, ddi_bus_config_op_t op
,
565 void *arg
, dev_info_t
**child
)
569 if (op
== BUS_CONFIG_ONE
|| op
== BUS_UNCONFIG_ONE
)
570 guid
= tvhci_devnm_to_guid((char *)arg
);
574 if (mdi_vhci_bus_config(pdip
, flags
, op
, arg
, child
, guid
)
576 return (NDI_SUCCESS
);
578 return (NDI_FAILURE
);
582 tvhci_bus_unconfig(dev_info_t
*parent
, uint_t flags
,
583 ddi_bus_config_op_t op
, void *arg
)
585 return (ndi_busop_bus_unconfig(parent
, flags
, op
, arg
));