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 */
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 **);
49
50 /* bus_ops entry points */
51 static int tvhci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
52 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 *,
56 dev_info_t **);
57 static int tvhci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
58 void *);
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);
61
62 /* vhci ops */
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);
68
69 static void *tvhci_state;
70 struct tvhci_state {
71 dev_info_t *dip;
72 };
73
74 static 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
82 static 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
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 */
133 };
134
135 static 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
150 extern struct mod_ops mod_driverops;
151
152 static struct modldrv modldrv = {
153 &mod_driverops,
154 "test vhci driver",
155 &tvhci_ops
156 };
157
158 static struct modlinkage modlinkage = {
159 MODREV_1,
160 &modldrv,
161 NULL
162 };
163
164 int
_init(void)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
181 int
_fini(void)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
200 int
_info(struct modinfo * modinfop)201 _info(struct modinfo *modinfop)
202 {
203 return (mod_info(&modlinkage, modinfop));
204 }
205
206 /* ARGSUSED */
207 static int
tvhci_open(dev_t * devp,int flag,int otype,cred_t * credp)208 tvhci_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 */
226 static int
tvhci_close(dev_t dev,int flag,int otype,cred_t * credp)227 tvhci_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 */
243 static int
tvhci_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)244 tvhci_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 */
253 static int
tvhci_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)254 tvhci_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
329 attach_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*/
339 static int
tvhci_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)340 tvhci_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*/
373 static int
tvhci_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)374 tvhci_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*/
402 static int
tvhci_pi_init(dev_info_t * vdip,mdi_pathinfo_t * pip,int flags)403 tvhci_pi_init(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
404 {
405 return (MDI_SUCCESS);
406 }
407
408 /*ARGSUSED*/
409 static int
tvhci_pi_uninit(dev_info_t * vdip,mdi_pathinfo_t * pip,int flags)410 tvhci_pi_uninit(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
411 {
412 return (MDI_SUCCESS);
413 }
414
415 /*ARGSUSED*/
416 static int
tvhci_pi_state_change(dev_info_t * vdip,mdi_pathinfo_t * pip,mdi_pathinfo_state_t state,uint32_t ext_state,int flags)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)
419 {
420 return (MDI_SUCCESS);
421 }
422
423 /*ARGSUSED*/
424 static int
tvhci_failover(dev_info_t * vdip,dev_info_t * cdip,int flags)425 tvhci_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*/
434 static int
tvhci_intr_op(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t op,ddi_intr_handle_impl_t * hdlp,void * result)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)
437 {
438 return (DDI_FAILURE);
439 }
440
441 /*ARGSUSED*/
442 static int
tvhci_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)443 tvhci_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>" */
505 static int
tvhci_initchild(dev_info_t * dip,dev_info_t * child)506 tvhci_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*/
527 static int
tvhci_uninitchild(dev_info_t * dip,dev_info_t * child)528 tvhci_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> */
535 static char *
tvh_get_phci_devname(char * cname,char * guid,dev_info_t * pdip,char * pname,int len)536 tvh_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 */
548 static char *
tvhci_devnm_to_guid(char * devnm)549 tvhci_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
563 static int
tvhci_bus_config(dev_info_t * pdip,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)564 tvhci_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
581 static int
tvhci_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)582 tvhci_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