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 * Sample skeleton USB driver. 29 * This driver provides a framework for developing USB client drivers. 30 * 31 * As a simplistic example, usbskel implements a data transfer by reading 32 * raw configuration data, which every USB device has. It is expected that 33 * the caller will issue an initial 4-byte read to get the total length of the 34 * first configuration, and follow up with a second read, passing the total 35 * length to read the entire configuration cloud. 36 * 37 * The device has four states (refer to usbai.h): 38 * USB_DEV_ONLINE: In action or ready for action. 39 * USB_DEV_DISCONNECTED: Hotplug removed, or device not present/correct on 40 * resume (CPR). 41 * USB_DEV_SUSPENDED: Device has been suspended along with the system. 42 * USB_DEV_PWRED_DOWN: Device has been powered down. (Note that this 43 * driver supports only two power states, powered down and 44 * full power.) 45 * 46 * In order to avoid race conditions between driver entry points, 47 * access to the device is serialized. Race conditions are an issue in 48 * particular between disconnect event callbacks, detach, power, open 49 * and data transfer callbacks. The functions usbskel_serialize/release_access 50 * are implemented for this purpose. 51 * 52 * Mutexes should never be held when making calls into USBA or when 53 * sleeping. 54 * 55 * pm_busy_component and pm_idle_component mark the device as busy or idle to 56 * the system. These functions are paired, and are called only from code 57 * bracketed by usbskel_serialize_access and usbskel_release_access. 58 * 59 * NOTE: PM and CPR will be enabled at a later release of S10. 60 */ 61 62 #if defined(lint) && !defined(DEBUG) 63 #define DEBUG 64 #endif 65 66 #define USBDRV_MAJOR_VER 2 67 #define USBDRV_MINOR_VER 0 68 69 /* Uncomment to enable Power Management, when the OS PM framework is ready. */ 70 /* 71 * #ifndef USBSKEL_PM 72 * #define USBSKEL_PM 73 * #endif 74 */ 75 76 /* 77 * Uncomment to enable Check Point Resume (system suspend and resume) when the 78 * OS CPR framework is ready. 79 */ 80 /* 81 * #ifndef USBSKEL_CPR 82 * #define USBSKEL_CPR 83 * #endif 84 */ 85 86 #include <sys/usb/usba.h> 87 #include <sys/usb/clients/usbskel/usbskel.h> 88 89 int usbskel_errlevel = USBSKEL_LOG_LOG; 90 static char *name = "usbskl"; /* Driver name, used all over. */ 91 92 /* 93 * Boolean set to whether or not to dump the device's descriptor tree. 94 * Can be changed with the usblog_dumptree property in a usbskel.conf file. 95 */ 96 static boolean_t usbskel_dumptree; 97 98 /* 99 * Function Prototypes 100 */ 101 static int usbskel_attach(dev_info_t *, ddi_attach_cmd_t); 102 static int usbskel_detach(dev_info_t *, ddi_detach_cmd_t); 103 static int usbskel_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 104 static int usbskel_cleanup(dev_info_t *, usbskel_state_t *); 105 static int usbskel_open(dev_t *, int, int, cred_t *); 106 static int usbskel_close(dev_t, int, int, cred_t *); 107 static int usbskel_read(dev_t, struct uio *uip_p, cred_t *); 108 static int usbskel_strategy(struct buf *); 109 static void usbskel_minphys(struct buf *); 110 static void usbskel_normal_callback(usb_pipe_handle_t, usb_ctrl_req_t *); 111 static void usbskel_exception_callback(usb_pipe_handle_t, usb_ctrl_req_t *); 112 static int usbskel_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 113 static int usbskel_disconnect_callback(dev_info_t *); 114 static int usbskel_reconnect_callback(dev_info_t *); 115 static void usbskel_restore_device_state(dev_info_t *, usbskel_state_t *); 116 static int usbskel_cpr_suspend(dev_info_t *); 117 static void usbskel_cpr_resume(dev_info_t *); 118 static int usbskel_test_and_adjust_device_state(usbskel_state_t *); 119 static int usbskel_open_pipes(usbskel_state_t *); 120 static void usbskel_close_pipes(usbskel_state_t *); 121 static int usbskel_pm_busy_component(usbskel_state_t *); 122 static void usbskel_pm_idle_component(usbskel_state_t *); 123 static int usbskel_power(dev_info_t *, int, int); 124 #ifdef USBSKEL_PM 125 static int usbskel_init_power_mgmt(usbskel_state_t *); 126 static void usbskel_destroy_power_mgmt(usbskel_state_t *); 127 #endif 128 static int usbskel_serialize_access(usbskel_state_t *, boolean_t); 129 static void usbskel_release_access(usbskel_state_t *); 130 static int usbskel_check_same_device(usbskel_state_t *); 131 132 /*PRINTFLIKE3*/ 133 static void usbskel_log(usbskel_state_t *, int, char *, ...); 134 135 /* _NOTE is an advice for locklint. Locklint checks lock use for deadlocks. */ 136 _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_ctrl_req)) 137 _NOTE(SCHEME_PROTECTS_DATA("unique per call", buf)) 138 139 /* module loading stuff */ 140 struct cb_ops usbskel_cb_ops = { 141 usbskel_open, /* open */ 142 usbskel_close, /* close */ 143 usbskel_strategy, /* strategy */ 144 nulldev, /* print */ 145 nulldev, /* dump */ 146 usbskel_read, /* read */ 147 nodev, /* write */ 148 usbskel_ioctl, /* ioctl */ 149 nulldev, /* devmap */ 150 nodev, /* mmap */ 151 nodev, /* segmap */ 152 nochpoll, /* poll */ 153 ddi_prop_op, /* cb_prop_op */ 154 NULL, /* streamtab */ 155 D_MP 156 }; 157 158 static struct dev_ops usbskel_ops = { 159 DEVO_REV, /* devo_rev, */ 160 0, /* refcnt */ 161 usbskel_info, /* info */ 162 nulldev, /* identify */ 163 nulldev, /* probe */ 164 usbskel_attach, /* attach */ 165 usbskel_detach, /* detach */ 166 nodev, /* reset */ 167 &usbskel_cb_ops, /* driver operations */ 168 NULL, /* bus operations */ 169 usbskel_power /* power */ 170 }; 171 172 static struct modldrv usbskel_modldrv = { 173 &mod_driverops, 174 "USB skeleton driver", 175 &usbskel_ops 176 }; 177 178 static struct modlinkage modlinkage = { 179 MODREV_1, 180 &usbskel_modldrv, 181 NULL 182 }; 183 184 /* local variables */ 185 186 /* Soft state structures */ 187 #define USBSKEL_INITIAL_SOFT_SPACE 1 188 static void *usbskel_statep; 189 190 191 /* 192 * Module-wide initialization routine. 193 */ 194 int 195 _init(void) 196 { 197 int rval; 198 199 usbskel_log(NULL, USBSKEL_LOG_LOG, "usbskel _init"); 200 201 if ((rval = ddi_soft_state_init(&usbskel_statep, 202 sizeof (usbskel_state_t), USBSKEL_INITIAL_SOFT_SPACE)) != 0) { 203 204 return (rval); 205 } 206 207 if ((rval = mod_install(&modlinkage)) != 0) { 208 ddi_soft_state_fini(&usbskel_statep); 209 } 210 211 usbskel_log(NULL, USBSKEL_LOG_LOG, "usbskel _init done"); 212 213 return (rval); 214 } 215 216 217 /* 218 * Module-wide tear-down routine. 219 */ 220 int 221 _fini(void) 222 { 223 int rval; 224 225 usbskel_log(NULL, USBSKEL_LOG_LOG, "usbskel _fini"); 226 if ((rval = mod_remove(&modlinkage)) != 0) { 227 228 return (rval); 229 } 230 231 ddi_soft_state_fini(&usbskel_statep); 232 usbskel_log(NULL, USBSKEL_LOG_LOG, "usbskel _fini done"); 233 234 return (rval); 235 } 236 237 238 int 239 _info(struct modinfo *modinfop) 240 { 241 return (mod_info(&modlinkage, modinfop)); 242 } 243 244 245 /* 246 * usbskel_info: 247 * Get minor number, soft state structure, etc. 248 */ 249 /*ARGSUSED*/ 250 static int 251 usbskel_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 252 void *arg, void **result) 253 { 254 usbskel_state_t *usbskelp; 255 int error = DDI_FAILURE; 256 257 switch (infocmd) { 258 case DDI_INFO_DEVT2DEVINFO: 259 if ((usbskelp = ddi_get_soft_state(usbskel_statep, 260 getminor((dev_t)arg))) != NULL) { 261 *result = usbskelp->usbskel_dip; 262 if (*result != NULL) { 263 error = DDI_SUCCESS; 264 } 265 } else { 266 *result = NULL; 267 } 268 break; 269 case DDI_INFO_DEVT2INSTANCE: 270 *result = (void *)(uintptr_t)getminor((dev_t)arg); 271 error = DDI_SUCCESS; 272 break; 273 default: 274 break; 275 } 276 277 return (error); 278 } 279 280 281 /* 282 * usbskel_attach: 283 * Attach or resume. 284 * 285 * For attach, initialize state and device, including: 286 * state variables, locks, device node 287 * device registration with system 288 * power management, hotplugging 289 * For resume, restore device and state 290 */ 291 static int 292 usbskel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 293 { 294 int instance = ddi_get_instance(dip); 295 char *devinst; 296 int devinstlen; 297 usbskel_state_t *usbskelp = NULL; 298 usb_reg_parse_lvl_t parse_level; 299 usb_ep_data_t *ep_datap; 300 int status; 301 302 switch (cmd) { 303 case DDI_ATTACH: 304 break; 305 306 case DDI_RESUME: 307 usbskel_cpr_resume(dip); 308 309 /* 310 * Always return success to work around enumeration failures. 311 * This works around an issue where devices which are present 312 * before a suspend and absent upon resume could cause a system 313 * panic on resume. 314 */ 315 return (DDI_SUCCESS); 316 default: 317 return (DDI_FAILURE); 318 } 319 320 if (ddi_soft_state_zalloc(usbskel_statep, instance) == DDI_SUCCESS) { 321 usbskelp = ddi_get_soft_state(usbskel_statep, instance); 322 } 323 if (usbskelp == NULL) { 324 325 return (DDI_FAILURE); 326 } 327 328 usbskelp->usbskel_dip = dip; 329 330 devinst = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); 331 devinstlen = snprintf(devinst, USB_MAXSTRINGLEN, "%s%d: ", 332 ddi_driver_name(dip), instance); 333 334 usbskelp->usbskel_devinst = kmem_zalloc(devinstlen + 1, KM_SLEEP); 335 (void) strncpy(usbskelp->usbskel_devinst, devinst, devinstlen); 336 kmem_free(devinst, USB_MAXSTRINGLEN); 337 338 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "Attach: enter for attach"); 339 340 usbskel_dumptree = (ddi_prop_exists(DDI_DEV_T_ANY, dip, 341 DDI_PROP_DONTPASS, "usbskel_dumptree") == 1); 342 343 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "Tree will %sbe dumped", 344 ((usbskel_dumptree) ? "" : "not ")); 345 346 parse_level = (usb_reg_parse_lvl_t)ddi_prop_get_int( 347 DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 348 "usbskel_parse_level", USB_PARSE_LVL_ALL); 349 350 switch (parse_level) { 351 case USB_PARSE_LVL_NONE: 352 /* This driver needs a tree. */ 353 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 354 "parse_level requested to NOT DUMP"); 355 parse_level = USB_PARSE_LVL_IF; 356 /*FALLTHROUGH*/ 357 case USB_PARSE_LVL_IF: 358 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 359 "parse_level set to dump specific interface"); 360 break; 361 case USB_PARSE_LVL_CFG: 362 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 363 "parse_level set to dump specific config"); 364 break; 365 case USB_PARSE_LVL_ALL: 366 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 367 "parse_level set to dump everything"); 368 break; 369 default: 370 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 371 "attach: parse_level will default to dump everything"); 372 parse_level = USB_PARSE_LVL_ALL; 373 } 374 375 if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) != 376 USB_SUCCESS) { 377 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 378 "attach: usb_client_attach failed, error code:%d", status); 379 goto fail; 380 } 381 382 if ((status = usb_get_dev_data(dip, &usbskelp->usbskel_reg, parse_level, 383 0)) != USB_SUCCESS) { 384 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 385 "attach: usb_get_dev_data failed, error code:%d", status); 386 goto fail; 387 } 388 389 if (usbskel_dumptree) { 390 (void) usb_print_descr_tree( 391 usbskelp->usbskel_dip, usbskelp->usbskel_reg); 392 } 393 394 /* 395 * Get the descriptor for an intr pipe at alt 0 of current interface. 396 * This will be used later to open the pipe. 397 */ 398 if ((ep_datap = usb_lookup_ep_data(dip, usbskelp->usbskel_reg, 399 usbskelp->usbskel_reg->dev_curr_if, 0, 0, 400 USB_EP_ATTR_INTR, USB_EP_DIR_IN)) == NULL) { 401 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 402 "attach: Error getting intr endpoint descriptor"); 403 goto fail; 404 } 405 usbskelp->usbskel_intr_ep_descr = ep_datap->ep_descr; 406 407 usb_free_descr_tree(dip, usbskelp->usbskel_reg); 408 409 mutex_init(&usbskelp->usbskel_mutex, NULL, MUTEX_DRIVER, 410 usbskelp->usbskel_reg->dev_iblock_cookie); 411 412 cv_init(&usbskelp->usbskel_serial_cv, NULL, CV_DRIVER, NULL); 413 usbskelp->usbskel_serial_inuse = B_FALSE; 414 415 usbskelp->usbskel_locks_initialized = B_TRUE; 416 417 /* create minor node */ 418 if (ddi_create_minor_node(dip, name, S_IFCHR, instance, 419 "usb_skeleton", 0) != DDI_SUCCESS) { 420 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 421 "attach: Error creating minor node"); 422 goto fail; 423 } 424 425 /* Put online before PM init as can get power managed afterward. */ 426 usbskelp->usbskel_dev_state = USB_DEV_ONLINE; 427 428 #ifdef USBSKEL_PM 429 /* initialize power management */ 430 if (usbskel_init_power_mgmt(usbskelp) != USB_SUCCESS) { 431 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 432 "attach: Could not initialize power mgmt"); 433 } 434 #endif 435 436 if (usb_register_hotplug_cbs(dip, usbskel_disconnect_callback, 437 usbskel_reconnect_callback) != USB_SUCCESS) { 438 439 goto fail; 440 } 441 442 /* Report device */ 443 ddi_report_dev(dip); 444 445 return (DDI_SUCCESS); 446 447 fail: 448 if (usbskelp) { 449 (void) usbskel_cleanup(dip, usbskelp); 450 } 451 452 return (DDI_FAILURE); 453 } 454 455 456 /* 457 * usbskel_detach: 458 * detach or suspend driver instance 459 * 460 * Note: in detach, only contention threads is from pm and disconnnect. 461 */ 462 static int 463 usbskel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 464 { 465 int instance = ddi_get_instance(dip); 466 usbskel_state_t *usbskelp = 467 ddi_get_soft_state(usbskel_statep, instance); 468 int rval = DDI_FAILURE; 469 470 switch (cmd) { 471 case DDI_DETACH: 472 mutex_enter(&usbskelp->usbskel_mutex); 473 ASSERT((usbskelp->usbskel_drv_state & USBSKEL_OPEN) == 0); 474 mutex_exit(&usbskelp->usbskel_mutex); 475 476 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 477 "Detach: enter for detach"); 478 479 rval = usbskel_cleanup(dip, usbskelp); 480 481 break; 482 case DDI_SUSPEND: 483 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 484 "Detach: enter for suspend"); 485 486 rval = usbskel_cpr_suspend(dip); 487 default: 488 489 break; 490 } 491 492 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 493 } 494 495 496 /* 497 * usbskel_cleanup: 498 * clean up the driver state for detach 499 */ 500 static int 501 usbskel_cleanup(dev_info_t *dip, usbskel_state_t *usbskelp) 502 { 503 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "Cleanup: enter"); 504 505 if (usbskelp->usbskel_locks_initialized) { 506 507 /* This must be done 1st to prevent more events from coming. */ 508 usb_unregister_hotplug_cbs(dip); 509 510 /* 511 * At this point, no new activity can be initiated. The driver 512 * has disabled hotplug callbacks. The Solaris framework has 513 * disabled new opens on a device being detached, and does not 514 * allow detaching an open device. 515 * 516 * The following ensures that all driver activity has drained. 517 */ 518 mutex_enter(&usbskelp->usbskel_mutex); 519 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 520 usbskel_release_access(usbskelp); 521 mutex_exit(&usbskelp->usbskel_mutex); 522 523 #ifdef USBSKEL_PM 524 /* All device activity has died down. */ 525 usbskel_destroy_power_mgmt(usbskelp); 526 #endif 527 528 /* start dismantling */ 529 ddi_remove_minor_node(dip, NULL); 530 531 cv_destroy(&usbskelp->usbskel_serial_cv); 532 mutex_destroy(&usbskelp->usbskel_mutex); 533 } 534 535 usb_client_detach(dip, usbskelp->usbskel_reg); 536 537 if (usbskelp->usbskel_devinst != NULL) { 538 kmem_free(usbskelp->usbskel_devinst, 539 strlen(usbskelp->usbskel_devinst) + 1); 540 } 541 542 ddi_soft_state_free(usbskel_statep, ddi_get_instance(dip)); 543 ddi_prop_remove_all(dip); 544 545 return (USB_SUCCESS); 546 } 547 548 549 /*ARGSUSED*/ 550 static int 551 usbskel_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 552 { 553 usbskel_state_t *usbskelp = 554 ddi_get_soft_state(usbskel_statep, getminor(*devp)); 555 int rval = 0; 556 557 if (usbskelp == NULL) { 558 559 return (ENXIO); 560 } 561 562 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "open: enter"); 563 564 /* 565 * Keep it simple: one client at a time. 566 * Exclusive open only 567 */ 568 mutex_enter(&usbskelp->usbskel_mutex); 569 if ((usbskelp->usbskel_drv_state & USBSKEL_OPEN) != 0) { 570 mutex_exit(&usbskelp->usbskel_mutex); 571 572 return (EBUSY); 573 } 574 usbskelp->usbskel_drv_state |= USBSKEL_OPEN; 575 576 /* 577 * This is in place so that a disconnect or CPR doesn't interfere with 578 * pipe opens. 579 */ 580 if (usbskel_serialize_access(usbskelp, USBSKEL_SER_SIG) == 0) { 581 usbskelp->usbskel_drv_state &= ~USBSKEL_OPEN; 582 mutex_exit(&usbskelp->usbskel_mutex); 583 584 return (EINTR); 585 } 586 587 mutex_exit(&usbskelp->usbskel_mutex); 588 if (usbskel_pm_busy_component(usbskelp) != DDI_SUCCESS) { 589 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 590 "open: Error raising power"); 591 rval = EIO; 592 goto done; 593 } 594 mutex_enter(&usbskelp->usbskel_mutex); 595 596 /* Fail if device is no longer ready. */ 597 if (usbskelp->usbskel_dev_state != USB_DEV_ONLINE) { 598 mutex_exit(&usbskelp->usbskel_mutex); 599 rval = EIO; 600 goto done; 601 } 602 603 mutex_exit(&usbskelp->usbskel_mutex); 604 if (usbskel_open_pipes(usbskelp) != USB_SUCCESS) { 605 rval = EIO; 606 goto done; 607 } 608 609 /* Device specific initialization goes here. */ 610 611 done: 612 if (rval != 0) { 613 mutex_enter(&usbskelp->usbskel_mutex); 614 usbskelp->usbskel_drv_state &= ~USBSKEL_OPEN; 615 616 usbskel_release_access(usbskelp); 617 mutex_exit(&usbskelp->usbskel_mutex); 618 619 usbskel_pm_idle_component(usbskelp); 620 } else { 621 622 /* Device is idle until it is used. */ 623 mutex_enter(&usbskelp->usbskel_mutex); 624 usbskel_release_access(usbskelp); 625 mutex_exit(&usbskelp->usbskel_mutex); 626 } 627 628 return (rval); 629 } 630 631 632 /*ARGSUSED*/ 633 static int 634 usbskel_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 635 { 636 usbskel_state_t *usbskelp = 637 ddi_get_soft_state(usbskel_statep, getminor(dev)); 638 639 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "close: enter"); 640 641 mutex_enter(&usbskelp->usbskel_mutex); 642 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 643 mutex_exit(&usbskelp->usbskel_mutex); 644 645 /* Perform device session cleanup here. */ 646 647 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "close: cleaning up..."); 648 649 /* 650 * USBA automatically flushes/resets active non-default pipes 651 * when they are closed. We can't reset default pipe, but we 652 * can wait for all requests on it from this dip to drain. 653 */ 654 (void) usb_pipe_drain_reqs(usbskelp->usbskel_dip, 655 usbskelp->usbskel_reg->dev_default_ph, 0, 656 USB_FLAGS_SLEEP, NULL, 0); 657 658 mutex_enter(&usbskelp->usbskel_mutex); 659 usbskel_close_pipes(usbskelp); 660 661 usbskelp->usbskel_drv_state &= ~USBSKEL_OPEN; 662 663 usbskel_release_access(usbskelp); 664 mutex_exit(&usbskelp->usbskel_mutex); 665 666 usbskel_pm_idle_component(usbskelp); 667 668 return (0); 669 } 670 671 672 /*ARGSUSED*/ 673 static int 674 usbskel_read(dev_t dev, struct uio *uio_p, cred_t *cred_p) 675 { 676 usbskel_state_t *usbskelp = 677 ddi_get_soft_state(usbskel_statep, getminor(dev)); 678 679 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "read enter"); 680 681 return (physio(usbskel_strategy, NULL, dev, B_READ, 682 usbskel_minphys, uio_p)); 683 } 684 685 686 /* 687 * strategy: 688 * Called through physio to setup and start the transfer. 689 */ 690 static int 691 usbskel_strategy(struct buf *bp) 692 { 693 usbskel_state_t *usbskelp = ddi_get_soft_state(usbskel_statep, 694 getminor(bp->b_edev)); 695 usb_pipe_handle_t pipe = usbskelp->usbskel_reg->dev_default_ph; 696 usb_ctrl_req_t *request; 697 int status; 698 699 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "strategy enter"); 700 701 /* 702 * Initialize residual count here in case transfer doesn't even get 703 * started. 704 */ 705 bp->b_resid = bp->b_bcount; 706 707 /* Needed as this is a character driver. */ 708 if (bp->b_flags & (B_PHYS | B_PAGEIO)) { 709 bp_mapin(bp); 710 } 711 712 mutex_enter(&usbskelp->usbskel_mutex); 713 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 714 715 /* Make sure device has not been disconnected. */ 716 if (usbskelp->usbskel_dev_state != USB_DEV_ONLINE) { 717 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 718 "usbskel_strategy: device can't be accessed"); 719 mutex_exit(&usbskelp->usbskel_mutex); 720 goto fail; 721 } 722 mutex_exit(&usbskelp->usbskel_mutex); 723 724 /* 725 * Since every device has raw configuration data, set up a control 726 * transfer to read the raw configuration data. In a production driver 727 * a read would probably be done on a pipe other than the default pipe, 728 * and would be reading data streamed by the device. 729 */ 730 731 /* Allocate and initialize the request. */ 732 if ((bp->b_private = request = usb_alloc_ctrl_req( 733 usbskelp->usbskel_dip, bp->b_bcount, USB_FLAGS_SLEEP)) == 734 NULL) { 735 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 736 "usbskel_read: Error allocating request"); 737 goto fail; 738 } 739 740 request->ctrl_bmRequestType = 741 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD | 742 USB_DEV_REQ_RCPT_DEV; 743 request->ctrl_bRequest = USB_REQ_GET_DESCR; 744 745 /* For now, return only the first configuration. */ 746 request->ctrl_wValue = USB_DESCR_TYPE_SETUP_CFG | 0; 747 request->ctrl_wIndex = 0; 748 request->ctrl_wLength = bp->b_bcount; 749 request->ctrl_timeout = 3; 750 751 /* Autoclearing automatically set on default pipe. */ 752 request->ctrl_attributes = USB_ATTRS_SHORT_XFER_OK; 753 754 request->ctrl_cb = usbskel_normal_callback; 755 request->ctrl_exc_cb = usbskel_exception_callback; 756 757 /* Hook the req to the bp, so callback knows where to put the data. */ 758 /* Now both bp and request know about each other. */ 759 request->ctrl_client_private = (usb_opaque_t)bp; 760 761 /* 762 * Issue the request asynchronously. Physio will block waiting for an 763 * "interrupt" which comes as a callback. The callback calls biodone 764 * to release physio from its wait. 765 */ 766 if ((status = usb_pipe_ctrl_xfer(pipe, request, USB_FLAGS_NOSLEEP)) != 767 USB_SUCCESS) { 768 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 769 "usbskel_strategy: can't start transfer: status: %d", 770 status); 771 goto fail; 772 } 773 774 /* 775 * Normally, usbskel_release_access() and usbskel_pm_idle_component 776 * is called in callback handler. 777 */ 778 779 return (0); 780 781 fail: 782 mutex_enter(&usbskelp->usbskel_mutex); 783 usbskel_release_access(usbskelp); 784 mutex_exit(&usbskelp->usbskel_mutex); 785 786 bioerror(bp, EIO); 787 biodone(bp); 788 789 return (0); 790 } 791 792 793 static void 794 usbskel_minphys(struct buf *bp) 795 { 796 /* the config cloud is limited to 64k */ 797 if (bp->b_bcount > USBSKEL_REQUEST_SIZE) { 798 bp->b_bcount = USBSKEL_REQUEST_SIZE; 799 } 800 minphys(bp); 801 } 802 803 804 /* 805 * usbskel_normal_callback: 806 * Completion handler for successful transfer. 807 * Copy data from mblk returned by USBA, into 808 * buffer passed by physio, to get it back to user. 809 * Idle device 810 * update counts, etc. 811 * release request. 812 * signal completion via biodone 813 */ 814 /*ARGSUSED*/ 815 static void 816 usbskel_normal_callback(usb_pipe_handle_t pipe, usb_ctrl_req_t *request) 817 { 818 struct buf *bp = (struct buf *)request->ctrl_client_private; 819 usbskel_state_t *usbskelp = ddi_get_soft_state(usbskel_statep, 820 getminor(bp->b_edev)); 821 mblk_t *data = request->ctrl_data; 822 int amt_transferred = data->b_wptr - data->b_rptr; 823 824 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "normal callback enter"); 825 826 ASSERT((request->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0); 827 828 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 829 "at entry, b_bcount = %lu, b_resid = %lu, trans = %d", bp->b_bcount, 830 bp->b_resid, amt_transferred); 831 832 mutex_enter(&usbskelp->usbskel_mutex); 833 usbskel_release_access(usbskelp); 834 mutex_exit(&usbskelp->usbskel_mutex); 835 836 /* Copy data out of mblk, into buffer. */ 837 if (amt_transferred) { 838 bcopy(data->b_rptr, bp->b_un.b_addr, amt_transferred); 839 } 840 841 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 842 "normal callback: transferring %d bytes from 0x%p to 0x%p", 843 amt_transferred, (void *)data, (void *)(bp->b_un.b_addr)); 844 845 /* Unhook. */ 846 bp->b_private = NULL; 847 request->ctrl_client_private = NULL; 848 849 /* Free request. */ 850 usb_free_ctrl_req(request); 851 852 /* Finish up. */ 853 bp->b_resid = bp->b_bcount - amt_transferred; 854 855 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 856 "at exit, b_bcount = %lu, b_resid = %lu, trans = %d", bp->b_bcount, 857 bp->b_resid, amt_transferred); 858 859 biodone(bp); 860 } 861 862 863 /* 864 * usbskel_exception_callback: 865 * Completion handler for an erred transfer. 866 * Copy data from mblk returned by USBA, if any, into 867 * buffer passed by physio, to get it back to user. 868 * Idle device 869 * update counts, etc. 870 * release request. 871 * signal completion via biodone 872 */ 873 /*ARGSUSED*/ 874 static void 875 usbskel_exception_callback(usb_pipe_handle_t pipe, usb_ctrl_req_t *request) 876 { 877 struct buf *bp = (struct buf *)request->ctrl_client_private; 878 usbskel_state_t *usbskelp = ddi_get_soft_state(usbskel_statep, 879 getminor(bp->b_edev)); 880 mblk_t *data = request->ctrl_data; 881 int amt_transferred = (data ? data->b_wptr - data->b_rptr : 0); 882 883 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 884 "at except cb entry, b_bcount = %lu, b_resid = %lu, trans = %d", 885 bp->b_bcount, bp->b_resid, amt_transferred); 886 887 ASSERT((request->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0); 888 889 mutex_enter(&usbskelp->usbskel_mutex); 890 usbskel_release_access(usbskelp); 891 mutex_exit(&usbskelp->usbskel_mutex); 892 893 /* Copy data, if any, out of mblk, into buffer. */ 894 if (amt_transferred) { 895 bcopy(data, bp->b_un.b_addr, amt_transferred); 896 } 897 bp->b_resid = bp->b_bcount - amt_transferred; 898 899 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 900 "exception cb: req = 0x%p, cr = %d\n\t cb_flags = 0x%x " 901 "data = 0x%p, amt xfered = %d", (void *)request, 902 request->ctrl_completion_reason, request->ctrl_cb_flags, 903 (void *)(request->ctrl_data), amt_transferred); 904 905 /* Unhook */ 906 bp->b_private = NULL; 907 request->ctrl_client_private = NULL; 908 909 /* Free request. */ 910 usb_free_ctrl_req(request); 911 912 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 913 "at except cb exit, b_bcount = %lu, b_resid = %lu, trans = %d", 914 bp->b_bcount, bp->b_resid, amt_transferred); 915 916 bioerror(bp, EIO); 917 biodone(bp); 918 } 919 920 921 /* 922 * XXX Empty ioctl for now. 923 */ 924 /*ARGSUSED*/ 925 static int 926 usbskel_ioctl(dev_t dev, int cmd, intptr_t arg, 927 int mode, cred_t *cred_p, int *rval_p) 928 { 929 usbskel_state_t *usbskelp = 930 ddi_get_soft_state(usbskel_statep, getminor(dev)); 931 932 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "ioctl enter"); 933 934 return (ENOTTY); 935 } 936 937 938 /* 939 * usbskel_disconnect_callback: 940 * Called when device hotplug-removed. 941 * Close pipes. (This does not attempt to contact device.) 942 * Set state to DISCONNECTED 943 */ 944 static int 945 usbskel_disconnect_callback(dev_info_t *dip) 946 { 947 int instance = ddi_get_instance(dip); 948 usbskel_state_t *usbskelp = 949 ddi_get_soft_state(usbskel_statep, instance); 950 951 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "disconnect: enter"); 952 953 mutex_enter(&usbskelp->usbskel_mutex); 954 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 955 956 /* 957 * Save any state of device or IO in progress required by 958 * usbskel_restore_device_state for proper device "thawing" later. 959 */ 960 usbskelp->usbskel_dev_state = USB_DEV_DISCONNECTED; 961 962 usbskel_release_access(usbskelp); 963 mutex_exit(&usbskelp->usbskel_mutex); 964 965 return (USB_SUCCESS); 966 } 967 968 969 /* 970 * usbskel_reconnect_callback: 971 * Called with device hotplug-inserted 972 * Restore state 973 */ 974 static int 975 usbskel_reconnect_callback(dev_info_t *dip) 976 { 977 int instance = ddi_get_instance(dip); 978 usbskel_state_t *usbskelp = 979 ddi_get_soft_state(usbskel_statep, instance); 980 981 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "reconnect: enter"); 982 983 mutex_enter(&usbskelp->usbskel_mutex); 984 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 985 usbskel_restore_device_state(dip, usbskelp); 986 usbskel_release_access(usbskelp); 987 mutex_exit(&usbskelp->usbskel_mutex); 988 989 return (USB_SUCCESS); 990 } 991 992 993 /* 994 * usbskel_restore_device_state: 995 * Called during hotplug-reconnect and resume. 996 * reenable power management 997 * Verify the device is the same as before the disconnect/suspend. 998 * Restore device state 999 * Thaw any IO which was frozen. 1000 * Quiesce device. (Other routines will activate if thawed IO.) 1001 * Set device online. 1002 * Leave device disconnected if there are problems. 1003 */ 1004 static void 1005 usbskel_restore_device_state(dev_info_t *dip, usbskel_state_t *usbskelp) 1006 { 1007 int rval; 1008 1009 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1010 "usbskel_restore_device_state: enter"); 1011 1012 ASSERT(mutex_owned(&usbskelp->usbskel_mutex)); 1013 1014 ASSERT((usbskelp->usbskel_dev_state == USB_DEV_DISCONNECTED) || 1015 (usbskelp->usbskel_dev_state == USB_DEV_SUSPENDED)); 1016 1017 mutex_exit(&usbskelp->usbskel_mutex); 1018 1019 if (usbskel_pm_busy_component(usbskelp) != DDI_SUCCESS) { 1020 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1021 "usbskel_restore_device_state: Error raising power"); 1022 1023 goto fail; 1024 } 1025 1026 /* Check if we are talking to the same device */ 1027 if (usbskel_check_same_device(usbskelp) != USB_SUCCESS) { 1028 1029 goto fail; 1030 } 1031 1032 mutex_enter(&usbskelp->usbskel_mutex); 1033 if ((rval = usbskel_test_and_adjust_device_state(usbskelp)) != 1034 USB_SUCCESS) { 1035 mutex_exit(&usbskelp->usbskel_mutex); 1036 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1037 "usbskel_restore_device_state: " 1038 "Error adjusting device: rval = %d", rval); 1039 1040 goto fail; 1041 } 1042 usbskelp->usbskel_dev_state = USB_DEV_ONLINE; 1043 mutex_exit(&usbskelp->usbskel_mutex); 1044 1045 if (usbskelp->usbskel_pm) { 1046 1047 /* Failure here means device disappeared again. */ 1048 if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) != 1049 USB_SUCCESS) { 1050 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1051 "device may or may not be accessible. " 1052 "Please verify reconnection"); 1053 } 1054 usbskel_pm_idle_component(usbskelp); 1055 } 1056 1057 1058 mutex_enter(&usbskelp->usbskel_mutex); 1059 1060 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1061 "usbskel_restore_device_state: end"); 1062 1063 return; 1064 1065 fail: 1066 /* change the device state from suspended to disconnected */ 1067 mutex_enter(&usbskelp->usbskel_mutex); 1068 usbskelp->usbskel_dev_state = USB_DEV_DISCONNECTED; 1069 mutex_exit(&usbskelp->usbskel_mutex); 1070 1071 usbskel_pm_idle_component(usbskelp); 1072 mutex_enter(&usbskelp->usbskel_mutex); 1073 } 1074 1075 1076 /* 1077 * usbskel_cpr_suspend: 1078 * Clean up device. 1079 * Wait for any IO to finish, then close pipes. 1080 * Quiesce device. 1081 */ 1082 static int 1083 usbskel_cpr_suspend(dev_info_t *dip) 1084 { 1085 int instance = ddi_get_instance(dip); 1086 usbskel_state_t *usbskelp = ddi_get_soft_state(usbskel_statep, 1087 instance); 1088 1089 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "suspend enter"); 1090 1091 /* Serialize to prevent races with detach, open, device access. */ 1092 mutex_enter(&usbskelp->usbskel_mutex); 1093 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 1094 mutex_exit(&usbskelp->usbskel_mutex); 1095 1096 if (usbskel_pm_busy_component(usbskelp) != DDI_SUCCESS) { 1097 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1098 "suspend: Error raising power"); 1099 usbskel_pm_idle_component(usbskelp); 1100 1101 return (USB_FAILURE); 1102 } 1103 1104 mutex_enter(&usbskelp->usbskel_mutex); 1105 1106 /* 1107 * Set dev_state to suspended so other driver threads don't start any 1108 * new I/O. In a real driver, there may be draining of requests done 1109 * afterwards, and we don't want the draining to compete with new 1110 * requests being queued. 1111 */ 1112 1113 /* Don't suspend if the device is open. */ 1114 if ((usbskelp->usbskel_drv_state & USBSKEL_OPEN) != 0) { 1115 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1116 "suspend: Device is open. Can't suspend"); 1117 1118 usbskel_release_access(usbskelp); 1119 mutex_exit(&usbskelp->usbskel_mutex); 1120 1121 usbskel_pm_idle_component(usbskelp); 1122 1123 return (USB_FAILURE); 1124 } 1125 1126 /* Access device here to clean it up. */ 1127 1128 usbskelp->usbskel_dev_state = USB_DEV_SUSPENDED; 1129 1130 /* 1131 * Save any state of device required by usbskel_restore_device_state 1132 * for proper device "thawing" later. 1133 */ 1134 1135 usbskel_release_access(usbskelp); 1136 mutex_exit(&usbskelp->usbskel_mutex); 1137 1138 usbskel_pm_idle_component(usbskelp); 1139 1140 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "suspend: success"); 1141 1142 return (USB_SUCCESS); 1143 } 1144 1145 1146 /* 1147 * usbskel_cpr_resume: 1148 * 1149 * usbskel_restore_device_state marks success by putting device back online 1150 */ 1151 static void 1152 usbskel_cpr_resume(dev_info_t *dip) 1153 { 1154 int instance = ddi_get_instance(dip); 1155 usbskel_state_t *usbskelp = ddi_get_soft_state(usbskel_statep, 1156 instance); 1157 1158 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "resume: enter"); 1159 1160 /* 1161 * NOTE: A pm_raise_power in usbskel_restore_device_state will bring 1162 * the power-up state of device into synch with the system. 1163 */ 1164 mutex_enter(&usbskelp->usbskel_mutex); 1165 usbskel_restore_device_state(dip, usbskelp); 1166 mutex_exit(&usbskelp->usbskel_mutex); 1167 } 1168 1169 1170 /* 1171 * usbskel_test_and_adjust_device_state: 1172 * Place any device-specific initialization or sanity verification here. 1173 */ 1174 static int 1175 usbskel_test_and_adjust_device_state(usbskel_state_t *usbskelp) 1176 { 1177 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "test and adjust enter"); 1178 1179 return (USB_SUCCESS); 1180 } 1181 1182 1183 /* 1184 * usbskel_open_pipes: 1185 * Open any pipes other than default pipe. 1186 * Mutex is assumed to be held. 1187 */ 1188 static int 1189 usbskel_open_pipes(usbskel_state_t *usbskelp) 1190 { 1191 1192 int rval = USB_SUCCESS; 1193 usb_pipe_policy_t pipe_policy; 1194 usb_pipe_handle_t pipe_handle; 1195 1196 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "open_pipes enter"); 1197 1198 bzero(&pipe_policy, sizeof (pipe_policy)); 1199 1200 /* 1201 * Allow that pipes can support at least two asynchronous operations 1202 * going on simultaneously. Operations include asynchronous callbacks, 1203 * resets, closures. 1204 */ 1205 pipe_policy.pp_max_async_reqs = 2; 1206 1207 if ((rval = usb_pipe_open(usbskelp->usbskel_dip, 1208 &usbskelp->usbskel_intr_ep_descr, &pipe_policy, 1209 USB_FLAGS_SLEEP, &pipe_handle)) != USB_SUCCESS) { 1210 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1211 "usbskel_open_pipes: Error opening intr pipe: status = %d", 1212 rval); 1213 rval = USB_FAILURE; 1214 } 1215 mutex_enter(&usbskelp->usbskel_mutex); 1216 usbskelp->usbskel_intr_ph = pipe_handle; 1217 mutex_exit(&usbskelp->usbskel_mutex); 1218 1219 /* 1220 * At this point, polling could be started on the pipe by making an 1221 * asynchronous input request on the pipe. Allocate a request by 1222 * calling usb_alloc_intr_req(9F) with a zero length, initialize 1223 * attributes with USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING, 1224 * initialize length to be packetsize of the endpoint, specify the 1225 * callbacks. Pass this request to usb_pipe_intr_xfer to start polling. 1226 * Call usb_pipe_stop_intr_poling(9F) to stop polling. 1227 */ 1228 1229 return (rval); 1230 } 1231 1232 1233 /* 1234 * usbskel_close_pipes: 1235 * Close pipes. Mutex is assumed to be held. 1236 */ 1237 /*ARGSUSED*/ 1238 static void 1239 usbskel_close_pipes(usbskel_state_t *usbskelp) 1240 { 1241 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "close_pipes enter"); 1242 1243 if (usbskelp->usbskel_intr_ph) { 1244 usb_pipe_handle_t pipe_handle = usbskelp->usbskel_intr_ph; 1245 usbskelp->usbskel_intr_ph = NULL; 1246 mutex_exit(&usbskelp->usbskel_mutex); 1247 1248 usb_pipe_close(usbskelp->usbskel_dip, pipe_handle, 1249 USB_FLAGS_SLEEP, NULL, 0); 1250 1251 mutex_enter(&usbskelp->usbskel_mutex); 1252 } 1253 } 1254 1255 static int 1256 usbskel_pm_busy_component(usbskel_state_t *usbskelp) 1257 { 1258 int rval = DDI_SUCCESS; 1259 1260 mutex_enter(&usbskelp->usbskel_mutex); 1261 if (usbskelp->usbskel_pm != NULL) { 1262 usbskelp->usbskel_pm->usbskel_pm_busy++; 1263 mutex_exit(&usbskelp->usbskel_mutex); 1264 if (pm_busy_component(usbskelp->usbskel_dip, 0) == 1265 DDI_SUCCESS) { 1266 (void) pm_raise_power( 1267 usbskelp->usbskel_dip, 0, USB_DEV_OS_FULL_PWR); 1268 mutex_enter(&usbskelp->usbskel_mutex); 1269 } else { 1270 mutex_enter(&usbskelp->usbskel_mutex); 1271 usbskelp->usbskel_pm->usbskel_pm_busy--; 1272 rval = DDI_FAILURE; 1273 } 1274 } 1275 mutex_exit(&usbskelp->usbskel_mutex); 1276 1277 return (rval); 1278 } 1279 1280 static void 1281 usbskel_pm_idle_component(usbskel_state_t *usbskelp) 1282 { 1283 mutex_enter(&usbskelp->usbskel_mutex); 1284 if (usbskelp->usbskel_pm != NULL) { 1285 mutex_exit(&usbskelp->usbskel_mutex); 1286 if (pm_idle_component(usbskelp->usbskel_dip, 0) == 1287 DDI_SUCCESS) { 1288 mutex_enter(&usbskelp->usbskel_mutex); 1289 ASSERT(usbskelp->usbskel_pm->usbskel_pm_busy > 0); 1290 usbskelp->usbskel_pm->usbskel_pm_busy--; 1291 mutex_exit(&usbskelp->usbskel_mutex); 1292 } 1293 mutex_enter(&usbskelp->usbskel_mutex); 1294 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1295 "usbskel_pm_idle_component: %d", 1296 usbskelp->usbskel_pm->usbskel_pm_busy); 1297 } 1298 mutex_exit(&usbskelp->usbskel_mutex); 1299 } 1300 1301 /* 1302 * usbskel_power : 1303 * Power entry point, the workhorse behind pm_raise_power, pm_lower_power, 1304 * usb_req_raise_power and usb_req_lower_power. 1305 */ 1306 /* ARGSUSED */ 1307 static int 1308 usbskel_power(dev_info_t *dip, int comp, int level) 1309 { 1310 usbskel_state_t *usbskelp; 1311 usbskel_power_t *pm; 1312 int rval = USB_FAILURE; 1313 1314 usbskelp = ddi_get_soft_state(usbskel_statep, ddi_get_instance(dip)); 1315 1316 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1317 "usbskel_power: enter: level = %d", level); 1318 1319 mutex_enter(&usbskelp->usbskel_mutex); 1320 (void) usbskel_serialize_access(usbskelp, USBSKEL_SER_NOSIG); 1321 1322 1323 /* 1324 * If we are disconnected/suspended, return success. Note that if we 1325 * return failure, bringing down the system will hang when 1326 * PM tries to power up all devices 1327 */ 1328 if ((usbskelp->usbskel_dev_state == USB_DEV_DISCONNECTED) || 1329 (usbskelp->usbskel_dev_state == USB_DEV_SUSPENDED)) { 1330 1331 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1332 "usbskel_power: disconnected/suspended " 1333 "dev_state=%d", usbskelp->usbskel_dev_state); 1334 rval = USB_SUCCESS; 1335 1336 goto done; 1337 } 1338 1339 if (usbskelp->usbskel_pm == NULL) { 1340 1341 goto done; 1342 } 1343 1344 pm = usbskelp->usbskel_pm; 1345 1346 /* Check if we are transitioning to a legal power level */ 1347 if (USB_DEV_PWRSTATE_OK(pm->usbskel_pwr_states, level)) { 1348 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1349 "usbskel_power: illegal power level = %d " 1350 "pwr_states: %x", level, pm->usbskel_pwr_states); 1351 1352 goto done; 1353 } 1354 1355 switch (level) { 1356 case USB_DEV_OS_PWR_OFF : 1357 /* fail attempt to go to low power if busy */ 1358 if (pm->usbskel_pm_busy) { 1359 1360 goto done; 1361 } 1362 if (usbskelp->usbskel_dev_state == USB_DEV_ONLINE) { 1363 usbskelp->usbskel_dev_state = USB_DEV_PWRED_DOWN; 1364 usbskelp->usbskel_pm->usbskel_current_power = 1365 USB_DEV_OS_PWR_OFF; 1366 } else { 1367 rval = USB_SUCCESS; 1368 } 1369 break; 1370 1371 case USB_DEV_OS_FULL_PWR : 1372 /* 1373 * PM framework tries to put us in full power during system 1374 * shutdown. 1375 */ 1376 usbskelp->usbskel_dev_state = USB_DEV_ONLINE; 1377 usbskelp->usbskel_pm->usbskel_current_power = 1378 USB_DEV_OS_FULL_PWR; 1379 break; 1380 1381 /* Levels 1 and 2 are not supported by this driver to keep it simple. */ 1382 default: 1383 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1384 "usbskel_power: power level %d not supported", level); 1385 break; 1386 } 1387 done: 1388 usbskel_release_access(usbskelp); 1389 mutex_exit(&usbskelp->usbskel_mutex); 1390 1391 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); 1392 } 1393 1394 1395 #ifdef USBSKEL_PM 1396 /* 1397 * usbskel_init_power_mgmt: 1398 * Initialize power management and remote wakeup functionality. 1399 * No mutex is necessary in this function as it's called only by attach. 1400 */ 1401 static int 1402 usbskel_init_power_mgmt(usbskel_state_t *usbskelp) 1403 { 1404 int rval = USB_FAILURE; 1405 1406 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "init_power_mgmt enter"); 1407 1408 /* 1409 * If remote wakeup is not available you may not want to do 1410 * power management. 1411 */ 1412 if (usb_handle_remote_wakeup(usbskelp->usbskel_dip, 1413 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { 1414 usbskel_power_t *usbskelpm; 1415 uint_t pwr_states; 1416 1417 /* Allocate the state structure */ 1418 usbskelpm = kmem_zalloc(sizeof (usbskel_power_t), KM_SLEEP); 1419 usbskelp->usbskel_pm = usbskelpm; 1420 usbskelpm->usbskel_state = usbskelp; 1421 usbskelpm->usbskel_pm_capabilities = 0; 1422 usbskelpm->usbskel_current_power = USB_DEV_OS_FULL_PWR; 1423 1424 if ((rval = usb_create_pm_components( 1425 usbskelp->usbskel_dip, &pwr_states)) == USB_SUCCESS) { 1426 1427 usbskel_log(usbskelp, USBSKEL_LOG_LOG, 1428 "usbskel_init_power_mgmt: created PM components"); 1429 1430 usbskelpm->usbskel_pwr_states = 1431 (uint8_t)pwr_states; 1432 (void) pm_raise_power( 1433 usbskelp->usbskel_dip, 0, USB_DEV_OS_FULL_PWR); 1434 } else { 1435 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1436 "usbskel_init_power_mgmt: create_pm_compts failed"); 1437 } 1438 } else { 1439 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1440 "usbskel_init_power_mgmt: failure enabling remote wakeup"); 1441 } 1442 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "usbskel_init_power_mgmt: end"); 1443 1444 return (rval); 1445 } 1446 1447 1448 /* 1449 * usbskel_destroy_power_mgmt: 1450 * Shut down and destroy power management and remote wakeup functionality. 1451 */ 1452 static void 1453 usbskel_destroy_power_mgmt(usbskel_state_t *usbskelp) 1454 { 1455 usbskel_log(usbskelp, USBSKEL_LOG_LOG, "destroy_power_mgmt enter"); 1456 1457 ASSERT(!mutex_owned(&usbskelp->usbskel_mutex)); 1458 1459 if (usbskelp->usbskel_pm) { 1460 (void) usbskel_pm_busy_component(usbskelp); 1461 1462 mutex_enter(&usbskelp->usbskel_mutex); 1463 if (usbskelp->usbskel_dev_state != USB_DEV_DISCONNECTED) { 1464 int rval; 1465 1466 mutex_exit(&usbskelp->usbskel_mutex); 1467 1468 if ((rval = usb_handle_remote_wakeup( 1469 usbskelp->usbskel_dip, 1470 USB_REMOTE_WAKEUP_DISABLE)) != 1471 USB_SUCCESS) { 1472 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1473 "usbskel_destroy_power_mgmt: " 1474 "Error disabling rmt wakeup: rval = %d", 1475 rval); 1476 } 1477 } else { 1478 mutex_exit(&usbskelp->usbskel_mutex); 1479 } 1480 1481 /* 1482 * Since remote wakeup is disabled now, 1483 * no one can raise power 1484 * and get to device once power is lowered here. 1485 */ 1486 pm_lower_power(usbskelp->usbskel_dip, 0, USB_DEV_OS_PWR_OFF); 1487 usbskel_pm_idle_component(usbskelp); 1488 kmem_free(usbskelp->usbskel_pm, sizeof (usbskel_power_t)); 1489 usbskelp->usbskel_pm = NULL; 1490 } 1491 } 1492 #endif 1493 1494 1495 /* 1496 * usbskel_serialize_access: 1497 * Get the serial synchronization object before returning. 1498 * 1499 * Arguments: 1500 * usbskelp - Pointer to usbskel state structure 1501 * waitsig - Set to: 1502 * USBSKEL_SER_SIG - to wait such that a signal can interrupt 1503 * USBSKEL_SER_NOSIG - to wait such that a signal cannot interrupt 1504 */ 1505 static int 1506 usbskel_serialize_access(usbskel_state_t *usbskelp, boolean_t waitsig) 1507 { 1508 int rval = 1; 1509 1510 ASSERT(mutex_owned(&usbskelp->usbskel_mutex)); 1511 1512 while (usbskelp->usbskel_serial_inuse) { 1513 if (waitsig == USBSKEL_SER_SIG) { 1514 rval = cv_wait_sig(&usbskelp->usbskel_serial_cv, 1515 &usbskelp->usbskel_mutex); 1516 } else { 1517 cv_wait(&usbskelp->usbskel_serial_cv, 1518 &usbskelp->usbskel_mutex); 1519 } 1520 } 1521 usbskelp->usbskel_serial_inuse = B_TRUE; 1522 1523 return (rval); 1524 } 1525 1526 1527 /* 1528 * usbskel_release_access: 1529 * Release the serial synchronization object. 1530 */ 1531 static void 1532 usbskel_release_access(usbskel_state_t *usbskelp) 1533 { 1534 ASSERT(mutex_owned(&usbskelp->usbskel_mutex)); 1535 usbskelp->usbskel_serial_inuse = B_FALSE; 1536 cv_broadcast(&usbskelp->usbskel_serial_cv); 1537 } 1538 1539 1540 /* 1541 * usbskel_check_same_device: 1542 * Check if the device connected to the port is the same as 1543 * the previous device that was in the port. The previous device is 1544 * represented by the dip on record for the port. Print a message 1545 * if the device is different. Can block. 1546 * 1547 * return values: 1548 * USB_SUCCESS: same device 1549 * USB_INVALID_VERSION not same device 1550 * USB_FAILURE: Failure processing request 1551 */ 1552 static int 1553 usbskel_check_same_device(usbskel_state_t *usbskelp) 1554 { 1555 usb_dev_descr_t *orig_usb_dev_descr; 1556 usb_dev_descr_t usb_dev_descr; 1557 mblk_t *pdata = NULL; 1558 int rval; 1559 char *buf; 1560 usb_cr_t completion_reason; 1561 usb_cb_flags_t cb_flags; 1562 boolean_t match = B_TRUE; 1563 1564 usb_ctrl_setup_t setup = { 1565 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD | 1566 USB_DEV_REQ_RCPT_DEV, 1567 USB_REQ_GET_DESCR, /* bRequest */ 1568 USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 1569 0, /* wIndex */ 1570 USB_DEV_DESCR_SIZE, /* wLength */ 1571 0 /* request attributes */ 1572 }; 1573 1574 ASSERT(!mutex_owned(&usbskelp->usbskel_mutex)); 1575 1576 orig_usb_dev_descr = usbskelp->usbskel_reg->dev_descr; 1577 1578 /* get the "new" device descriptor */ 1579 rval = usb_pipe_ctrl_xfer_wait(usbskelp->usbskel_reg->dev_default_ph, 1580 &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); 1581 1582 if (rval != USB_SUCCESS) { 1583 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1584 "usbskel_check_same_device: " 1585 "getting device descriptor failed " 1586 "rval=%d, cr=%d, cb=0x%x\n", 1587 rval, completion_reason, cb_flags); 1588 freemsg(pdata); 1589 1590 return (USB_FAILURE); 1591 } 1592 1593 ASSERT(pdata != NULL); 1594 1595 (void) usb_parse_data("2cs4c3s4c", pdata->b_rptr, 1596 pdata->b_wptr - pdata->b_rptr, &usb_dev_descr, 1597 sizeof (usb_dev_descr_t)); 1598 1599 freemsg(pdata); 1600 pdata = NULL; 1601 1602 /* Always check the device descriptor length. */ 1603 if (usb_dev_descr.bLength != USB_DEV_DESCR_SIZE) { 1604 match = B_FALSE; 1605 1606 /* Always check the device descriptor. */ 1607 } else if (bcmp(orig_usb_dev_descr, 1608 (char *)&usb_dev_descr, USB_DEV_DESCR_SIZE) != 0) { 1609 match = B_FALSE; 1610 } 1611 1612 /* if requested & this device has a serial number check and compare */ 1613 if ((match == B_TRUE) && 1614 (usbskelp->usbskel_reg->dev_serial != NULL)) { 1615 buf = kmem_alloc(USB_MAXSTRINGLEN, KM_SLEEP); 1616 if (usb_get_string_descr(usbskelp->usbskel_dip, USB_LANG_ID, 1617 usb_dev_descr.iSerialNumber, buf, 1618 USB_MAXSTRINGLEN) == USB_SUCCESS) { 1619 match = 1620 (strcmp(buf, 1621 usbskelp->usbskel_reg->dev_serial) == 0); 1622 } 1623 kmem_free(buf, USB_MAXSTRINGLEN); 1624 } 1625 1626 if (match == B_FALSE) { 1627 usbskel_log(usbskelp, USBSKEL_LOG_CONSOLE, 1628 "Device is not identical to the " 1629 "previous one this port.\n" 1630 "Please disconnect and reconnect"); 1631 1632 return (USB_INVALID_VERSION); 1633 } 1634 1635 return (USB_SUCCESS); 1636 } 1637 1638 /* 1639 * usbskel_log: 1640 * Switchable logging to logfile and screen. 1641 * 1642 * Arguments: 1643 * usbskelp: usbskel state pointer. 1644 * if NULL, driver name and instance won't print with the message 1645 * msglevel: 1646 * if USBSKEL_LOG_LOG, goes only to logfile. 1647 * (usbskel_errlevel must be set to USBSKEL_LOG_LOG too.) 1648 * if USBSKEL_LOG_CONSOLE, goes to both logfile and screen 1649 * (usbskel_errlevel can be either value for this to work.) 1650 * cmn_err_level: error level passed to cmn_err(9F) 1651 * format and args: as you would call cmn_err, except without special 1652 * first routing character. 1653 * 1654 * Do not call this in an interrupt context, since kmem_alloc can sleep. 1655 */ 1656 static void 1657 usbskel_log(usbskel_state_t *usbskelp, int msglevel, char *formatarg, ...) 1658 { 1659 va_list ap; 1660 1661 if (msglevel <= usbskel_errlevel) { 1662 char *format; 1663 int formatlen = strlen(formatarg) + 2; /* '!' and NULL char */ 1664 int devinst_start = 0; 1665 1666 /* Allocate extra room if driver name and instance is present */ 1667 if (usbskelp != NULL) { 1668 formatlen += strlen(usbskelp->usbskel_devinst); 1669 } 1670 1671 format = kmem_zalloc(formatlen, KM_SLEEP); 1672 1673 if (msglevel == USBSKEL_LOG_LOG) { 1674 format[0] = '!'; 1675 devinst_start = 1; 1676 } 1677 1678 if (usbskelp != NULL) { 1679 (void) strcpy(&format[devinst_start], 1680 usbskelp->usbskel_devinst); 1681 } 1682 1683 va_start(ap, formatarg); 1684 (void) strcat(format, formatarg); 1685 vcmn_err(CE_CONT, format, ap); 1686 va_end(ap); 1687 1688 kmem_free(format, formatlen); 1689 } 1690 } 1691