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 tphci driver can be used to exercise the mpxio framework together
29 * with tvhci/tclient.
30 */
31
32 #include <sys/conf.h>
33 #include <sys/file.h>
34 #include <sys/open.h>
35 #include <sys/stat.h>
36 #include <sys/modctl.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunndi.h>
40 #include <sys/sunmdi.h>
41 #include <sys/disp.h>
42
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 **);
50
51 /* bus_ops entry points */
52 static int tphci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
53 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 *,
57 dev_info_t **);
58 static int tphci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
59 void *);
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);
62
63
64 static void *tphci_state;
65 struct tphci_state {
66 dev_info_t *dip;
67 };
68
69 static struct cb_ops tphci_cb_ops = {
70 tphci_open, /* open */
71 tphci_close, /* close */
72 nodev, /* strategy */
73 nodev, /* print */
74 nodev, /* dump */
75 nodev, /* read */
76 nodev, /* write */
77 tphci_ioctl, /* ioctl */
78 nodev, /* devmap */
79 nodev, /* mmap */
80 nodev, /* segmap */
81 nochpoll, /* chpoll */
82 ddi_prop_op, /* cb_prop_op */
83 0, /* streamtab */
84 D_NEW | D_MP, /* cb_flag */
85 CB_REV, /* rev */
86 nodev, /* aread */
87 nodev /* awrite */
88 };
89
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 */
120 };
121
122 static struct dev_ops tphci_ops = {
123 DEVO_REV,
124 0,
125 tphci_getinfo,
126 nulldev, /* identify */
127 nulldev, /* probe */
128 tphci_attach, /* attach and detach are mandatory */
129 tphci_detach,
130 nodev, /* reset */
131 &tphci_cb_ops, /* cb_ops */
132 &tphci_bus_ops, /* bus_ops */
133 NULL, /* power */
134 ddi_quiesce_not_needed, /* quiesce */
135 };
136
137 extern struct mod_ops mod_driverops;
138
139 static struct modldrv modldrv = {
140 &mod_driverops,
141 "test phci driver",
142 &tphci_ops
143 };
144
145 static struct modlinkage modlinkage = {
146 MODREV_1,
147 { &modldrv, NULL }
148 };
149
150 int
151 _init(void)
152 {
153 int rval;
154
155 if ((rval = ddi_soft_state_init(&tphci_state,
156 sizeof (struct tphci_state), 2)) != 0) {
157 return (rval);
158 }
159
160 if ((rval = mod_install(&modlinkage)) != 0) {
161 ddi_soft_state_fini(&tphci_state);
162 }
163 return (rval);
164 }
165
166
167 int
168 _fini(void)
169 {
170 int rval;
171
172 /*
173 * don't start cleaning up until we know that the module remove
174 * has worked -- if this works, then we know that each instance
175 * has successfully been detached
176 */
177 if ((rval = mod_remove(&modlinkage)) != 0) {
178 return (rval);
179 }
180
181 ddi_soft_state_fini(&tphci_state);
182
183 return (rval);
184 }
185
186 int
187 _info(struct modinfo *modinfop)
188 {
189 return (mod_info(&modlinkage, modinfop));
190 }
191
192 /* ARGSUSED */
193 static int
194 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp)
195 {
196 struct tphci_state *phci;
197
198 if (otype != OTYP_CHR) {
199 return (EINVAL);
200 }
201
202 phci = ddi_get_soft_state(tphci_state, getminor(*devp));
203 if (phci == NULL) {
204 return (ENXIO);
205 }
206
207 return (0);
208 }
209
210
211 /* ARGSUSED */
212 static int
213 tphci_close(dev_t dev, int flag, int otype, cred_t *credp)
214 {
215 struct tphci_state *phci;
216 if (otype != OTYP_CHR) {
217 return (EINVAL);
218 }
219
220 phci = ddi_get_soft_state(tphci_state, getminor(dev));
221 if (phci == NULL) {
222 return (ENXIO);
223 }
224
225 return (0);
226 }
227
228 /* ARGSUSED */
229 static int
230 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
231 cred_t *credp, int *rval)
232 {
233 return (0);
234 }
235
236 /*
237 * attach the module
238 */
239 static int
240 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
241 {
242 char *vclass;
243 int instance, phci_regis = 0;
244 struct tphci_state *phci = NULL;
245
246 instance = ddi_get_instance(dip);
247
248 switch (cmd) {
249 case DDI_ATTACH:
250 break;
251
252 case DDI_RESUME:
253 case DDI_PM_RESUME:
254 return (0); /* nothing to do */
255
256 default:
257 return (DDI_FAILURE);
258 }
259
260 /*
261 * Allocate phci data structure.
262 */
263 if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) {
264 return (DDI_FAILURE);
265 }
266
267 phci = ddi_get_soft_state(tphci_state, instance);
268 ASSERT(phci != NULL);
269 phci->dip = dip;
270
271 /* bus_addr has the form #,<vhci_class> */
272 vclass = strchr(ddi_get_name_addr(dip), ',');
273 if (vclass == NULL || vclass[1] == '\0') {
274 cmn_err(CE_NOTE, "tphci invalid bus_addr %s",
275 ddi_get_name_addr(dip));
276 goto attach_fail;
277 }
278
279 /*
280 * Attach this instance with the mpxio framework
281 */
282 if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) {
283 cmn_err(CE_WARN, "%s mdi_phci_register failed",
284 ddi_node_name(dip));
285 goto attach_fail;
286 }
287 phci_regis++;
288
289 if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
290 instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
291 cmn_err(CE_NOTE, "%s ddi_create_minor_node failed",
292 ddi_node_name(dip));
293 goto attach_fail;
294 }
295
296 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
297 ddi_report_dev(dip);
298 return (DDI_SUCCESS);
299
300 attach_fail:
301 if (phci_regis)
302 (void) mdi_phci_unregister(dip, 0);
303
304 ddi_soft_state_free(tphci_state, instance);
305 return (DDI_FAILURE);
306 }
307
308
309 /*ARGSUSED*/
310 static int
311 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
312 {
313 int instance = ddi_get_instance(dip);
314
315 switch (cmd) {
316 case DDI_DETACH:
317 break;
318
319 case DDI_SUSPEND:
320 case DDI_PM_SUSPEND:
321 return (0); /* nothing to do */
322
323 default:
324 return (DDI_FAILURE);
325 }
326
327 if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS)
328 return (DDI_FAILURE);
329
330 ddi_remove_minor_node(dip, NULL);
331 ddi_soft_state_free(tphci_state, instance);
332
333 return (DDI_SUCCESS);
334 }
335
336 /*
337 * tphci_getinfo()
338 * Given the device number, return the devinfo pointer or the
339 * instance number.
340 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
341 */
342
343 /*ARGSUSED*/
344 static int
345 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
346 {
347 struct tphci_state *phci;
348 int instance = getminor((dev_t)arg);
349
350 switch (cmd) {
351 case DDI_INFO_DEVT2DEVINFO:
352 phci = ddi_get_soft_state(tphci_state, instance);
353 if (phci != NULL)
354 *result = phci->dip;
355 else {
356 *result = NULL;
357 return (DDI_FAILURE);
358 }
359 break;
360
361 case DDI_INFO_DEVT2INSTANCE:
362 *result = (void *)(uintptr_t)instance;
363 break;
364
365 default:
366 return (DDI_FAILURE);
367 }
368
369 return (DDI_SUCCESS);
370 }
371
372 /*
373 * Interrupt stuff. NO OP for pseudo drivers.
374 */
375 /*ARGSUSED*/
376 static int
377 tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
378 ddi_intr_handle_impl_t *hdlp, void *result)
379 {
380 return (DDI_FAILURE);
381 }
382
383 static int
384 tphci_ctl(dev_info_t *dip, dev_info_t *rdip,
385 ddi_ctl_enum_t ctlop, void *arg, void *result)
386 {
387 switch (ctlop) {
388 case DDI_CTLOPS_REPORTDEV:
389 if (rdip == (dev_info_t *)0)
390 return (DDI_FAILURE);
391 cmn_err(CE_CONT, "?tphci-device: %s%d\n",
392 ddi_get_name(rdip), ddi_get_instance(rdip));
393 return (DDI_SUCCESS);
394
395 case DDI_CTLOPS_INITCHILD:
396 {
397 dev_info_t *child = (dev_info_t *)arg;
398 return (tphci_initchild(dip, child));
399 }
400
401 case DDI_CTLOPS_UNINITCHILD:
402 {
403 dev_info_t *child = (dev_info_t *)arg;
404 return (tphci_uninitchild(dip, child));
405 }
406
407 case DDI_CTLOPS_DMAPMAPC:
408 case DDI_CTLOPS_REPORTINT:
409 case DDI_CTLOPS_REGSIZE:
410 case DDI_CTLOPS_NREGS:
411 case DDI_CTLOPS_SIDDEV:
412 case DDI_CTLOPS_SLAVEONLY:
413 case DDI_CTLOPS_AFFINITY:
414 case DDI_CTLOPS_POKE:
415 case DDI_CTLOPS_PEEK:
416 /*
417 * These ops correspond to functions that "shouldn't" be called
418 * by a pseudo driver. So we whine when we're called.
419 */
420 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
421 ddi_get_name(dip), ddi_get_instance(dip),
422 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
423 return (DDI_FAILURE);
424
425 case DDI_CTLOPS_ATTACH:
426 case DDI_CTLOPS_BTOP:
427 case DDI_CTLOPS_BTOPR:
428 case DDI_CTLOPS_DETACH:
429 case DDI_CTLOPS_DVMAPAGESIZE:
430 case DDI_CTLOPS_IOMIN:
431 case DDI_CTLOPS_POWER:
432 case DDI_CTLOPS_PTOB:
433 default:
434 /*
435 * The ops that we pass up (default). We pass up memory
436 * allocation oriented ops that we receive - these may be
437 * associated with pseudo HBA drivers below us with target
438 * drivers below them that use ddi memory allocation
439 * interfaces like scsi_alloc_consistent_buf.
440 */
441 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
442 }
443 }
444
445 static int
446 tphci_initchild(dev_info_t *dip, dev_info_t *child)
447 {
448 _NOTE(ARGUNUSED(dip))
449 ddi_set_name_addr(child, "0");
450 return (DDI_SUCCESS);
451 }
452
453 /*ARGSUSED*/
454 static int
455 tphci_uninitchild(dev_info_t *dip, dev_info_t *child)
456 {
457 ddi_set_name_addr(child, NULL);
458 return (DDI_SUCCESS);
459 }
460
461 static int
462 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid)
463 {
464 char *tmp;
465
466 i_ddi_parse_name(devnm, cname, paddr, NULL);
467 if ((strcmp(*cname, "tclient") != 0) &&
468 (strcmp(*cname, "tphci") != 0) || *paddr == NULL)
469 return (-1);
470
471 tmp = strchr(*paddr, ',');
472 if (tmp == NULL || tmp[1] == '\0')
473 return (-1);
474
475 *guid = tmp + 1;
476 return (0);
477 }
478
479 static int
480 tphci_bus_config(dev_info_t *parent, uint_t flags,
481 ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
482 {
483 _NOTE(ARGUNUSED(flags))
484 char *cname, *paddr, *guid, *devnm;
485 mdi_pathinfo_t *pip;
486 int len, circ, rval;
487
488 switch (op) {
489 case BUS_CONFIG_ONE:
490 break;
491 case BUS_CONFIG_DRIVER: /* no direct children to configure */
492 case BUS_CONFIG_ALL:
493 return (NDI_SUCCESS);
494 default:
495 return (NDI_FAILURE);
496 }
497
498 /* only implement BUS_CONFIG_ONE */
499 devnm = i_ddi_strdup((char *)arg, KM_SLEEP);
500 len = strlen(devnm) + 1;
501
502 /* caddr is hardcoded in the form *,<guid> */
503 if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) {
504 cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s",
505 (char *)arg);
506 kmem_free(devnm, len);
507 return (NDI_FAILURE);
508 }
509
510 mdi_devi_enter(parent, &circ);
511 rval = mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip);
512 kmem_free(devnm, len);
513 if (rval != MDI_SUCCESS) {
514 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed");
515 mdi_devi_exit(parent, circ);
516 return (NDI_FAILURE);
517 }
518
519 /*
520 * Hold the path and exit the pHCI while calling mdi_pi_online
521 * to avoid deadlock with power management of pHCI.
522 */
523 mdi_hold_path(pip);
524 mdi_devi_exit_phci(parent, circ);
525 rval = mdi_pi_online(pip, 0);
526 mdi_devi_enter_phci(parent, &circ);
527 mdi_rele_path(pip);
528
529 if (rval != MDI_SUCCESS) {
530 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed");
531 (void) mdi_pi_free(pip, 0);
532 mdi_devi_exit(parent, circ);
533 return (NDI_FAILURE);
534 }
535
536 if (childp) {
537 *childp = mdi_pi_get_client(pip);
538 ndi_hold_devi(*childp);
539 }
540 mdi_devi_exit(parent, circ);
541
542 return (NDI_SUCCESS);
543 }
544
545 static int
546 tphci_bus_unconfig(dev_info_t *parent, uint_t flags,
547 ddi_bus_config_op_t op, void *arg)
548 {
549 int rval = MDI_SUCCESS;
550 int circ;
551 mdi_pathinfo_t *pip, *next;
552 char *devnm, *cname, *caddr;
553
554 switch (op) {
555 case BUS_UNCONFIG_ONE:
556 devnm = (char *)arg;
557 i_ddi_parse_name(devnm, &cname, &caddr, NULL);
558 if (strcmp(cname, "tclient") != 0)
559 return (NDI_SUCCESS); /* no such device */
560
561 mdi_devi_enter(parent, &circ);
562 pip = mdi_pi_find(parent, NULL, caddr);
563 if (pip) {
564 mdi_hold_path(pip);
565 mdi_devi_exit_phci(parent, circ);
566 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
567 mdi_devi_enter_phci(parent, &circ);
568 mdi_rele_path(pip);
569
570 if (rval == MDI_SUCCESS)
571 (void) mdi_pi_free(pip, 0);
572 }
573 mdi_devi_exit(parent, circ);
574 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
575
576 case BUS_UNCONFIG_ALL:
577 if (flags & NDI_AUTODETACH)
578 return (NDI_FAILURE);
579
580 mdi_devi_enter(parent, &circ);
581 next = mdi_get_next_client_path(parent, NULL);
582 while ((pip = next) != NULL) {
583 next = mdi_get_next_client_path(parent, pip);
584
585 mdi_hold_path(pip);
586 mdi_devi_exit_phci(parent, circ);
587 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
588 mdi_devi_enter_phci(parent, &circ);
589 mdi_rele_path(pip);
590
591 if (rval != MDI_SUCCESS)
592 break;
593 (void) mdi_pi_free(pip, 0);
594 }
595 mdi_devi_exit(parent, circ);
596 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
597
598 case BUS_UNCONFIG_DRIVER: /* nothing to do */
599 return (NDI_SUCCESS);
600
601 default:
602 return (NDI_FAILURE);
603 }
604 /*NOTREACHED*/
605 }