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 * The tvhci driver can be used to exercise the mpxio framework together
29 * with tphci/tclient.
30 */
31
32#include <sys/conf.h>
33#include <sys/file.h>
34#include <sys/ddi.h>
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>
40#include <sys/disp.h>
41
42/* cb_ops entry points */
43static int tvhci_open(dev_t *, int, int, cred_t *);
44static int tvhci_close(dev_t, int, int, cred_t *);
45static int tvhci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
46static int tvhci_attach(dev_info_t *, ddi_attach_cmd_t);
47static int tvhci_detach(dev_info_t *, ddi_detach_cmd_t);
48static int tvhci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
49
50/* bus_ops entry points */
51static int tvhci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
52    void *);
53static int tvhci_initchild(dev_info_t *, dev_info_t *);
54static int tvhci_uninitchild(dev_info_t *, dev_info_t *);
55static int tvhci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *,
56    dev_info_t **);
57static int tvhci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
58    void *);
59static 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);
61
62/* vhci ops */
63static int tvhci_pi_init(dev_info_t *, mdi_pathinfo_t *, int);
64static int tvhci_pi_uninit(dev_info_t *, mdi_pathinfo_t *, int);
65static int tvhci_pi_state_change(dev_info_t *, mdi_pathinfo_t *,
66    mdi_pathinfo_state_t, uint32_t, int);
67static int tvhci_failover(dev_info_t *, dev_info_t *, int);
68
69static void *tvhci_state;
70struct tvhci_state {
71	dev_info_t *dip;
72};
73
74static mdi_vhci_ops_t tvhci_opinfo = {
75	MDI_VHCI_OPS_REV,
76	tvhci_pi_init,
77	tvhci_pi_uninit,
78	tvhci_pi_state_change,
79	tvhci_failover
80};
81
82static struct cb_ops tvhci_cb_ops = {
83	tvhci_open,			/* open */
84	tvhci_close,			/* close */
85	nodev,				/* strategy */
86	nodev,				/* print */
87	nodev,				/* dump */
88	nodev,				/* read */
89	nodev,				/* write */
90	tvhci_ioctl,			/* ioctl */
91	nodev,				/* devmap */
92	nodev,				/* mmap */
93	nodev,				/* segmap */
94	nochpoll,			/* chpoll */
95	ddi_prop_op,			/* cb_prop_op */
96	0,				/* streamtab */
97	D_NEW | D_MP,			/* cb_flag */
98	CB_REV,				/* rev */
99	nodev,				/* aread */
100	nodev				/* awrite */
101};
102
103static 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 */
133};
134
135static struct dev_ops tvhci_ops = {
136	DEVO_REV,
137	0,
138	tvhci_getinfo,
139	nulldev,		/* identify */
140	nulldev,		/* probe */
141	tvhci_attach,		/* attach and detach are mandatory */
142	tvhci_detach,
143	nodev,			/* reset */
144	&tvhci_cb_ops,		/* cb_ops */
145	&tvhci_bus_ops,		/* bus_ops */
146	NULL,			/* power */
147	ddi_quiesce_not_needed,		/* quiesce */
148};
149
150extern struct mod_ops mod_driverops;
151
152static struct modldrv modldrv = {
153	&mod_driverops,
154	"test vhci driver",
155	&tvhci_ops
156};
157
158static struct modlinkage modlinkage = {
159	MODREV_1,
160	&modldrv,
161	NULL
162};
163
164int
165_init(void)
166{
167	int rval;
168
169	if ((rval = ddi_soft_state_init(&tvhci_state,
170	    sizeof (struct tvhci_state), 2)) != 0) {
171		return (rval);
172	}
173
174	if ((rval = mod_install(&modlinkage)) != 0) {
175		ddi_soft_state_fini(&tvhci_state);
176	}
177	return (rval);
178}
179
180
181int
182_fini(void)
183{
184	int rval;
185
186	/*
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
190	 */
191	if ((rval = mod_remove(&modlinkage)) != 0) {
192		return (rval);
193	}
194
195	ddi_soft_state_fini(&tvhci_state);
196
197	return (rval);
198}
199
200int
201_info(struct modinfo *modinfop)
202{
203	return (mod_info(&modlinkage, modinfop));
204}
205
206/* ARGSUSED */
207static int
208tvhci_open(dev_t *devp, int flag, int otype, cred_t *credp)
209{
210	struct tvhci_state *vhci;
211
212	if (otype != OTYP_CHR) {
213		return (EINVAL);
214	}
215
216	vhci = ddi_get_soft_state(tvhci_state, getminor(*devp));
217	if (vhci == NULL) {
218		return (ENXIO);
219	}
220
221	return (0);
222}
223
224
225/* ARGSUSED */
226static int
227tvhci_close(dev_t dev, int flag, int otype, cred_t *credp)
228{
229	struct tvhci_state *vhci;
230	if (otype != OTYP_CHR) {
231		return (EINVAL);
232	}
233
234	vhci = ddi_get_soft_state(tvhci_state, getminor(dev));
235	if (vhci == NULL) {
236		return (ENXIO);
237	}
238
239	return (0);
240}
241
242/* ARGSUSED */
243static int
244tvhci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
245	cred_t *credp, int *rval)
246{
247	return (0);
248}
249
250/*
251 * attach the module
252 */
253static int
254tvhci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
255{
256	char *vclass;
257	int instance, vhci_regis = 0;
258	struct tvhci_state *vhci = NULL;
259	dev_info_t *pdip;
260
261	instance = ddi_get_instance(dip);
262
263	switch (cmd) {
264	case DDI_ATTACH:
265		break;
266
267	case DDI_RESUME:
268	case DDI_PM_RESUME:
269		return (0);	/* nothing to do */
270
271	default:
272		return (DDI_FAILURE);
273	}
274
275	/*
276	 * Allocate vhci data structure.
277	 */
278	if (ddi_soft_state_zalloc(tvhci_state, instance) != DDI_SUCCESS) {
279		return (DDI_FAILURE);
280	}
281
282	vhci = ddi_get_soft_state(tvhci_state, instance);
283	ASSERT(vhci != NULL);
284	vhci->dip = dip;
285
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/");
291		goto attach_fail;
292	}
293
294	/*
295	 * XXX add mpxio-disable property. need to remove the check
296	 *	from the framework
297	 */
298	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
299	    "mpxio-disable", "no");
300
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");
305		goto attach_fail;
306	}
307
308	/*
309	 * Attach this instance with the mpxio framework
310	 */
311	if (mdi_vhci_register(vclass, dip, &tvhci_opinfo, 0) != MDI_SUCCESS) {
312		cmn_err(CE_WARN, "%s mdi_vhci_register failed",
313		    ddi_node_name(dip));
314		goto attach_fail;
315	}
316	vhci_regis++;
317
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",
321		    ddi_node_name(dip));
322		goto attach_fail;
323	}
324
325	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
326	ddi_report_dev(dip);
327	return (DDI_SUCCESS);
328
329attach_fail:
330	if (vhci_regis)
331		(void) mdi_vhci_unregister(dip, 0);
332
333	ddi_soft_state_free(tvhci_state, instance);
334	return (DDI_FAILURE);
335}
336
337
338/*ARGSUSED*/
339static int
340tvhci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
341{
342	int instance = ddi_get_instance(dip);
343
344	switch (cmd) {
345	case DDI_DETACH:
346		break;
347
348	case DDI_SUSPEND:
349	case DDI_PM_SUSPEND:
350		return (0);	/* nothing to do */
351
352	default:
353		return (DDI_FAILURE);
354	}
355
356	if (mdi_vhci_unregister(dip, 0) != MDI_SUCCESS)
357		return (DDI_FAILURE);
358
359	ddi_remove_minor_node(dip, NULL);
360	ddi_soft_state_free(tvhci_state, instance);
361
362	return (DDI_SUCCESS);
363}
364
365/*
366 * tvhci_getinfo()
367 * Given the device number, return the devinfo pointer or the
368 * instance number.
369 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
370 */
371
372/*ARGSUSED*/
373static int
374tvhci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
375{
376	struct tvhci_state *vhci;
377	int instance = getminor((dev_t)arg);
378
379	switch (cmd) {
380	case DDI_INFO_DEVT2DEVINFO:
381		vhci = ddi_get_soft_state(tvhci_state, instance);
382		if (vhci != NULL)
383			*result = vhci->dip;
384		else {
385			*result = NULL;
386			return (DDI_FAILURE);
387		}
388		break;
389
390	case DDI_INFO_DEVT2INSTANCE:
391		*result = (void *)(uintptr_t)instance;
392		break;
393
394	default:
395		return (DDI_FAILURE);
396	}
397
398	return (DDI_SUCCESS);
399}
400
401/*ARGSUSED*/
402static int
403tvhci_pi_init(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
404{
405	return (MDI_SUCCESS);
406}
407
408/*ARGSUSED*/
409static int
410tvhci_pi_uninit(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
411{
412	return (MDI_SUCCESS);
413}
414
415/*ARGSUSED*/
416static int
417tvhci_pi_state_change(dev_info_t *vdip, mdi_pathinfo_t *pip,
418    mdi_pathinfo_state_t state, uint32_t ext_state, int flags)
419{
420	return (MDI_SUCCESS);
421}
422
423/*ARGSUSED*/
424static int
425tvhci_failover(dev_info_t *vdip, dev_info_t *cdip, int flags)
426{
427	return (MDI_SUCCESS);
428}
429
430/*
431 * Interrupt stuff. NO OP for pseudo drivers.
432 */
433/*ARGSUSED*/
434static int
435tvhci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
436    ddi_intr_handle_impl_t *hdlp, void *result)
437{
438	return (DDI_FAILURE);
439}
440
441/*ARGSUSED*/
442static int
443tvhci_ctl(dev_info_t *dip, dev_info_t *rdip,
444	ddi_ctl_enum_t ctlop, void *arg, void *result)
445{
446	switch (ctlop) {
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);
453
454	case DDI_CTLOPS_INITCHILD:
455	{
456		dev_info_t *child = (dev_info_t *)arg;
457		return (tvhci_initchild(dip, child));
458	}
459
460	case DDI_CTLOPS_UNINITCHILD:
461	{
462		dev_info_t *child = (dev_info_t *)arg;
463		return (tvhci_uninitchild(dip, child));
464	}
465
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:
475		/*
476		 * These ops correspond to functions that "shouldn't" be called
477		 * by a pseudo driver.  So we whine when we're called.
478		 */
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);
483
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:
492	default:
493		/*
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.
499		 */
500		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
501	}
502}
503
504/* set devi_addr to "g<guid>" */
505static int
506tvhci_initchild(dev_info_t *dip, dev_info_t *child)
507{
508	_NOTE(ARGUNUSED(dip))
509	char *guid, *addr;
510
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);
515	}
516
517	addr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
518	(void) snprintf(addr, MAXNAMELEN, "g%s", guid);
519	ddi_set_name_addr(child, addr);
520
521	kmem_free(addr, MAXNAMELEN);
522	ddi_prop_free(guid);
523	return (DDI_SUCCESS);
524}
525
526/*ARGSUSED*/
527static int
528tvhci_uninitchild(dev_info_t *dip, dev_info_t *child)
529{
530	ddi_set_name_addr(child, NULL);
531	return (DDI_SUCCESS);
532}
533
534/* form paddr by cname@<phci_inst>,<guid> */
535static char *
536tvh_get_phci_devname(char *cname, char *guid,
537    dev_info_t *pdip, char *pname, int len)
538{
539	(void) snprintf(pname, len, "%s@%d,%s",
540	    cname, ddi_get_instance(pdip), guid);
541	return (pname);
542}
543
544/*
545 * Return a pointer to the guid part of the devnm.
546 * devnm format is "nodename@busaddr", busaddr format is "gGUID".
547 */
548static char *
549tvhci_devnm_to_guid(char *devnm)
550{
551	char *cp = devnm;
552
553	if (devnm == NULL)
554		return (NULL);
555
556	while (*cp != '\0' && *cp != '@')
557		cp++;
558	if (*cp == '@' && *(cp + 1) == 'g')
559		return (cp + 2);
560	return (NULL);
561}
562
563static int
564tvhci_bus_config(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op,
565    void *arg, dev_info_t **child)
566{
567	char *guid;
568
569	if (op == BUS_CONFIG_ONE || op == BUS_UNCONFIG_ONE)
570		guid = tvhci_devnm_to_guid((char *)arg);
571	else
572		guid = NULL;
573
574	if (mdi_vhci_bus_config(pdip, flags, op, arg, child, guid)
575	    == MDI_SUCCESS)
576		return (NDI_SUCCESS);
577	else
578		return (NDI_FAILURE);
579}
580
581static int
582tvhci_bus_unconfig(dev_info_t *parent, uint_t flags,
583    ddi_bus_config_op_t op, void *arg)
584{
585	return (ndi_busop_bus_unconfig(parent, flags, op, arg));
586}
587