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,
148 NULL
149 };
150
151 int
_init(void)152 _init(void)
153 {
154 int rval;
155
156 if ((rval = ddi_soft_state_init(&tphci_state,
157 sizeof (struct tphci_state), 2)) != 0) {
158 return (rval);
159 }
160
161 if ((rval = mod_install(&modlinkage)) != 0) {
162 ddi_soft_state_fini(&tphci_state);
163 }
164 return (rval);
165 }
166
167
168 int
_fini(void)169 _fini(void)
170 {
171 int rval;
172
173 /*
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
177 */
178 if ((rval = mod_remove(&modlinkage)) != 0) {
179 return (rval);
180 }
181
182 ddi_soft_state_fini(&tphci_state);
183
184 return (rval);
185 }
186
187 int
_info(struct modinfo * modinfop)188 _info(struct modinfo *modinfop)
189 {
190 return (mod_info(&modlinkage, modinfop));
191 }
192
193 /* ARGSUSED */
194 static int
tphci_open(dev_t * devp,int flag,int otype,cred_t * credp)195 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp)
196 {
197 struct tphci_state *phci;
198
199 if (otype != OTYP_CHR) {
200 return (EINVAL);
201 }
202
203 phci = ddi_get_soft_state(tphci_state, getminor(*devp));
204 if (phci == NULL) {
205 return (ENXIO);
206 }
207
208 return (0);
209 }
210
211
212 /* ARGSUSED */
213 static int
tphci_close(dev_t dev,int flag,int otype,cred_t * credp)214 tphci_close(dev_t dev, int flag, int otype, cred_t *credp)
215 {
216 struct tphci_state *phci;
217 if (otype != OTYP_CHR) {
218 return (EINVAL);
219 }
220
221 phci = ddi_get_soft_state(tphci_state, getminor(dev));
222 if (phci == NULL) {
223 return (ENXIO);
224 }
225
226 return (0);
227 }
228
229 /* ARGSUSED */
230 static int
tphci_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)231 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
232 cred_t *credp, int *rval)
233 {
234 return (0);
235 }
236
237 /*
238 * attach the module
239 */
240 static int
tphci_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)241 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
242 {
243 char *vclass;
244 int instance, phci_regis = 0;
245 struct tphci_state *phci = NULL;
246
247 instance = ddi_get_instance(dip);
248
249 switch (cmd) {
250 case DDI_ATTACH:
251 break;
252
253 case DDI_RESUME:
254 case DDI_PM_RESUME:
255 return (0); /* nothing to do */
256
257 default:
258 return (DDI_FAILURE);
259 }
260
261 /*
262 * Allocate phci data structure.
263 */
264 if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) {
265 return (DDI_FAILURE);
266 }
267
268 phci = ddi_get_soft_state(tphci_state, instance);
269 ASSERT(phci != NULL);
270 phci->dip = dip;
271
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));
277 goto attach_fail;
278 }
279
280 /*
281 * Attach this instance with the mpxio framework
282 */
283 if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) {
284 cmn_err(CE_WARN, "%s mdi_phci_register failed",
285 ddi_node_name(dip));
286 goto attach_fail;
287 }
288 phci_regis++;
289
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",
293 ddi_node_name(dip));
294 goto attach_fail;
295 }
296
297 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
298 ddi_report_dev(dip);
299 return (DDI_SUCCESS);
300
301 attach_fail:
302 if (phci_regis)
303 (void) mdi_phci_unregister(dip, 0);
304
305 ddi_soft_state_free(tphci_state, instance);
306 return (DDI_FAILURE);
307 }
308
309
310 /*ARGSUSED*/
311 static int
tphci_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)312 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
313 {
314 int instance = ddi_get_instance(dip);
315
316 switch (cmd) {
317 case DDI_DETACH:
318 break;
319
320 case DDI_SUSPEND:
321 case DDI_PM_SUSPEND:
322 return (0); /* nothing to do */
323
324 default:
325 return (DDI_FAILURE);
326 }
327
328 if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS)
329 return (DDI_FAILURE);
330
331 ddi_remove_minor_node(dip, NULL);
332 ddi_soft_state_free(tphci_state, instance);
333
334 return (DDI_SUCCESS);
335 }
336
337 /*
338 * tphci_getinfo()
339 * Given the device number, return the devinfo pointer or the
340 * instance number.
341 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
342 */
343
344 /*ARGSUSED*/
345 static int
tphci_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)346 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
347 {
348 struct tphci_state *phci;
349 int instance = getminor((dev_t)arg);
350
351 switch (cmd) {
352 case DDI_INFO_DEVT2DEVINFO:
353 phci = ddi_get_soft_state(tphci_state, instance);
354 if (phci != NULL)
355 *result = phci->dip;
356 else {
357 *result = NULL;
358 return (DDI_FAILURE);
359 }
360 break;
361
362 case DDI_INFO_DEVT2INSTANCE:
363 *result = (void *)(uintptr_t)instance;
364 break;
365
366 default:
367 return (DDI_FAILURE);
368 }
369
370 return (DDI_SUCCESS);
371 }
372
373 /*
374 * Interrupt stuff. NO OP for pseudo drivers.
375 */
376 /*ARGSUSED*/
377 static int
tphci_intr_op(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t op,ddi_intr_handle_impl_t * hdlp,void * result)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)
380 {
381 return (DDI_FAILURE);
382 }
383
384 static int
tphci_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)385 tphci_ctl(dev_info_t *dip, dev_info_t *rdip,
386 ddi_ctl_enum_t ctlop, void *arg, void *result)
387 {
388 switch (ctlop) {
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);
395
396 case DDI_CTLOPS_INITCHILD:
397 {
398 dev_info_t *child = (dev_info_t *)arg;
399 return (tphci_initchild(dip, child));
400 }
401
402 case DDI_CTLOPS_UNINITCHILD:
403 {
404 dev_info_t *child = (dev_info_t *)arg;
405 return (tphci_uninitchild(dip, child));
406 }
407
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:
417 /*
418 * These ops correspond to functions that "shouldn't" be called
419 * by a pseudo driver. So we whine when we're called.
420 */
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);
425
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:
434 default:
435 /*
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.
441 */
442 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
443 }
444 }
445
446 static int
tphci_initchild(dev_info_t * dip,dev_info_t * child)447 tphci_initchild(dev_info_t *dip, dev_info_t *child)
448 {
449 _NOTE(ARGUNUSED(dip))
450 ddi_set_name_addr(child, "0");
451 return (DDI_SUCCESS);
452 }
453
454 /*ARGSUSED*/
455 static int
tphci_uninitchild(dev_info_t * dip,dev_info_t * child)456 tphci_uninitchild(dev_info_t *dip, dev_info_t *child)
457 {
458 ddi_set_name_addr(child, NULL);
459 return (DDI_SUCCESS);
460 }
461
462 static int
tp_decode_name(char * devnm,char ** cname,char ** paddr,char ** guid)463 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid)
464 {
465 char *tmp;
466
467 i_ddi_parse_name(devnm, cname, paddr, NULL);
468 if ((strcmp(*cname, "tclient") != 0) &&
469 (strcmp(*cname, "tphci") != 0) || *paddr == NULL)
470 return (-1);
471
472 tmp = strchr(*paddr, ',');
473 if (tmp == NULL || tmp[1] == '\0')
474 return (-1);
475
476 *guid = tmp + 1;
477 return (0);
478 }
479
480 static int
tphci_bus_config(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)481 tphci_bus_config(dev_info_t *parent, uint_t flags,
482 ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
483 {
484 _NOTE(ARGUNUSED(flags))
485 char *cname, *paddr, *guid, *devnm;
486 mdi_pathinfo_t *pip;
487 int len, circ, rval;
488
489 switch (op) {
490 case BUS_CONFIG_ONE:
491 break;
492 case BUS_CONFIG_DRIVER: /* no direct children to configure */
493 case BUS_CONFIG_ALL:
494 return (NDI_SUCCESS);
495 default:
496 return (NDI_FAILURE);
497 }
498
499 /* only implement BUS_CONFIG_ONE */
500 devnm = i_ddi_strdup((char *)arg, KM_SLEEP);
501 len = strlen(devnm) + 1;
502
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",
506 (char *)arg);
507 kmem_free(devnm, len);
508 return (NDI_FAILURE);
509 }
510
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);
518 }
519
520 /*
521 * Hold the path and exit the pHCI while calling mdi_pi_online
522 * to avoid deadlock with power management of pHCI.
523 */
524 mdi_hold_path(pip);
525 mdi_devi_exit_phci(parent, circ);
526 rval = mdi_pi_online(pip, 0);
527 mdi_devi_enter_phci(parent, &circ);
528 mdi_rele_path(pip);
529
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);
535 }
536
537 if (childp) {
538 *childp = mdi_pi_get_client(pip);
539 ndi_hold_devi(*childp);
540 }
541 mdi_devi_exit(parent, circ);
542
543 return (NDI_SUCCESS);
544 }
545
546 static int
tphci_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)547 tphci_bus_unconfig(dev_info_t *parent, uint_t flags,
548 ddi_bus_config_op_t op, void *arg)
549 {
550 int rval = MDI_SUCCESS;
551 int circ;
552 mdi_pathinfo_t *pip, *next;
553 char *devnm, *cname, *caddr;
554
555 switch (op) {
556 case BUS_UNCONFIG_ONE:
557 devnm = (char *)arg;
558 i_ddi_parse_name(devnm, &cname, &caddr, NULL);
559 if (strcmp(cname, "tclient") != 0)
560 return (NDI_SUCCESS); /* no such device */
561
562 mdi_devi_enter(parent, &circ);
563 pip = mdi_pi_find(parent, NULL, caddr);
564 if (pip) {
565 mdi_hold_path(pip);
566 mdi_devi_exit_phci(parent, circ);
567 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
568 mdi_devi_enter_phci(parent, &circ);
569 mdi_rele_path(pip);
570
571 if (rval == MDI_SUCCESS)
572 (void) mdi_pi_free(pip, 0);
573 }
574 mdi_devi_exit(parent, circ);
575 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
576
577 case BUS_UNCONFIG_ALL:
578 if (flags & NDI_AUTODETACH)
579 return (NDI_FAILURE);
580
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);
585
586 mdi_hold_path(pip);
587 mdi_devi_exit_phci(parent, circ);
588 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE);
589 mdi_devi_enter_phci(parent, &circ);
590 mdi_rele_path(pip);
591
592 if (rval != MDI_SUCCESS)
593 break;
594 (void) mdi_pi_free(pip, 0);
595 }
596 mdi_devi_exit(parent, circ);
597 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE);
598
599 case BUS_UNCONFIG_DRIVER: /* nothing to do */
600 return (NDI_SUCCESS);
601
602 default:
603 return (NDI_FAILURE);
604 }
605 /*NOTREACHED*/
606 }
607