1d73ae94eSgc /* 2d73ae94eSgc * CDDL HEADER START 3d73ae94eSgc * 4d73ae94eSgc * The contents of this file are subject to the terms of the 5d73ae94eSgc * Common Development and Distribution License (the "License"). 6d73ae94eSgc * You may not use this file except in compliance with the License. 7d73ae94eSgc * 8d73ae94eSgc * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9d73ae94eSgc * or http://www.opensolaris.org/os/licensing. 10d73ae94eSgc * See the License for the specific language governing permissions 11d73ae94eSgc * and limitations under the License. 12d73ae94eSgc * 13d73ae94eSgc * When distributing Covered Code, include this CDDL HEADER in each 14d73ae94eSgc * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15d73ae94eSgc * If applicable, add the following below this CDDL HEADER, with the 16d73ae94eSgc * fields enclosed by brackets "[]" replaced with your own identifying 17d73ae94eSgc * information: Portions Copyright [yyyy] [name of copyright owner] 18d73ae94eSgc * 19d73ae94eSgc * CDDL HEADER END 20d73ae94eSgc */ 21d73ae94eSgc /* 22*112116d8Sfb * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23d73ae94eSgc * Use is subject to license terms. 24d73ae94eSgc */ 25d73ae94eSgc 26d73ae94eSgc #pragma ident "%Z%%M% %I% %E% SMI" 27d73ae94eSgc 28d73ae94eSgc /* 29d73ae94eSgc * usb interface association driver 30d73ae94eSgc * 31d73ae94eSgc * this driver attempts to the interface association node and 32d73ae94eSgc * creates/manages child nodes for the included interfaces. 33d73ae94eSgc */ 34d73ae94eSgc 35d73ae94eSgc #if defined(lint) && !defined(DEBUG) 36d73ae94eSgc #define DEBUG 1 37d73ae94eSgc #endif 38d73ae94eSgc #include <sys/usb/usba/usbai_version.h> 39d73ae94eSgc #include <sys/usb/usba.h> 40d73ae94eSgc #include <sys/usb/usba/usba_types.h> 41d73ae94eSgc #include <sys/usb/usba/usba_impl.h> 42d73ae94eSgc #include <sys/usb/usb_ia/usb_iavar.h> 43d73ae94eSgc 44d73ae94eSgc /* Debugging support */ 45d73ae94eSgc uint_t usb_ia_errlevel = USB_LOG_L4; 46d73ae94eSgc uint_t usb_ia_errmask = (uint_t)DPRINT_MASK_ALL; 47d73ae94eSgc uint_t usb_ia_instance_debug = (uint_t)-1; 48d73ae94eSgc uint_t usb_ia_bus_config_debug = 0; 49d73ae94eSgc 50d73ae94eSgc _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errlevel)) 51d73ae94eSgc _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errmask)) 52d73ae94eSgc _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_instance_debug)) 53d73ae94eSgc 54d73ae94eSgc _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) 55d73ae94eSgc _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info)) 56d73ae94eSgc _NOTE(SCHEME_PROTECTS_DATA("unique", usb_pipe_policy)) 57d73ae94eSgc 58d73ae94eSgc static struct cb_ops usb_ia_cb_ops = { 59d73ae94eSgc nodev, /* open */ 60d73ae94eSgc nodev, /* close */ 61d73ae94eSgc nodev, /* strategy */ 62d73ae94eSgc nodev, /* print */ 63d73ae94eSgc nodev, /* dump */ 64d73ae94eSgc nodev, /* read */ 65d73ae94eSgc nodev, /* write */ 66d73ae94eSgc nodev, /* ioctl */ 67d73ae94eSgc nodev, /* devmap */ 68d73ae94eSgc nodev, /* mmap */ 69d73ae94eSgc nodev, /* segmap */ 70d73ae94eSgc nochpoll, /* poll */ 71d73ae94eSgc ddi_prop_op, /* prop_op */ 72d73ae94eSgc NULL, /* aread */ 73d73ae94eSgc D_MP 74d73ae94eSgc }; 75d73ae94eSgc 76d73ae94eSgc static int usb_ia_busop_get_eventcookie(dev_info_t *dip, 77d73ae94eSgc dev_info_t *rdip, 78d73ae94eSgc char *eventname, 79d73ae94eSgc ddi_eventcookie_t *cookie); 80d73ae94eSgc static int usb_ia_busop_add_eventcall(dev_info_t *dip, 81d73ae94eSgc dev_info_t *rdip, 82d73ae94eSgc ddi_eventcookie_t cookie, 83d73ae94eSgc void (*callback)(dev_info_t *dip, 84d73ae94eSgc ddi_eventcookie_t cookie, void *arg, 85d73ae94eSgc void *bus_impldata), 86d73ae94eSgc void *arg, ddi_callback_id_t *cb_id); 87d73ae94eSgc static int usb_ia_busop_remove_eventcall(dev_info_t *dip, 88d73ae94eSgc ddi_callback_id_t cb_id); 89d73ae94eSgc static int usb_ia_busop_post_event(dev_info_t *dip, 90d73ae94eSgc dev_info_t *rdip, 91d73ae94eSgc ddi_eventcookie_t cookie, 92d73ae94eSgc void *bus_impldata); 93d73ae94eSgc static int usb_ia_bus_config(dev_info_t *dip, 94d73ae94eSgc uint_t flag, 95d73ae94eSgc ddi_bus_config_op_t op, 96d73ae94eSgc void *arg, 97d73ae94eSgc dev_info_t **child); 98d73ae94eSgc static int usb_ia_bus_unconfig(dev_info_t *dip, 99d73ae94eSgc uint_t flag, 100d73ae94eSgc ddi_bus_config_op_t op, 101d73ae94eSgc void *arg); 102d73ae94eSgc 103d73ae94eSgc /* 104d73ae94eSgc * autoconfiguration data and routines. 105d73ae94eSgc */ 106d73ae94eSgc static int usb_ia_info(dev_info_t *, ddi_info_cmd_t, 107d73ae94eSgc void *, void **); 108d73ae94eSgc static int usb_ia_attach(dev_info_t *, ddi_attach_cmd_t); 109d73ae94eSgc static int usb_ia_detach(dev_info_t *, ddi_detach_cmd_t); 110d73ae94eSgc 111d73ae94eSgc /* other routines */ 112d73ae94eSgc static void usb_ia_create_pm_components(dev_info_t *, usb_ia_t *); 113d73ae94eSgc static int usb_ia_bus_ctl(dev_info_t *, dev_info_t *, 114d73ae94eSgc ddi_ctl_enum_t, void *, void *); 115d73ae94eSgc static int usb_ia_power(dev_info_t *, int, int); 116d73ae94eSgc static int usb_ia_restore_device_state(dev_info_t *, usb_ia_t *); 117d73ae94eSgc static usb_ia_t *usb_ia_obtain_state(dev_info_t *); 118d73ae94eSgc static void usb_ia_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *); 119d73ae94eSgc 120d73ae94eSgc /* prototypes */ 121d73ae94eSgc static void usb_ia_create_children(usb_ia_t *); 122d73ae94eSgc static int usb_ia_cleanup(usb_ia_t *); 123d73ae94eSgc 124d73ae94eSgc /* 125d73ae94eSgc * Busops vector 126d73ae94eSgc */ 127d73ae94eSgc static struct bus_ops usb_ia_busops = { 128d73ae94eSgc BUSO_REV, 129d73ae94eSgc nullbusmap, /* bus_map */ 130d73ae94eSgc NULL, /* bus_get_intrspec */ 131d73ae94eSgc NULL, /* bus_add_intrspec */ 132d73ae94eSgc NULL, /* bus_remove_intrspec */ 133d73ae94eSgc NULL, /* XXXX bus_map_fault */ 134d73ae94eSgc ddi_dma_map, /* bus_dma_map */ 135d73ae94eSgc ddi_dma_allochdl, 136d73ae94eSgc ddi_dma_freehdl, 137d73ae94eSgc ddi_dma_bindhdl, 138d73ae94eSgc ddi_dma_unbindhdl, 139d73ae94eSgc ddi_dma_flush, 140d73ae94eSgc ddi_dma_win, 141d73ae94eSgc ddi_dma_mctl, /* bus_dma_ctl */ 142d73ae94eSgc usb_ia_bus_ctl, /* bus_ctl */ 143d73ae94eSgc ddi_bus_prop_op, /* bus_prop_op */ 144d73ae94eSgc usb_ia_busop_get_eventcookie, 145d73ae94eSgc usb_ia_busop_add_eventcall, 146d73ae94eSgc usb_ia_busop_remove_eventcall, 147d73ae94eSgc usb_ia_busop_post_event, /* bus_post_event */ 148d73ae94eSgc NULL, /* bus_intr_ctl */ 149d73ae94eSgc usb_ia_bus_config, /* bus_config */ 150d73ae94eSgc usb_ia_bus_unconfig, /* bus_unconfig */ 151d73ae94eSgc NULL, /* bus_fm_init */ 152d73ae94eSgc NULL, /* bus_fm_fini */ 153d73ae94eSgc NULL, /* bus_fm_access_enter */ 154d73ae94eSgc NULL, /* bus_fm_access_exit */ 155d73ae94eSgc NULL /* bus_power */ 156d73ae94eSgc }; 157d73ae94eSgc 158d73ae94eSgc 159d73ae94eSgc static struct dev_ops usb_ia_ops = { 160d73ae94eSgc DEVO_REV, /* devo_rev, */ 161d73ae94eSgc 0, /* refcnt */ 162d73ae94eSgc usb_ia_info, /* info */ 163d73ae94eSgc nulldev, /* identify */ 164d73ae94eSgc nulldev, /* probe */ 165d73ae94eSgc usb_ia_attach, /* attach */ 166d73ae94eSgc usb_ia_detach, /* detach */ 167d73ae94eSgc nodev, /* reset */ 168d73ae94eSgc &usb_ia_cb_ops, /* driver operations */ 169d73ae94eSgc &usb_ia_busops, /* bus operations */ 170d73ae94eSgc usb_ia_power /* power */ 171d73ae94eSgc }; 172d73ae94eSgc 173d73ae94eSgc static struct modldrv modldrv = { 174d73ae94eSgc &mod_driverops, /* Type of module. This one is a driver */ 175d73ae94eSgc "USB Interface Association Driver %I%", /* Name of the module. */ 176d73ae94eSgc &usb_ia_ops, /* driver ops */ 177d73ae94eSgc }; 178d73ae94eSgc 179d73ae94eSgc static struct modlinkage modlinkage = { 180d73ae94eSgc MODREV_1, (void *)&modldrv, NULL 181d73ae94eSgc }; 182d73ae94eSgc 183d73ae94eSgc #define USB_IA_INITIAL_SOFT_SPACE 4 184d73ae94eSgc static void *usb_ia_statep; 185d73ae94eSgc 186d73ae94eSgc /* 187d73ae94eSgc * event definition 188d73ae94eSgc */ 189d73ae94eSgc static ndi_event_definition_t usb_ia_ndi_event_defs[] = { 190d73ae94eSgc {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 191d73ae94eSgc NDI_EVENT_POST_TO_ALL}, 192d73ae94eSgc {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 193d73ae94eSgc NDI_EVENT_POST_TO_ALL}, 194d73ae94eSgc {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL, 195d73ae94eSgc NDI_EVENT_POST_TO_ALL}, 196d73ae94eSgc {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL, 197d73ae94eSgc NDI_EVENT_POST_TO_ALL} 198d73ae94eSgc }; 199d73ae94eSgc 200d73ae94eSgc #define USB_IA_N_NDI_EVENTS \ 201d73ae94eSgc (sizeof (usb_ia_ndi_event_defs) / sizeof (ndi_event_definition_t)) 202d73ae94eSgc 203d73ae94eSgc static ndi_event_set_t usb_ia_ndi_events = { 204d73ae94eSgc NDI_EVENTS_REV1, USB_IA_N_NDI_EVENTS, usb_ia_ndi_event_defs}; 205d73ae94eSgc 206d73ae94eSgc 207d73ae94eSgc /* 208d73ae94eSgc * standard driver entry points 209d73ae94eSgc */ 210d73ae94eSgc int 211d73ae94eSgc _init(void) 212d73ae94eSgc { 213d73ae94eSgc int rval; 214d73ae94eSgc 215d73ae94eSgc rval = ddi_soft_state_init(&usb_ia_statep, sizeof (struct usb_ia), 216d73ae94eSgc USB_IA_INITIAL_SOFT_SPACE); 217d73ae94eSgc if (rval != 0) { 218d73ae94eSgc return (rval); 219d73ae94eSgc } 220d73ae94eSgc 221d73ae94eSgc if ((rval = mod_install(&modlinkage)) != 0) { 222d73ae94eSgc ddi_soft_state_fini(&usb_ia_statep); 223d73ae94eSgc return (rval); 224d73ae94eSgc } 225d73ae94eSgc 226d73ae94eSgc return (rval); 227d73ae94eSgc } 228d73ae94eSgc 229d73ae94eSgc 230d73ae94eSgc int 231d73ae94eSgc _fini(void) 232d73ae94eSgc { 233d73ae94eSgc int rval; 234d73ae94eSgc 235d73ae94eSgc rval = mod_remove(&modlinkage); 236d73ae94eSgc 237d73ae94eSgc if (rval) { 238d73ae94eSgc return (rval); 239d73ae94eSgc } 240d73ae94eSgc 241d73ae94eSgc ddi_soft_state_fini(&usb_ia_statep); 242d73ae94eSgc 243d73ae94eSgc return (rval); 244d73ae94eSgc } 245d73ae94eSgc 246d73ae94eSgc 247d73ae94eSgc int 248d73ae94eSgc _info(struct modinfo *modinfop) 249d73ae94eSgc { 250d73ae94eSgc return (mod_info(&modlinkage, modinfop)); 251d73ae94eSgc } 252d73ae94eSgc 253d73ae94eSgc 254d73ae94eSgc /*ARGSUSED*/ 255d73ae94eSgc static int 256d73ae94eSgc usb_ia_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 257d73ae94eSgc { 258d73ae94eSgc usb_ia_t *usb_ia; 259d73ae94eSgc int instance = getminor((dev_t)arg); 260d73ae94eSgc int error = DDI_FAILURE; 261d73ae94eSgc 262d73ae94eSgc switch (infocmd) { 263d73ae94eSgc case DDI_INFO_DEVT2DEVINFO: 264d73ae94eSgc if ((usb_ia = ddi_get_soft_state(usb_ia_statep, 265d73ae94eSgc instance)) != NULL) { 266d73ae94eSgc *result = (void *)usb_ia->ia_dip; 267d73ae94eSgc if (*result != NULL) { 268d73ae94eSgc error = DDI_SUCCESS; 269d73ae94eSgc } 270d73ae94eSgc } else { 271d73ae94eSgc *result = NULL; 272d73ae94eSgc } 273d73ae94eSgc break; 274d73ae94eSgc 275d73ae94eSgc case DDI_INFO_DEVT2INSTANCE: 276d73ae94eSgc *result = (void *)(intptr_t)instance; 277d73ae94eSgc error = DDI_SUCCESS; 278d73ae94eSgc break; 279d73ae94eSgc default: 280d73ae94eSgc break; 281d73ae94eSgc } 282d73ae94eSgc 283d73ae94eSgc return (error); 284d73ae94eSgc } 285d73ae94eSgc 286d73ae94eSgc 287d73ae94eSgc /* 288d73ae94eSgc * child post attach/detach notification 289d73ae94eSgc */ 290d73ae94eSgc static void 291d73ae94eSgc usb_ia_post_attach(usb_ia_t *usb_ia, uint8_t ifno, struct attachspec *as) 292d73ae94eSgc { 293d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle, 294d73ae94eSgc "usb_ia_post_attach: ifno = %d result = %d", ifno, as->result); 295d73ae94eSgc 296d73ae94eSgc } 297d73ae94eSgc 298d73ae94eSgc 299d73ae94eSgc static void 300d73ae94eSgc usb_ia_post_detach(usb_ia_t *usb_ia, uint8_t ifno, struct detachspec *ds) 301d73ae94eSgc { 302d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle, 303d73ae94eSgc "usb_ia_post_detach: ifno = %d result = %d", ifno, ds->result); 304d73ae94eSgc 305d73ae94eSgc } 306d73ae94eSgc 307d73ae94eSgc 308d73ae94eSgc /* 309d73ae94eSgc * bus ctl support. we handle notifications here and the 310d73ae94eSgc * rest goes up to root hub/hcd 311d73ae94eSgc */ 312d73ae94eSgc /*ARGSUSED*/ 313d73ae94eSgc static int 314d73ae94eSgc usb_ia_bus_ctl(dev_info_t *dip, 315d73ae94eSgc dev_info_t *rdip, 316d73ae94eSgc ddi_ctl_enum_t op, 317d73ae94eSgc void *arg, 318d73ae94eSgc void *result) 319d73ae94eSgc { 320d73ae94eSgc usba_device_t *hub_usba_device = usba_get_usba_device(rdip); 321d73ae94eSgc dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip; 322d73ae94eSgc usb_ia_t *usb_ia; 323d73ae94eSgc struct attachspec *as; 324d73ae94eSgc struct detachspec *ds; 325d73ae94eSgc 326d73ae94eSgc usb_ia = usb_ia_obtain_state(dip); 327d73ae94eSgc 328d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle, 329d73ae94eSgc "usb_ia_bus_ctl:\n\t" 330d73ae94eSgc "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p", 331*112116d8Sfb (void *)dip, (void *)rdip, op, arg); 332d73ae94eSgc 333d73ae94eSgc switch (op) { 334d73ae94eSgc case DDI_CTLOPS_ATTACH: 335d73ae94eSgc as = (struct attachspec *)arg; 336d73ae94eSgc 337d73ae94eSgc switch (as->when) { 338d73ae94eSgc case DDI_PRE : 339d73ae94eSgc /* nothing to do basically */ 340d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle, 341d73ae94eSgc "DDI_PRE DDI_CTLOPS_ATTACH"); 342d73ae94eSgc break; 343d73ae94eSgc case DDI_POST : 344d73ae94eSgc usb_ia_post_attach(usb_ia, usba_get_ifno(rdip), 345d73ae94eSgc (struct attachspec *)arg); 346d73ae94eSgc break; 347d73ae94eSgc } 348d73ae94eSgc 349d73ae94eSgc break; 350d73ae94eSgc case DDI_CTLOPS_DETACH: 351d73ae94eSgc ds = (struct detachspec *)arg; 352d73ae94eSgc 353d73ae94eSgc switch (ds->when) { 354d73ae94eSgc case DDI_PRE : 355d73ae94eSgc /* nothing to do basically */ 356d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle, 357d73ae94eSgc "DDI_PRE DDI_CTLOPS_DETACH"); 358d73ae94eSgc break; 359d73ae94eSgc case DDI_POST : 360d73ae94eSgc usb_ia_post_detach(usb_ia, usba_get_ifno(rdip), 361d73ae94eSgc (struct detachspec *)arg); 362d73ae94eSgc break; 363d73ae94eSgc } 364d73ae94eSgc 365d73ae94eSgc break; 366d73ae94eSgc default: 367d73ae94eSgc /* pass to root hub to handle */ 368d73ae94eSgc return (usba_bus_ctl(root_hub_dip, rdip, op, arg, result)); 369d73ae94eSgc } 370d73ae94eSgc 371d73ae94eSgc return (DDI_SUCCESS); 372d73ae94eSgc } 373d73ae94eSgc 374d73ae94eSgc 375d73ae94eSgc /* 376d73ae94eSgc * bus enumeration entry points 377d73ae94eSgc */ 378d73ae94eSgc static int 379d73ae94eSgc usb_ia_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 380d73ae94eSgc void *arg, dev_info_t **child) 381d73ae94eSgc { 382d73ae94eSgc int rval, circ; 383d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 384d73ae94eSgc 385d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle, 386d73ae94eSgc "usb_ia_bus_config: op=%d", op); 387d73ae94eSgc 388d73ae94eSgc if (usb_ia_bus_config_debug) { 389d73ae94eSgc flag |= NDI_DEVI_DEBUG; 390d73ae94eSgc } 391d73ae94eSgc 392d73ae94eSgc ndi_devi_enter(dip, &circ); 393d73ae94eSgc 394d73ae94eSgc /* enumerate each interface below us */ 395d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 396d73ae94eSgc usb_ia_create_children(usb_ia); 397d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 398d73ae94eSgc 399d73ae94eSgc rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0); 400d73ae94eSgc ndi_devi_exit(dip, circ); 401d73ae94eSgc 402d73ae94eSgc return (rval); 403d73ae94eSgc } 404d73ae94eSgc 405d73ae94eSgc 406d73ae94eSgc static int 407d73ae94eSgc usb_ia_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op, 408d73ae94eSgc void *arg) 409d73ae94eSgc { 410d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 411d73ae94eSgc 412d73ae94eSgc dev_info_t *cdip, *mdip; 413d73ae94eSgc int interface, circular_count; 414d73ae94eSgc int rval = NDI_SUCCESS; 415d73ae94eSgc 416d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle, 417d73ae94eSgc "usb_ia_bus_unconfig: op=%d", op); 418d73ae94eSgc 419d73ae94eSgc if (usb_ia_bus_config_debug) { 420d73ae94eSgc flag |= NDI_DEVI_DEBUG; 421d73ae94eSgc } 422d73ae94eSgc 423d73ae94eSgc /* 424d73ae94eSgc * first offline and if offlining successful, then 425d73ae94eSgc * remove children 426d73ae94eSgc */ 427d73ae94eSgc if (op == BUS_UNCONFIG_ALL) { 428d73ae94eSgc flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG); 429d73ae94eSgc } 430d73ae94eSgc 431d73ae94eSgc ndi_devi_enter(dip, &circular_count); 432d73ae94eSgc rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 433d73ae94eSgc 434d73ae94eSgc if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS && 435d73ae94eSgc (flag & NDI_AUTODETACH) == 0) { 436d73ae94eSgc flag |= NDI_DEVI_REMOVE; 437d73ae94eSgc rval = ndi_busop_bus_unconfig(dip, flag, op, arg); 438d73ae94eSgc } 439d73ae94eSgc 440d73ae94eSgc /* update children's list */ 441d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 442d73ae94eSgc for (interface = 0; usb_ia->ia_children_dips && 443d73ae94eSgc (interface < usb_ia->ia_n_ifs); interface++) { 444d73ae94eSgc mdip = usb_ia->ia_children_dips[interface]; 445d73ae94eSgc 446d73ae94eSgc /* now search if this dip still exists */ 447*112116d8Sfb for (cdip = ddi_get_child(dip); cdip && (cdip != mdip); ) 448*112116d8Sfb cdip = ddi_get_next_sibling(cdip); 449d73ae94eSgc 450d73ae94eSgc if (cdip != mdip) { 451d73ae94eSgc /* we lost the dip on this interface */ 452d73ae94eSgc usb_ia->ia_children_dips[interface] = NULL; 453d73ae94eSgc } else if (cdip) { 454d73ae94eSgc /* 455d73ae94eSgc * keep in DS_INITALIZED to prevent parent 456d73ae94eSgc * from detaching 457d73ae94eSgc */ 458d73ae94eSgc (void) ddi_initchild(ddi_get_parent(cdip), cdip); 459d73ae94eSgc } 460d73ae94eSgc } 461d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 462d73ae94eSgc 463d73ae94eSgc ndi_devi_exit(dip, circular_count); 464d73ae94eSgc 465d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle, 466d73ae94eSgc "usb_ia_bus_config: rval=%d", rval); 467d73ae94eSgc 468d73ae94eSgc return (rval); 469d73ae94eSgc } 470d73ae94eSgc 471d73ae94eSgc 472d73ae94eSgc /* power entry point */ 473d73ae94eSgc /* ARGSUSED */ 474d73ae94eSgc static int 475d73ae94eSgc usb_ia_power(dev_info_t *dip, int comp, int level) 476d73ae94eSgc { 477d73ae94eSgc usb_ia_t *usb_ia; 478d73ae94eSgc usb_common_power_t *pm; 479d73ae94eSgc int rval = DDI_FAILURE; 480d73ae94eSgc 481d73ae94eSgc usb_ia = usb_ia_obtain_state(dip); 482d73ae94eSgc 483d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle, 484*112116d8Sfb "usb_ia_power: Begin: usb_ia = %p, level = %d", 485*112116d8Sfb (void *)usb_ia, level); 486d73ae94eSgc 487d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 488d73ae94eSgc pm = usb_ia->ia_pm; 489d73ae94eSgc 490d73ae94eSgc /* check if we are transitioning to a legal power level */ 491d73ae94eSgc if (USB_DEV_PWRSTATE_OK(pm->uc_pwr_states, level)) { 492d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle, 493d73ae94eSgc "usb_ia_power: illegal power level = %d " 494d73ae94eSgc "uc_pwr_states = %x", level, pm->uc_pwr_states); 495d73ae94eSgc 496d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 497d73ae94eSgc 498d73ae94eSgc return (rval); 499d73ae94eSgc } 500d73ae94eSgc 501036aa261Sgc rval = usba_common_power(dip, &(pm->uc_current_power), 502036aa261Sgc &(usb_ia->ia_dev_state), level); 503d73ae94eSgc 504d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 505d73ae94eSgc 506d73ae94eSgc return (rval); 507d73ae94eSgc } 508d73ae94eSgc 509d73ae94eSgc /* 510d73ae94eSgc * attach/resume entry point 511d73ae94eSgc */ 512d73ae94eSgc static int 513d73ae94eSgc usb_ia_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 514d73ae94eSgc { 515d73ae94eSgc int instance = ddi_get_instance(dip); 516d73ae94eSgc usb_ia_t *usb_ia = NULL; 517d73ae94eSgc uint_t n_ifs; 518d73ae94eSgc size_t size; 519d73ae94eSgc 520d73ae94eSgc switch (cmd) { 521d73ae94eSgc case DDI_ATTACH: 522d73ae94eSgc 523d73ae94eSgc break; 524d73ae94eSgc case DDI_RESUME: 525d73ae94eSgc usb_ia = ddi_get_soft_state(usb_ia_statep, instance); 526d73ae94eSgc (void) usb_ia_restore_device_state(dip, usb_ia); 527d73ae94eSgc 528d73ae94eSgc return (DDI_SUCCESS); 529d73ae94eSgc default: 530d73ae94eSgc 531d73ae94eSgc return (DDI_FAILURE); 532d73ae94eSgc } 533d73ae94eSgc 534d73ae94eSgc /* 535d73ae94eSgc * Attach: 536d73ae94eSgc * 537d73ae94eSgc * Allocate soft state and initialize 538d73ae94eSgc */ 539d73ae94eSgc if (ddi_soft_state_zalloc(usb_ia_statep, instance) != DDI_SUCCESS) { 540d73ae94eSgc goto fail; 541d73ae94eSgc } 542d73ae94eSgc 543d73ae94eSgc usb_ia = ddi_get_soft_state(usb_ia_statep, instance); 544d73ae94eSgc if (usb_ia == NULL) { 545d73ae94eSgc 546d73ae94eSgc goto fail; 547d73ae94eSgc } 548d73ae94eSgc 549d73ae94eSgc /* allocate handle for logging of messages */ 550d73ae94eSgc usb_ia->ia_log_handle = usb_alloc_log_hdl(dip, "ia", 551*112116d8Sfb &usb_ia_errlevel, 552*112116d8Sfb &usb_ia_errmask, &usb_ia_instance_debug, 553*112116d8Sfb 0); 554d73ae94eSgc 555d73ae94eSgc usb_ia->ia_dip = dip; 556d73ae94eSgc usb_ia->ia_instance = instance; 557d73ae94eSgc usb_ia->ia_first_if = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 558d73ae94eSgc DDI_PROP_DONTPASS, "interface", -1); 559d73ae94eSgc usb_ia->ia_n_ifs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 560d73ae94eSgc DDI_PROP_DONTPASS, "interface-count", -1); 561d73ae94eSgc 562d73ae94eSgc if (usb_ia->ia_first_if < 0 || usb_ia->ia_n_ifs < 0) { 563d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 564d73ae94eSgc "interface-association property failed"); 565d73ae94eSgc 566d73ae94eSgc goto fail; 567d73ae94eSgc } 568d73ae94eSgc 569d73ae94eSgc /* attach client driver to USBA */ 570d73ae94eSgc if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { 571d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 572d73ae94eSgc "usb_client_attach failed"); 573d73ae94eSgc goto fail; 574d73ae94eSgc } 575d73ae94eSgc if (usb_get_dev_data(dip, &usb_ia->ia_dev_data, USB_PARSE_LVL_NONE, 576d73ae94eSgc 0) != USB_SUCCESS) { 577d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 578d73ae94eSgc "usb_get_dev_data failed"); 579d73ae94eSgc goto fail; 580d73ae94eSgc } 581d73ae94eSgc 582d73ae94eSgc mutex_init(&usb_ia->ia_mutex, NULL, MUTEX_DRIVER, 583d73ae94eSgc usb_ia->ia_dev_data->dev_iblock_cookie); 584d73ae94eSgc 585d73ae94eSgc usb_free_dev_data(dip, usb_ia->ia_dev_data); 586d73ae94eSgc usb_ia->ia_dev_data = NULL; 587d73ae94eSgc 588d73ae94eSgc usb_ia->ia_init_state |= USB_IA_LOCK_INIT; 589d73ae94eSgc 590d73ae94eSgc if (ddi_create_minor_node(dip, "usb_ia", S_IFCHR, instance, 591d73ae94eSgc DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 592d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 593d73ae94eSgc "cannot create devctl minor node"); 594d73ae94eSgc goto fail; 595d73ae94eSgc } 596d73ae94eSgc 597d73ae94eSgc usb_ia->ia_init_state |= USB_IA_MINOR_NODE_CREATED; 598d73ae94eSgc 599d73ae94eSgc /* 600d73ae94eSgc * allocate array for keeping track of child dips 601d73ae94eSgc */ 602d73ae94eSgc n_ifs = usb_ia->ia_n_ifs; 603d73ae94eSgc usb_ia->ia_cd_list_length = size = (sizeof (dev_info_t *)) * n_ifs; 604d73ae94eSgc 605d73ae94eSgc usb_ia->ia_children_dips = kmem_zalloc(size, KM_SLEEP); 606d73ae94eSgc usb_ia->ia_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs, 607*112116d8Sfb KM_SLEEP); 608d73ae94eSgc /* 609d73ae94eSgc * Event handling: definition and registration 610d73ae94eSgc * get event handle for events that we have defined 611d73ae94eSgc */ 612d73ae94eSgc (void) ndi_event_alloc_hdl(dip, 0, &usb_ia->ia_ndi_event_hdl, 613*112116d8Sfb NDI_SLEEP); 614d73ae94eSgc 615d73ae94eSgc /* bind event set to the handle */ 616d73ae94eSgc if (ndi_event_bind_set(usb_ia->ia_ndi_event_hdl, &usb_ia_ndi_events, 617d73ae94eSgc NDI_SLEEP)) { 618d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 619d73ae94eSgc "usb_ia_attach: binding event set failed"); 620d73ae94eSgc 621d73ae94eSgc goto fail; 622d73ae94eSgc } 623d73ae94eSgc 624d73ae94eSgc usb_ia->ia_dev_state = USB_DEV_ONLINE; 625d73ae94eSgc 626d73ae94eSgc /* 627d73ae94eSgc * now create components to power manage this device 628d73ae94eSgc * before attaching children 629d73ae94eSgc */ 630d73ae94eSgc usb_ia_create_pm_components(dip, usb_ia); 631d73ae94eSgc 632d73ae94eSgc /* event registration for events from our parent */ 633d73ae94eSgc usba_common_register_events(dip, n_ifs, usb_ia_event_cb); 634d73ae94eSgc 635d73ae94eSgc usb_ia->ia_init_state |= USB_IA_EVENTS_REGISTERED; 636d73ae94eSgc 637d73ae94eSgc ddi_report_dev(dip); 638d73ae94eSgc 639d73ae94eSgc return (DDI_SUCCESS); 640d73ae94eSgc 641d73ae94eSgc fail: 642d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, NULL, "usb_ia%d cannot attach", 643d73ae94eSgc instance); 644d73ae94eSgc 645d73ae94eSgc if (usb_ia) { 646d73ae94eSgc (void) usb_ia_cleanup(usb_ia); 647d73ae94eSgc } 648d73ae94eSgc 649d73ae94eSgc return (DDI_FAILURE); 650d73ae94eSgc } 651d73ae94eSgc 652d73ae94eSgc 653d73ae94eSgc /* detach or suspend this instance */ 654d73ae94eSgc static int 655d73ae94eSgc usb_ia_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 656d73ae94eSgc { 657d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 658d73ae94eSgc 659d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 660d73ae94eSgc "usb_ia_detach: cmd = 0x%x", cmd); 661d73ae94eSgc 662d73ae94eSgc switch (cmd) { 663d73ae94eSgc case DDI_DETACH: 664d73ae94eSgc 665d73ae94eSgc return (usb_ia_cleanup(usb_ia)); 666d73ae94eSgc case DDI_SUSPEND: 667d73ae94eSgc /* nothing to do */ 668d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 669d73ae94eSgc usb_ia->ia_dev_state = USB_DEV_SUSPENDED; 670d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 671d73ae94eSgc 672d73ae94eSgc return (DDI_SUCCESS); 673d73ae94eSgc default: 674d73ae94eSgc 675d73ae94eSgc return (DDI_FAILURE); 676d73ae94eSgc } 677d73ae94eSgc 678d73ae94eSgc _NOTE(NOT_REACHED) 679d73ae94eSgc /* NOTREACHED */ 680d73ae94eSgc } 681d73ae94eSgc 682d73ae94eSgc 683d73ae94eSgc /* 684d73ae94eSgc * usb_ia_cleanup: 685d73ae94eSgc * cleanup usb_ia and deallocate. this function is called for 686d73ae94eSgc * handling attach failures and detaching including dynamic 687d73ae94eSgc * reconfiguration 688d73ae94eSgc */ 689d73ae94eSgc /*ARGSUSED*/ 690d73ae94eSgc static int 691d73ae94eSgc usb_ia_cleanup(usb_ia_t *usb_ia) 692d73ae94eSgc { 693d73ae94eSgc usb_common_power_t *iapm; 694d73ae94eSgc int rval; 695d73ae94eSgc dev_info_t *dip = usb_ia->ia_dip; 696d73ae94eSgc 697d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 698d73ae94eSgc "usb_ia_cleanup:"); 699d73ae94eSgc 700d73ae94eSgc if ((usb_ia->ia_init_state & USB_IA_LOCK_INIT) == 0) { 701d73ae94eSgc 702d73ae94eSgc goto done; 703d73ae94eSgc } 704d73ae94eSgc 705d73ae94eSgc /* 706d73ae94eSgc * deallocate events, if events are still registered 707d73ae94eSgc * (ie. children still attached) then we have to fail the detach 708d73ae94eSgc */ 709d73ae94eSgc if (usb_ia->ia_ndi_event_hdl && 710d73ae94eSgc (ndi_event_free_hdl(usb_ia->ia_ndi_event_hdl) != NDI_SUCCESS)) { 711d73ae94eSgc 712d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 713d73ae94eSgc "usb_ia_cleanup: ndi_event_free_hdl failed"); 714d73ae94eSgc 715d73ae94eSgc return (DDI_FAILURE); 716d73ae94eSgc } 717d73ae94eSgc 718d73ae94eSgc /* 719d73ae94eSgc * Disable the event callbacks, after this point, event 720d73ae94eSgc * callbacks will never get called. Note we shouldn't hold 721d73ae94eSgc * mutex while unregistering events because there may be a 722d73ae94eSgc * competing event callback thread. Event callbacks are done 723d73ae94eSgc * with ndi mutex held and this can cause a potential deadlock. 724d73ae94eSgc * Note that cleanup can't fail after deregistration of events. 725d73ae94eSgc */ 726d73ae94eSgc if (usb_ia->ia_init_state & USB_IA_EVENTS_REGISTERED) { 727d73ae94eSgc 728d73ae94eSgc usba_common_unregister_events(usb_ia->ia_dip, usb_ia->ia_n_ifs); 729d73ae94eSgc } 730d73ae94eSgc 731d73ae94eSgc iapm = usb_ia->ia_pm; 732d73ae94eSgc 733d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 734d73ae94eSgc 735d73ae94eSgc if ((iapm) && (usb_ia->ia_dev_state != USB_DEV_DISCONNECTED)) { 736d73ae94eSgc 737d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 738d73ae94eSgc 739d73ae94eSgc (void) pm_busy_component(dip, 0); 740d73ae94eSgc if (iapm->uc_wakeup_enabled) { 741d73ae94eSgc 742d73ae94eSgc /* First bring the device to full power */ 743d73ae94eSgc (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 744d73ae94eSgc 745d73ae94eSgc rval = usb_handle_remote_wakeup(dip, 746d73ae94eSgc USB_REMOTE_WAKEUP_DISABLE); 747d73ae94eSgc 748d73ae94eSgc if (rval != DDI_SUCCESS) { 749d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_EVENTS, 750d73ae94eSgc usb_ia->ia_log_handle, 751d73ae94eSgc "usb_cleanup: disable remote " 752d73ae94eSgc "wakeup failed, rval=%d", rval); 753d73ae94eSgc } 754d73ae94eSgc } 755d73ae94eSgc 756d73ae94eSgc (void) pm_lower_power(usb_ia->ia_dip, 0, USB_DEV_OS_PWR_OFF); 757d73ae94eSgc (void) pm_idle_component(dip, 0); 758d73ae94eSgc } else { 759d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 760d73ae94eSgc } 761d73ae94eSgc 762d73ae94eSgc if (iapm) { 763d73ae94eSgc kmem_free(iapm, sizeof (usb_common_power_t)); 764d73ae94eSgc } 765d73ae94eSgc 766d73ae94eSgc /* free children list */ 767d73ae94eSgc if (usb_ia->ia_children_dips) { 768d73ae94eSgc kmem_free(usb_ia->ia_children_dips, 769*112116d8Sfb usb_ia->ia_cd_list_length); 770d73ae94eSgc } 771d73ae94eSgc 772d73ae94eSgc if (usb_ia->ia_child_events) { 773d73ae94eSgc kmem_free(usb_ia->ia_child_events, sizeof (uint8_t) * 774*112116d8Sfb usb_ia->ia_n_ifs); 775d73ae94eSgc } 776d73ae94eSgc 777d73ae94eSgc if (usb_ia->ia_init_state & USB_IA_MINOR_NODE_CREATED) { 778d73ae94eSgc ddi_remove_minor_node(dip, NULL); 779d73ae94eSgc } 780d73ae94eSgc 781d73ae94eSgc mutex_destroy(&usb_ia->ia_mutex); 782d73ae94eSgc 783d73ae94eSgc done: 784d73ae94eSgc usb_client_detach(dip, usb_ia->ia_dev_data); 785d73ae94eSgc 786d73ae94eSgc usb_free_log_hdl(usb_ia->ia_log_handle); 787d73ae94eSgc ddi_soft_state_free(usb_ia_statep, ddi_get_instance(dip)); 788d73ae94eSgc 789d73ae94eSgc ddi_prop_remove_all(dip); 790d73ae94eSgc 791d73ae94eSgc return (DDI_SUCCESS); 792d73ae94eSgc } 793d73ae94eSgc 794d73ae94eSgc /* 795d73ae94eSgc * usb_ia_create_children: 796d73ae94eSgc */ 797d73ae94eSgc static void 798d73ae94eSgc usb_ia_create_children(usb_ia_t *usb_ia) 799d73ae94eSgc { 800d73ae94eSgc usba_device_t *usba_device; 801d73ae94eSgc uint_t n_ifs, first_if; 802d73ae94eSgc uint_t i; 803d73ae94eSgc dev_info_t *cdip; 804d73ae94eSgc 805d73ae94eSgc usba_device = usba_get_usba_device(usb_ia->ia_dip); 806d73ae94eSgc 807d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle, 808d73ae94eSgc "usb_ia_attach_child_drivers: port = %d, address = %d", 809d73ae94eSgc usba_device->usb_port, usba_device->usb_addr); 810d73ae94eSgc 811d73ae94eSgc n_ifs = usb_ia->ia_n_ifs; 812d73ae94eSgc first_if = usb_ia->ia_first_if; 813d73ae94eSgc 814d73ae94eSgc /* 815d73ae94eSgc * create all children if not already present 816d73ae94eSgc */ 817d73ae94eSgc for (i = 0; i < n_ifs; i++) { 818d73ae94eSgc if (usb_ia->ia_children_dips[i] != NULL) { 819d73ae94eSgc 820d73ae94eSgc continue; 821d73ae94eSgc } 822d73ae94eSgc 823d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 824d73ae94eSgc cdip = usba_ready_interface_node(usb_ia->ia_dip, first_if + i); 825d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 826d73ae94eSgc 827d73ae94eSgc if (cdip != NULL) { 828d73ae94eSgc (void) usba_bind_driver(cdip); 829d73ae94eSgc usb_ia->ia_children_dips[i] = cdip; 830d73ae94eSgc } 831d73ae94eSgc } 832d73ae94eSgc 833d73ae94eSgc } 834d73ae94eSgc 835d73ae94eSgc 836d73ae94eSgc /* 837d73ae94eSgc * event support 838d73ae94eSgc */ 839d73ae94eSgc static int 840d73ae94eSgc usb_ia_busop_get_eventcookie(dev_info_t *dip, 841d73ae94eSgc dev_info_t *rdip, char *eventname, ddi_eventcookie_t *cookie) 842d73ae94eSgc { 843d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 844d73ae94eSgc 845d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 846d73ae94eSgc "usb_ia_busop_get_eventcookie: dip=0x%p, rdip=0x%p, " 847d73ae94eSgc "event=%s", (void *)dip, (void *)rdip, eventname); 848d73ae94eSgc USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 849d73ae94eSgc "(dip=%s%d rdip=%s%d)", 850d73ae94eSgc ddi_driver_name(dip), ddi_get_instance(dip), 851d73ae94eSgc ddi_driver_name(rdip), ddi_get_instance(rdip)); 852d73ae94eSgc 853d73ae94eSgc /* return event cookie, iblock cookie, and level */ 854d73ae94eSgc return (ndi_event_retrieve_cookie(usb_ia->ia_ndi_event_hdl, 855d73ae94eSgc rdip, eventname, cookie, NDI_EVENT_NOPASS)); 856d73ae94eSgc } 857d73ae94eSgc 858d73ae94eSgc 859d73ae94eSgc static int 860d73ae94eSgc usb_ia_busop_add_eventcall(dev_info_t *dip, 861d73ae94eSgc dev_info_t *rdip, 862d73ae94eSgc ddi_eventcookie_t cookie, 863d73ae94eSgc void (*callback)(dev_info_t *dip, 864d73ae94eSgc ddi_eventcookie_t cookie, void *arg, 865d73ae94eSgc void *bus_impldata), 866d73ae94eSgc void *arg, ddi_callback_id_t *cb_id) 867d73ae94eSgc { 868d73ae94eSgc int ifno; 869d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 870d73ae94eSgc 871d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 872d73ae94eSgc ifno = usba_get_ifno(rdip)- usb_ia->ia_first_if; 873d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 874d73ae94eSgc 875d73ae94eSgc if (ifno < 0) { 876d73ae94eSgc ifno = 0; 877d73ae94eSgc } 878d73ae94eSgc 879d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 880d73ae94eSgc "usb_ia_busop_add_eventcall: dip=0x%p, rdip=0x%p " 881d73ae94eSgc "cookie=0x%p, cb=0x%p, arg=0x%p", 882d73ae94eSgc (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg); 883d73ae94eSgc USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 884d73ae94eSgc "(dip=%s%d rdip=%s%d event=%s)", 885d73ae94eSgc ddi_driver_name(dip), ddi_get_instance(dip), 886d73ae94eSgc ddi_driver_name(rdip), ddi_get_instance(rdip), 887d73ae94eSgc ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie)); 888d73ae94eSgc 889d73ae94eSgc /* Set flag on children registering events */ 890d73ae94eSgc switch (ndi_event_cookie_to_tag(usb_ia->ia_ndi_event_hdl, cookie)) { 891d73ae94eSgc case USBA_EVENT_TAG_HOT_REMOVAL: 892d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 893d73ae94eSgc usb_ia->ia_child_events[ifno] |= 894d73ae94eSgc USB_IA_CHILD_EVENT_DISCONNECT; 895d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 896d73ae94eSgc 897d73ae94eSgc break; 898d73ae94eSgc case USBA_EVENT_TAG_PRE_SUSPEND: 899d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 900d73ae94eSgc usb_ia->ia_child_events[ifno] |= 901d73ae94eSgc USB_IA_CHILD_EVENT_PRESUSPEND; 902d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 903d73ae94eSgc 904d73ae94eSgc break; 905d73ae94eSgc default: 906d73ae94eSgc 907d73ae94eSgc break; 908d73ae94eSgc } 909d73ae94eSgc /* add callback (perform registration) */ 910d73ae94eSgc return (ndi_event_add_callback(usb_ia->ia_ndi_event_hdl, 911d73ae94eSgc rdip, cookie, callback, arg, NDI_SLEEP, cb_id)); 912d73ae94eSgc } 913d73ae94eSgc 914d73ae94eSgc 915d73ae94eSgc static int 916d73ae94eSgc usb_ia_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 917d73ae94eSgc { 918d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 919d73ae94eSgc ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id; 920d73ae94eSgc 921d73ae94eSgc ASSERT(cb); 922d73ae94eSgc 923d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 924d73ae94eSgc "usb_ia_busop_remove_eventcall: dip=0x%p, rdip=0x%p " 925*112116d8Sfb "cookie=0x%p", (void *)dip, (void *)cb->ndi_evtcb_dip, 926*112116d8Sfb (void *)cb->ndi_evtcb_cookie); 927d73ae94eSgc USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 928d73ae94eSgc "(dip=%s%d rdip=%s%d event=%s)", 929d73ae94eSgc ddi_driver_name(dip), ddi_get_instance(dip), 930d73ae94eSgc ddi_driver_name(cb->ndi_evtcb_dip), 931d73ae94eSgc ddi_get_instance(cb->ndi_evtcb_dip), 932d73ae94eSgc ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, 933d73ae94eSgc cb->ndi_evtcb_cookie)); 934d73ae94eSgc 935d73ae94eSgc /* remove event registration from our event set */ 936d73ae94eSgc return (ndi_event_remove_callback(usb_ia->ia_ndi_event_hdl, cb_id)); 937d73ae94eSgc } 938d73ae94eSgc 939d73ae94eSgc 940d73ae94eSgc static int 941d73ae94eSgc usb_ia_busop_post_event(dev_info_t *dip, 942d73ae94eSgc dev_info_t *rdip, 943d73ae94eSgc ddi_eventcookie_t cookie, 944d73ae94eSgc void *bus_impldata) 945d73ae94eSgc { 946d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 947d73ae94eSgc 948d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 949d73ae94eSgc "usb_ia_busop_post_event: dip=0x%p, rdip=0x%p " 950d73ae94eSgc "cookie=0x%p, impl=0x%p", 951d73ae94eSgc (void *)dip, (void *)rdip, (void *)cookie, bus_impldata); 952d73ae94eSgc USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 953d73ae94eSgc "(dip=%s%d rdip=%s%d event=%s)", 954d73ae94eSgc ddi_driver_name(dip), ddi_get_instance(dip), 955d73ae94eSgc ddi_driver_name(rdip), ddi_get_instance(rdip), 956d73ae94eSgc ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie)); 957d73ae94eSgc 958d73ae94eSgc /* post event to all children registered for this event */ 959d73ae94eSgc return (ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl, rdip, 960d73ae94eSgc cookie, bus_impldata)); 961d73ae94eSgc } 962d73ae94eSgc 963d73ae94eSgc 964d73ae94eSgc /* 965d73ae94eSgc * usb_ia_restore_device_state 966d73ae94eSgc * set the original configuration of the device 967d73ae94eSgc */ 968d73ae94eSgc static int 969d73ae94eSgc usb_ia_restore_device_state(dev_info_t *dip, usb_ia_t *usb_ia) 970d73ae94eSgc { 971d73ae94eSgc usb_common_power_t *iapm; 972d73ae94eSgc 973d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 974*112116d8Sfb "usb_ia_restore_device_state: usb_ia = %p", (void *)usb_ia); 975d73ae94eSgc 976d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 977d73ae94eSgc iapm = usb_ia->ia_pm; 978d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 979d73ae94eSgc 980d73ae94eSgc /* First bring the device to full power */ 981d73ae94eSgc (void) pm_busy_component(dip, 0); 982d73ae94eSgc (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 983d73ae94eSgc 984d73ae94eSgc if (usb_check_same_device(dip, usb_ia->ia_log_handle, USB_LOG_L0, 985d73ae94eSgc DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) { 986d73ae94eSgc 987d73ae94eSgc /* change the device state from suspended to disconnected */ 988d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 989d73ae94eSgc usb_ia->ia_dev_state = USB_DEV_DISCONNECTED; 990d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 991d73ae94eSgc (void) pm_idle_component(dip, 0); 992d73ae94eSgc 993d73ae94eSgc return (USB_FAILURE); 994d73ae94eSgc } 995d73ae94eSgc 996d73ae94eSgc /* 997d73ae94eSgc * if the device had remote wakeup earlier, 998d73ae94eSgc * enable it again 999d73ae94eSgc */ 1000d73ae94eSgc if (iapm->uc_wakeup_enabled) { 1001d73ae94eSgc (void) usb_handle_remote_wakeup(usb_ia->ia_dip, 1002d73ae94eSgc USB_REMOTE_WAKEUP_ENABLE); 1003d73ae94eSgc } 1004d73ae94eSgc 1005d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1006d73ae94eSgc usb_ia->ia_dev_state = USB_DEV_ONLINE; 1007d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1008d73ae94eSgc 1009d73ae94eSgc (void) pm_idle_component(dip, 0); 1010d73ae94eSgc 1011d73ae94eSgc return (USB_SUCCESS); 1012d73ae94eSgc } 1013d73ae94eSgc 1014d73ae94eSgc 1015d73ae94eSgc /* 1016d73ae94eSgc * usb_ia_event_cb() 1017d73ae94eSgc * handle disconnect and connect events 1018d73ae94eSgc */ 1019d73ae94eSgc static void 1020d73ae94eSgc usb_ia_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, 1021d73ae94eSgc void *arg, void *bus_impldata) 1022d73ae94eSgc { 1023d73ae94eSgc int i, tag; 1024d73ae94eSgc usb_ia_t *usb_ia = usb_ia_obtain_state(dip); 1025d73ae94eSgc dev_info_t *child_dip; 1026d73ae94eSgc ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie; 1027d73ae94eSgc 1028d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 1029d73ae94eSgc "usb_ia_event_cb: dip=0x%p, cookie=0x%p, " 1030d73ae94eSgc "arg=0x%p, impl=0x%p", 1031d73ae94eSgc (void *)dip, (void *)cookie, arg, bus_impldata); 1032d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle, 1033d73ae94eSgc "(dip=%s%d event=%s)", 1034d73ae94eSgc ddi_driver_name(dip), ddi_get_instance(dip), 1035d73ae94eSgc ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie)); 1036d73ae94eSgc 1037d73ae94eSgc tag = NDI_EVENT_TAG(cookie); 1038d73ae94eSgc rm_cookie = ndi_event_tag_to_cookie( 1039d73ae94eSgc usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL); 1040d73ae94eSgc suspend_cookie = ndi_event_tag_to_cookie( 1041d73ae94eSgc usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND); 1042d73ae94eSgc ins_cookie = ndi_event_tag_to_cookie( 1043d73ae94eSgc usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION); 1044d73ae94eSgc resume_cookie = ndi_event_tag_to_cookie( 1045d73ae94eSgc usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME); 1046d73ae94eSgc 1047d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1048d73ae94eSgc switch (tag) { 1049d73ae94eSgc case USBA_EVENT_TAG_HOT_REMOVAL: 1050d73ae94eSgc if (usb_ia->ia_dev_state == USB_DEV_DISCONNECTED) { 1051d73ae94eSgc USB_DPRINTF_L2(DPRINT_MASK_EVENTS, 1052d73ae94eSgc usb_ia->ia_log_handle, 1053d73ae94eSgc "usb_ia_event_cb: Device already disconnected"); 1054d73ae94eSgc } else { 1055d73ae94eSgc /* we are disconnected so set our state now */ 1056d73ae94eSgc usb_ia->ia_dev_state = USB_DEV_DISCONNECTED; 1057d73ae94eSgc for (i = 0; i < usb_ia->ia_n_ifs; i++) { 1058d73ae94eSgc usb_ia->ia_child_events[i] &= ~ 1059d73ae94eSgc USB_IA_CHILD_EVENT_DISCONNECT; 1060d73ae94eSgc } 1061d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1062d73ae94eSgc 1063d73ae94eSgc /* pass disconnect event to all the children */ 1064d73ae94eSgc (void) ndi_event_run_callbacks( 1065d73ae94eSgc usb_ia->ia_ndi_event_hdl, NULL, 1066d73ae94eSgc rm_cookie, bus_impldata); 1067d73ae94eSgc 1068d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1069d73ae94eSgc } 1070d73ae94eSgc break; 1071d73ae94eSgc case USBA_EVENT_TAG_PRE_SUSPEND: 1072d73ae94eSgc /* set our state *after* suspending children */ 1073d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1074d73ae94eSgc 1075d73ae94eSgc /* pass pre_suspend event to all the children */ 1076d73ae94eSgc (void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl, 1077d73ae94eSgc NULL, suspend_cookie, bus_impldata); 1078d73ae94eSgc 1079d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1080d73ae94eSgc for (i = 0; i < usb_ia->ia_n_ifs; i++) { 1081d73ae94eSgc usb_ia->ia_child_events[i] &= ~ 1082d73ae94eSgc USB_IA_CHILD_EVENT_PRESUSPEND; 1083d73ae94eSgc } 1084d73ae94eSgc break; 1085d73ae94eSgc case USBA_EVENT_TAG_HOT_INSERTION: 1086d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1087d73ae94eSgc if (usb_ia_restore_device_state(dip, usb_ia) == USB_SUCCESS) { 1088d73ae94eSgc 1089d73ae94eSgc /* 1090d73ae94eSgc * Check to see if this child has missed the disconnect 1091d73ae94eSgc * event before it registered for event cb 1092d73ae94eSgc */ 1093d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1094d73ae94eSgc for (i = 0; i < usb_ia->ia_n_ifs; i++) { 1095d73ae94eSgc if (usb_ia->ia_child_events[i] & 1096d73ae94eSgc USB_IA_CHILD_EVENT_DISCONNECT) { 1097d73ae94eSgc usb_ia->ia_child_events[i] &= 1098d73ae94eSgc ~USB_IA_CHILD_EVENT_DISCONNECT; 1099d73ae94eSgc child_dip = 1100d73ae94eSgc usb_ia->ia_children_dips[i]; 1101d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1102d73ae94eSgc 1103d73ae94eSgc /* post the missed disconnect */ 1104d73ae94eSgc (void) ndi_event_do_callback( 1105d73ae94eSgc usb_ia->ia_ndi_event_hdl, 1106d73ae94eSgc child_dip, 1107d73ae94eSgc rm_cookie, 1108d73ae94eSgc bus_impldata); 1109d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1110d73ae94eSgc } 1111d73ae94eSgc } 1112d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1113d73ae94eSgc 1114d73ae94eSgc /* pass reconnect event to all the children */ 1115d73ae94eSgc (void) ndi_event_run_callbacks( 1116d73ae94eSgc usb_ia->ia_ndi_event_hdl, NULL, 1117d73ae94eSgc ins_cookie, bus_impldata); 1118d73ae94eSgc 1119d73ae94eSgc } 1120d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1121d73ae94eSgc break; 1122d73ae94eSgc case USBA_EVENT_TAG_POST_RESUME: 1123d73ae94eSgc /* 1124d73ae94eSgc * Check to see if this child has missed the pre-suspend 1125d73ae94eSgc * event before it registered for event cb 1126d73ae94eSgc */ 1127d73ae94eSgc for (i = 0; i < usb_ia->ia_n_ifs; i++) { 1128d73ae94eSgc if (usb_ia->ia_child_events[i] & 1129d73ae94eSgc USB_IA_CHILD_EVENT_PRESUSPEND) { 1130d73ae94eSgc usb_ia->ia_child_events[i] &= 1131d73ae94eSgc ~USB_IA_CHILD_EVENT_PRESUSPEND; 1132d73ae94eSgc child_dip = usb_ia->ia_children_dips[i]; 1133d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1134d73ae94eSgc 1135d73ae94eSgc /* post the missed pre-suspend event */ 1136d73ae94eSgc (void) ndi_event_do_callback( 1137d73ae94eSgc usb_ia->ia_ndi_event_hdl, 1138d73ae94eSgc child_dip, suspend_cookie, 1139d73ae94eSgc bus_impldata); 1140d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1141d73ae94eSgc } 1142d73ae94eSgc } 1143d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1144d73ae94eSgc 1145d73ae94eSgc /* pass post_resume event to all the children */ 1146d73ae94eSgc (void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl, 1147d73ae94eSgc NULL, resume_cookie, bus_impldata); 1148d73ae94eSgc 1149d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1150d73ae94eSgc break; 1151d73ae94eSgc } 1152d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1153d73ae94eSgc 1154d73ae94eSgc } 1155d73ae94eSgc 1156d73ae94eSgc /* 1157d73ae94eSgc * create the pm components required for power management 1158d73ae94eSgc */ 1159d73ae94eSgc static void 1160d73ae94eSgc usb_ia_create_pm_components(dev_info_t *dip, usb_ia_t *usb_ia) 1161d73ae94eSgc { 1162d73ae94eSgc usb_common_power_t *iapm; 1163d73ae94eSgc uint_t pwr_states; 1164d73ae94eSgc 1165d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle, 1166d73ae94eSgc "usb_ia_create_pm_components: Begin"); 1167d73ae94eSgc 1168d73ae94eSgc /* Allocate the PM state structure */ 1169d73ae94eSgc iapm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP); 1170d73ae94eSgc 1171d73ae94eSgc mutex_enter(&usb_ia->ia_mutex); 1172d73ae94eSgc usb_ia->ia_pm = iapm; 1173d73ae94eSgc iapm->uc_usb_statep = usb_ia; 1174d73ae94eSgc iapm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */ 1175d73ae94eSgc iapm->uc_current_power = USB_DEV_OS_FULL_PWR; 1176d73ae94eSgc mutex_exit(&usb_ia->ia_mutex); 1177d73ae94eSgc 1178d73ae94eSgc /* 1179d73ae94eSgc * By not enabling parental notification, PM enforces 1180d73ae94eSgc * "strict parental dependency" meaning, usb_ia won't 1181d73ae94eSgc * power off until any of its children are in full power. 1182d73ae94eSgc */ 1183d73ae94eSgc 1184d73ae94eSgc /* 1185d73ae94eSgc * there are 3 scenarios: 1186d73ae94eSgc * 1. a well behaved device should have remote wakeup 1187d73ae94eSgc * at interface and device level. If the interface 1188d73ae94eSgc * wakes up, usb_ia will wake up 1189d73ae94eSgc * 2. if the device doesn't have remote wake up and 1190d73ae94eSgc * the interface has, PM will still work, ie. 1191d73ae94eSgc * the interfaces wakes up and usb_ia wakes up 1192d73ae94eSgc * 3. if neither the interface nor device has remote 1193d73ae94eSgc * wakeup, the interface will wake up when it is opened 1194d73ae94eSgc * and goes to sleep after being closed for a while 1195d73ae94eSgc * In this case usb_ia should also go to sleep shortly 1196d73ae94eSgc * thereafter 1197d73ae94eSgc * In all scenarios it doesn't really matter whether 1198d73ae94eSgc * remote wakeup at the device level is enabled or not 1199d73ae94eSgc * but we do it anyways 1200d73ae94eSgc */ 1201d73ae94eSgc if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) == 1202d73ae94eSgc USB_SUCCESS) { 1203d73ae94eSgc USB_DPRINTF_L3(DPRINT_MASK_PM, usb_ia->ia_log_handle, 1204d73ae94eSgc "usb_ia_create_pm_components: " 1205d73ae94eSgc "Remote Wakeup Enabled"); 1206d73ae94eSgc iapm->uc_wakeup_enabled = 1; 1207d73ae94eSgc } 1208d73ae94eSgc 1209d73ae94eSgc if (usb_create_pm_components(dip, &pwr_states) == 1210d73ae94eSgc USB_SUCCESS) { 1211d73ae94eSgc iapm->uc_pwr_states = (uint8_t)pwr_states; 1212d73ae94eSgc (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); 1213d73ae94eSgc } 1214d73ae94eSgc 1215d73ae94eSgc USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle, 1216d73ae94eSgc "usb_ia_create_pm_components: End"); 1217d73ae94eSgc } 1218d73ae94eSgc 1219d73ae94eSgc 1220d73ae94eSgc /* 1221d73ae94eSgc * usb_ia_obtain_state: 1222d73ae94eSgc */ 1223d73ae94eSgc static usb_ia_t * 1224d73ae94eSgc usb_ia_obtain_state(dev_info_t *dip) 1225d73ae94eSgc { 1226d73ae94eSgc int instance = ddi_get_instance(dip); 1227d73ae94eSgc usb_ia_t *statep = ddi_get_soft_state(usb_ia_statep, instance); 1228d73ae94eSgc 1229d73ae94eSgc ASSERT(statep != NULL); 1230d73ae94eSgc 1231d73ae94eSgc return (statep); 1232d73ae94eSgc } 1233