/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WUSB cable association driver * * This driver implements the cable association mechanism defined in * "Association Models Supplement to the Certified Wireless Universal * Serial Bus Specification 1.0". Cable association compliant devices, * i.e. with bInterfaceClass=0xEF, bInterfaceSubClass=0x03 and * bInterfaceProtocol=0x01(compatible names: "usbif,classef.3.1") will * be supported by this driver. * * The Cable Association Model uses the USB Cable-Based Association * Framework (CBAF). The basic operation under this framework is: * * - The user connects the device to the host using a USB cable. * * - The host detects that the device supports the CBAF and it is capable * of configuring WUSB CC(Connection Context). * * - The host sends its CHID to the device, along with other information * * - If the device has a valid CC with a matching CHID, the device will * respond to the host with the CDID from the CC. * * - As of this driver implementation, no matter if the CDID returned * from the device matches the CDID in the host's copy of CC, we choose * to skip further explicit user conditioning, generate a new CC and send * it to the device. * * - Upon receiving the CC, the device must store the CC in non-volatile * memory, replacing any existing CC with a matching CHID if it exists. * * - First time association is complete: Host has securely transferred the CC * to the device * * CBAF requires device to use the default control endpoint to exchange * requests and data with host. Three control requests are defined by spec * and supported by this driver: * - GET_ASSOCIATION_INFORMATION * - GET_ASSOCIATION_REQUEST * - SET_ASSOCIATION_RESPONSE * */ #if defined(lint) && !defined(DEBUG) #define DEBUG #endif #define USBDRV_MAJOR_VER 2 #define USBDRV_MINOR_VER 0 #include #include #include #include #include uint_t wusb_ca_errlevel = USB_LOG_L4; uint_t wusb_ca_errmask = (uint_t)PRINT_MASK_ALL; uint_t wusb_ca_instance_debug = (uint_t)-1; /* * Function Prototypes */ static int wusb_ca_attach(dev_info_t *, ddi_attach_cmd_t); static int wusb_ca_detach(dev_info_t *, ddi_detach_cmd_t); static int wusb_ca_info(dev_info_t *, ddi_info_cmd_t, void *, void **); static int wusb_ca_cleanup(dev_info_t *, wusb_ca_state_t *); static int wusb_ca_open(dev_t *, int, int, cred_t *); static int wusb_ca_close(dev_t, int, int, cred_t *); static int wusb_ca_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); static int wusb_ca_disconnect_callback(dev_info_t *); static int wusb_ca_reconnect_callback(dev_info_t *); static void wusb_ca_restore_device_state(dev_info_t *, wusb_ca_state_t *); static int wusb_ca_cpr_suspend(dev_info_t *); static void wusb_ca_cpr_resume(dev_info_t *); static int wusb_ca_pm_busy_component(wusb_ca_state_t *); static void wusb_ca_pm_idle_component(wusb_ca_state_t *); static int wusb_ca_power(dev_info_t *, int, int); static void wusb_ca_init_power_mgmt(wusb_ca_state_t *); static void wusb_ca_destroy_power_mgmt(wusb_ca_state_t *); static int wusb_ca_serialize_access(wusb_ca_state_t *, boolean_t); static void wusb_ca_release_access(wusb_ca_state_t *); static int wusb_ca_check_same_device(wusb_ca_state_t *); static mblk_t *trans_from_host_info(wusb_cbaf_host_info_t *); static mblk_t *trans_from_cc_data(wusb_cbaf_cc_data_t *); static mblk_t *trans_from_cc_fail(wusb_cbaf_cc_fail_t *); static int trans_to_device_info(wusb_ca_state_t *, mblk_t *, wusb_cbaf_device_info_t *); /* _NOTE is an advice for locklint. Locklint checks lock use for deadlocks. */ _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_ctrl_req)) _NOTE(SCHEME_PROTECTS_DATA("unique per call", buf)) _NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb)) /* module loading stuff */ struct cb_ops wusb_ca_cb_ops = { wusb_ca_open, /* open */ wusb_ca_close, /* close */ nulldev, /* strategy */ nulldev, /* print */ nulldev, /* dump */ nodev, /* read */ nodev, /* write */ wusb_ca_ioctl, /* ioctl */ nulldev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* cb_prop_op */ NULL, /* streamtab */ D_MP }; static struct dev_ops wusb_ca_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ wusb_ca_info, /* info */ nulldev, /* identify */ nulldev, /* probe */ wusb_ca_attach, /* attach */ wusb_ca_detach, /* detach */ nodev, /* reset */ &wusb_ca_cb_ops, /* driver operations */ NULL, /* bus operations */ wusb_ca_power, /* power */ ddi_quiesce_not_needed, /* quiesce */ }; static struct modldrv wusb_ca_modldrv = { &mod_driverops, "WUSB Cable Association driver", &wusb_ca_ops }; static struct modlinkage modlinkage = { MODREV_1, &wusb_ca_modldrv, NULL }; /* local variables */ /* Soft state structures */ #define WUSB_CA_INITIAL_SOFT_SPACE 1 static void *wusb_ca_statep; /* * Module-wide initialization routine. */ int _init(void) { int rval; if ((rval = ddi_soft_state_init(&wusb_ca_statep, sizeof (wusb_ca_state_t), WUSB_CA_INITIAL_SOFT_SPACE)) != 0) { return (rval); } if ((rval = mod_install(&modlinkage)) != 0) { ddi_soft_state_fini(&wusb_ca_statep); } return (rval); } /* * Module-wide tear-down routine. */ int _fini(void) { int rval; if ((rval = mod_remove(&modlinkage)) != 0) { return (rval); } ddi_soft_state_fini(&wusb_ca_statep); return (rval); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * wusb_ca_info: * Get minor number, soft state structure, etc. */ /*ARGSUSED*/ static int wusb_ca_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { wusb_ca_state_t *wusb_cap; int error = DDI_FAILURE; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if ((wusb_cap = ddi_get_soft_state(wusb_ca_statep, getminor((dev_t)arg))) != NULL) { *result = wusb_cap->wusb_ca_dip; if (*result != NULL) { error = DDI_SUCCESS; } } else { *result = NULL; } break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)(uintptr_t)getminor((dev_t)arg); error = DDI_SUCCESS; break; default: break; } return (error); } /* * wusb_ca_attach: * Attach or resume. * * For attach, initialize state and device, including: * state variables, locks, device node * device registration with system * power management, hotplugging * For resume, restore device and state */ static int wusb_ca_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance = ddi_get_instance(dip); char *devinst; int devinstlen; wusb_ca_state_t *wusb_cap = NULL; int status; switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: wusb_ca_cpr_resume(dip); /* * Always return success to work around enumeration failures. * This works around an issue where devices which are present * before a suspend and absent upon resume could cause a system * panic on resume. */ return (DDI_SUCCESS); default: return (DDI_FAILURE); } if (ddi_soft_state_zalloc(wusb_ca_statep, instance) == DDI_SUCCESS) { wusb_cap = ddi_get_soft_state(wusb_ca_statep, instance); } if (wusb_cap == NULL) { return (DDI_FAILURE); } wusb_cap->wusb_ca_dip = dip; devinst = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); devinstlen = snprintf(devinst, USB_MAXSTRINGLEN, "%s%d: ", ddi_driver_name(dip), instance); wusb_cap->wusb_ca_devinst = kmem_zalloc(devinstlen + 1, KM_SLEEP); (void) strncpy(wusb_cap->wusb_ca_devinst, devinst, devinstlen); kmem_free(devinst, USB_MAXSTRINGLEN); wusb_cap->wusb_ca_log_hdl = usb_alloc_log_hdl(dip, "wusb_ca", &wusb_ca_errlevel, &wusb_ca_errmask, &wusb_ca_instance_debug, 0); USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "Attach: enter for attach"); if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "attach: usb_client_attach failed, error code:%d", status); goto fail; } if ((status = usb_get_dev_data(dip, &wusb_cap->wusb_ca_reg, USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "attach: usb_get_dev_data failed, error code:%d", status); goto fail; } usb_free_descr_tree(dip, wusb_cap->wusb_ca_reg); mutex_init(&wusb_cap->wusb_ca_mutex, NULL, MUTEX_DRIVER, wusb_cap->wusb_ca_reg->dev_iblock_cookie); cv_init(&wusb_cap->wusb_ca_serial_cv, NULL, CV_DRIVER, NULL); wusb_cap->wusb_ca_serial_inuse = B_FALSE; wusb_cap->wusb_ca_locks_initialized = B_TRUE; /* create minor node */ if (ddi_create_minor_node(dip, "wusb_ca", S_IFCHR, instance, "wusb_ca", 0) != DDI_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "attach: Error creating minor node"); goto fail; } /* Put online before PM init as can get power managed afterward. */ wusb_cap->wusb_ca_dev_state = USB_DEV_ONLINE; /* initialize power management */ wusb_ca_init_power_mgmt(wusb_cap); if (usb_register_hotplug_cbs(dip, wusb_ca_disconnect_callback, wusb_ca_reconnect_callback) != USB_SUCCESS) { goto fail; } /* Report device */ ddi_report_dev(dip); return (DDI_SUCCESS); fail: if (wusb_cap) { (void) wusb_ca_cleanup(dip, wusb_cap); } return (DDI_FAILURE); } /* * wusb_ca_detach: * detach or suspend driver instance * * Note: in detach, only contention threads is from pm and disconnnect. */ static int wusb_ca_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance = ddi_get_instance(dip); int rval = DDI_FAILURE; wusb_ca_state_t *wusb_cap; wusb_cap = ddi_get_soft_state(wusb_ca_statep, instance); switch (cmd) { case DDI_DETACH: mutex_enter(&wusb_cap->wusb_ca_mutex); ASSERT((wusb_cap->wusb_ca_drv_state & WUSB_CA_OPEN) == 0); mutex_exit(&wusb_cap->wusb_ca_mutex); USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "Detach: enter for detach"); rval = wusb_ca_cleanup(dip, wusb_cap); break; case DDI_SUSPEND: USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "Detach: enter for suspend"); rval = wusb_ca_cpr_suspend(dip); default: break; } return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); } /* * wusb_ca_cleanup: * clean up the driver state for detach */ static int wusb_ca_cleanup(dev_info_t *dip, wusb_ca_state_t *wusb_cap) { USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_cap->wusb_ca_log_hdl, "Cleanup: enter"); if (wusb_cap->wusb_ca_locks_initialized) { /* This must be done 1st to prevent more events from coming. */ usb_unregister_hotplug_cbs(dip); /* * At this point, no new activity can be initiated. The driver * has disabled hotplug callbacks. The Solaris framework has * disabled new opens on a device being detached, and does not * allow detaching an open device. * * The following ensures that all driver activity has drained. */ mutex_enter(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_NOSIG); wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); /* All device activity has died down. */ wusb_ca_destroy_power_mgmt(wusb_cap); /* start dismantling */ ddi_remove_minor_node(dip, NULL); cv_destroy(&wusb_cap->wusb_ca_serial_cv); mutex_destroy(&wusb_cap->wusb_ca_mutex); } usb_client_detach(dip, wusb_cap->wusb_ca_reg); usb_free_log_hdl(wusb_cap->wusb_ca_log_hdl); if (wusb_cap->wusb_ca_devinst != NULL) { kmem_free(wusb_cap->wusb_ca_devinst, strlen(wusb_cap->wusb_ca_devinst) + 1); } ddi_soft_state_free(wusb_ca_statep, ddi_get_instance(dip)); ddi_prop_remove_all(dip); return (USB_SUCCESS); } /*ARGSUSED*/ static int wusb_ca_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) { wusb_ca_state_t *wusb_cap = ddi_get_soft_state(wusb_ca_statep, getminor(*devp)); int rval = 0; if (wusb_cap == NULL) { return (ENXIO); } USB_DPRINTF_L4(PRINT_MASK_OPEN, wusb_cap->wusb_ca_log_hdl, "open: enter"); /* * Keep it simple: one client at a time. * Exclusive open only */ mutex_enter(&wusb_cap->wusb_ca_mutex); if ((wusb_cap->wusb_ca_drv_state & WUSB_CA_OPEN) != 0) { mutex_exit(&wusb_cap->wusb_ca_mutex); return (EBUSY); } wusb_cap->wusb_ca_drv_state |= WUSB_CA_OPEN; /* * This is in place so that a disconnect or CPR doesn't interfere with * pipe opens. */ if (wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_SIG) == 0) { wusb_cap->wusb_ca_drv_state &= ~WUSB_CA_OPEN; mutex_exit(&wusb_cap->wusb_ca_mutex); return (EINTR); } mutex_exit(&wusb_cap->wusb_ca_mutex); if (wusb_ca_pm_busy_component(wusb_cap) != DDI_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_OPEN, wusb_cap->wusb_ca_log_hdl, "open: Error raising power"); rval = EIO; goto done; } mutex_enter(&wusb_cap->wusb_ca_mutex); /* Fail if device is no longer ready. */ if (wusb_cap->wusb_ca_dev_state != USB_DEV_ONLINE) { mutex_exit(&wusb_cap->wusb_ca_mutex); rval = EIO; goto done; } mutex_exit(&wusb_cap->wusb_ca_mutex); done: if (rval != 0) { mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_cap->wusb_ca_drv_state &= ~WUSB_CA_OPEN; wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); wusb_ca_pm_idle_component(wusb_cap); } else { /* Device is idle until it is used. */ mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); } return (rval); } /*ARGSUSED*/ static int wusb_ca_close(dev_t dev, int flag, int otyp, cred_t *cred_p) { wusb_ca_state_t *wusb_cap = ddi_get_soft_state(wusb_ca_statep, getminor(dev)); USB_DPRINTF_L4(PRINT_MASK_CLOSE, wusb_cap->wusb_ca_log_hdl, "close: enter"); mutex_enter(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_NOSIG); mutex_exit(&wusb_cap->wusb_ca_mutex); /* Perform device session cleanup here. */ USB_DPRINTF_L4(PRINT_MASK_CLOSE, wusb_cap->wusb_ca_log_hdl, "close: cleaning up..."); /* * USBA automatically flushes/resets active non-default pipes * when they are closed. We can't reset default pipe, but we * can wait for all requests on it from this dip to drain. */ (void) usb_pipe_drain_reqs(wusb_cap->wusb_ca_dip, wusb_cap->wusb_ca_reg->dev_default_ph, 0, USB_FLAGS_SLEEP, NULL, 0); mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_cap->wusb_ca_drv_state &= ~WUSB_CA_OPEN; wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); wusb_ca_pm_idle_component(wusb_cap); return (0); } /* * ioctl for cable association operations. */ /*ARGSUSED*/ static int wusb_ca_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) { wusb_ca_state_t *wusb_cap = ddi_get_soft_state(wusb_ca_statep, getminor(dev)); USB_DPRINTF_L4(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ioctl enter"); mutex_enter(&wusb_cap->wusb_ca_mutex); switch (cmd) { case CBAF_IOCTL_GET_ASSO_INFO: *rval_p = wusb_cbaf_get_asso_info(wusb_cap, arg, mode); break; case CBAF_IOCTL_GET_ASSO_REQS: *rval_p = wusb_cbaf_get_asso_reqs(wusb_cap, arg, mode); break; case CBAF_IOCTL_SET_HOST_INFO: *rval_p = wusb_cbaf_set_host_info(wusb_cap, arg, mode); break; case CBAF_IOCTL_GET_DEVICE_INFO: *rval_p = wusb_cbaf_get_device_info(wusb_cap, arg, mode); break; case CBAF_IOCTL_SET_CONNECTION: *rval_p = wusb_cbaf_set_connection(wusb_cap, arg, mode); break; case CBAF_IOCTL_SET_FAILURE: *rval_p = wusb_cbaf_set_failure(wusb_cap, arg, mode); break; default: *rval_p = EINVAL; } mutex_exit(&wusb_cap->wusb_ca_mutex); return (*rval_p); } /* * wusb_ca_disconnect_callback: * Called when device hotplug-removed. * Close pipes. (This does not attempt to contact device.) * Set state to DISCONNECTED */ static int wusb_ca_disconnect_callback(dev_info_t *dip) { int instance = ddi_get_instance(dip); wusb_ca_state_t *wusb_cap; wusb_cap = ddi_get_soft_state(wusb_ca_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CB, wusb_cap->wusb_ca_log_hdl, "disconnect: enter"); mutex_enter(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_NOSIG); /* * Save any state of device or IO in progress required by * wusb_ca_restore_device_state for proper device "thawing" later. */ wusb_cap->wusb_ca_dev_state = USB_DEV_DISCONNECTED; wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); return (USB_SUCCESS); } /* * wusb_ca_reconnect_callback: * Called with device hotplug-inserted * Restore state */ static int wusb_ca_reconnect_callback(dev_info_t *dip) { int instance = ddi_get_instance(dip); wusb_ca_state_t *wusb_cap; wusb_cap = ddi_get_soft_state(wusb_ca_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CB, wusb_cap->wusb_ca_log_hdl, "reconnect: enter"); mutex_enter(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_NOSIG); wusb_ca_restore_device_state(dip, wusb_cap); wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); return (USB_SUCCESS); } /* * wusb_ca_restore_device_state: * Called during hotplug-reconnect and resume. * reenable power management * Verify the device is the same as before the disconnect/suspend. * Restore device state * Thaw any IO which was frozen. * Quiesce device. (Other routines will activate if thawed IO.) * Set device online. * Leave device disconnected if there are problems. */ static void wusb_ca_restore_device_state(dev_info_t *dip, wusb_ca_state_t *wusb_cap) { USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "wusb_ca_restore_device_state: enter"); ASSERT(mutex_owned(&wusb_cap->wusb_ca_mutex)); ASSERT((wusb_cap->wusb_ca_dev_state == USB_DEV_DISCONNECTED) || (wusb_cap->wusb_ca_dev_state == USB_DEV_SUSPENDED)); mutex_exit(&wusb_cap->wusb_ca_mutex); if (wusb_ca_pm_busy_component(wusb_cap) != DDI_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "wusb_ca_restore_device_state: Error raising power"); goto fail; } /* Check if we are talking to the same device */ if (wusb_ca_check_same_device(wusb_cap) != USB_SUCCESS) { wusb_ca_pm_idle_component(wusb_cap); goto fail; } mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_cap->wusb_ca_dev_state = USB_DEV_ONLINE; if ((wusb_cap->wusb_ca_pm) && (wusb_cap->wusb_ca_pm->wusb_ca_remote_wakeup)) { mutex_exit(&wusb_cap->wusb_ca_mutex); /* Failure here means device disappeared again. */ if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "device may or may not be accessible. " "Please verify reconnection"); } mutex_enter(&wusb_cap->wusb_ca_mutex); } mutex_exit(&wusb_cap->wusb_ca_mutex); wusb_ca_pm_idle_component(wusb_cap); mutex_enter(&wusb_cap->wusb_ca_mutex); USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "wusb_ca_restore_device_state: end"); return; fail: /* change the device state from suspended to disconnected */ mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_cap->wusb_ca_dev_state = USB_DEV_DISCONNECTED; } /* * wusb_ca_cpr_suspend: * Clean up device. * Wait for any IO to finish, then close pipes. * Quiesce device. */ static int wusb_ca_cpr_suspend(dev_info_t *dip) { int instance = ddi_get_instance(dip); wusb_ca_state_t *wusb_cap; wusb_cap = ddi_get_soft_state(wusb_ca_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "suspend enter"); /* Serialize to prevent races with detach, open, device access. */ mutex_enter(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_NOSIG); mutex_exit(&wusb_cap->wusb_ca_mutex); if (wusb_ca_pm_busy_component(wusb_cap) != DDI_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "suspend: Error raising power"); wusb_ca_pm_idle_component(wusb_cap); return (USB_FAILURE); } mutex_enter(&wusb_cap->wusb_ca_mutex); /* * Set dev_state to suspended so other driver threads don't start any * new I/O. */ /* Don't suspend if the device is open. */ if ((wusb_cap->wusb_ca_drv_state & WUSB_CA_OPEN) != 0) { USB_DPRINTF_L2(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "suspend: Device is open. Can't suspend"); wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); wusb_ca_pm_idle_component(wusb_cap); return (USB_FAILURE); } wusb_cap->wusb_ca_dev_state = USB_DEV_SUSPENDED; wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); wusb_ca_pm_idle_component(wusb_cap); USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "suspend: success"); return (USB_SUCCESS); } /* * wusb_ca_cpr_resume: * * wusb_ca_restore_device_state marks success by putting device back online */ static void wusb_ca_cpr_resume(dev_info_t *dip) { int instance = ddi_get_instance(dip); wusb_ca_state_t *wusb_cap; wusb_cap = ddi_get_soft_state(wusb_ca_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_cap->wusb_ca_log_hdl, "resume: enter"); /* * A pm_raise_power in wusb_ca_restore_device_state will bring * the power-up state of device into synch with the system. */ mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_ca_restore_device_state(dip, wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); } static int wusb_ca_pm_busy_component(wusb_ca_state_t *wusb_cap) { int rval = DDI_SUCCESS; mutex_enter(&wusb_cap->wusb_ca_mutex); if (wusb_cap->wusb_ca_pm != NULL) { wusb_cap->wusb_ca_pm->wusb_ca_pm_busy++; mutex_exit(&wusb_cap->wusb_ca_mutex); if (pm_busy_component(wusb_cap->wusb_ca_dip, 0) == DDI_SUCCESS) { (void) pm_raise_power( wusb_cap->wusb_ca_dip, 0, USB_DEV_OS_FULL_PWR); mutex_enter(&wusb_cap->wusb_ca_mutex); } else { mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_cap->wusb_ca_pm->wusb_ca_pm_busy--; } } mutex_exit(&wusb_cap->wusb_ca_mutex); return (rval); } static void wusb_ca_pm_idle_component(wusb_ca_state_t *wusb_cap) { mutex_enter(&wusb_cap->wusb_ca_mutex); if (wusb_cap->wusb_ca_pm != NULL) { mutex_exit(&wusb_cap->wusb_ca_mutex); if (pm_idle_component(wusb_cap->wusb_ca_dip, 0) == DDI_SUCCESS) { mutex_enter(&wusb_cap->wusb_ca_mutex); ASSERT(wusb_cap->wusb_ca_pm->wusb_ca_pm_busy > 0); wusb_cap->wusb_ca_pm->wusb_ca_pm_busy--; mutex_exit(&wusb_cap->wusb_ca_mutex); } mutex_enter(&wusb_cap->wusb_ca_mutex); USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_pm_idle_component: %d", wusb_cap->wusb_ca_pm->wusb_ca_pm_busy); } mutex_exit(&wusb_cap->wusb_ca_mutex); } /* * wusb_ca_power : * Power entry point, the workhorse behind pm_raise_power, pm_lower_power, * usb_req_raise_power and usb_req_lower_power. */ /* ARGSUSED */ static int wusb_ca_power(dev_info_t *dip, int comp, int level) { wusb_ca_state_t *wusb_cap; wusb_ca_power_t *pm; int rval = USB_SUCCESS; wusb_cap = ddi_get_soft_state(wusb_ca_statep, ddi_get_instance(dip)); USB_DPRINTF_L4(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_power: enter: level = %d", level); mutex_enter(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_serialize_access(wusb_cap, WUSB_CA_SER_NOSIG); /* * If we are disconnected/suspended, return success. Note that if we * return failure, bringing down the system will hang when PM tries * to power up all devices */ if ((wusb_cap->wusb_ca_dev_state == USB_DEV_DISCONNECTED) || (wusb_cap->wusb_ca_dev_state == USB_DEV_SUSPENDED)) { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_power: disconnected/suspended " "dev_state=%d", wusb_cap->wusb_ca_dev_state); rval = USB_SUCCESS; goto done; } if (wusb_cap->wusb_ca_pm == NULL) { goto done; } pm = wusb_cap->wusb_ca_pm; /* Check if we are transitioning to a legal power level */ if (USB_DEV_PWRSTATE_OK(pm->wusb_ca_pwr_states, level)) { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_power: illegal power level = %d " "pwr_states: %x", level, pm->wusb_ca_pwr_states); goto done; } switch (level) { case USB_DEV_OS_PWR_OFF : /* fail attempt to go to low power if busy */ if (pm->wusb_ca_pm_busy) { goto done; } if (wusb_cap->wusb_ca_dev_state == USB_DEV_ONLINE) { (void) usb_set_device_pwrlvl3(wusb_cap->wusb_ca_dip); wusb_cap->wusb_ca_dev_state = USB_DEV_PWRED_DOWN; wusb_cap->wusb_ca_pm->wusb_ca_current_power = USB_DEV_OS_PWR_OFF; } break; case USB_DEV_OS_FULL_PWR : /* * PM framework tries to put us in full power during system * shutdown. Handle USB_DEV_PWRED_DOWN only. */ if (wusb_cap->wusb_ca_dev_state == USB_DEV_PWRED_DOWN) { (void) usb_set_device_pwrlvl0(wusb_cap->wusb_ca_dip); wusb_cap->wusb_ca_dev_state = USB_DEV_ONLINE; wusb_cap->wusb_ca_pm->wusb_ca_current_power = USB_DEV_OS_FULL_PWR; } break; /* Levels 1 and 2 are not supported by this driver to keep it simple. */ default: USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_power: power level %d not supported", level); break; } done: wusb_ca_release_access(wusb_cap); mutex_exit(&wusb_cap->wusb_ca_mutex); /* Generally return success to make PM succeed */ return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); } /* * wusb_ca_init_power_mgmt: * Initialize power management and remote wakeup functionality. * No mutex is necessary in this function as it's called only by attach. */ static void wusb_ca_init_power_mgmt(wusb_ca_state_t *wusb_cap) { wusb_ca_power_t *wusb_capm; uint_t pwr_states; USB_DPRINTF_L4(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "init_power_mgmt enter"); /* Allocate the state structure */ wusb_capm = kmem_zalloc(sizeof (wusb_ca_power_t), KM_SLEEP); wusb_cap->wusb_ca_pm = wusb_capm; wusb_capm->wusb_ca_state = wusb_cap; wusb_capm->wusb_ca_pm_capabilities = 0; wusb_capm->wusb_ca_current_power = USB_DEV_OS_FULL_PWR; if (usb_create_pm_components(wusb_cap->wusb_ca_dip, &pwr_states) == USB_SUCCESS) { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_init_power_mgmt: created PM components"); wusb_capm->wusb_ca_pwr_states = (uint8_t)pwr_states; (void) pm_raise_power(wusb_cap->wusb_ca_dip, 0, USB_DEV_OS_FULL_PWR); } else { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_init_power_mgmt: create_pm_compts failed"); } /* * If remote wakeup is not available you may not want to do * power management. */ if (usb_handle_remote_wakeup(wusb_cap->wusb_ca_dip, USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { wusb_capm->wusb_ca_remote_wakeup = 1; } else { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_init_power_mgmt: failure enabling remote wakeup"); } USB_DPRINTF_L4(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_init_power_mgmt: end"); } /* * wusb_ca_destroy_power_mgmt: * Shut down and destroy power management and remote wakeup functionality. */ static void wusb_ca_destroy_power_mgmt(wusb_ca_state_t *wusb_cap) { wusb_ca_power_t *wusb_capm; USB_DPRINTF_L4(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "destroy_power_mgmt enter"); ASSERT(!mutex_owned(&wusb_cap->wusb_ca_mutex)); mutex_enter(&wusb_cap->wusb_ca_mutex); wusb_capm = wusb_cap->wusb_ca_pm; if (!wusb_capm) { mutex_exit(&wusb_cap->wusb_ca_mutex); return; } mutex_exit(&wusb_cap->wusb_ca_mutex); (void) wusb_ca_pm_busy_component(wusb_cap); mutex_enter(&wusb_cap->wusb_ca_mutex); if (wusb_cap->wusb_ca_dev_state != USB_DEV_DISCONNECTED) { int rval; if (wusb_capm->wusb_ca_remote_wakeup) { mutex_exit(&wusb_cap->wusb_ca_mutex); (void) pm_raise_power(wusb_cap->wusb_ca_dip, 0, USB_DEV_OS_FULL_PWR); rval = usb_handle_remote_wakeup( wusb_cap->wusb_ca_dip, USB_REMOTE_WAKEUP_DISABLE); USB_DPRINTF_L2(PRINT_MASK_PM, wusb_cap->wusb_ca_log_hdl, "wusb_ca_destroy_power_mgmt: " "Error disabling rmt wakeup: rval = %d", rval); mutex_enter(&wusb_cap->wusb_ca_mutex); } } mutex_exit(&wusb_cap->wusb_ca_mutex); /* * Since remote wakeup is disabled now, * no one can raise power * and get to device once power is lowered here. */ (void) pm_lower_power(wusb_cap->wusb_ca_dip, 0, USB_DEV_OS_PWR_OFF); wusb_ca_pm_idle_component(wusb_cap); mutex_enter(&wusb_cap->wusb_ca_mutex); kmem_free(wusb_cap->wusb_ca_pm, sizeof (wusb_ca_power_t)); wusb_cap->wusb_ca_pm = NULL; mutex_exit(&wusb_cap->wusb_ca_mutex); } /* * wusb_ca_serialize_access: * Get the serial synchronization object before returning. * * Arguments: * wusb_cap - Pointer to wusb_ca state structure * waitsig - Set to: * WUSB_CA_SER_SIG - to wait such that a signal can interrupt * WUSB_CA_SER_NOSIG - to wait such that a signal cannot interrupt */ static int wusb_ca_serialize_access(wusb_ca_state_t *wusb_cap, boolean_t waitsig) { int rval = 1; ASSERT(mutex_owned(&wusb_cap->wusb_ca_mutex)); while (wusb_cap->wusb_ca_serial_inuse) { if (waitsig == WUSB_CA_SER_SIG) { rval = cv_wait_sig(&wusb_cap->wusb_ca_serial_cv, &wusb_cap->wusb_ca_mutex); } else { cv_wait(&wusb_cap->wusb_ca_serial_cv, &wusb_cap->wusb_ca_mutex); } } wusb_cap->wusb_ca_serial_inuse = B_TRUE; return (rval); } /* * wusb_ca_release_access: * Release the serial synchronization object. */ static void wusb_ca_release_access(wusb_ca_state_t *wusb_cap) { ASSERT(mutex_owned(&wusb_cap->wusb_ca_mutex)); wusb_cap->wusb_ca_serial_inuse = B_FALSE; cv_broadcast(&wusb_cap->wusb_ca_serial_cv); } /* * wusb_ca_check_same_device: * Check if the device connected to the port is the same as * the previous device that was in the port. The previous device is * represented by the dip on record for the port. Print a message * if the device is different. Can block. * * return values: * USB_SUCCESS: same device * USB_INVALID_VERSION not same device * USB_FAILURE: Failure processing request */ static int wusb_ca_check_same_device(wusb_ca_state_t *wusb_cap) { usb_dev_descr_t *orig_usb_dev_descr; usb_dev_descr_t usb_dev_descr; mblk_t *pdata = NULL; int rval; char *buf; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; boolean_t match = B_TRUE; usb_ctrl_setup_t setup = { USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD | USB_DEV_REQ_RCPT_DEV, USB_REQ_GET_DESCR, /* bRequest */ USB_DESCR_TYPE_SETUP_DEV, /* wValue */ 0, /* wIndex */ USB_DEV_DESCR_SIZE, /* wLength */ 0 /* request attributes */ }; ASSERT(!mutex_owned(&wusb_cap->wusb_ca_mutex)); orig_usb_dev_descr = wusb_cap->wusb_ca_reg->dev_descr; /* get the "new" device descriptor */ rval = usb_pipe_ctrl_xfer_wait(wusb_cap->wusb_ca_reg->dev_default_ph, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "wusb_ca_check_same_device: " "getting device descriptor failed " "rval=%d, cr=%d, cb=0x%x\n", rval, completion_reason, cb_flags); freemsg(pdata); return (USB_FAILURE); } ASSERT(pdata != NULL); (void) usb_parse_data("2cs4c3s4c", pdata->b_rptr, (intptr_t)pdata->b_wptr - (intptr_t)pdata->b_rptr, &usb_dev_descr, sizeof (usb_dev_descr_t)); freemsg(pdata); pdata = NULL; /* Always check the device descriptor length. */ if (usb_dev_descr.bLength != USB_DEV_DESCR_SIZE) { match = B_FALSE; /* Always check the device descriptor. */ } else if (bcmp(orig_usb_dev_descr, (char *)&usb_dev_descr, USB_DEV_DESCR_SIZE) != 0) { match = B_FALSE; } /* if requested & this device has a serial number check and compare */ if ((match == B_TRUE) && (wusb_cap->wusb_ca_reg->dev_serial != NULL)) { buf = kmem_alloc(USB_MAXSTRINGLEN, KM_SLEEP); if (usb_get_string_descr(wusb_cap->wusb_ca_dip, USB_LANG_ID, usb_dev_descr.iSerialNumber, buf, USB_MAXSTRINGLEN) == USB_SUCCESS) { match = (strcmp(buf, wusb_cap->wusb_ca_reg->dev_serial) == 0); } kmem_free(buf, USB_MAXSTRINGLEN); } if (match == B_FALSE) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "Device is not identical to the " "previous one this port.\n" "Please disconnect and reconnect"); return (USB_INVALID_VERSION); } return (USB_SUCCESS); } /* get association info */ int wusb_cbaf_get_asso_info(wusb_ca_state_t *wusb_cap, intptr_t arg, int flag) { usb_pipe_handle_t pipe = wusb_cap->wusb_ca_reg->dev_default_ph; usb_ctrl_setup_t setup; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; wusb_cbaf_asso_info_t ca_info; mblk_t *pdata = NULL; int rval; setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; setup.bRequest = WUSB_CBAF_GET_ASSOCIATION_INFORMATION; setup.wValue = 0; setup.wIndex = 0; setup.wLength = WUSB_ASSO_INFO_SIZE; setup.attrs = USB_ATTRS_NONE; rval = usb_pipe_ctrl_xfer_wait(pipe, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "cr = %d cb_flags = %d", completion_reason, cb_flags); return (EIO); } if (pdata == NULL || msgsize(pdata) == 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "empty pdata"); return (EIO); } rval = usb_parse_data("scs", pdata->b_rptr, WUSB_ASSO_INFO_SIZE, &ca_info, sizeof (ca_info)); if (rval <= 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "parse data"); return (EIO); } rval = ddi_copyout(&ca_info, (void *)arg, sizeof (ca_info), flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyout"); return (EIO); } freemsg(pdata); return (0); } /* get request array */ int wusb_cbaf_get_asso_reqs(wusb_ca_state_t *wusb_cap, intptr_t arg, int flag) { usb_pipe_handle_t pipe = wusb_cap->wusb_ca_reg->dev_default_ph; usb_ctrl_setup_t setup; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; wusb_cbaf_asso_info_t ca_info; wusb_cbaf_asso_req_t *ca_reqs; mblk_t *pdata = NULL; uchar_t *data; int rval, reqs_size, i; rval = ddi_copyin((void *)arg, &ca_info, sizeof (ca_info), flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyin"); return (EIO); } setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; setup.bRequest = WUSB_CBAF_GET_ASSOCIATION_INFORMATION; setup.wValue = 0; setup.wIndex = 0; setup.wLength = ca_info.Length; setup.attrs = USB_ATTRS_NONE; rval = usb_pipe_ctrl_xfer_wait(pipe, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "cr = %d cb_flags = %d", completion_reason, cb_flags); return (EIO); } if (pdata == NULL || msgsize(pdata) == 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "empty pdata"); return (EIO); } reqs_size = sizeof (wusb_cbaf_asso_req_t) * ca_info.NumAssociationRequests; ca_reqs = (wusb_cbaf_asso_req_t *)kmem_zalloc(reqs_size, KM_SLEEP); data = pdata->b_rptr + WUSB_ASSO_INFO_SIZE; for (i = 0; i < ca_info.NumAssociationRequests; i++) { rval = usb_parse_data("ccssl", data, WUSB_ASSO_REQUEST_SIZE, &(ca_reqs[i]), sizeof (wusb_cbaf_asso_req_t)); if (rval <= 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "parse data"); return (EIO); } data += WUSB_ASSO_REQUEST_SIZE; } rval = ddi_copyout(ca_reqs, (void *)(arg + sizeof (ca_info)), reqs_size, flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyout"); return (EIO); } freemsg(pdata); kmem_free(ca_reqs, reqs_size); return (0); } /* set host info */ int wusb_cbaf_set_host_info(wusb_ca_state_t *wusb_cap, intptr_t arg, int flag) { usb_pipe_handle_t pipe = wusb_cap->wusb_ca_reg->dev_default_ph; usb_ctrl_setup_t setup; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; wusb_cbaf_host_info_t host_info; mblk_t *pdata; int rval; rval = ddi_copyin((void *)arg, &host_info, sizeof (host_info), flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyin"); return (EIO); } if ((pdata = trans_from_host_info(&host_info)) == NULL) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "trans host info"); return (EIO); } setup.bmRequestType = USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; setup.bRequest = WUSB_CBAF_SET_ASSOCIATION_RESPONSE; setup.wValue = 0x101; setup.wIndex = 0; setup.wLength = WUSB_HOST_INFO_SIZE; setup.attrs = USB_ATTRS_NONE; rval = usb_pipe_ctrl_xfer_wait(pipe, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); freemsg(pdata); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "cr = %d cb_flags = 0x%03x", completion_reason, cb_flags); return (EIO); } return (0); } /* get device info */ int wusb_cbaf_get_device_info(wusb_ca_state_t *wusb_cap, intptr_t arg, int flag) { usb_pipe_handle_t pipe = wusb_cap->wusb_ca_reg->dev_default_ph; usb_ctrl_setup_t setup; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; wusb_cbaf_device_info_t device_info; mblk_t *pdata = NULL; int rval; setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; setup.bRequest = WUSB_CBAF_GET_ASSOCIATION_REQUEST; setup.wValue = 0x200; setup.wIndex = 0; setup.wLength = WUSB_DEVICE_INFO_SIZE; setup.attrs = USB_ATTRS_NONE; rval = usb_pipe_ctrl_xfer_wait(pipe, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "cr = %d cb_flags = %d", completion_reason, cb_flags); return (EIO); } if (pdata == NULL || msgsize(pdata) == 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "empty pdata"); return (EIO); } if (trans_to_device_info(wusb_cap, pdata, &device_info) != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "trans to device_info"); return (EIO); } rval = ddi_copyout(&device_info, (void *)arg, sizeof (device_info), flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyout"); return (EIO); } freemsg(pdata); return (0); } /* set connection to device */ int wusb_cbaf_set_connection(wusb_ca_state_t *wusb_cap, intptr_t arg, int flag) { usb_pipe_handle_t pipe = wusb_cap->wusb_ca_reg->dev_default_ph; usb_ctrl_setup_t setup; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; wusb_cbaf_cc_data_t cc_data; mblk_t *pdata = NULL; int rval; rval = ddi_copyin((void *)arg, &cc_data, sizeof (cc_data), flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyin"); return (EIO); } if ((pdata = trans_from_cc_data(&cc_data)) == NULL) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "trans cc data"); return (EIO); } setup.bmRequestType = USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; setup.bRequest = WUSB_CBAF_SET_ASSOCIATION_RESPONSE; setup.wValue = 0x201; setup.wIndex = 0; setup.wLength = WUSB_CC_DATA_SIZE; setup.attrs = USB_ATTRS_NONE; rval = usb_pipe_ctrl_xfer_wait(pipe, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); freemsg(pdata); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "cr = %d cb_flags = %d", completion_reason, cb_flags); return (EIO); } return (0); } /* set failure */ int wusb_cbaf_set_failure(wusb_ca_state_t *wusb_cap, intptr_t arg, int flag) { usb_pipe_handle_t pipe = wusb_cap->wusb_ca_reg->dev_default_ph; usb_ctrl_setup_t setup; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; wusb_cbaf_cc_fail_t cc_fail; mblk_t *pdata = NULL; int rval; rval = ddi_copyin((void *)arg, &cc_fail, sizeof (cc_fail), flag); if (rval != 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "ddi_copyin"); return (EIO); } if ((pdata = trans_from_cc_fail(&cc_fail)) == NULL) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "trans cc fail"); return (EIO); } setup.bmRequestType = USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; setup.bRequest = WUSB_CBAF_SET_ASSOCIATION_RESPONSE; setup.wValue = 0x201; setup.wIndex = 0; setup.wLength = WUSB_CC_DATA_SIZE; setup.attrs = USB_ATTRS_NONE; rval = usb_pipe_ctrl_xfer_wait(pipe, &setup, &pdata, &completion_reason, &cb_flags, USB_FLAGS_SLEEP); freemsg(pdata); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "cr = %d cb_flags = %d", completion_reason, cb_flags); return (EIO); } return (0); } #define DRAW_BYTE(x, sh) (((x) >> (sh * 8)) & 0xff) static mblk_t * trans_from_host_info(wusb_cbaf_host_info_t *host_info) { mblk_t *pdata; if ((pdata = allocb(WUSB_HOST_INFO_SIZE, BPRI_HI)) == NULL) { return (NULL); } bcopy(fieldAssociationTypeId, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(host_info->AssociationTypeId, 0); *pdata->b_wptr++ = DRAW_BYTE(host_info->AssociationTypeId, 1); bcopy(fieldAssociationSubTypeId, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(host_info->AssociationSubTypeId, 0); *pdata->b_wptr++ = DRAW_BYTE(host_info->AssociationSubTypeId, 1); bcopy(fieldCHID, pdata->b_wptr, 4); pdata->b_wptr += 4; bcopy(host_info->CHID, pdata->b_wptr, 16); pdata->b_wptr += 16; bcopy(fieldLangID, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(host_info->LangID, 0); *pdata->b_wptr++ = DRAW_BYTE(host_info->LangID, 1); bcopy(fieldHostFriendlyName, pdata->b_wptr, 4); pdata->b_wptr += 4; bcopy(host_info->HostFriendlyName, pdata->b_wptr, 64); pdata->b_wptr += 64; return (pdata); } static mblk_t * trans_from_cc_data(wusb_cbaf_cc_data_t *cc_data) { mblk_t *pdata; if ((pdata = allocb(WUSB_CC_DATA_SIZE, BPRI_HI)) == NULL) { return (NULL); } bcopy(fieldAssociationTypeId, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_data->AssociationTypeId, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_data->AssociationTypeId, 1); bcopy(fieldAssociationSubTypeId, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_data->AssociationSubTypeId, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_data->AssociationSubTypeId, 1); bcopy(fieldLength, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_data->Length, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_data->Length, 1); *pdata->b_wptr++ = DRAW_BYTE(cc_data->Length, 2); *pdata->b_wptr++ = DRAW_BYTE(cc_data->Length, 3); bcopy(fieldConnectionContext, pdata->b_wptr, 4); pdata->b_wptr += 4; bcopy(&(cc_data->CC), pdata->b_wptr, 48); pdata->b_wptr += 48; bcopy(fieldBandGroups, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_data->BandGroups, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_data->BandGroups, 1); return (pdata); } static mblk_t * trans_from_cc_fail(wusb_cbaf_cc_fail_t *cc_fail) { mblk_t *pdata; if ((pdata = allocb(WUSB_CC_FAILURE_SIZE, BPRI_HI)) == NULL) { return (NULL); } bcopy(fieldAssociationTypeId, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationTypeId, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationTypeId, 1); bcopy(fieldAssociationSubTypeId, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationSubTypeId, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationSubTypeId, 1); bcopy(fieldLength, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_fail->Length, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->Length, 1); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->Length, 2); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->Length, 3); bcopy(fieldAssociationStatus, pdata->b_wptr, 4); pdata->b_wptr += 4; *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationStatus, 0); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationStatus, 1); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationStatus, 2); *pdata->b_wptr++ = DRAW_BYTE(cc_fail->AssociationStatus, 3); return (pdata); } static int trans_to_device_info(wusb_ca_state_t *wusb_cap, mblk_t *pdata, wusb_cbaf_device_info_t *device_info) { int i, plen; void *paddr; char *mode; uchar_t *ptr = (uchar_t *)pdata->b_rptr; wusb_cbaf_info_item_t item; for (i = 0; i < 5; i++) { if (((int)usb_parse_data("ss", ptr, 4, &item, sizeof (item))) <= 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "parse item[%d] failed", i); return (-1); } ptr += 4; switch (item.typeID) { case attrLength: mode = "l"; paddr = &(device_info->Length); plen = sizeof (uint32_t); break; case attrCDID: mode = "16c"; paddr = &(device_info->CDID); plen = 16 * sizeof (uint8_t); break; case attrBandGroups: mode = "s"; paddr = &(device_info->BandGroups); plen = sizeof (uint16_t); break; case attrLangID: mode = "s"; paddr = &(device_info->LangID); plen = sizeof (uint16_t); break; case attrDeviceFriendlyName: mode = "l"; paddr = &(device_info->DeviceFriendlyName); plen = 64 * sizeof (char); break; default: USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "item[%d]: 0x%04x", i, item.typeID); return (-1); } if (((int)usb_parse_data(mode, ptr, item.length, paddr, plen)) <= 0) { USB_DPRINTF_L2(PRINT_MASK_ALL, wusb_cap->wusb_ca_log_hdl, "item[%d]: 0x%04x", i, item.typeID); return (-1); } ptr += item.length; } return (0); }