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