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 tphci driver can be used to exercise the mpxio framework together
36 #include <sys/modctl.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunndi.h>
40 #include <sys/sunmdi.h>
43 /* cb_ops entry points */
44 static int tphci_open(dev_t
*, int, int, cred_t
*);
45 static int tphci_close(dev_t
, int, int, cred_t
*);
46 static int tphci_ioctl(dev_t
, int, intptr_t, int, cred_t
*, int *);
47 static int tphci_attach(dev_info_t
*, ddi_attach_cmd_t
);
48 static int tphci_detach(dev_info_t
*, ddi_detach_cmd_t
);
49 static int tphci_getinfo(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
51 /* bus_ops entry points */
52 static int tphci_ctl(dev_info_t
*, dev_info_t
*, ddi_ctl_enum_t
, void *,
54 static int tphci_initchild(dev_info_t
*, dev_info_t
*);
55 static int tphci_uninitchild(dev_info_t
*, dev_info_t
*);
56 static int tphci_bus_config(dev_info_t
*, uint_t
, ddi_bus_config_op_t
, void *,
58 static int tphci_bus_unconfig(dev_info_t
*, uint_t
, ddi_bus_config_op_t
,
60 static int tphci_intr_op(dev_info_t
*dip
, dev_info_t
*rdip
,
61 ddi_intr_op_t op
, ddi_intr_handle_impl_t
*hdlp
, void *result
);
64 static void *tphci_state
;
69 static struct cb_ops tphci_cb_ops
= {
70 tphci_open
, /* open */
71 tphci_close
, /* close */
77 tphci_ioctl
, /* ioctl */
81 nochpoll
, /* chpoll */
82 ddi_prop_op
, /* cb_prop_op */
84 D_NEW
| D_MP
, /* cb_flag */
90 static struct bus_ops tphci_bus_ops
= {
91 BUSO_REV
, /* busops_rev */
92 nullbusmap
, /* bus_map */
93 NULL
, /* bus_get_intrspec */
94 NULL
, /* bus_add_interspec */
95 NULL
, /* bus_remove_interspec */
96 i_ddi_map_fault
, /* bus_map_fault */
97 ddi_no_dma_map
, /* bus_dma_map */
98 ddi_no_dma_allochdl
, /* bus_dma_allochdl */
99 NULL
, /* bus_dma_freehdl */
100 NULL
, /* bus_dma_bindhdl */
101 NULL
, /* bus_dma_unbindhdl */
102 NULL
, /* bus_dma_flush */
103 NULL
, /* bus_dma_win */
104 NULL
, /* bus_dma_ctl */
105 tphci_ctl
, /* bus_ctl */
106 ddi_bus_prop_op
, /* bus_prop_op */
107 NULL
, /* bus_get_eventcookie */
108 NULL
, /* bus_add_eventcall */
109 NULL
, /* bus_remove_event */
110 NULL
, /* bus_post_event */
111 NULL
, /* bus_intr_ctl */
112 tphci_bus_config
, /* bus_config */
113 tphci_bus_unconfig
, /* bus_unconfig */
114 NULL
, /* bus_fm_init */
115 NULL
, /* bus_fm_fini */
116 NULL
, /* bus_fm_access_enter */
117 NULL
, /* bus_fm_access_exit */
118 NULL
, /* bus_power */
119 tphci_intr_op
/* bus_intr_op */
122 static struct dev_ops tphci_ops
= {
126 nulldev
, /* identify */
128 tphci_attach
, /* attach and detach are mandatory */
131 &tphci_cb_ops
, /* cb_ops */
132 &tphci_bus_ops
, /* bus_ops */
134 ddi_quiesce_not_needed
, /* quiesce */
137 extern struct mod_ops mod_driverops
;
139 static struct modldrv modldrv
= {
145 static struct modlinkage modlinkage
= {
156 if ((rval
= ddi_soft_state_init(&tphci_state
,
157 sizeof (struct tphci_state
), 2)) != 0) {
161 if ((rval
= mod_install(&modlinkage
)) != 0) {
162 ddi_soft_state_fini(&tphci_state
);
174 * don't start cleaning up until we know that the module remove
175 * has worked -- if this works, then we know that each instance
176 * has successfully been detached
178 if ((rval
= mod_remove(&modlinkage
)) != 0) {
182 ddi_soft_state_fini(&tphci_state
);
188 _info(struct modinfo
*modinfop
)
190 return (mod_info(&modlinkage
, modinfop
));
195 tphci_open(dev_t
*devp
, int flag
, int otype
, cred_t
*credp
)
197 struct tphci_state
*phci
;
199 if (otype
!= OTYP_CHR
) {
203 phci
= ddi_get_soft_state(tphci_state
, getminor(*devp
));
214 tphci_close(dev_t dev
, int flag
, int otype
, cred_t
*credp
)
216 struct tphci_state
*phci
;
217 if (otype
!= OTYP_CHR
) {
221 phci
= ddi_get_soft_state(tphci_state
, getminor(dev
));
231 tphci_ioctl(dev_t dev
, int cmd
, intptr_t data
, int mode
,
232 cred_t
*credp
, int *rval
)
241 tphci_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
244 int instance
, phci_regis
= 0;
245 struct tphci_state
*phci
= NULL
;
247 instance
= ddi_get_instance(dip
);
255 return (0); /* nothing to do */
258 return (DDI_FAILURE
);
262 * Allocate phci data structure.
264 if (ddi_soft_state_zalloc(tphci_state
, instance
) != DDI_SUCCESS
) {
265 return (DDI_FAILURE
);
268 phci
= ddi_get_soft_state(tphci_state
, instance
);
269 ASSERT(phci
!= NULL
);
272 /* bus_addr has the form #,<vhci_class> */
273 vclass
= strchr(ddi_get_name_addr(dip
), ',');
274 if (vclass
== NULL
|| vclass
[1] == '\0') {
275 cmn_err(CE_NOTE
, "tphci invalid bus_addr %s",
276 ddi_get_name_addr(dip
));
281 * Attach this instance with the mpxio framework
283 if (mdi_phci_register(vclass
+ 1, dip
, 0) != MDI_SUCCESS
) {
284 cmn_err(CE_WARN
, "%s mdi_phci_register failed",
290 if (ddi_create_minor_node(dip
, "devctl", S_IFCHR
,
291 instance
, DDI_NT_SCSI_NEXUS
, 0) != DDI_SUCCESS
) {
292 cmn_err(CE_NOTE
, "%s ddi_create_minor_node failed",
297 (void) ddi_prop_update_int(DDI_DEV_T_NONE
, dip
, DDI_NO_AUTODETACH
, 1);
299 return (DDI_SUCCESS
);
303 (void) mdi_phci_unregister(dip
, 0);
305 ddi_soft_state_free(tphci_state
, instance
);
306 return (DDI_FAILURE
);
312 tphci_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
314 int instance
= ddi_get_instance(dip
);
322 return (0); /* nothing to do */
325 return (DDI_FAILURE
);
328 if (mdi_phci_unregister(dip
, 0) != MDI_SUCCESS
)
329 return (DDI_FAILURE
);
331 ddi_remove_minor_node(dip
, NULL
);
332 ddi_soft_state_free(tphci_state
, instance
);
334 return (DDI_SUCCESS
);
339 * Given the device number, return the devinfo pointer or the
341 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
346 tphci_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **result
)
348 struct tphci_state
*phci
;
349 int instance
= getminor((dev_t
)arg
);
352 case DDI_INFO_DEVT2DEVINFO
:
353 phci
= ddi_get_soft_state(tphci_state
, instance
);
358 return (DDI_FAILURE
);
362 case DDI_INFO_DEVT2INSTANCE
:
363 *result
= (void *)(uintptr_t)instance
;
367 return (DDI_FAILURE
);
370 return (DDI_SUCCESS
);
374 * Interrupt stuff. NO OP for pseudo drivers.
378 tphci_intr_op(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_intr_op_t op
,
379 ddi_intr_handle_impl_t
*hdlp
, void *result
)
381 return (DDI_FAILURE
);
385 tphci_ctl(dev_info_t
*dip
, dev_info_t
*rdip
,
386 ddi_ctl_enum_t ctlop
, void *arg
, void *result
)
389 case DDI_CTLOPS_REPORTDEV
:
390 if (rdip
== (dev_info_t
*)0)
391 return (DDI_FAILURE
);
392 cmn_err(CE_CONT
, "?tphci-device: %s%d\n",
393 ddi_get_name(rdip
), ddi_get_instance(rdip
));
394 return (DDI_SUCCESS
);
396 case DDI_CTLOPS_INITCHILD
:
398 dev_info_t
*child
= (dev_info_t
*)arg
;
399 return (tphci_initchild(dip
, child
));
402 case DDI_CTLOPS_UNINITCHILD
:
404 dev_info_t
*child
= (dev_info_t
*)arg
;
405 return (tphci_uninitchild(dip
, child
));
408 case DDI_CTLOPS_DMAPMAPC
:
409 case DDI_CTLOPS_REPORTINT
:
410 case DDI_CTLOPS_REGSIZE
:
411 case DDI_CTLOPS_NREGS
:
412 case DDI_CTLOPS_SIDDEV
:
413 case DDI_CTLOPS_SLAVEONLY
:
414 case DDI_CTLOPS_AFFINITY
:
415 case DDI_CTLOPS_POKE
:
416 case DDI_CTLOPS_PEEK
:
418 * These ops correspond to functions that "shouldn't" be called
419 * by a pseudo driver. So we whine when we're called.
421 cmn_err(CE_CONT
, "%s%d: invalid op (%d) from %s%d\n",
422 ddi_get_name(dip
), ddi_get_instance(dip
),
423 ctlop
, ddi_get_name(rdip
), ddi_get_instance(rdip
));
424 return (DDI_FAILURE
);
426 case DDI_CTLOPS_ATTACH
:
427 case DDI_CTLOPS_BTOP
:
428 case DDI_CTLOPS_BTOPR
:
429 case DDI_CTLOPS_DETACH
:
430 case DDI_CTLOPS_DVMAPAGESIZE
:
431 case DDI_CTLOPS_IOMIN
:
432 case DDI_CTLOPS_POWER
:
433 case DDI_CTLOPS_PTOB
:
436 * The ops that we pass up (default). We pass up memory
437 * allocation oriented ops that we receive - these may be
438 * associated with pseudo HBA drivers below us with target
439 * drivers below them that use ddi memory allocation
440 * interfaces like scsi_alloc_consistent_buf.
442 return (ddi_ctlops(dip
, rdip
, ctlop
, arg
, result
));
447 tphci_initchild(dev_info_t
*dip
, dev_info_t
*child
)
449 _NOTE(ARGUNUSED(dip
))
450 ddi_set_name_addr(child
, "0");
451 return (DDI_SUCCESS
);
456 tphci_uninitchild(dev_info_t
*dip
, dev_info_t
*child
)
458 ddi_set_name_addr(child
, NULL
);
459 return (DDI_SUCCESS
);
463 tp_decode_name(char *devnm
, char **cname
, char **paddr
, char **guid
)
467 i_ddi_parse_name(devnm
, cname
, paddr
, NULL
);
468 if ((strcmp(*cname
, "tclient") != 0) &&
469 (strcmp(*cname
, "tphci") != 0) || *paddr
== NULL
)
472 tmp
= strchr(*paddr
, ',');
473 if (tmp
== NULL
|| tmp
[1] == '\0')
481 tphci_bus_config(dev_info_t
*parent
, uint_t flags
,
482 ddi_bus_config_op_t op
, void *arg
, dev_info_t
**childp
)
484 _NOTE(ARGUNUSED(flags
))
485 char *cname
, *paddr
, *guid
, *devnm
;
492 case BUS_CONFIG_DRIVER
: /* no direct children to configure */
494 return (NDI_SUCCESS
);
496 return (NDI_FAILURE
);
499 /* only implement BUS_CONFIG_ONE */
500 devnm
= i_ddi_strdup((char *)arg
, KM_SLEEP
);
501 len
= strlen(devnm
) + 1;
503 /* caddr is hardcoded in the form *,<guid> */
504 if (tp_decode_name(devnm
, &cname
, &paddr
, &guid
) != 0) {
505 cmn_err(CE_NOTE
, "tphci_bus_config -- invalid device %s",
507 kmem_free(devnm
, len
);
508 return (NDI_FAILURE
);
511 mdi_devi_enter(parent
, &circ
);
512 rval
= mdi_pi_alloc(parent
, cname
, guid
, paddr
, 0, &pip
);
513 kmem_free(devnm
, len
);
514 if (rval
!= MDI_SUCCESS
) {
515 cmn_err(CE_NOTE
, "tphci_bus_config -- mdi_pi_alloc failed");
516 mdi_devi_exit(parent
, circ
);
517 return (NDI_FAILURE
);
521 * Hold the path and exit the pHCI while calling mdi_pi_online
522 * to avoid deadlock with power management of pHCI.
525 mdi_devi_exit_phci(parent
, circ
);
526 rval
= mdi_pi_online(pip
, 0);
527 mdi_devi_enter_phci(parent
, &circ
);
530 if (rval
!= MDI_SUCCESS
) {
531 cmn_err(CE_NOTE
, "tphci_bus_config -- mdi_pi_online failed");
532 (void) mdi_pi_free(pip
, 0);
533 mdi_devi_exit(parent
, circ
);
534 return (NDI_FAILURE
);
538 *childp
= mdi_pi_get_client(pip
);
539 ndi_hold_devi(*childp
);
541 mdi_devi_exit(parent
, circ
);
543 return (NDI_SUCCESS
);
547 tphci_bus_unconfig(dev_info_t
*parent
, uint_t flags
,
548 ddi_bus_config_op_t op
, void *arg
)
550 int rval
= MDI_SUCCESS
;
552 mdi_pathinfo_t
*pip
, *next
;
553 char *devnm
, *cname
, *caddr
;
556 case BUS_UNCONFIG_ONE
:
558 i_ddi_parse_name(devnm
, &cname
, &caddr
, NULL
);
559 if (strcmp(cname
, "tclient") != 0)
560 return (NDI_SUCCESS
); /* no such device */
562 mdi_devi_enter(parent
, &circ
);
563 pip
= mdi_pi_find(parent
, NULL
, caddr
);
566 mdi_devi_exit_phci(parent
, circ
);
567 rval
= mdi_pi_offline(pip
, NDI_DEVI_REMOVE
);
568 mdi_devi_enter_phci(parent
, &circ
);
571 if (rval
== MDI_SUCCESS
)
572 (void) mdi_pi_free(pip
, 0);
574 mdi_devi_exit(parent
, circ
);
575 return (rval
== MDI_SUCCESS
? NDI_SUCCESS
: NDI_FAILURE
);
577 case BUS_UNCONFIG_ALL
:
578 if (flags
& NDI_AUTODETACH
)
579 return (NDI_FAILURE
);
581 mdi_devi_enter(parent
, &circ
);
582 next
= mdi_get_next_client_path(parent
, NULL
);
583 while ((pip
= next
) != NULL
) {
584 next
= mdi_get_next_client_path(parent
, pip
);
587 mdi_devi_exit_phci(parent
, circ
);
588 rval
= mdi_pi_offline(pip
, NDI_DEVI_REMOVE
);
589 mdi_devi_enter_phci(parent
, &circ
);
592 if (rval
!= MDI_SUCCESS
)
594 (void) mdi_pi_free(pip
, 0);
596 mdi_devi_exit(parent
, circ
);
597 return (rval
== MDI_SUCCESS
? NDI_SUCCESS
: NDI_FAILURE
);
599 case BUS_UNCONFIG_DRIVER
: /* nothing to do */
600 return (NDI_SUCCESS
);
603 return (NDI_FAILURE
);