1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
27 */
28
29 /*
30 * The Data Transfer Interface driver for Host Wire Adapter device
31 *
32 * HWA device has two interfaces, one is the data transfer interface,
33 * another is the radio control interface. This driver (hwahc) is only
34 * for data transfer interface support, but it depends on the radio
35 * control interface driver (hwarc) to work. That means the hwarc
36 * driver must be loaded while the hwahc is working. This is now
37 * ensured by holding hwarc open until hwahc detaches or powers down.
38 *
39 * The data transfer interface has three endpoints besides the default
40 * control endpoint which is shared between the two interfaces. The
41 * three endpoints are:
42 *
43 * - notification endpoint (intr in type, for asynchronous event
44 * notifications and transfer status notifications)
45 *
46 * - data transfer OUT endpoint (bulk out type, for sending transfer
47 * requests and transfer data from the host to the HWA device)
48 *
49 * - data transfer IN endpoint (bulk in type, for returning transfer
50 * status and transfer data from the HWA device to the host)
51 *
52 * The HWA device is a USB 2.0 device, so it supports the standard USB
53 * requests defined in chapter 9 of USB 2.0 specification as other USB
54 * client devices. But its most important functionality is to work as
55 * a wireless USB host. This means the hwahc driver needs to supply
56 * host controller functionalities, which include children hotplug
57 * support and data transfer support to children device endpoints.
58 *
59 * So hwahc driver is implemented as a nexus driver and it follows the
60 * event mechanism in existing USBA framework to support children
61 * hotplug events.
62 *
63 * The hwahc driver works as the root-hub on wireless USB bus. And it
64 * relays data transfers to/from wireless bus to the USB bus where ehci/
65 * ohci/uhci works as the root-hub. This makes a bus cascading topology.
66 *
67 * The data transfer to/from wireless device endpoints is implemented by
68 * remote pipe (rpipe) mechanism. The rpipe descriptor on the HWA defines
69 * the attributes of a wireless USB transfer, such as the transfer type,
70 * the target device address, the target endpoint address and the max
71 * packet size. And the transfer requests through data transfer OUT
72 * endpoint will take a certain rpipe as the transfer target, thus
73 * fulfills the data transfer across buses. Refer to chapter 8 of WUSB
74 * 1.0 specification for details of this.
75 */
76
77 #define USBDRV_MAJOR_VER 2
78 #define USBDRV_MINOR_VER 0
79
80 #include <sys/usb/hwa/hwahc/hwahc.h>
81 #include <sys/usb/hwa/hwahc/hwahc_util.h>
82 #include <sys/usb/usba/wa.h>
83 #include <sys/usb/usba/wusba.h>
84 #include <sys/usb/usba/whcdi.h>
85 #include <sys/usb/usba.h>
86 #include <sys/usb/usba/usba_impl.h>
87 #include <sys/usb/usba/usba_devdb.h> /* for usba_devdb_refresh */
88 #include <sys/usb/hubd/hubdvar.h>
89 #include <sys/usb/hubd/hubd_impl.h> /* for hubd_ioctl_data_t */
90 #include <sys/strsubr.h> /* for allocb_wait */
91 #include <sys/strsun.h> /* for MBLKL macro */
92 #include <sys/fs/dv_node.h> /* for devfs_clean */
93 #include <sys/uwb/uwbai.h> /* for uwb ioctls */
94 #include <sys/random.h>
95
96 void *hwahc_statep;
97
98 /* number of instances */
99 #define HWAHC_INSTS 1
100
101 /* default value for set number DNTS slots request */
102 #define HWAHC_DEFAULT_DNTS_INTERVAL 2 /* ms */
103 #define HWAHC_DEFAULT_DNTS_SLOT_NUM 4
104
105
106 /* debug support */
107 uint_t hwahc_errmask = (uint_t)PRINT_MASK_ALL;
108 uint_t hwahc_errlevel = USB_LOG_L4;
109 uint_t hwahc_instance_debug = (uint_t)-1;
110
111 /* bus config debug flag */
112 uint_t hwahc_bus_config_debug = 0;
113 uint8_t hwahc_enable_trust_timeout = 1;
114
115
116 /*
117 * Use the default GTK for the whole life of HWA driver.
118 * Not so compatible with WUSB spec.
119 */
120 static uint8_t dft_gtk[16];
121 static uint8_t dft_gtkid[3];
122
123 extern usb_log_handle_t whcdi_log_handle;
124
125 /*
126 * Function Prototypes
127 */
128 /* driver operations (dev_ops) entry points */
129 static int hwahc_open(dev_t *, int, int, cred_t *);
130 static int hwahc_close(dev_t, int, int, cred_t *);
131 static int hwahc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
132
133 static int hwahc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
134 static int hwahc_attach(dev_info_t *, ddi_attach_cmd_t);
135 static int hwahc_detach(dev_info_t *, ddi_detach_cmd_t);
136 static int hwahc_power(dev_info_t *, int, int);
137
138 /* bus_ops entry points */
139 static int hwahc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
140 void *, void *);
141 static int hwahc_busop_get_eventcookie(dev_info_t *, dev_info_t *,
142 char *, ddi_eventcookie_t *);
143 static int hwahc_busop_add_eventcall(
144 dev_info_t *, dev_info_t *, ddi_eventcookie_t,
145 void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
146 void *, ddi_callback_id_t *);
147 static int hwahc_busop_remove_eventcall(dev_info_t *, ddi_callback_id_t);
148 static int hwahc_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
149 void *, dev_info_t **);
150 static int hwahc_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
151 void *);
152
153 /* hotplug and power management supporting functions */
154 static int hwahc_disconnect_event_cb(dev_info_t *dip);
155 static int hwahc_reconnect_event_cb(dev_info_t *dip);
156 static int hwahc_pre_suspend_event_cb(dev_info_t *dip);
157 static int hwahc_post_resume_event_cb(dev_info_t *dip);
158 static int hwahc_cpr_suspend(dev_info_t *);
159 static int hwahc_cpr_resume(dev_info_t *);
160 static void hwahc_restore_device_state(dev_info_t *, hwahc_state_t *);
161 static void hwahc_run_callbacks(hwahc_state_t *, usba_event_t);
162 static void hwahc_post_event(hwahc_state_t *, usb_port_t, usba_event_t);
163
164 static int hwahc_cleanup(dev_info_t *, hwahc_state_t *);
165 static void hwahc_create_pm_components(dev_info_t *, hwahc_state_t *);
166 static void hwahc_destroy_pm_components(hwahc_state_t *);
167 static void hwahc_pm_busy_component(hwahc_state_t *);
168 static void hwahc_pm_idle_component(hwahc_state_t *);
169 static int hwahc_pwrlvl0(hwahc_state_t *);
170 static int hwahc_pwrlvl1(hwahc_state_t *);
171 static int hwahc_pwrlvl2(hwahc_state_t *);
172 static int hwahc_pwrlvl3(hwahc_state_t *);
173 static int hwahc_hc_channel_suspend(hwahc_state_t *);
174
175 /* hardware initialization and deinitialization functions */
176 static int hwahc_parse_security_data(wusb_secrt_data_t *,
177 usb_cfg_data_t *);
178 static void hwahc_print_secrt_data(hwahc_state_t *);
179
180 static int hwahc_hub_attach(hwahc_state_t *);
181 static int hwahc_hub_detach(hwahc_state_t *);
182
183 static int hwahc_hc_initial_start(hwahc_state_t *);
184 static int hwahc_hc_final_stop(hwahc_state_t *);
185 static int hwahc_wa_start(hwahc_state_t *);
186 static void hwahc_wa_stop(hwahc_state_t *);
187 static int hwahc_hc_channel_start(hwahc_state_t *);
188 static int hwahc_hc_channel_stop(hwahc_state_t *);
189 static void hwahc_hc_data_init(hwahc_state_t *);
190 static void hwahc_hc_data_fini(hwahc_state_t *);
191
192 /* ioctl support */
193 static int hwahc_cfgadm_ioctl(hwahc_state_t *, int, intptr_t, int,
194 cred_t *, int *);
195 static int hwahc_wusb_ioctl(hwahc_state_t *, int, intptr_t, int,
196 cred_t *, int *);
197
198 /* callbacks registered to USBA */
199 static void hwahc_disconnect_dev(dev_info_t *, usb_port_t);
200 static void hwahc_reconnect_dev(dev_info_t *, usb_port_t);
201 static int hwahc_create_child(dev_info_t *, usb_port_t);
202 static int hwahc_destroy_child(dev_info_t *, usb_port_t);
203 static int hwahc_cleanup_child(dev_info_t *);
204 static int hwahc_delete_child(dev_info_t *, usb_port_t, uint_t, boolean_t);
205
206 /* data transfer and notification handling */
207 static void hwahc_intr_cb(usb_pipe_handle_t, struct usb_intr_req *);
208 static void hwahc_intr_exc_cb(usb_pipe_handle_t, struct usb_intr_req *);
209 static void hwahc_handle_notif(hwahc_state_t *, mblk_t *);
210 static void hwahc_handle_xfer_result(hwahc_state_t *, uint8_t);
211 static void hwahc_stop_result_thread(hwahc_state_t *);
212 static void hwahc_result_thread(void *);
213 static void hwahc_handle_dn_notif(hwahc_state_t *, hwa_notif_dn_recvd_t *);
214 static void hwahc_notif_thread(void *);
215 static void hwahc_handle_dn(hwahc_state_t *, hwa_notif_dn_recvd_t *);
216 static void hwahc_drain_notif_queue(hwahc_state_t *);
217 static void hwahc_rpipe_xfer_cb(dev_info_t *, usba_pipe_handle_data_t *,
218 wusb_wa_trans_wrapper_t *, usb_cr_t);
219
220 static void hwahc_trust_timeout_handler(void *arg);
221 static void hwahc_stop_trust_timer(wusb_dev_info_t *dev);
222
223 static int hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
224 usba_pipe_handle_data_t *ph);
225
226 /* hwa specific requests */
227 static int hwahc_set_chid(hwahc_state_t *, uint8_t *);
228
229 /* helper functions */
230 static usb_port_t hwahc_get_port_num(hwahc_state_t *, struct devctl_iocdata *);
231 static dev_info_t *hwahc_get_child_dip(hwahc_state_t *, usb_port_t);
232
233 static struct cb_ops hwahc_cb_ops = {
234 hwahc_open, /* Open */
235 hwahc_close, /* Close */
236 nodev, /* Strategy */
237 nodev, /* Print */
238 nodev, /* Dump */
239 nodev, /* Read */
240 nodev, /* Write */
241 hwahc_ioctl, /* Ioctl */
242 nodev, /* Devmap */
243 nodev, /* Mmap */
244 nodev, /* Segmap */
245 nochpoll, /* Poll */
246 ddi_prop_op, /* cb_prop_op */
247 NULL, /* Streamtab */
248 D_MP /* Driver compatibility flag */
249 };
250
251 static struct bus_ops hwahc_busops = {
252 BUSO_REV,
253 nullbusmap, /* bus_map */
254 NULL, /* bus_get_intrspec */
255 NULL, /* bus_add_intrspec */
256 NULL, /* bus_remove_intrspec */
257 NULL, /* bus_map_fault */
258 NULL, /* bus_dma_map */
259 ddi_dma_allochdl,
260 ddi_dma_freehdl,
261 ddi_dma_bindhdl,
262 ddi_dma_unbindhdl,
263 ddi_dma_flush,
264 ddi_dma_win,
265 ddi_dma_mctl, /* bus_dma_ctl */
266 hwahc_bus_ctl, /* bus_ctl */
267 ddi_bus_prop_op, /* bus_prop_op */
268 hwahc_busop_get_eventcookie, /* bus_get_eventcookie */
269 hwahc_busop_add_eventcall, /* bus_add_eventcall */
270 hwahc_busop_remove_eventcall, /* bus_remove_eventcall */
271 NULL, /* bus_post_event */
272 NULL, /* bus_intr_ctl */
273 hwahc_bus_config, /* bus_config */
274 hwahc_bus_unconfig, /* bus_unconfig */
275 NULL, /* bus_fm_init */
276 NULL, /* bus_fm_fini */
277 NULL, /* bus_fm_access_enter */
278 NULL, /* bus_fm_access_exit */
279 NULL, /* bus_power */
280 };
281
282 static struct dev_ops hwahc_ops = {
283 DEVO_REV, /* Devo_rev */
284 0, /* Refcnt */
285 hwahc_info, /* Info */
286 nulldev, /* Identify */
287 nulldev, /* Probe */
288 hwahc_attach, /* Attach */
289 hwahc_detach, /* Detach */
290 nodev, /* Reset */
291 &hwahc_cb_ops, /* Driver operations */
292 &hwahc_busops, /* Bus operations */
293 hwahc_power, /* Power */
294 ddi_quiesce_not_needed, /* devo_quiesce */
295 };
296
297 static struct modldrv hwahc_modldrv = {
298 &mod_driverops,
299 "WUSB hwa-hc driver",
300 &hwahc_ops
301 };
302
303 static struct modlinkage modlinkage = {
304 MODREV_1,
305 &hwahc_modldrv,
306 NULL
307 };
308
309 /* events from parent */
310 static usb_event_t hwahc_events = {
311 hwahc_disconnect_event_cb,
312 hwahc_reconnect_event_cb,
313 hwahc_pre_suspend_event_cb,
314 hwahc_post_resume_event_cb
315 };
316
317 /*
318 * events support for children
319 * A map tween USBA_EVENTs and DDI_EVENTs.
320 */
321 static ndi_event_definition_t hwahc_ndi_event_defs[] = {
322 {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
323 NDI_EVENT_POST_TO_ALL},
324 {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
325 NDI_EVENT_POST_TO_ALL},
326 {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
327 NDI_EVENT_POST_TO_ALL},
328 {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
329 NDI_EVENT_POST_TO_ALL}
330 };
331
332 #define HWAHC_N_NDI_EVENTS \
333 (sizeof (hwahc_ndi_event_defs) / sizeof (ndi_event_definition_t))
334
335 static ndi_event_set_t hwahc_ndi_events = {
336 NDI_EVENTS_REV1, HWAHC_N_NDI_EVENTS, hwahc_ndi_event_defs};
337
338 /* transfer callbacks */
339 static wusb_wa_cb_t hwahc_cbs = {
340 hwahc_pipe_submit_periodic_req,
341 hwahc_intr_cb,
342 hwahc_intr_exc_cb,
343 hwahc_rpipe_xfer_cb
344 };
345
346
347 /*
348 * Module-wide initialization routine.
349 */
350 int
351 _init(void)
352 {
353 int rval;
354
355 if ((rval = ddi_soft_state_init(&hwahc_statep, sizeof (hwahc_state_t),
356 HWAHC_INSTS)) != 0) {
357
358 return (rval);
359 }
360
361 if ((rval = mod_install(&modlinkage)) != 0) {
362 ddi_soft_state_fini(&hwahc_statep);
363 }
364
365 return (rval);
366 }
367
368
369 /*
370 * Module-wide tear-down routine.
371 */
372 int
373 _fini(void)
374 {
375 int rval;
376
377 if ((rval = mod_remove(&modlinkage)) == 0) {
378 /* Release per module resources */
379 ddi_soft_state_fini(&hwahc_statep);
380 }
381
382 return (rval);
383 }
384
385
386 int
387 _info(struct modinfo *modinfop)
388 {
389 return (mod_info(&modlinkage, modinfop));
390 }
391
392
393 /*
394 * hwahc_info:
395 * Get minor number, instance number, etc.
396 */
397 /*ARGSUSED*/
398 static int
399 hwahc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
400 void *arg, void **result)
401 {
402 hwahc_state_t *hwahcp;
403 int error = DDI_FAILURE;
404 int instance = HWAHC_MINOR_TO_INSTANCE(getminor((dev_t)arg));
405
406 switch (infocmd) {
407 case DDI_INFO_DEVT2DEVINFO:
408 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
409 instance)) != NULL) {
410 *result = hwahcp->hwahc_dip;
411 if (*result != NULL) {
412 error = DDI_SUCCESS;
413 }
414 } else {
415 *result = NULL;
416 }
417 break;
418 case DDI_INFO_DEVT2INSTANCE:
419 *result = (void *)(uintptr_t)instance;
420 error = DDI_SUCCESS;
421 break;
422 default:
423 break;
424 }
425
426 return (error);
427 }
428
429
430 /*
431 * hwahc_attach:
432 * Attach or resume.
433 *
434 * For attach, initialize state and device, including:
435 * state variables, locks, device node,
436 * resource initialization, event registration,
437 * device registration with system
438 * power management, hotplugging
439 * For resume, restore device and state
440 */
441 static int
442 hwahc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
443 {
444 int instance = ddi_get_instance(dip);
445 hwahc_state_t *hwahcp = NULL;
446 usb_client_dev_data_t *dev_data;
447 struct usb_cfg_data *cfg_data;
448 usba_hcdi_register_args_t hcdi_args;
449 int rval;
450 char *pathname;
451
452 USB_DPRINTF_L3(PRINT_MASK_ATTA, NULL, "hwahc_attach: cmd=%d", cmd);
453
454 switch (cmd) {
455 case DDI_ATTACH:
456 break;
457 case DDI_RESUME:
458 (void) hwahc_cpr_resume(dip);
459
460 return (DDI_SUCCESS);
461 default:
462 USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
463 "hwahc_attach: failed");
464
465 return (DDI_FAILURE);
466 }
467
468 /*
469 * Allocate soft state information.
470 */
471 rval = ddi_soft_state_zalloc(hwahc_statep, instance);
472 if (rval != DDI_SUCCESS) {
473 USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
474 "hwahc_attach: cannot allocate soft state for instance %d",
475 instance);
476
477 return (USB_FAILURE);
478 }
479
480 hwahcp = ddi_get_soft_state(hwahc_statep, instance);
481 if (hwahcp == NULL) {
482 USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
483 "hwahc_attach: get soft state failed for instance %d",
484 instance);
485
486 return (USB_FAILURE);
487 }
488
489 hwahcp->hwahc_log_handle = usb_alloc_log_hdl(dip, "hwahc",
490 &hwahc_errlevel, &hwahc_errmask, &hwahc_instance_debug, 0);
491
492 /* initialize hc state */
493 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
494 hwahcp->hwahc_dip = dip;
495 hwahcp->hwahc_instance = instance;
496
497 /* register with USBA as client driver */
498 if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
499 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
500 "hwahc_attach: client attach failed");
501
502 goto fail;
503 }
504
505 if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
506 USB_SUCCESS) {
507 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
508 "hwahc_attach: cannot get dev_data");
509
510 goto fail;
511 }
512
513 /* initialize mutex and cv */
514 mutex_init(&hwahcp->hwahc_mutex, NULL, MUTEX_DRIVER,
515 dev_data->dev_iblock_cookie);
516 cv_init(&hwahcp->hwahc_result_thread_cv, NULL, CV_DRIVER, NULL);
517
518 hwahcp->hwahc_flags |= HWAHC_LOCK_INITED;
519 hwahcp->hwahc_dev_data = dev_data;
520
521 /* initialize data transfer function related structure */
522 if (wusb_wa_data_init(dip, &hwahcp->hwahc_wa_data, &hwahc_cbs,
523 dev_data, PRINT_MASK_ATTA,
524 hwahcp->hwahc_log_handle) != USB_SUCCESS) {
525 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
526 "hwahc_attach: init wa data failed");
527
528 goto fail;
529 }
530
531 hwahcp->hwahc_flags |= HWAHC_WA_INITED;
532 cfg_data = dev_data->dev_curr_cfg;
533
534 /* parse the security descrs from the configuration descr cloud */
535 if (hwahc_parse_security_data(&hwahcp->hwahc_secrt_data, cfg_data) !=
536 USB_SUCCESS) {
537 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
538 "hwahc_attach: parse security descrs failed");
539
540 goto fail;
541 }
542
543 hwahcp->hwahc_default_pipe = dev_data->dev_default_ph;
544 hwahcp->hwahc_wa_data.wa_private_data = (void *)hwahcp;
545 hwahcp->hwahc_wa_data.wa_default_pipe = hwahcp->hwahc_default_pipe;
546
547 usb_free_descr_tree(dip, dev_data);
548
549 hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
550
551 /* now create components to power manage this device */
552 hwahc_create_pm_components(dip, hwahcp);
553
554 /*
555 * Event definition and registration
556 *
557 * allocate a new NDI event handle as a nexus driver
558 */
559 (void) ndi_event_alloc_hdl(dip, 0, &hwahcp->hwahc_ndi_event_hdl,
560 NDI_SLEEP);
561
562 /*
563 * bind our NDI events with the event handle,
564 * i.e. Define the events set we're to support as a nexus driver.
565 *
566 * These events will be used by bus_ops functions to register callbacks.
567 */
568 if (ndi_event_bind_set(hwahcp->hwahc_ndi_event_hdl, &hwahc_ndi_events,
569 NDI_SLEEP)) {
570 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
571 "hwahc_attach: binding event set failed");
572
573 goto fail;
574 }
575
576
577 /*
578 * Register USB events to USBA(the parent) to get callbacks as a
579 * child of (root) hub
580 */
581 if (usb_register_event_cbs(dip, &hwahc_events, 0) != USB_SUCCESS) {
582 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
583 "hwahc_attach: register_events failed");
584
585 goto fail;
586 }
587
588 hwahcp->hwahc_flags |= HWAHC_EVENTS_REGISTERED;
589
590 /* create minor nodes */
591 if (ddi_create_minor_node(dip, "hwahc", S_IFCHR,
592 instance << HWAHC_MINOR_INSTANCE_SHIFT,
593 DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
594
595 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
596 "hwahc_attach: cannot create minor node");
597
598 goto fail;
599 }
600
601 hwahcp->hwahc_flags |= HWAHC_MINOR_NODE_CREATED;
602
603 hwahcp->hwahc_hcdi_ops = hwahc_alloc_hcdi_ops(hwahcp);
604
605 /* register this hc instance with usba HCD interface */
606 hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
607 hcdi_args.usba_hcdi_register_dip = dip;
608 hcdi_args.usba_hcdi_register_ops = hwahcp->hwahc_hcdi_ops;
609
610 /* use parent dma attr here */
611 hcdi_args.usba_hcdi_register_dma_attr = usba_get_hc_dma_attr(dip);
612 hcdi_args.usba_hcdi_register_iblock_cookie = NULL;
613
614 if (usba_hcdi_register(&hcdi_args, 0) != USB_SUCCESS) {
615 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
616 "hwahc_attach: usba_hcdi_register failed");
617
618 goto fail;
619 }
620
621 hwahcp->hwahc_flags |= HWAHC_HCDI_REGISTERED;
622
623 /* create hub minor node and register to usba HUBD interface */
624 if (hwahc_hub_attach(hwahcp) != USB_SUCCESS) {
625 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
626 "hwahc_attach: hub attach failed");
627
628 goto fail;
629 }
630 hwahcp->hwahc_flags |= HWAHC_HUBREG;
631
632 /* intialize WUSB host function related structure */
633 hwahc_hc_data_init(hwahcp);
634 hwahcp->hwahc_flags |= HWAHC_HC_INITED;
635
636 /* can be combined with wusb_wa_data_init() */
637 if (hwahc_wa_start(hwahcp) != USB_SUCCESS) {
638
639 goto fail;
640 }
641
642 hwahcp->hwahc_flags |= HWAHC_WA_STARTED;
643
644 /* report this dev */
645 ddi_report_dev(dip);
646
647 hwahc_pm_idle_component(hwahcp);
648
649 mutex_enter(&(hwahcp->hwahc_mutex));
650 hwahc_print_secrt_data(hwahcp);
651 mutex_exit(&(hwahcp->hwahc_mutex));
652
653 if (uwb_dev_online(dip) != USB_SUCCESS) {
654 goto fail;
655 }
656
657 return (DDI_SUCCESS);
658
659 fail:
660 pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
661
662 /* log this message to usba_debug_buf */
663 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
664 "cannot attach %s", ddi_pathname(dip, pathname));
665
666 kmem_free(pathname, MAXPATHLEN);
667
668 if (hwahcp) {
669 hwahc_pm_idle_component(hwahcp);
670
671 rval = hwahc_cleanup(dip, hwahcp);
672 if (rval != USB_SUCCESS) {
673 USB_DPRINTF_L2(PRINT_MASK_ATTA,
674 hwahcp->hwahc_log_handle,
675 "failure to complete cleanup after attach failure");
676 }
677 }
678
679 return (DDI_FAILURE);
680 }
681
682
683 /*
684 * hwahc_detach:
685 * detach or suspend driver instance
686 *
687 * Note: in detach, only contention threads is from pm and disconnnect.
688 */
689 static int
690 hwahc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
691 {
692 int instance = ddi_get_instance(dip);
693 hwahc_state_t *hwahcp = ddi_get_soft_state(hwahc_statep, instance);
694 int rval = DDI_FAILURE;
695
696
697 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
698 "hwahc_detach: cmd = %d", cmd);
699
700 switch (cmd) {
701 case DDI_DETACH:
702 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
703 "offline uwb device for dip: 0x%p", (void *)dip);
704 /* offline the hwarc interface */
705 (void) uwb_dev_offline(dip);
706 if (hwahcp) {
707 rval = hwahc_cleanup(dip, hwahcp);
708 }
709
710 break;
711 case DDI_SUSPEND:
712 rval = hwahc_cpr_suspend(dip);
713
714 break;
715 default:
716
717 break;
718 }
719
720 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
721 }
722
723
724 /*
725 * hwahc_cleanup:
726 * clean up on attach failure or detach
727 */
728 static int
729 hwahc_cleanup(dev_info_t *dip, hwahc_state_t *hwahcp)
730 {
731 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
732 "hwahc_cleanup: start");
733
734 if ((hwahcp->hwahc_flags & HWAHC_LOCK_INITED) == 0) {
735
736 goto done;
737 }
738
739 /*
740 * deallocate events, if events are still registered
741 * (ie. children still attached) then we have to fail the detach
742 */
743 if (hwahcp->hwahc_ndi_event_hdl &&
744 (ndi_event_free_hdl(hwahcp->hwahc_ndi_event_hdl) != NDI_SUCCESS)) {
745
746 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
747 "hwahc_cleanup: ndi_event_free_hdl failed");
748
749 return (USB_FAILURE);
750 }
751
752 if (hwahcp->hwahc_flags & HWAHC_EVENTS_REGISTERED) {
753 /* unregister events */
754 usb_unregister_event_cbs(dip, &hwahc_events);
755 }
756
757 if (hwahcp->hwahc_flags & HWAHC_HCDI_REGISTERED) {
758 /* unregister the instance with usba HCD interface */
759 usba_hcdi_unregister(hwahcp->hwahc_dip);
760 }
761
762 mutex_enter(&hwahcp->hwahc_mutex);
763
764 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
765 /* stop the hw if it is enabled */
766 (void) hwahc_hc_final_stop(hwahcp);
767 }
768
769 if (hwahcp->hwahc_flags & HWAHC_WA_STARTED) {
770 /* can be combined with wusb_wa_data_fini() */
771 mutex_exit(&hwahcp->hwahc_mutex);
772 hwahc_wa_stop(hwahcp);
773 mutex_enter(&hwahcp->hwahc_mutex);
774 }
775
776 if (hwahcp->hwahc_flags & HWAHC_HC_INITED) {
777 /* deinitialize the WUSB host function related structure */
778 hwahc_hc_data_fini(hwahcp);
779 }
780
781 mutex_exit(&hwahcp->hwahc_mutex);
782
783 if (hwahcp->hwahc_pm) {
784 /* destroy power management components */
785 hwahc_destroy_pm_components(hwahcp);
786 }
787
788 if (hwahcp->hwahc_flags & HWAHC_HUBREG) {
789 /* unregister the instance from usba HUBD interface */
790 if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
791
792 return (USB_FAILURE);
793 }
794 }
795
796 if (hwahcp->hwahc_hcdi_ops) {
797 usba_free_hcdi_ops(hwahcp->hwahc_hcdi_ops);
798 }
799
800 mutex_enter(&hwahcp->hwahc_mutex);
801 if (hwahcp->hwahc_secrt_data.secrt_encry_descr) {
802 /* free security descrs */
803 kmem_free(hwahcp->hwahc_secrt_data.secrt_encry_descr,
804 sizeof (usb_encryption_descr_t) *
805 hwahcp->hwahc_secrt_data.secrt_n_encry);
806 }
807
808 if (hwahcp->hwahc_flags & HWAHC_WA_INITED) {
809 /* deinitialize data transfer function related structure */
810 wusb_wa_data_fini(&hwahcp->hwahc_wa_data);
811 }
812 mutex_exit(&hwahcp->hwahc_mutex);
813
814 if (hwahcp->hwahc_flags & HWAHC_MINOR_NODE_CREATED) {
815 /* remove all the minor nodes */
816 ddi_remove_minor_node(dip, NULL);
817 }
818
819 /* destroy mutex and cv */
820 mutex_destroy(&hwahcp->hwahc_mutex);
821 cv_destroy(&hwahcp->hwahc_result_thread_cv);
822
823 done:
824 /* unregister the client driver from usba */
825 usb_client_detach(dip, hwahcp->hwahc_dev_data);
826
827 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
828 "hwahc_cleanup: end");
829
830 usb_free_log_hdl(hwahcp->hwahc_log_handle);
831
832 /* remove all properties created */
833 ddi_prop_remove_all(dip);
834
835 /* free the soft state information */
836 ddi_soft_state_free(hwahc_statep, ddi_get_instance(dip));
837
838 return (USB_SUCCESS);
839 }
840
841
842 /*ARGSUSED*/
843 static int
844 hwahc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
845 {
846 hwahc_state_t *hwahcp;
847
848 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
849 HWAHC_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
850
851 return (ENXIO);
852 }
853
854 USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
855 "hwahc_open: start");
856
857 mutex_enter(&hwahcp->hwahc_mutex);
858 /* exclusive open */
859 if ((flag & FEXCL) && (hwahcp->hwahc_open_count > 0)) {
860 mutex_exit(&hwahcp->hwahc_mutex);
861
862 return (EBUSY);
863 }
864
865 if ((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
866 (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED)) {
867 mutex_exit(&hwahcp->hwahc_mutex);
868
869 return (EIO);
870 }
871
872 hwahcp->hwahc_open_count++;
873
874 mutex_exit(&hwahcp->hwahc_mutex);
875
876 /* raise to full power and keep it until close */
877 hwahc_pm_busy_component(hwahcp);
878 (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
879
880 USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
881 "hwahc_open: end");
882
883 return (0);
884 }
885
886
887 /*ARGSUSED*/
888 static int
889 hwahc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
890 {
891 hwahc_state_t *hwahcp;
892
893 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
894 HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
895
896 return (ENXIO);
897 }
898
899 USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
900 "hwahc_close: start");
901
902 mutex_enter(&hwahcp->hwahc_mutex);
903 if (hwahcp->hwahc_open_count == 0) {
904 USB_DPRINTF_L2(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
905 "hwahc_close: already closed");
906 mutex_exit(&hwahcp->hwahc_mutex);
907
908 return (EINVAL);
909 }
910
911 hwahcp->hwahc_open_count--;
912 mutex_exit(&hwahcp->hwahc_mutex);
913
914 hwahc_pm_idle_component(hwahcp);
915
916 USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
917 "hwahc_close: end");
918
919 return (0);
920 }
921
922 /* retrieve port number from devctl data */
923 static usb_port_t
924 hwahc_get_port_num(hwahc_state_t *hwahcp, struct devctl_iocdata *dcp)
925 {
926 int32_t port;
927
928 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
929
930 /* Get which port to operate on. */
931 if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
932 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
933 "hwahc_get_port_num: port lookup failed");
934 port = 0;
935 }
936
937 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
938 "hwahc_get_port_num: hwahcp=0x%p, port=%d", (void *)hwahcp,
939 port);
940
941 return ((usb_port_t)port);
942 }
943
944 /* return the child dip on a certain port */
945 static dev_info_t *
946 hwahc_get_child_dip(hwahc_state_t *hwahcp, usb_port_t port)
947 {
948 wusb_hc_data_t *hc_data;
949 dev_info_t *child_dip;
950
951 hc_data = &hwahcp->hwahc_hc_data;
952
953 /* check port range to prevent an illegal number */
954 if (port > hc_data->hc_num_ports) {
955 return (NULL);
956 }
957
958 mutex_enter(&hc_data->hc_mutex);
959 child_dip = hc_data->hc_children_dips[port];
960 mutex_exit(&hc_data->hc_mutex);
961
962 return (child_dip);
963 }
964
965 /*
966 * hwahc_cfgadm_state:
967 *
968 * child_dip list child_state cfgadm_state
969 * -------------- ---------- ------------
970 * != NULL connected configured or
971 * unconfigured
972 * != NULL not connected disconnect but
973 * busy/still referenced
974 * NULL connected logically disconnected
975 * NULL not connected empty
976 */
977 static uint_t
978 hwahc_cfgadm_state(hwahc_state_t *hwahcp, usb_port_t port)
979 {
980 uint_t state;
981 dev_info_t *child_dip = hwahc_get_child_dip(hwahcp, port);
982 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
983 wusb_dev_info_t *dev_info;
984
985 if (child_dip == NULL) {
986
987 return (HWAHC_CFGADM_INVALID);
988 }
989
990 mutex_enter(&hc_data->hc_mutex);
991 dev_info = hc_data->hc_dev_infos[port];
992 if (dev_info) {
993 if (dev_info->wdev_state == WUSB_STATE_CONFIGURED) {
994 if (child_dip &&
995 (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
996 !i_ddi_devi_attached(child_dip))) {
997 state = HWAHC_CFGADM_UNCONFIGURED;
998 } else if (!child_dip) {
999 state = HWAHC_CFGADM_UNCONFIGURED;
1000 } else {
1001 state = HWAHC_CFGADM_CONFIGURED;
1002 }
1003 } else if (dev_info->wdev_state == WUSB_STATE_UNCONNTED) {
1004 if (child_dip) {
1005 state = HWAHC_CFGADM_STILL_REFERENCED;
1006 } else {
1007 state = HWAHC_CFGADM_DISCONNECTED;
1008 }
1009 } else {
1010 if (child_dip) {
1011 state = HWAHC_CFGADM_STILL_REFERENCED;
1012 } else {
1013 state = HWAHC_CFGADM_UNCONFIGURED;
1014 }
1015 }
1016 } else {
1017 state = HWAHC_CFGADM_EMPTY;
1018 }
1019 mutex_exit(&hc_data->hc_mutex);
1020
1021 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1022 "hwahc_cfgadm_state: hwahcp=0x%p, port=%d state=0x%x",
1023 (void *) hwahcp, port, state);
1024
1025 return (state);
1026 }
1027
1028 /* cfgadm ioctl support, now only implements list function */
1029 /* ARGSUSED */
1030 static int
1031 hwahc_cfgadm_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
1032 int mode, cred_t *credp, int *rvalp)
1033 {
1034 dev_info_t *rh_dip;
1035 dev_info_t *child_dip;
1036 struct devctl_iocdata *dcp = NULL;
1037 usb_port_t port = 0;
1038 devctl_ap_state_t ap_state;
1039 int circ, rh_circ, prh_circ;
1040 int rv = 0;
1041 char *msg;
1042
1043 /* read devctl ioctl data */
1044 if ((cmd != DEVCTL_AP_CONTROL) &&
1045 (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
1046
1047 return (EFAULT);
1048 }
1049
1050 mutex_enter(&hwahcp->hwahc_mutex);
1051
1052 rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
1053
1054 switch (cmd) {
1055 case DEVCTL_AP_DISCONNECT:
1056 case DEVCTL_AP_UNCONFIGURE:
1057 case DEVCTL_AP_CONFIGURE:
1058 if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
1059 USB_DPRINTF_L2(PRINT_MASK_ATTA,
1060 hwahcp->hwahc_log_handle,
1061 "hwahc_cfgadm_ioctl: dev already gone");
1062 mutex_exit(&hwahcp->hwahc_mutex);
1063 if (dcp) {
1064 ndi_dc_freehdl(dcp);
1065 }
1066
1067 return (EIO);
1068 }
1069 /* FALLTHROUGH */
1070 case DEVCTL_AP_GETSTATE:
1071 if ((port = hwahc_get_port_num(hwahcp, dcp)) == 0) {
1072 USB_DPRINTF_L2(PRINT_MASK_ATTA,
1073 hwahcp->hwahc_log_handle,
1074 "hwahc_cfgadm_ioctl: bad port");
1075 mutex_exit(&hwahcp->hwahc_mutex);
1076 if (dcp) {
1077 ndi_dc_freehdl(dcp);
1078 }
1079
1080 return (EINVAL);
1081 }
1082 break;
1083 case DEVCTL_AP_CONTROL:
1084
1085 break;
1086 default:
1087 mutex_exit(&hwahcp->hwahc_mutex);
1088 if (dcp) {
1089 ndi_dc_freehdl(dcp);
1090 }
1091
1092 return (ENOTTY);
1093 }
1094
1095 /* should not happen, just in case */
1096 if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
1097 mutex_exit(&hwahcp->hwahc_mutex);
1098 if (dcp) {
1099 ndi_dc_freehdl(dcp);
1100 }
1101
1102 return (EIO);
1103 }
1104
1105 mutex_exit(&hwahcp->hwahc_mutex);
1106
1107 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
1108 ndi_devi_enter(rh_dip, &rh_circ);
1109 ndi_devi_enter(hwahcp->hwahc_dip, &circ);
1110
1111 mutex_enter(&hwahcp->hwahc_mutex);
1112
1113 switch (cmd) {
1114 case DEVCTL_AP_DISCONNECT:
1115 /* TODO: not supported now */
1116 rv = EIO;
1117 break;
1118 case DEVCTL_AP_UNCONFIGURE:
1119 /* TODO: not supported now */
1120 rv = EIO;
1121 break;
1122 case DEVCTL_AP_CONFIGURE:
1123 /* TODO: not supported now */
1124 rv = EIO;
1125 break;
1126 case DEVCTL_AP_GETSTATE:
1127 switch (hwahc_cfgadm_state(hwahcp, port)) {
1128 case HWAHC_CFGADM_DISCONNECTED:
1129 /* port previously 'disconnected' by cfgadm */
1130 ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
1131 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1132 ap_state.ap_condition = AP_COND_OK;
1133
1134 break;
1135 case HWAHC_CFGADM_UNCONFIGURED:
1136 ap_state.ap_rstate = AP_RSTATE_CONNECTED;
1137 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1138 ap_state.ap_condition = AP_COND_OK;
1139
1140 break;
1141 case HWAHC_CFGADM_CONFIGURED:
1142 ap_state.ap_rstate = AP_RSTATE_CONNECTED;
1143 ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
1144 ap_state.ap_condition = AP_COND_OK;
1145
1146 break;
1147 case HWAHC_CFGADM_STILL_REFERENCED:
1148 ap_state.ap_rstate = AP_RSTATE_EMPTY;
1149 ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
1150 ap_state.ap_condition = AP_COND_UNUSABLE;
1151
1152 break;
1153 case HWAHC_CFGADM_EMPTY:
1154 default:
1155 ap_state.ap_rstate = AP_RSTATE_EMPTY;
1156 ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1157 ap_state.ap_condition = AP_COND_OK;
1158
1159 break;
1160 }
1161
1162 ap_state.ap_last_change = (time_t)-1;
1163 ap_state.ap_error_code = 0;
1164 ap_state.ap_in_transition = 0;
1165
1166 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1167 "DEVCTL_AP_GETSTATE: "
1168 "ostate=0x%x, rstate=0x%x, condition=0x%x",
1169 ap_state.ap_ostate,
1170 ap_state.ap_rstate, ap_state.ap_condition);
1171
1172 /* copy the return-AP-state information to the user space */
1173 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
1174 rv = EFAULT;
1175 }
1176
1177 break;
1178 case DEVCTL_AP_CONTROL:
1179 {
1180 /*
1181 * Generic devctl for hardware-specific functionality.
1182 * For list of sub-commands see hubd_impl.h
1183 */
1184 hubd_ioctl_data_t ioc; /* for 64 byte copies */
1185
1186 /* copy user ioctl data in first */
1187 #ifdef _MULTI_DATAMODEL
1188 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1189 hubd_ioctl_data_32_t ioc32;
1190
1191 if (ddi_copyin((void *)arg, (void *)&ioc32,
1192 sizeof (ioc32), mode) != 0) {
1193 rv = EFAULT;
1194
1195 break;
1196 }
1197 ioc.cmd = (uint_t)ioc32.cmd;
1198 ioc.port = (uint_t)ioc32.port;
1199 ioc.get_size = (uint_t)ioc32.get_size;
1200 ioc.buf = (caddr_t)(uintptr_t)ioc32.buf;
1201 ioc.bufsiz = (uint_t)ioc32.bufsiz;
1202 ioc.misc_arg = (uint_t)ioc32.misc_arg;
1203 } else
1204 #endif /* _MULTI_DATAMODEL */
1205 if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
1206 mode) != 0) {
1207 rv = EFAULT;
1208
1209 break;
1210 }
1211
1212 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1213 "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
1214 "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd,
1215 ioc.port, ioc.get_size, (void *) ioc.buf, ioc.bufsiz,
1216 ioc.misc_arg);
1217
1218 /*
1219 * To avoid BE/LE and 32/64 issues, a get_size always
1220 * returns a 32-bit number.
1221 */
1222 if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
1223 rv = EINVAL;
1224
1225 break;
1226 }
1227
1228 switch (ioc.cmd) {
1229 case USB_DESCR_TYPE_DEV:
1230 msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
1231 if (ioc.get_size) {
1232 /* uint32 so this works 32/64 */
1233 uint32_t size = sizeof (usb_dev_descr_t);
1234
1235 if (ddi_copyout((void *)&size, ioc.buf,
1236 ioc.bufsiz, mode) != 0) {
1237 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1238 hwahcp->hwahc_log_handle,
1239 "%s: get_size copyout failed", msg);
1240 rv = EIO;
1241
1242 break;
1243 }
1244 } else { /* send out the actual descr */
1245 usb_dev_descr_t *dev_descrp;
1246
1247 /* check child_dip */
1248 if ((child_dip = hwahc_get_child_dip(hwahcp,
1249 ioc.port)) == NULL) {
1250 rv = EINVAL;
1251
1252 break;
1253 }
1254
1255 dev_descrp = usb_get_dev_descr(child_dip);
1256 if (ioc.bufsiz != sizeof (*dev_descrp)) {
1257 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1258 hwahcp->hwahc_log_handle,
1259 "%s: bufsize passed (%d) != sizeof "
1260 "usba_device_descr_t (%d)", msg,
1261 ioc.bufsiz, dev_descrp->bLength);
1262 rv = EINVAL;
1263
1264 break;
1265 }
1266
1267 if (ddi_copyout((void *)dev_descrp,
1268 ioc.buf, ioc.bufsiz, mode) != 0) {
1269 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1270 hwahcp->hwahc_log_handle,
1271 "%s: copyout failed.", msg);
1272 rv = EIO;
1273
1274 break;
1275 }
1276 }
1277 break;
1278 case USB_DESCR_TYPE_CFG:
1279 {
1280 usba_device_t *child_ud = NULL;
1281 uint32_t idx = ioc.misc_arg;
1282 uint32_t cfg_len = 0;
1283
1284 if ((child_dip =
1285 hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1286 rv = EINVAL;
1287
1288 break;
1289 }
1290 child_ud = usba_get_usba_device(child_dip);
1291 cfg_len = (uint32_t)child_ud->usb_cfg_array_len[idx];
1292
1293 msg = "DEVCTL_AP_CONTROL: GET_CONFIG_DESC";
1294 if (ioc.get_size) {
1295 if (ddi_copyout((void *)&cfg_len, ioc.buf,
1296 ioc.bufsiz, mode) != 0) {
1297 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1298 hwahcp->hwahc_log_handle,
1299 "%s: get_size copyout failed", msg);
1300 rv = EIO;
1301
1302 break;
1303 }
1304 } else { /* send out the actual descr */
1305 uchar_t *cfg_descr =
1306 child_ud->usb_cfg_array[idx];
1307
1308 if (ioc.bufsiz != cfg_len) {
1309 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1310 hwahcp->hwahc_log_handle,
1311 "%s: bufsize passed (%d) != size "
1312 "of cfg_descr (%d)", msg,
1313 ioc.bufsiz, cfg_len);
1314 rv = EINVAL;
1315
1316 break;
1317 }
1318
1319 if (ddi_copyout((void *)cfg_descr,
1320 ioc.buf, ioc.bufsiz, mode) != 0) {
1321 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1322 hwahcp->hwahc_log_handle,
1323 "%s: copyout failed.", msg);
1324 rv = EIO;
1325
1326 break;
1327 }
1328 }
1329 break;
1330 }
1331 case USB_DESCR_TYPE_STRING:
1332 {
1333 char *str;
1334 uint32_t size;
1335 usba_device_t *usba_device;
1336
1337 msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
1338 USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1339 hwahcp->hwahc_log_handle,
1340 "%s: string request: %d", msg, ioc.misc_arg);
1341
1342 /* recheck */
1343 if ((child_dip =
1344 hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1345 rv = EINVAL;
1346
1347 break;
1348 }
1349 usba_device = usba_get_usba_device(child_dip);
1350
1351 switch (ioc.misc_arg) {
1352 case HUBD_MFG_STR:
1353 str = usba_device->usb_mfg_str;
1354
1355 break;
1356 case HUBD_PRODUCT_STR:
1357 str = usba_device->usb_product_str;
1358
1359 break;
1360 case HUBD_SERIALNO_STR:
1361 str = usba_device->usb_serialno_str;
1362
1363 break;
1364 case HUBD_CFG_DESCR_STR:
1365 mutex_enter(&usba_device->usb_mutex);
1366 str = usba_device->usb_cfg_str_descr[
1367 usba_device->usb_active_cfg_ndx];
1368 mutex_exit(&usba_device->usb_mutex);
1369
1370 break;
1371 default:
1372 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1373 hwahcp->hwahc_log_handle,
1374 "%s: Invalid string request", msg);
1375 rv = EINVAL;
1376
1377 break;
1378 } /* end of switch */
1379
1380 if (rv != 0) {
1381
1382 break;
1383 }
1384
1385 size = (str != NULL) ? strlen(str) + 1 : 0;
1386 if (ioc.get_size) {
1387 if (ddi_copyout((void *)&size, ioc.buf,
1388 ioc.bufsiz, mode) != 0) {
1389 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1390 hwahcp->hwahc_log_handle,
1391 "%s: copyout of size failed.", msg);
1392 rv = EIO;
1393
1394 break;
1395 }
1396 } else {
1397 if (size == 0) {
1398 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1399 hwahcp->hwahc_log_handle,
1400 "%s: String is NULL", msg);
1401 rv = EINVAL;
1402
1403 break;
1404 }
1405
1406 if (ioc.bufsiz != size) {
1407 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1408 hwahcp->hwahc_log_handle,
1409 "%s: string buf size wrong", msg);
1410 rv = EINVAL;
1411
1412 break;
1413 }
1414
1415 if (ddi_copyout((void *)str, ioc.buf,
1416 ioc.bufsiz, mode) != 0) {
1417 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1418 hwahcp->hwahc_log_handle,
1419 "%s: copyout failed.", msg);
1420 rv = EIO;
1421
1422 break;
1423 }
1424 }
1425 break;
1426 }
1427 case HUBD_GET_CFGADM_NAME:
1428 {
1429 uint32_t name_len;
1430 const char *name;
1431
1432 /* recheck */
1433 if ((child_dip =
1434 hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1435 rv = EINVAL;
1436
1437 break;
1438 }
1439 name = ddi_node_name(child_dip);
1440 if (name == NULL) {
1441 name = "unsupported";
1442 }
1443 name_len = strlen(name) + 1;
1444
1445 msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
1446 USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1447 hwahcp->hwahc_log_handle,
1448 "%s: name=%s name_len=%d", msg, name, name_len);
1449
1450 if (ioc.get_size) {
1451 if (ddi_copyout((void *)&name_len,
1452 ioc.buf, ioc.bufsiz, mode) != 0) {
1453 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1454 hwahcp->hwahc_log_handle,
1455 "%s: copyout of size failed", msg);
1456 rv = EIO;
1457
1458 break;
1459 }
1460 } else {
1461 if (ioc.bufsiz != name_len) {
1462 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1463 hwahcp->hwahc_log_handle,
1464 "%s: string buf length wrong", msg);
1465 rv = EINVAL;
1466
1467 break;
1468 }
1469
1470 if (ddi_copyout((void *)name, ioc.buf,
1471 ioc.bufsiz, mode) != 0) {
1472 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1473 hwahcp->hwahc_log_handle,
1474 "%s: copyout failed.", msg);
1475 rv = EIO;
1476
1477 break;
1478 }
1479 }
1480
1481 break;
1482 }
1483
1484 /*
1485 * Return the config index for the currently-configured
1486 * configuration.
1487 */
1488 case HUBD_GET_CURRENT_CONFIG:
1489 {
1490 uint_t config_index;
1491 uint32_t size = sizeof (config_index);
1492 usba_device_t *usba_device;
1493
1494 msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
1495 USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1496 hwahcp->hwahc_log_handle, "%s", msg);
1497
1498 /*
1499 * Return the config index for the configuration
1500 * currently in use.
1501 * Recheck if child_dip exists
1502 */
1503 if ((child_dip =
1504 hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1505 rv = EINVAL;
1506
1507 break;
1508 }
1509
1510 usba_device = usba_get_usba_device(child_dip);
1511 mutex_enter(&usba_device->usb_mutex);
1512 config_index = usba_device->usb_active_cfg_ndx;
1513 mutex_exit(&usba_device->usb_mutex);
1514
1515 if (ioc.get_size) {
1516 if (ddi_copyout((void *)&size,
1517 ioc.buf, ioc.bufsiz, mode) != 0) {
1518 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1519 hwahcp->hwahc_log_handle,
1520 "%s: copyout of size failed.", msg);
1521 rv = EIO;
1522
1523 break;
1524 }
1525 } else {
1526 if (ioc.bufsiz != size) {
1527 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1528 hwahcp->hwahc_log_handle,
1529 "%s: buffer size wrong", msg);
1530 rv = EINVAL;
1531
1532 break;
1533 }
1534 if (ddi_copyout((void *)&config_index,
1535 ioc.buf, ioc.bufsiz, mode) != 0) {
1536 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1537 hwahcp->hwahc_log_handle,
1538 "%s: copyout failed", msg);
1539 rv = EIO;
1540 }
1541 }
1542
1543 break;
1544 }
1545 case HUBD_GET_DEVICE_PATH:
1546 {
1547 char *path;
1548 uint32_t size;
1549
1550 msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
1551 USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1552 hwahcp->hwahc_log_handle, "%s", msg);
1553
1554 /* Recheck if child_dip exists */
1555 if ((child_dip =
1556 hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1557 rv = EINVAL;
1558
1559 break;
1560 }
1561
1562 /* ddi_pathname doesn't supply /devices, so we do. */
1563 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1564 (void) strcpy(path, "/devices");
1565 (void) ddi_pathname(child_dip, path + strlen(path));
1566 size = strlen(path) + 1;
1567
1568 USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1569 hwahcp->hwahc_log_handle,
1570 "%s: device path=%s size=%d", msg, path, size);
1571
1572 if (ioc.get_size) {
1573 if (ddi_copyout((void *)&size,
1574 ioc.buf, ioc.bufsiz, mode) != 0) {
1575
1576 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1577 hwahcp->hwahc_log_handle,
1578 "%s: copyout of size failed.", msg);
1579 rv = EIO;
1580 }
1581 } else {
1582 if (ioc.bufsiz != size) {
1583 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1584 hwahcp->hwahc_log_handle,
1585 "%s: buffer wrong size.", msg);
1586 rv = EINVAL;
1587 } else if (ddi_copyout((void *)path,
1588 ioc.buf, ioc.bufsiz, mode) != 0) {
1589 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1590 hwahcp->hwahc_log_handle,
1591 "%s: copyout failed.", msg);
1592 rv = EIO;
1593 }
1594 }
1595 kmem_free(path, MAXPATHLEN);
1596
1597 break;
1598 }
1599 case HUBD_REFRESH_DEVDB:
1600 msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
1601 USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1602 hwahcp->hwahc_log_handle, "%s", msg);
1603
1604 if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
1605 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1606 hwahcp->hwahc_log_handle,
1607 "%s: Failed: %d", msg, rv);
1608 rv = EIO;
1609 }
1610
1611 break;
1612 default:
1613 rv = ENOTSUP;
1614 } /* end switch */
1615
1616 break;
1617 }
1618
1619 default:
1620 rv = ENOTTY;
1621 }
1622
1623 if (dcp) {
1624 ndi_dc_freehdl(dcp);
1625 }
1626
1627 mutex_exit(&hwahcp->hwahc_mutex);
1628
1629 ndi_devi_exit(hwahcp->hwahc_dip, circ);
1630 ndi_devi_exit(rh_dip, rh_circ);
1631 ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
1632
1633 return (rv);
1634 }
1635
1636 /* update CHID for the hc driver, return 0 on success */
1637 static int
1638 hwahc_set_chid(hwahc_state_t *hwahcp, uint8_t *chid)
1639 {
1640 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
1641
1642 ASSERT(!mutex_owned(&hc_data->hc_mutex));
1643
1644 /* same as the old CHID, return success */
1645 if (memcmp(chid, hc_data->hc_chid, 16) == 0) {
1646
1647 return (0);
1648 }
1649
1650 /*
1651 * stop hw from working before updating CHID
1652 * this may not be necessary but so far we don't know
1653 * other ways to do it safely
1654 */
1655 if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
1656 /* use final_stop to fully stop the hwa */
1657 if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
1658
1659 return (EIO);
1660 }
1661
1662 mutex_enter(&hc_data->hc_mutex);
1663 (void) memcpy(hc_data->hc_chid, chid, 16);
1664 mutex_exit(&hc_data->hc_mutex);
1665
1666 /* restart the host */
1667 if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
1668
1669 return (EIO);
1670 }
1671
1672 return (0);
1673 }
1674
1675 /* hc is stopped or partially stopped, simply update */
1676 mutex_enter(&hc_data->hc_mutex);
1677 (void) memcpy(hc_data->hc_chid, chid, 16);
1678 mutex_exit(&hc_data->hc_mutex);
1679
1680 return (0);
1681 }
1682
1683 /*
1684 * wusbadm ioctl support
1685 */
1686 /* ARGSUSED */
1687 static int
1688 hwahc_wusb_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
1689 int mode, cred_t *credp, int *rvalp)
1690 {
1691 int rv = 0;
1692 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
1693
1694 if (drv_priv(credp) != 0) {
1695 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1696 "hwahc_wusb_ioctl: user must have SYS_DEVICE privilege,"
1697 "cmd=%x", cmd);
1698
1699 return (EPERM);
1700 }
1701
1702 mutex_enter(&hwahcp->hwahc_mutex);
1703
1704 switch (cmd) {
1705 case WUSB_HC_GET_DSTATE: /* Get device state: wusbadm list */
1706 {
1707 wusb_hc_get_dstate_t state;
1708 usb_port_t port = 0;
1709
1710 if (ddi_copyin((void *)arg, (void *)&state, sizeof (state),
1711 mode) != 0) {
1712 rv = EFAULT;
1713
1714 break;
1715 }
1716
1717 mutex_enter(&hc_data->hc_mutex);
1718
1719 if (wusb_hc_is_dev_connected(hc_data, &state.cdid[0], &port)) {
1720 state.state = hc_data->hc_dev_infos[port]->wdev_state;
1721 } else {
1722 /* cdid not found */
1723 state.state = WUSB_STATE_UNCONNTED;
1724 }
1725
1726 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1727 "hwahc_wusb_ioctl: hc_data=%p, port = %d, state=%d",
1728 (void *) hc_data, port, state.state);
1729
1730 mutex_exit(&hc_data->hc_mutex);
1731
1732 if (state.state == WUSB_STATE_CONFIGURED) {
1733 /* Get the bind device node name of this child */
1734 (void) memset(state.nodename, 0, MAX_USB_NODENAME);
1735 (void) snprintf(state.nodename, MAX_USB_NODENAME, "%s",
1736 ddi_node_name(hwahc_get_child_dip(hwahcp, port)));
1737
1738 USB_DPRINTF_L3(PRINT_MASK_CBOPS,
1739 hwahcp->hwahc_log_handle,
1740 "WUSB_HC_GET_DSTATE: nodename %s", state.nodename);
1741 }
1742
1743 if (ddi_copyout((void *)&state, (void *)arg,
1744 sizeof (state), mode) != 0) {
1745 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1746 hwahcp->hwahc_log_handle,
1747 "WUSB_HC_GET_DSTATE: copyout failed");
1748 rv = EIO;
1749 }
1750
1751 break;
1752 }
1753
1754 case WUSB_HC_GET_MAC_ADDR: /* Get host MAC addr */
1755 {
1756 uint8_t mac_addr[6];
1757
1758 bzero(mac_addr, 6);
1759
1760 /*
1761 * get UWB 48-bit mac address
1762 * Section 8.6.2.2.
1763 */
1764 if (uwb_get_mac_addr(hwahcp->hwahc_dip, mac_addr) !=
1765 USB_SUCCESS) {
1766 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1767 hwahcp->hwahc_log_handle,
1768 "WUSB_HC_GET_MAC_ADDR: get mac failed");
1769 rv = EIO;
1770
1771 break;
1772 }
1773
1774 if (ddi_copyout((void *)mac_addr, (void *)arg,
1775 6, mode) != 0) {
1776 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1777 hwahcp->hwahc_log_handle,
1778 "WUSB_HC_GET_MAC_ADDR: copyout failed");
1779 rv = EIO;
1780 }
1781
1782 break;
1783 }
1784 case WUSB_HC_ADD_CC:
1785 {
1786 /*
1787 * add a new device CC to host's list: wusbadm associate
1788 * Or, the application can pass in a fake CC with only CHID set
1789 * to set the host's CHID.
1790 */
1791 wusb_hc_cc_list_t *cc_list;
1792
1793 cc_list = kmem_zalloc(sizeof (wusb_hc_cc_list_t), KM_SLEEP);
1794
1795 if (ddi_copyin((void *)arg, (void *)&cc_list->cc,
1796 sizeof (wusb_cc_t), mode) != 0) {
1797 rv = EFAULT;
1798 kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1799
1800 break;
1801 }
1802
1803 /* update CHID only when cc list is empty */
1804 mutex_enter(&hc_data->hc_mutex);
1805 if (hc_data->hc_cc_list == NULL) {
1806 mutex_exit(&hc_data->hc_mutex);
1807
1808 if ((rv = hwahc_set_chid(hwahcp,
1809 cc_list->cc.CHID)) != 0) {
1810 kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1811
1812 break;
1813 }
1814
1815 mutex_enter(&hc_data->hc_mutex);
1816 } else {
1817 /* fail if the CHID in the new CC does not match */
1818 if (memcmp(cc_list->cc.CHID, hc_data->hc_chid,
1819 16) != 0) {
1820 rv = EINVAL;
1821 kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1822
1823 mutex_exit(&hc_data->hc_mutex);
1824 break;
1825 }
1826 }
1827 cc_list->next = NULL;
1828
1829 wusb_hc_add_cc(&hc_data->hc_cc_list, cc_list);
1830 mutex_exit(&hc_data->hc_mutex);
1831
1832 break;
1833 }
1834 case WUSB_HC_REM_CC:
1835 {
1836 wusb_cc_t cc;
1837 usb_port_t port;
1838
1839 if (ddi_copyin((void *)arg, (void *)&cc, sizeof (wusb_cc_t),
1840 mode) != 0) {
1841 rv = EFAULT;
1842
1843 break;
1844 }
1845
1846 /* check if the CHID in the CC matches */
1847 if (memcmp(cc.CHID, hc_data->hc_chid, 16) != 0) {
1848 rv = EINVAL;
1849
1850 break;
1851 }
1852
1853 /* if the device is connected, disconnect it first */
1854 mutex_enter(&hc_data->hc_mutex);
1855 if (wusb_hc_is_dev_connected(hc_data, cc.CDID, &port)) {
1856 mutex_exit(&hc_data->hc_mutex);
1857 mutex_exit(&hwahcp->hwahc_mutex);
1858 /*
1859 * clean up host side state, device not
1860 * really disconnected. But user can safely remove
1861 * the device now.
1862 */
1863 (void) hwahc_destroy_child(hc_data->hc_dip, port);
1864 mutex_enter(&hwahcp->hwahc_mutex);
1865 mutex_enter(&hc_data->hc_mutex);
1866 }
1867
1868 wusb_hc_rem_cc(&hc_data->hc_cc_list, &cc);
1869 mutex_exit(&hc_data->hc_mutex);
1870
1871 break;
1872 }
1873 case WUSB_HC_SET_CHANNEL: /* for debug purpose */
1874 {
1875 uint8_t channel;
1876
1877 channel = (uint8_t)arg;
1878
1879 if (hwahcp->hwahc_hc_data.hc_channel == channel) {
1880 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1881 hwahcp->hwahc_log_handle,
1882 "WUSB_HC_SET_CHANNEL ioctl: same as existing");
1883
1884 break;
1885 }
1886
1887 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
1888 /* beacon is already started, stop it first */
1889 if (uwb_stop_beacon(hwahcp->hwahc_dip) != USB_SUCCESS) {
1890 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1891 hwahcp->hwahc_log_handle,
1892 "WUSB_HC_SET_CHANNEL ioctl: "
1893 "stop beacon failed");
1894 rv = EIO;
1895
1896 break;
1897 }
1898 /* update channel number */
1899 hwahcp->hwahc_hc_data.hc_channel = channel;
1900 /* restart beacon on the new channel */
1901 if (uwb_start_beacon(hwahcp->hwahc_dip,
1902 channel) != USB_SUCCESS) {
1903 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1904 hwahcp->hwahc_log_handle,
1905 "WUSB_HC_SET_CHANNEL ioctl: "
1906 "restart beacon failed");
1907 rv = EIO;
1908 }
1909
1910 break;
1911 }
1912
1913 /* beacon is not started, simply update channel number */
1914 hwahcp->hwahc_hc_data.hc_channel = channel;
1915
1916 break;
1917 }
1918 case WUSB_HC_START:
1919 {
1920 int flag;
1921
1922
1923 flag = (int)arg;
1924
1925 if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
1926 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1927 hwahcp->hwahc_log_handle,
1928 "WUSB_HC_START ioctl: already started");
1929
1930 break;
1931 }
1932
1933 /*
1934 * now we start hc only when the cc list is not NULL
1935 * this limitation may be removed if we support
1936 * numeric association, but CHID needs to be set
1937 * in advance for the hc to work
1938 */
1939 mutex_enter(&hc_data->hc_mutex);
1940 if (hc_data->hc_cc_list == NULL) {
1941 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1942 hwahcp->hwahc_log_handle,
1943 "WUSB_HC_START ioctl: cc list not inited");
1944 rv = EINVAL;
1945
1946 mutex_exit(&hc_data->hc_mutex);
1947
1948 break;
1949 }
1950 mutex_exit(&hc_data->hc_mutex);
1951
1952 /* cannot be both */
1953 if ((flag & WUSB_HC_INITIAL_START) && (flag &
1954 WUSB_HC_CHANNEL_START)) {
1955 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1956 hwahcp->hwahc_log_handle,
1957 "WUSB_HC_START ioctl: flag cannot coexist");
1958 rv = EINVAL;
1959
1960 break;
1961 }
1962
1963 /*
1964 * init Mac layer 16-bit dev addr. it is important for
1965 * authentication. It'd be better to let UWB provide
1966 * this address.
1967 */
1968 mutex_enter(&hc_data->hc_mutex);
1969 if (hc_data->hc_addr == 0) {
1970 uint16_t dev_addr = HWAHC_DEV_ADDR_BASE +
1971 ddi_get_instance(hwahcp->hwahc_dip);
1972
1973 mutex_exit(&hc_data->hc_mutex);
1974 /* set UWB 16-bit dev address */
1975 if (uwb_set_dev_addr(hwahcp->hwahc_dip,
1976 dev_addr) != USB_SUCCESS) {
1977 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1978 hwahcp->hwahc_log_handle,
1979 "WUSB_HC_START ioctl: set dev addr failed");
1980 rv = EIO;
1981
1982 break;
1983 }
1984
1985 /* verify the dev addr is set correctly */
1986 if (uwb_get_dev_addr(hwahcp->hwahc_dip,
1987 &dev_addr) != USB_SUCCESS) {
1988 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1989 hwahcp->hwahc_log_handle,
1990 "WUSB_HC_START ioctl: get dev addr failed");
1991 rv = EIO;
1992
1993 break;
1994 }
1995
1996 mutex_enter(&hc_data->hc_mutex);
1997 hc_data->hc_addr = dev_addr;
1998 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1999 hwahcp->hwahc_log_handle,
2000 "host dev addr = 0x%x", dev_addr);
2001 }
2002 mutex_exit(&hc_data->hc_mutex);
2003
2004 /* start functions of wusb host */
2005 if ((flag & WUSB_HC_INITIAL_START) &&
2006 (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED)) {
2007 if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
2008 rv = EIO;
2009 }
2010 } else if ((flag & WUSB_HC_CHANNEL_START) &&
2011 (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED)) {
2012 if (hwahc_hc_channel_start(hwahcp) != USB_SUCCESS) {
2013 rv = EIO;
2014 }
2015 } else {
2016 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2017 hwahcp->hwahc_log_handle,
2018 "WUSB_HC_START ioctl: unknown flag (%d) or "
2019 "state (%d)", flag, hwahcp->hwahc_hw_state);
2020 rv = EINVAL;
2021 }
2022
2023 break;
2024 }
2025 case WUSB_HC_STOP:
2026 {
2027 int flag;
2028
2029 flag = (int)arg;
2030
2031 /* cannot be both */
2032 if ((flag & WUSB_HC_FINAL_STOP) && (flag &
2033 WUSB_HC_CHANNEL_STOP)) {
2034 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2035 hwahcp->hwahc_log_handle,
2036 "WUSB_HC_STOP ioctl: flag cannot coexist");
2037 rv = EINVAL;
2038
2039 break;
2040 }
2041
2042 if (flag & WUSB_HC_FINAL_STOP) {
2043 if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
2044 rv = EIO;
2045 }
2046 } else if (flag & WUSB_HC_CHANNEL_STOP) {
2047 if (hwahc_hc_channel_stop(hwahcp) != USB_SUCCESS) {
2048 rv = EIO;
2049 }
2050 } else {
2051 /* must be one of the STOP flag */
2052 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2053 hwahcp->hwahc_log_handle,
2054 "WUSB_HC_STOP ioctl: invalid flag = %d", flag);
2055 rv = EINVAL;
2056 }
2057
2058 /* REM_ALL_CC flag is optional */
2059 if ((rv == 0) && (flag & WUSB_HC_REM_ALL_CC)) {
2060 mutex_enter(&hc_data->hc_mutex);
2061 if (hc_data->hc_cc_list) {
2062 wusb_hc_free_cc_list(hc_data->hc_cc_list);
2063 hc_data->hc_cc_list = NULL;
2064 }
2065 mutex_exit(&hc_data->hc_mutex);
2066 }
2067
2068 break;
2069 }
2070 case WUSB_HC_GET_HSTATE:
2071 {
2072 int state;
2073
2074 if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
2075 state = WUSB_HC_DISCONNTED;
2076 } else {
2077 switch (hwahcp->hwahc_hw_state) {
2078 case HWAHC_HW_STOPPED:
2079 state = WUSB_HC_STOPPED;
2080 break;
2081 case HWAHC_HW_STARTED:
2082 state = WUSB_HC_STARTED;
2083 break;
2084 case HWAHC_HW_CH_STOPPED:
2085 /*
2086 * app can mark the hwa as disabled
2087 * for this state
2088 */
2089 state = WUSB_HC_CH_STOPPED;
2090 break;
2091 }
2092 }
2093
2094 if (ddi_copyout((void *)&state, (void *)arg,
2095 sizeof (int), mode) != 0) {
2096 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2097 hwahcp->hwahc_log_handle,
2098 "WUSB_HC_GET_HSTATE: copyout failed");
2099 rv = EIO;
2100 }
2101
2102 break;
2103 }
2104 default:
2105 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2106 "hwahc_ioctl: unsupported command");
2107
2108 rv = ENOTSUP;
2109 }
2110 mutex_exit(&hwahcp->hwahc_mutex);
2111
2112 return (rv);
2113 }
2114
2115 static int
2116 hwahc_ioctl(dev_t dev, int cmd, intptr_t arg,
2117 int mode, cred_t *credp, int *rvalp)
2118 {
2119 hwahc_state_t *hwahcp;
2120 int rval;
2121
2122 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2123 HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
2124
2125 return (ENXIO);
2126 }
2127
2128 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2129 "hwahc_ioctl: cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
2130 cmd, arg, mode, (void *) credp, (void *) rvalp, dev);
2131
2132 if (IS_DEVCTL(cmd)) {
2133 /* for cfgadm cmd support */
2134 rval = hwahc_cfgadm_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
2135 } else {
2136 /* for wusbadm cmd support */
2137 rval = hwahc_wusb_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
2138 }
2139
2140 return (rval);
2141 }
2142
2143 /* return the port number corresponding the child dip */
2144 static usb_port_t
2145 hwahc_child_dip2port(hwahc_state_t *hwahcp, dev_info_t *dip)
2146 {
2147 usb_port_t port;
2148 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
2149
2150 mutex_enter(&hc_data->hc_mutex);
2151 for (port = 1; port <= hc_data->hc_num_ports; port++) {
2152 if (hc_data->hc_children_dips[port] == dip) {
2153
2154 break;
2155 }
2156 }
2157 ASSERT(port <= hc_data->hc_num_ports);
2158 mutex_exit(&hc_data->hc_mutex);
2159
2160 return (port);
2161 }
2162
2163 /*
2164 * child post attach/detach notification
2165 */
2166 static void
2167 hwahc_post_attach(hwahc_state_t *hwahcp, dev_info_t *rdip,
2168 struct attachspec *as)
2169 {
2170 /* we don't need additional process for post-attach now */
2171 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2172 "hwahc_post_attach: rdip = 0x%p result = %d", (void *) rdip,
2173 as->result);
2174 }
2175
2176 static void
2177 hwahc_post_detach(hwahc_state_t *hwahcp, dev_info_t *rdip,
2178 struct detachspec *as)
2179 {
2180 /* we don't need additional process for post-detach now */
2181 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2182 "hwahc_post_detach: rdip = 0x%p result = %d", (void *) rdip,
2183 as->result);
2184 }
2185
2186 /*
2187 * bus ctl support.
2188 * To support different operations, such as a PreAttach preparation,
2189 * PostAttach operations. HWA only process the interested operations.
2190 * Other general ones are processed by usba_bus_ctl().
2191 */
2192 static int
2193 hwahc_bus_ctl(dev_info_t *dip, /* dip could be the parent */
2194 dev_info_t *rdip, /* rdip is the dev node to be operated */
2195 ddi_ctl_enum_t op,
2196 void *arg,
2197 void *result)
2198 {
2199 usba_device_t *usba_device = usba_get_usba_device(rdip);
2200 dev_info_t *hubdip = usba_device->usb_root_hub_dip;
2201 hwahc_state_t *hwahcp;
2202 struct attachspec *as;
2203 struct detachspec *ds;
2204
2205 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2206 ddi_get_instance(dip))) == NULL) {
2207
2208 return (DDI_FAILURE);
2209 }
2210
2211 USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2212 "hwahc_bus_ctl:\n\t"
2213 "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
2214 (void *) dip, (void *) rdip, op, (void *) arg);
2215
2216 switch (op) {
2217 case DDI_CTLOPS_ATTACH:
2218 as = (struct attachspec *)arg;
2219
2220 switch (as->when) {
2221 case DDI_PRE :
2222 /* nothing to do basically */
2223 USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2224 hwahcp->hwahc_log_handle,
2225 "DDI_PRE DDI_CTLOPS_ATTACH");
2226 break;
2227 case DDI_POST :
2228 hwahc_post_attach(hwahcp, rdip,
2229 (struct attachspec *)arg);
2230 break;
2231 }
2232
2233 break;
2234 case DDI_CTLOPS_DETACH:
2235 ds = (struct detachspec *)arg;
2236
2237 switch (ds->when) {
2238 case DDI_PRE :
2239 /* nothing to do basically */
2240 USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2241 hwahcp->hwahc_log_handle,
2242 "DDI_PRE DDI_CTLOPS_DETACH");
2243 break;
2244 case DDI_POST :
2245 hwahc_post_detach(hwahcp, rdip,
2246 (struct detachspec *)arg);
2247 break;
2248 }
2249
2250 break;
2251 case DDI_CTLOPS_REPORTDEV: /* the workhorse behind ddi_report_dev */
2252 {
2253 char *name, compat_name[64];
2254
2255 if (usb_owns_device(rdip)) {
2256 (void) snprintf(compat_name,
2257 sizeof (compat_name),
2258 "usb%x,%x",
2259 usba_device->usb_dev_descr->idVendor,
2260 usba_device->usb_dev_descr->idProduct);
2261 } else if (usba_owns_ia(rdip)) {
2262 (void) snprintf(compat_name,
2263 sizeof (compat_name),
2264 "usbia%x,%x.config%x.%x",
2265 usba_device->usb_dev_descr->idVendor,
2266 usba_device->usb_dev_descr->idProduct,
2267 usba_device->usb_cfg_value,
2268 usb_get_if_number(rdip));
2269 } else {
2270 (void) snprintf(compat_name,
2271 sizeof (compat_name),
2272 "usbif%x,%x.config%x.%x",
2273 usba_device->usb_dev_descr->idVendor,
2274 usba_device->usb_dev_descr->idProduct,
2275 usba_device->usb_cfg_value,
2276 usb_get_if_number(rdip));
2277 }
2278
2279 cmn_err(CE_CONT,
2280 "?USB %x.%x %s (%s) operating wirelessly with "
2281 "HWA device: "
2282 "%s@%s, %s%d at bus address %d\n",
2283 (usba_device->usb_dev_descr->bcdUSB & 0xff00) >> 8,
2284 usba_device->usb_dev_descr->bcdUSB & 0xff,
2285 (usb_owns_device(rdip) ? "device" :
2286 ((usba_owns_ia(rdip) ? "interface-association" :
2287 "interface"))),
2288 compat_name,
2289 ddi_node_name(rdip), ddi_get_name_addr(rdip),
2290 ddi_driver_name(rdip),
2291 ddi_get_instance(rdip), usba_device->usb_addr);
2292
2293 name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2294 (void) usba_get_mfg_prod_sn_str(rdip, name, MAXNAMELEN);
2295 if (name[0] != '\0') {
2296 cmn_err(CE_CONT, "?\t%s\n", name);
2297 }
2298 kmem_free(name, MAXNAMELEN);
2299
2300 break;
2301 }
2302 default:
2303 /* pass to usba to handle */
2304 return (usba_bus_ctl(hubdip, rdip, op, arg, result));
2305 }
2306
2307 return (DDI_SUCCESS);
2308 }
2309
2310 /*
2311 * bus enumeration entry points
2312 * Configures the named device(BUS_CONFIG_ONE) or all devices under
2313 * the nexus(BUS_CONFIG_ALL). Drives devinfo state to DS_READY,i.e.device
2314 * is fully operational.
2315 *
2316 * This operation is driven from devfs(reading /devices), devctl, libdevinfo;
2317 * or from within the kernel to attach a boot device or layered underlying
2318 * driver.
2319 */
2320 static int
2321 hwahc_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
2322 void *arg, dev_info_t **child)
2323 {
2324 hwahc_state_t *hwahcp;
2325 int rval, circ;
2326
2327 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2328 ddi_get_instance(dip))) == NULL) {
2329
2330 return (NDI_FAILURE);
2331 }
2332
2333 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2334 "hwahc_bus_config: op=%d", op);
2335
2336 if (hwahc_bus_config_debug) {
2337 flag |= NDI_DEVI_DEBUG;
2338 }
2339
2340 ndi_devi_enter(dip, &circ);
2341 rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
2342 ndi_devi_exit(dip, circ);
2343
2344 return (rval);
2345 }
2346
2347 /*
2348 * Unconfigures the named device or all devices under the nexus. The
2349 * devinfo state is not DS_READY anymore.
2350 * This operations is driven by modunload, devctl or DR branch removal or
2351 * rem_drv(1M).
2352 */
2353 static int
2354 hwahc_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
2355 void *arg)
2356 {
2357 hwahc_state_t *hwahcp;
2358 wusb_hc_data_t *hc_data;
2359 dev_info_t *cdip;
2360 usb_port_t port;
2361 int rval, circ;
2362
2363 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2364 ddi_get_instance(dip))) == NULL) {
2365
2366 return (NDI_FAILURE);
2367 }
2368
2369 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2370 "hwahc_bus_unconfig: op=%d", op);
2371
2372 if (hwahc_bus_config_debug) {
2373 flag |= NDI_DEVI_DEBUG;
2374 }
2375
2376 if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
2377 flag |= NDI_DEVI_REMOVE;
2378 }
2379
2380 /* serialize access */
2381 ndi_devi_enter(dip, &circ);
2382
2383 /* unconfig children, detach them */
2384 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
2385
2386 /* logically zap children's list */
2387 hc_data = &hwahcp->hwahc_hc_data;
2388
2389 mutex_enter(&hc_data->hc_mutex);
2390 for (port = 1; port <= hc_data->hc_num_ports; port++) {
2391 hc_data->hc_children_state[port] |= WUSB_CHILD_ZAP;
2392 }
2393 mutex_exit(&hc_data->hc_mutex);
2394
2395 /* fill in what's left */
2396 for (cdip = ddi_get_child(dip); cdip;
2397 cdip = ddi_get_next_sibling(cdip)) {
2398 usba_device_t *usba_device = usba_get_usba_device(cdip);
2399
2400 if (usba_device == NULL) {
2401
2402 continue;
2403 }
2404 mutex_enter(&hc_data->hc_mutex);
2405 port = usba_device->usb_port;
2406 hc_data->hc_children_dips[port] = cdip;
2407 hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
2408 mutex_exit(&hc_data->hc_mutex);
2409 }
2410
2411 /* physically zap the children we didn't find */
2412 mutex_enter(&hc_data->hc_mutex);
2413 for (port = 1; port <= hc_data->hc_num_ports; port++) {
2414 if (hc_data->hc_children_state[port] & WUSB_CHILD_ZAP) {
2415 wusb_dev_info_t *dev_info;
2416 wusb_secrt_data_t *csecrt_data;
2417 usba_device_t *child_ud;
2418
2419 USB_DPRINTF_L3(PRINT_MASK_EVENTS,
2420 hwahcp->hwahc_log_handle,
2421 "hwahc_bus_unconfig: physically zap port %d", port);
2422
2423 child_ud = hc_data->hc_usba_devices[port];
2424 mutex_exit(&hc_data->hc_mutex);
2425 /* zap the dip and usba_device structure as well */
2426 usba_free_usba_device(child_ud);
2427 mutex_enter(&hc_data->hc_mutex);
2428 hc_data->hc_usba_devices[port] = NULL;
2429
2430 /* dip freed in usba_destroy_child_devi */
2431 hc_data->hc_children_dips[port] = NULL;
2432 hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
2433
2434 /* free hc_dev_infos[port] */
2435 dev_info = hc_data->hc_dev_infos[port];
2436 if (dev_info == NULL) {
2437
2438 continue;
2439 }
2440
2441 /* stop the device's trust timer before deallocate it */
2442 hwahc_stop_trust_timer(dev_info);
2443
2444 if (dev_info->wdev_secrt_data.secrt_encry_descr) {
2445 csecrt_data = &dev_info->wdev_secrt_data;
2446 kmem_free(csecrt_data->secrt_encry_descr,
2447 sizeof (usb_encryption_descr_t) *
2448 csecrt_data->secrt_n_encry);
2449 }
2450 if (dev_info->wdev_uwb_descr) {
2451 kmem_free(dev_info->wdev_uwb_descr,
2452 sizeof (usb_uwb_cap_descr_t));
2453 }
2454 kmem_free(dev_info, sizeof (wusb_dev_info_t));
2455 hc_data->hc_dev_infos[port] = NULL;
2456 }
2457 }
2458 mutex_exit(&hc_data->hc_mutex);
2459
2460 ndi_devi_exit(dip, circ);
2461
2462 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2463 "hwahc_bus_unconfig: rval=%d", rval);
2464
2465 return (rval);
2466 }
2467
2468 /*
2469 * busctl event support
2470 *
2471 * Called by ndi_busop_get_eventcookie(). Return a event cookie
2472 * associated with one event name.
2473 * The eventname should be the one we defined in hwahc_ndi_event_defs
2474 */
2475 static int
2476 hwahc_busop_get_eventcookie(dev_info_t *dip,
2477 dev_info_t *rdip,
2478 char *eventname,
2479 ddi_eventcookie_t *cookie)
2480 {
2481 hwahc_state_t *hwahcp;
2482
2483 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2484 ddi_get_instance(dip))) == NULL) {
2485
2486 return (NDI_FAILURE);
2487 }
2488
2489 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2490 "hwahc_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
2491 "event=%s", (void *)dip, (void *)rdip, eventname);
2492 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2493 "(dip=%s%d, rdip=%s%d)",
2494 ddi_driver_name(dip), ddi_get_instance(dip),
2495 ddi_driver_name(rdip), ddi_get_instance(rdip));
2496
2497 /* return event cookie, iblock cookie, and level */
2498 return (ndi_event_retrieve_cookie(hwahcp->hwahc_ndi_event_hdl,
2499 rdip, eventname, cookie, NDI_EVENT_NOPASS));
2500 }
2501
2502 /*
2503 * Add event handler for a given event cookie
2504 */
2505 static int
2506 hwahc_busop_add_eventcall(dev_info_t *dip,
2507 dev_info_t *rdip,
2508 ddi_eventcookie_t cookie,
2509 void (*callback)(dev_info_t *dip,
2510 ddi_eventcookie_t cookie, void *arg,
2511 void *bus_impldata),
2512 void *arg, ddi_callback_id_t *cb_id)
2513 {
2514 hwahc_state_t *hwahcp;
2515 usb_port_t port;
2516
2517 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2518 ddi_get_instance(dip))) == NULL) {
2519
2520 return (NDI_FAILURE);
2521 }
2522
2523 port = hwahc_child_dip2port(hwahcp, rdip);
2524
2525 mutex_enter(&hwahcp->hwahc_mutex);
2526
2527 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2528 "hwahc_busop_add_eventcall: dip=0x%p, rdip=0x%p "
2529 "cookie=0x%p, cb=0x%p, arg=0x%p",
2530 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
2531 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2532 "(dip=%s%d, rdip=%s%d, event=%s)",
2533 ddi_driver_name(dip), ddi_get_instance(dip),
2534 ddi_driver_name(rdip), ddi_get_instance(rdip),
2535 ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl, cookie));
2536
2537 /* Set flag on children registering events */
2538 switch (ndi_event_cookie_to_tag(hwahcp->hwahc_ndi_event_hdl, cookie)) {
2539 case USBA_EVENT_TAG_HOT_REMOVAL:
2540 hwahcp->hwahc_child_events[port] |=
2541 HWAHC_CHILD_EVENT_DISCONNECT;
2542
2543 break;
2544 case USBA_EVENT_TAG_PRE_SUSPEND:
2545 hwahcp->hwahc_child_events[port] |=
2546 HWAHC_CHILD_EVENT_PRESUSPEND;
2547
2548 break;
2549 default:
2550
2551 break;
2552 }
2553
2554 mutex_exit(&hwahcp->hwahc_mutex);
2555
2556 /* add callback to our event set */
2557 return (ndi_event_add_callback(hwahcp->hwahc_ndi_event_hdl,
2558 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
2559
2560 }
2561
2562
2563 /*
2564 * Remove a callback previously added by bus_add_eventcall()
2565 */
2566 static int
2567 hwahc_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
2568 {
2569 hwahc_state_t *hwahcp;
2570 ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
2571
2572 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2573 ddi_get_instance(dip))) == NULL) {
2574
2575 return (NDI_FAILURE);
2576 }
2577
2578 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2579 "hwahc_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
2580 "cookie=0x%p", (void *)dip, (void *) id->ndi_evtcb_dip,
2581 (void *)id->ndi_evtcb_cookie);
2582 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2583 "(dip=%s%d, rdip=%s%d, event=%s)",
2584 ddi_driver_name(dip), ddi_get_instance(dip),
2585 ddi_driver_name(id->ndi_evtcb_dip),
2586 ddi_get_instance(id->ndi_evtcb_dip),
2587 ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl,
2588 id->ndi_evtcb_cookie));
2589
2590 /* remove event registration from our event set */
2591 return (ndi_event_remove_callback(hwahcp->hwahc_ndi_event_hdl, cb_id));
2592 }
2593
2594 /*
2595 * hwahc_post_event
2596 * post event to a single child on the port depending on the type, i.e.
2597 * to invoke the child's registered callback.
2598 */
2599 static void
2600 hwahc_post_event(hwahc_state_t *hwahcp, usb_port_t port, usba_event_t type)
2601 {
2602 int rval;
2603 dev_info_t *dip;
2604 usba_device_t *usba_device;
2605 ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
2606 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
2607
2608 USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2609 "hwahc_post_event: port=%d event=%s", port,
2610 ndi_event_tag_to_name(hwahcp->hwahc_ndi_event_hdl, type));
2611
2612 cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl, type);
2613 rm_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
2614 USBA_EVENT_TAG_HOT_REMOVAL);
2615 suspend_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
2616 USBA_EVENT_TAG_PRE_SUSPEND);
2617
2618 /*
2619 * Hotplug daemon may be attaching a driver that may be registering
2620 * event callbacks. So it already has got the device tree lock and
2621 * event handle mutex. So to prevent a deadlock while posting events,
2622 * we grab and release the locks in the same order.
2623 */
2624 mutex_enter(&hwahcp->hwahc_mutex);
2625 dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
2626 usba_device = hwahcp->hwahc_hc_data.hc_usba_devices[port];
2627 mutex_exit((&hwahcp->hwahc_mutex));
2628
2629 switch (type) {
2630 case USBA_EVENT_TAG_HOT_REMOVAL:
2631 /* stop this device's timer to prevent its further process */
2632 mutex_enter(&hc_data->hc_mutex);
2633
2634 hwahc_stop_trust_timer(hc_data->hc_dev_infos[port]);
2635 mutex_exit(&hc_data->hc_mutex);
2636
2637 /* Clear the registered event flag */
2638 mutex_enter(&hwahcp->hwahc_mutex);
2639 hwahcp->hwahc_child_events[port] &=
2640 ~HWAHC_CHILD_EVENT_DISCONNECT;
2641 mutex_exit(&hwahcp->hwahc_mutex);
2642
2643 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2644 dip, cookie, NULL);
2645 usba_persistent_pipe_close(usba_device);
2646
2647 /*
2648 * Mark the dip for deletion only after the driver has
2649 * seen the disconnect event to prevent cleanup thread
2650 * from stepping in between.
2651 */
2652 #ifndef __lock_lint
2653 mutex_enter(&DEVI(dip)->devi_lock);
2654 DEVI_SET_DEVICE_REMOVED(dip);
2655 mutex_exit(&DEVI(dip)->devi_lock);
2656 #endif
2657
2658 break;
2659 case USBA_EVENT_TAG_PRE_SUSPEND:
2660 mutex_enter(&hwahcp->hwahc_mutex);
2661 hwahcp->hwahc_child_events[port] &=
2662 ~HWAHC_CHILD_EVENT_PRESUSPEND;
2663 mutex_exit(&hwahcp->hwahc_mutex);
2664
2665 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2666 dip, cookie, NULL);
2667 /*
2668 * persistent pipe close for this event is taken care by the
2669 * caller after verfying that all children can suspend
2670 */
2671
2672 break;
2673 case USBA_EVENT_TAG_HOT_INSERTION:
2674 /*
2675 * Check if this child has missed the disconnect event before
2676 * it registered for event callbacks
2677 */
2678 mutex_enter(&hwahcp->hwahc_mutex);
2679 if (hwahcp->hwahc_child_events[port] &
2680 HWAHC_CHILD_EVENT_DISCONNECT) {
2681 /* clear the flag and post disconnect event */
2682 hwahcp->hwahc_child_events[port] &=
2683 ~HWAHC_CHILD_EVENT_DISCONNECT;
2684 mutex_exit(&hwahcp->hwahc_mutex);
2685
2686 (void) ndi_event_do_callback(
2687 hwahcp->hwahc_ndi_event_hdl,
2688 dip, rm_cookie, NULL);
2689 usba_persistent_pipe_close(usba_device);
2690 mutex_enter(&hwahcp->hwahc_mutex);
2691 }
2692 mutex_exit(&hwahcp->hwahc_mutex);
2693
2694 /*
2695 * Mark the dip as reinserted to prevent cleanup thread
2696 * from stepping in.
2697 */
2698 #ifndef __lock_lint
2699 mutex_enter(&(DEVI(dip)->devi_lock));
2700 DEVI_SET_DEVICE_REINSERTED(dip);
2701 mutex_exit(&(DEVI(dip)->devi_lock));
2702 #endif
2703
2704 rval = usba_persistent_pipe_open(usba_device);
2705 if (rval != USB_SUCCESS) {
2706 USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2707 hwahcp->hwahc_log_handle,
2708 "failed to reopen all pipes on reconnect");
2709 }
2710
2711 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2712 dip, cookie, NULL);
2713
2714 /*
2715 * We might see a connect event only if hotplug thread for
2716 * disconnect event don't run in time.
2717 * Set the flag again, so we don't miss posting a
2718 * disconnect event.
2719 */
2720 mutex_enter(&hwahcp->hwahc_mutex);
2721 hwahcp->hwahc_child_events[port] |=
2722 HWAHC_CHILD_EVENT_DISCONNECT;
2723 mutex_exit(&hwahcp->hwahc_mutex);
2724
2725 break;
2726 case USBA_EVENT_TAG_POST_RESUME:
2727 /*
2728 * Check if this child has missed the pre-suspend event before
2729 * it registered for event callbacks
2730 */
2731 mutex_enter(&hwahcp->hwahc_mutex);
2732 if (hwahcp->hwahc_child_events[port] &
2733 HWAHC_CHILD_EVENT_PRESUSPEND) {
2734 /* clear the flag and post pre_suspend event */
2735 hwahcp->hwahc_child_events[port] &=
2736 ~HWAHC_CHILD_EVENT_PRESUSPEND;
2737 mutex_exit(&hwahcp->hwahc_mutex);
2738 (void) ndi_event_do_callback(
2739 hwahcp->hwahc_ndi_event_hdl,
2740 dip, suspend_cookie, NULL);
2741 mutex_enter(&hwahcp->hwahc_mutex);
2742 }
2743 mutex_exit(&hwahcp->hwahc_mutex);
2744
2745 mutex_enter(&usba_device->usb_mutex);
2746 usba_device->usb_no_cpr = 0;
2747 mutex_exit(&usba_device->usb_mutex);
2748
2749 /*
2750 * Since the pipe has already been opened by whub
2751 * at DDI_RESUME time, there is no need for a
2752 * persistent pipe open
2753 */
2754 (void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2755 dip, cookie, NULL);
2756
2757 /*
2758 * Set the flag again, so we don't miss posting a
2759 * pre-suspend event. This enforces a tighter
2760 * dev_state model.
2761 */
2762 mutex_enter(&hwahcp->hwahc_mutex);
2763 hwahcp->hwahc_child_events[port] |=
2764 HWAHC_CHILD_EVENT_PRESUSPEND;
2765 mutex_exit(&hwahcp->hwahc_mutex);
2766 break;
2767 }
2768 }
2769
2770 /*
2771 * hwahc_run_callbacks:
2772 * Send an event to all children
2773 */
2774 static void
2775 hwahc_run_callbacks(hwahc_state_t *hwahcp, usba_event_t type)
2776 {
2777 usb_port_t port;
2778 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
2779
2780 USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2781 "hwahc_run_callbacks:");
2782
2783 mutex_enter(&hc_data->hc_mutex);
2784 for (port = 1; port <= hc_data->hc_num_ports; port++) {
2785 if (hc_data->hc_children_dips[port]) {
2786 mutex_exit(&hc_data->hc_mutex);
2787 hwahc_post_event(hwahcp, port, type);
2788 mutex_enter(&hc_data->hc_mutex);
2789 }
2790 }
2791 mutex_exit(&hc_data->hc_mutex);
2792 }
2793
2794 /*
2795 * hwahc_disconnect_event_cb:
2796 * Called when hwa device hotplug-removed.
2797 * Close pipes
2798 * Post event to child
2799 * Set state to DISCONNECTED
2800 */
2801 static int
2802 hwahc_disconnect_event_cb(dev_info_t *dip)
2803 {
2804 int instance = ddi_get_instance(dip);
2805 hwahc_state_t *hwahcp;
2806 int circ;
2807
2808 if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2809
2810 return (USB_FAILURE);
2811 }
2812
2813 USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2814 "hwahc_disconnect_event_cb: dip = 0x%p", (void *)dip);
2815
2816 ndi_devi_enter(dip, &circ);
2817
2818 mutex_enter(&hwahcp->hwahc_mutex);
2819
2820 USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2821 "hwahc_disconnect_event_cb: devstate= %d hw-state=%d",
2822 hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state);
2823 switch (hwahcp->hwahc_dev_state) {
2824 case USB_DEV_ONLINE:
2825 case USB_DEV_PWRED_DOWN:
2826 hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
2827
2828 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
2829 mutex_exit(&hwahcp->hwahc_mutex);
2830 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
2831 mutex_enter(&hwahcp->hwahc_mutex);
2832 hwahc_stop_result_thread(hwahcp);
2833 hwahc_drain_notif_queue(hwahcp);
2834 }
2835 /* FALLTHROUGH */
2836 case USB_DEV_SUSPENDED:
2837 /* remain in this state */
2838 mutex_exit(&hwahcp->hwahc_mutex);
2839 hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
2840 mutex_enter(&hwahcp->hwahc_mutex);
2841
2842 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
2843
2844 break;
2845 case USB_DEV_DISCONNECTED:
2846 USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2847 "hwahc_disconnect_event_cb: already disconnected");
2848
2849 break;
2850 default:
2851 USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2852 "hwahc_disconnect_event_cb: illegal devstate=%d",
2853 hwahcp->hwahc_dev_state);
2854
2855 break;
2856 }
2857 mutex_exit(&hwahcp->hwahc_mutex);
2858
2859 ndi_devi_exit(dip, circ);
2860
2861 return (USB_SUCCESS);
2862 }
2863
2864
2865 /*
2866 * hwahc_reconnect_event_cb:
2867 * Called with device hotplug-inserted
2868 * Restore state
2869 */
2870 static int
2871 hwahc_reconnect_event_cb(dev_info_t *dip)
2872 {
2873 int instance = ddi_get_instance(dip);
2874 hwahc_state_t *hwahcp;
2875 int circ;
2876
2877
2878 if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2879
2880 return (USB_FAILURE);
2881 }
2882
2883 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2884 "hwahc_reconnect_event_cb: dip = 0x%p", (void *)dip);
2885
2886 ndi_devi_enter(dip, &circ);
2887 hwahc_restore_device_state(dip, hwahcp);
2888 ndi_devi_exit(dip, circ);
2889
2890 return (USB_SUCCESS);
2891 }
2892
2893
2894 /*
2895 * hwahc_pre_suspend_event_cb:
2896 * Called before HWA device suspend
2897 */
2898 static int
2899 hwahc_pre_suspend_event_cb(dev_info_t *dip)
2900 {
2901 int instance = ddi_get_instance(dip);
2902 hwahc_state_t *hwahcp;
2903 int circ;
2904
2905 if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2906
2907 return (USB_FAILURE);
2908 }
2909
2910 USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2911 "hwahc_pre_suspend_event_cb: dip = 0x%p", (void *)dip);
2912
2913 mutex_enter(&hwahcp->hwahc_mutex);
2914 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2915 "hwahc_pre_suspend_event_cb: start, hw state = %d, softstate = %d",
2916 hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
2917 mutex_exit(&hwahcp->hwahc_mutex);
2918
2919 /* keep PM out till we see a cpr resume */
2920 (void) hwahc_pm_busy_component(hwahcp);
2921 (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
2922
2923 ndi_devi_enter(dip, &circ);
2924 hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_PRE_SUSPEND);
2925 ndi_devi_exit(dip, circ);
2926
2927 /*
2928 * rc driver is always suspended first, that fails the hc suspend.
2929 * need to suspend hc before rc is suspended, so move the suspend
2930 * operations here
2931 */
2932 mutex_enter(&hwahcp->hwahc_mutex);
2933 if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
2934 USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2935 "hwahc_pre_suspend_event_cb: dev_state = %d",
2936 hwahcp->hwahc_dev_state);
2937 mutex_exit(&hwahcp->hwahc_mutex);
2938
2939 return (USB_SUCCESS);
2940 }
2941
2942 if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
2943 /*
2944 * notify children the host is going to stop
2945 */
2946 (void) hwahc_hc_channel_suspend(hwahcp);
2947 }
2948
2949 /* stop the hc from functioning */
2950 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
2951 mutex_exit(&hwahcp->hwahc_mutex);
2952 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
2953
2954 mutex_enter(&hwahcp->hwahc_mutex);
2955 hwahc_stop_result_thread(hwahcp);
2956 hwahc_drain_notif_queue(hwahcp);
2957
2958 mutex_exit(&hwahcp->hwahc_mutex);
2959 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
2960 hwahcp->hwahc_default_pipe);
2961 mutex_enter(&hwahcp->hwahc_mutex);
2962
2963 }
2964
2965 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2966 "hwahc_pre_suspend_event_cb: end, devstate=%d "
2967 "hwstate=%d softstate = %d",
2968 hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state,
2969 hwahcp->hwahc_hc_soft_state);
2970
2971 mutex_exit(&hwahcp->hwahc_mutex);
2972
2973 return (USB_SUCCESS);
2974 }
2975
2976
2977 /*
2978 * hwahc_post_resume_event_cb:
2979 * Call after HWA device resume
2980 */
2981 static int
2982 hwahc_post_resume_event_cb(dev_info_t *dip)
2983 {
2984 int instance = ddi_get_instance(dip);
2985 hwahc_state_t *hwahcp;
2986 int circ;
2987
2988 if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2989
2990 return (USB_FAILURE);
2991 }
2992
2993 USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2994 "hwahc_post_resume_event_cb: dip = 0x%p", (void *)dip);
2995
2996 mutex_enter(&hwahcp->hwahc_mutex);
2997
2998 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2999 "hwahc_post_resume_event_cb: start, hw state = %d, softstate = %d",
3000 hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
3001 mutex_exit(&hwahcp->hwahc_mutex);
3002
3003 ndi_devi_enter(dip, &circ);
3004
3005 /* need to place hc restore here to make sure rc has resumed */
3006 hwahc_restore_device_state(dip, hwahcp);
3007
3008 hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_POST_RESUME);
3009
3010 ndi_devi_exit(dip, circ);
3011
3012 /* enable PM */
3013 (void) hwahc_pm_idle_component(hwahcp);
3014
3015 return (USB_SUCCESS);
3016 }
3017
3018
3019 /*
3020 * hwahc_restore_device_state:
3021 * Called during hotplug-reconnect and resume.
3022 * re-enable power management
3023 * Verify the device is the same as before the disconnect/suspend.
3024 * Restore device state
3025 * Thaw any IO which was frozen.
3026 * Quiesce device. (Other routines will activate if thawed IO.)
3027 * Set device online.
3028 * Leave device disconnected if there are problems.
3029 */
3030 static void
3031 hwahc_restore_device_state(dev_info_t *dip, hwahc_state_t *hwahcp)
3032 {
3033 int rval;
3034 int old_hw_state;
3035
3036 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3037 "hwahc_restore_device_state: dip = 0x%p", (void *)dip);
3038
3039 mutex_enter(&hwahcp->hwahc_mutex);
3040
3041 ASSERT((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
3042 (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED));
3043
3044 /* raise power */
3045 mutex_exit(&hwahcp->hwahc_mutex);
3046 hwahc_pm_busy_component(hwahcp);
3047 (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
3048
3049 /*
3050 * Check if we are talking to the same device
3051 * Some host controllers may see all devices disconnected
3052 * when they just resume. This may be a cause of not
3053 * finding the same device.
3054 *
3055 * Some HWA devices need to download firmware when it is
3056 * powered on. Before the firmware is downloaded, the device
3057 * will look differently.
3058 */
3059 if (usb_check_same_device(dip, hwahcp->hwahc_log_handle,
3060 USB_LOG_L0, PRINT_MASK_ALL,
3061 USB_CHK_BASIC | USB_CHK_SERIAL | USB_CHK_VIDPID, NULL) !=
3062 USB_SUCCESS) {
3063 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3064 "hwahc_restore_device_state: not the same device");
3065 /* change the device state from suspended to disconnected */
3066 mutex_enter(&hwahcp->hwahc_mutex);
3067 hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
3068 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_ERROR_STATE;
3069 mutex_exit(&hwahcp->hwahc_mutex);
3070 hwahc_pm_idle_component(hwahcp);
3071
3072 return;
3073 }
3074
3075 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3076 "hwahc_restore_device_state: Hwahc has been reconnected but"
3077 " data may have been lost");
3078
3079 mutex_enter(&hwahcp->hwahc_mutex);
3080
3081 /* reinitialize the hw */
3082 hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
3083 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
3084
3085 if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
3086 mutex_exit(&hwahcp->hwahc_mutex);
3087 /* no need to start hc */
3088 hwahc_pm_idle_component(hwahcp);
3089 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3090 "hwahc_restore_device_state: stopped hwa");
3091
3092 return;
3093 }
3094
3095
3096 rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data,
3097 hwahcp->hwahc_hc_data.hc_cluster_id);
3098
3099 if (rval != USB_SUCCESS) {
3100 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3101 "hwahc_restore_device_state: set cluster id fails");
3102
3103 goto err;
3104 }
3105
3106 if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
3107 old_hw_state = hwahcp->hwahc_hw_state;
3108 hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
3109 rval = hwahc_hc_channel_start(hwahcp);
3110 if (rval != USB_SUCCESS) {
3111 USB_DPRINTF_L2(PRINT_MASK_ATTA,
3112 hwahcp->hwahc_log_handle,
3113 "hwahc_restore_device_state: start hc fails");
3114 hwahcp->hwahc_hw_state = old_hw_state;
3115
3116 goto err;
3117 }
3118 hwahcp->hwahc_hw_state = old_hw_state;
3119 }
3120
3121 rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
3122 HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
3123 if (rval != USB_SUCCESS) {
3124 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3125 "hwahc_restore_device_state: set num dnts fails");
3126
3127 goto err;
3128 }
3129
3130 /* set default GTK */
3131 rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
3132 if (rval != USB_SUCCESS) {
3133 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3134 "hwahc_restore_device_state: set gtk fails");
3135
3136 goto err;
3137 }
3138
3139 mutex_exit(&hwahcp->hwahc_mutex);
3140
3141 rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
3142 hwahcp->hwahc_default_pipe);
3143 mutex_enter(&hwahcp->hwahc_mutex);
3144 if (rval != USB_SUCCESS) {
3145 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3146 "hwahc_restore_device_state: enable wa fails");
3147
3148 goto err;
3149 }
3150
3151 /*
3152 * This is a workaround, sometimes the ioctl and reconnect will
3153 * happen at the sametime, so the ioctl will start nep which makes
3154 * the below sart nep fail. Need more work to do to avoid such
3155 * issues
3156 */
3157 (void) wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
3158
3159 rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
3160 if (rval != USB_SUCCESS) {
3161 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3162 "hwahc_restore_device_state: start notifep fails rval =%d",
3163 rval);
3164 mutex_exit(&hwahcp->hwahc_mutex);
3165 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
3166 hwahcp->hwahc_default_pipe);
3167 mutex_enter(&hwahcp->hwahc_mutex);
3168
3169 goto err;
3170 }
3171
3172 /* Handle transfer results on bulk-in ep */
3173 rval = hwahc_start_result_thread(hwahcp);
3174 if (rval != USB_SUCCESS) {
3175 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3176 "hwahc_restore_device_state: start result thread fails");
3177 mutex_exit(&hwahcp->hwahc_mutex);
3178 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
3179 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
3180 hwahcp->hwahc_default_pipe);
3181 mutex_enter(&hwahcp->hwahc_mutex);
3182
3183 goto err;
3184 }
3185
3186 /* if the device had remote wakeup earlier, enable it again */
3187 if (hwahcp->hwahc_pm && hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
3188 mutex_exit(&hwahcp->hwahc_mutex);
3189 (void) usb_handle_remote_wakeup(hwahcp->hwahc_dip,
3190 USB_REMOTE_WAKEUP_ENABLE);
3191 mutex_enter(&hwahcp->hwahc_mutex);
3192 }
3193
3194 hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
3195 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
3196 mutex_exit(&hwahcp->hwahc_mutex);
3197 hwahc_pm_idle_component(hwahcp);
3198
3199 return;
3200
3201 err:
3202 hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
3203 mutex_exit(&hwahcp->hwahc_mutex);
3204 hwahc_pm_idle_component(hwahcp);
3205 }
3206
3207
3208 /*
3209 * hwahc_cpr_suspend:
3210 * Clean up device.
3211 * Wait for any IO to finish, then close pipes.
3212 * Quiesce device.
3213 * due to the dependency on hwarc, the actual suspend operations are
3214 * moved to hwahc_pre_suspend_event_cb function.
3215 */
3216 static int
3217 hwahc_cpr_suspend(dev_info_t *dip)
3218 {
3219 int instance = ddi_get_instance(dip);
3220 hwahc_state_t *hwahcp = ddi_get_soft_state(hwahc_statep, instance);
3221
3222 if (hwahcp == NULL) {
3223
3224 return (USB_FAILURE);
3225 }
3226
3227 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3228 "hwahc_cpr_suspend: start");
3229
3230 mutex_enter(&hwahcp->hwahc_mutex);
3231
3232 /* Don't suspend if the device is open. */
3233 if (hwahcp->hwahc_open_count > 0) {
3234 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3235 "hwahc_cpr_suspend: Device is open, cannot suspend");
3236 mutex_exit(&hwahcp->hwahc_mutex);
3237
3238 return (USB_FAILURE);
3239 }
3240
3241 mutex_exit(&hwahcp->hwahc_mutex);
3242 /* raise power */
3243 hwahc_pm_busy_component(hwahcp);
3244 (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
3245
3246 mutex_enter(&hwahcp->hwahc_mutex);
3247 switch (hwahcp->hwahc_dev_state) {
3248 case USB_DEV_ONLINE:
3249 /* real suspend operations put in pre_suspend function */
3250 /* FALLTHRU */
3251 case USB_DEV_DISCONNECTED:
3252 case USB_DEV_PWRED_DOWN:
3253 hwahcp->hwahc_dev_state = USB_DEV_SUSPENDED;
3254 hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
3255
3256 break;
3257 case USB_DEV_SUSPENDED:
3258 default:
3259 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3260 "hwahc_cpr_suspend: illegal dev state=%d",
3261 hwahcp->hwahc_dev_state);
3262
3263 break;
3264 }
3265
3266 mutex_exit(&hwahcp->hwahc_mutex);
3267 hwahc_pm_idle_component(hwahcp);
3268
3269 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3270 "hwahc_cpr_suspend: end");
3271
3272 return (USB_SUCCESS);
3273 }
3274
3275
3276 /*
3277 * hwahc_cpr_resume:
3278 *
3279 * hwahc_restore_device_state marks success by putting device back online
3280 */
3281 static int
3282 hwahc_cpr_resume(dev_info_t *dip)
3283 {
3284 int instance = ddi_get_instance(dip);
3285 hwahc_state_t *hwahcp = ddi_get_soft_state(hwahc_statep, instance);
3286
3287 if (hwahcp == NULL) {
3288
3289 return (USB_FAILURE);
3290 }
3291
3292 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3293 "hwahc_cpr_resume: hw state = %d, softstate = %d",
3294 hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
3295
3296 /*
3297 * rc is always resumed after hc. restoring hc before rc would fail.
3298 * move the restoring operations to hwahc_post_resume_event_cb.
3299 */
3300
3301 return (USB_SUCCESS);
3302 }
3303
3304 /*
3305 * hwahc_create_pm_components:
3306 * Create power managements components
3307 */
3308 static void
3309 hwahc_create_pm_components(dev_info_t *dip, hwahc_state_t *hwahcp)
3310 {
3311 hwahc_power_t *hwahcpm;
3312 uint_t pwr_states;
3313
3314 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3315 "hwahc_create_pm_components: Begin");
3316
3317 /* Allocate the state structure */
3318 hwahcpm = kmem_zalloc(sizeof (hwahc_power_t), KM_SLEEP);
3319 hwahcp->hwahc_pm = hwahcpm;
3320 hwahcpm->hwahc_state = hwahcp;
3321 hwahcpm->hwahc_pm_capabilities = 0;
3322 hwahcpm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
3323
3324 if (usb_create_pm_components(dip, &pwr_states) == USB_SUCCESS) {
3325 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3326 "hwahc_create_pm_components: created PM components");
3327
3328 if (usb_handle_remote_wakeup(dip,
3329 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
3330 hwahcpm->hwahc_wakeup_enabled = 1;
3331 }
3332 hwahcpm->hwahc_pwr_states = (uint8_t)pwr_states;
3333 /* make device busy till end of attach */
3334 hwahc_pm_busy_component(hwahcp);
3335 (void) pm_raise_power(hwahcp->hwahc_dip, 0,
3336 USB_DEV_OS_FULL_PWR);
3337 } else {
3338 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3339 "hwahc_create_pm_components: failed");
3340 }
3341
3342 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3343 "hwahc_create_pm_components: End");
3344 }
3345
3346 /*
3347 * hwahc_destroy_pm_components:
3348 * Shut down and destroy power management and remote wakeup functionality
3349 */
3350 static void
3351 hwahc_destroy_pm_components(hwahc_state_t *hwahcp)
3352 {
3353 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3354 "hwahc_destroy_pm_components: Begin");
3355
3356 ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3357
3358 mutex_enter(&hwahcp->hwahc_mutex);
3359 if (hwahcp->hwahc_pm && (hwahcp->hwahc_dev_state !=
3360 USB_DEV_DISCONNECTED)) {
3361 mutex_exit(&hwahcp->hwahc_mutex);
3362 hwahc_pm_busy_component(hwahcp);
3363 mutex_enter(&hwahcp->hwahc_mutex);
3364
3365 if (hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
3366 int rval;
3367
3368 mutex_exit(&hwahcp->hwahc_mutex);
3369 (void) pm_raise_power(hwahcp->hwahc_dip, 0,
3370 USB_DEV_OS_FULL_PWR);
3371
3372 if ((rval = usb_handle_remote_wakeup(
3373 hwahcp->hwahc_dip,
3374 USB_REMOTE_WAKEUP_DISABLE)) !=
3375 USB_SUCCESS) {
3376 USB_DPRINTF_L3(PRINT_MASK_PM,
3377 hwahcp->hwahc_log_handle,
3378 "hwahc_destroy_pm_components: "
3379 "Error disabling rmt wakeup: rval = %d",
3380 rval);
3381 }
3382 } else {
3383 mutex_exit(&hwahcp->hwahc_mutex);
3384 }
3385
3386 /*
3387 * Since remote wakeup is disabled now,
3388 * no one can raise power and get to device
3389 * once power is lowered here.
3390 */
3391 (void) pm_lower_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_PWR_OFF);
3392
3393 hwahc_pm_idle_component(hwahcp);
3394 mutex_enter(&hwahcp->hwahc_mutex);
3395 }
3396
3397 if (hwahcp->hwahc_pm) {
3398 kmem_free(hwahcp->hwahc_pm, sizeof (hwahc_power_t));
3399 hwahcp->hwahc_pm = NULL;
3400 }
3401 mutex_exit(&hwahcp->hwahc_mutex);
3402
3403 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3404 "hwahc_destroy_pm_components: End");
3405 }
3406
3407 /* mark component busy */
3408 static void
3409 hwahc_pm_busy_component(hwahc_state_t *hwahcp)
3410 {
3411 ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3412
3413 if (hwahcp->hwahc_pm != NULL) {
3414 mutex_enter(&hwahcp->hwahc_mutex);
3415 hwahcp->hwahc_pm->hwahc_pm_busy++;
3416 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3417 "hwahc_pm_busy_component: %d",
3418 hwahcp->hwahc_pm->hwahc_pm_busy);
3419 mutex_exit(&hwahcp->hwahc_mutex);
3420
3421 if (pm_busy_component(hwahcp->hwahc_dip, 0) !=
3422 DDI_SUCCESS) {
3423 mutex_enter(&hwahcp->hwahc_mutex);
3424 hwahcp->hwahc_pm->hwahc_pm_busy--;
3425 USB_DPRINTF_L2(PRINT_MASK_PM,
3426 hwahcp->hwahc_log_handle,
3427 "hwahc_pm_busy_component failed: %d",
3428 hwahcp->hwahc_pm->hwahc_pm_busy);
3429 mutex_exit(&hwahcp->hwahc_mutex);
3430 }
3431 }
3432 }
3433
3434 /* mark component idle */
3435 static void
3436 hwahc_pm_idle_component(hwahc_state_t *hwahcp)
3437 {
3438 ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3439
3440 if (hwahcp->hwahc_pm != NULL) {
3441
3442 if (pm_idle_component(hwahcp->hwahc_dip, 0) ==
3443 DDI_SUCCESS) {
3444 mutex_enter(&hwahcp->hwahc_mutex);
3445 ASSERT(hwahcp->hwahc_pm->hwahc_pm_busy > 0);
3446 hwahcp->hwahc_pm->hwahc_pm_busy--;
3447 USB_DPRINTF_L4(PRINT_MASK_PM,
3448 hwahcp->hwahc_log_handle,
3449 "hwahc_pm_idle_component: %d",
3450 hwahcp->hwahc_pm->hwahc_pm_busy);
3451 mutex_exit(&hwahcp->hwahc_mutex);
3452 }
3453 }
3454 }
3455
3456 /*
3457 * hwahc_power :
3458 * Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
3459 * usb_req_raise_power and usb_req_lower_power.
3460 */
3461 /* ARGSUSED */
3462 static int
3463 hwahc_power(dev_info_t *dip, int comp, int level)
3464 {
3465 hwahc_state_t *hwahcp;
3466 hwahc_power_t *pm;
3467 int rval = USB_FAILURE;
3468
3469 hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
3470
3471 if (hwahcp == NULL) {
3472
3473 return (DDI_FAILURE);
3474 }
3475
3476 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3477 "hwahc_power: dip = 0x%p", (void *)dip);
3478
3479 mutex_enter(&hwahcp->hwahc_mutex);
3480
3481 if (hwahcp->hwahc_pm == NULL) {
3482
3483 goto done;
3484 }
3485
3486 pm = hwahcp->hwahc_pm;
3487
3488 /* Check if we are transitioning to a legal power level */
3489 if (USB_DEV_PWRSTATE_OK(pm->hwahc_pwr_states, level)) {
3490 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3491 "hwahc_power: illegal power level = %d "
3492 "pwr_states: %x", level, pm->hwahc_pwr_states);
3493
3494 goto done;
3495 }
3496
3497 switch (level) {
3498 case USB_DEV_OS_PWR_OFF :
3499 rval = hwahc_pwrlvl0(hwahcp);
3500
3501 break;
3502 case USB_DEV_OS_PWR_1:
3503 rval = hwahc_pwrlvl1(hwahcp);
3504
3505 break;
3506 case USB_DEV_OS_PWR_2:
3507 rval = hwahc_pwrlvl2(hwahcp);
3508
3509 break;
3510 case USB_DEV_OS_FULL_PWR :
3511 rval = hwahc_pwrlvl3(hwahcp);
3512
3513 break;
3514 }
3515 done:
3516 mutex_exit(&hwahcp->hwahc_mutex);
3517
3518 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
3519 }
3520
3521 /*
3522 * hwahc_pwrlvl0:
3523 * Functions to handle power transition for OS levels 0 -> 3
3524 * OS 0 <--> USB D3, no or minimal power
3525 */
3526 static int
3527 hwahc_pwrlvl0(hwahc_state_t *hwahcp)
3528 {
3529 int rval;
3530
3531 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3532 "hwahc_pwrlvl0: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
3533
3534 switch (hwahcp->hwahc_dev_state) {
3535 case USB_DEV_ONLINE:
3536 /* Deny the powerdown request if the device is busy */
3537 if (hwahcp->hwahc_pm->hwahc_pm_busy != 0) {
3538 USB_DPRINTF_L2(PRINT_MASK_PM,
3539 hwahcp->hwahc_log_handle,
3540 "hwahc_pwrlvl0: hwahc_pm is busy");
3541
3542 return (USB_FAILURE);
3543 }
3544 /*
3545 * only when final_stop gets called, we allow the system
3546 * to do PM on us. At this moment, we don't need to do
3547 * more operations other than those in final_stop.
3548 */
3549
3550 /* Issue USB D3 command to the device here */
3551 rval = usb_set_device_pwrlvl3(hwahcp->hwahc_dip);
3552 ASSERT(rval == USB_SUCCESS);
3553
3554 hwahcp->hwahc_dev_state = USB_DEV_PWRED_DOWN;
3555
3556 hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_PWR_OFF;
3557
3558 break;
3559 case USB_DEV_DISCONNECTED:
3560 case USB_DEV_SUSPENDED:
3561 case USB_DEV_PWRED_DOWN:
3562 default:
3563 break;
3564 }
3565
3566 return (USB_SUCCESS);
3567 }
3568
3569 /*
3570 * hwahc_pwrlvl1:
3571 * Functions to handle power transition to OS levels -> 2
3572 * OS level 1 <--> D2
3573 */
3574 static int
3575 hwahc_pwrlvl1(hwahc_state_t *hwahcp)
3576 {
3577 int rval;
3578
3579 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3580 "hwahc_pwrlvl1:");
3581
3582 /* Issue USB D2 command to the device here */
3583 rval = usb_set_device_pwrlvl2(hwahcp->hwahc_dip);
3584 ASSERT(rval == USB_SUCCESS);
3585
3586 return (USB_FAILURE);
3587 }
3588
3589 /*
3590 * hwahc_pwrlvl2:
3591 * Functions to handle power transition to OS levels -> 1
3592 * OS leve 2 <--> D1
3593 */
3594 static int
3595 hwahc_pwrlvl2(hwahc_state_t *hwahcp)
3596 {
3597 int rval;
3598
3599 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3600 "hwahc_pwrlvl2:");
3601
3602 /* Issue USB D1 command to the device here */
3603 rval = usb_set_device_pwrlvl1(hwahcp->hwahc_dip);
3604 ASSERT(rval == USB_SUCCESS);
3605
3606 return (USB_FAILURE);
3607 }
3608
3609
3610 /*
3611 * hwahc_pwrlvl3:
3612 * Functions to handle power transition to OS level -> 0
3613 * OS level 3 <--> D0 (full power)
3614 */
3615 static int
3616 hwahc_pwrlvl3(hwahc_state_t *hwahcp)
3617 {
3618 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3619 "hwahc_pwrlvl3: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
3620
3621 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3622
3623 switch (hwahcp->hwahc_dev_state) {
3624 case USB_DEV_PWRED_DOWN:
3625 /* Issue USB D0 command to the device here */
3626 (void) usb_set_device_pwrlvl0(hwahcp->hwahc_dip);
3627
3628 /*
3629 * Due to our current PM policy, it's not possible
3630 * for hwa to be in USB_DEV_PWRED_DOWN between
3631 * initial_start and final_stop. If it's PWRED_DOWN,
3632 * it should not start. We don't need to resume
3633 * soft or hardware state in this case.
3634 */
3635 if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
3636 /* no need to start hc */
3637 hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
3638 hwahcp->hwahc_pm->hwahc_current_power =
3639 USB_DEV_OS_FULL_PWR;
3640
3641 return (USB_SUCCESS);
3642 }
3643
3644 hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
3645
3646 /* FALLTHRU */
3647 case USB_DEV_ONLINE:
3648 /* we are already in full power */
3649 /* FALLTHRU */
3650 case USB_DEV_DISCONNECTED:
3651 case USB_DEV_SUSPENDED:
3652 /*
3653 * PM framework tries to put you in full power
3654 * during system shutdown. If we are disconnected
3655 * return success. Also, we should not change state
3656 * when we are disconnected or suspended or about to
3657 * transition to that state
3658 */
3659
3660 return (USB_SUCCESS);
3661 default:
3662 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3663 "hwahc_pwrlvl3: illegal dev_state=%d",
3664 hwahcp->hwahc_dev_state);
3665
3666
3667 return (USB_FAILURE);
3668 }
3669 }
3670
3671 /*
3672 * Host power management: stop channel
3673 * See Section 4.16.2.1 for details
3674 * See Section 8.1.0 for HWA suspend/resume
3675 */
3676 static int
3677 hwahc_hc_channel_suspend(hwahc_state_t *hwahcp)
3678 {
3679 int rval;
3680
3681 USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3682 "hwahc_hc_channel_suspend:");
3683
3684 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3685
3686 /* no need to suspend if host hw was not started */
3687 if (hwahcp->hwahc_hw_state != HWAHC_HW_STARTED) {
3688 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3689 "hwahc_hc_channel_suspend: hw already stopped");
3690
3691 return (USB_SUCCESS);
3692 }
3693
3694 if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_SUSPEND) {
3695 USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3696 "hwahc_hc_channel_suspend: already suspended");
3697
3698 return (USB_SUCCESS);
3699 }
3700
3701 mutex_exit(&hwahcp->hwahc_mutex);
3702 /* suspend host, refer to WUSB 1.0 spec 8.5.3.14 */
3703 rval = wusb_hc_stop_ch(&hwahcp->hwahc_hc_data, 10000); /* 10ms */
3704 mutex_enter(&hwahcp->hwahc_mutex);
3705 if (rval != USB_SUCCESS) {
3706 USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3707 "hwahc_hc_channel_suspend: wusb channel stop fails");
3708
3709 return (rval);
3710 }
3711
3712 hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
3713
3714 return (USB_SUCCESS);
3715 }
3716
3717 /*
3718 * Parse security descriptors, see T.8-43
3719 * put result in secrt_data
3720 */
3721 static int
3722 hwahc_parse_security_data(wusb_secrt_data_t *secrt_data,
3723 usb_cfg_data_t *cfg_data)
3724 {
3725 int i, j;
3726 usb_cvs_data_t *cvs_data;
3727 size_t count, len;
3728
3729 if ((secrt_data == NULL) || (cfg_data == NULL)) {
3730 return (USB_INVALID_ARGS);
3731 }
3732
3733 for (i = 0; i < cfg_data->cfg_n_cvs; i++) {
3734 cvs_data = &cfg_data->cfg_cvs[i];
3735 if (cvs_data == NULL) {
3736 continue;
3737 }
3738 if (cvs_data->cvs_buf[1] == USB_DESCR_TYPE_SECURITY) {
3739 count = usb_parse_data("ccsc",
3740 cvs_data->cvs_buf, cvs_data->cvs_buf_len,
3741 (void *)&secrt_data->secrt_descr,
3742 (size_t)USB_SECURITY_DESCR_SIZE);
3743 if (count != USB_SECURITY_DESCR_SIZE) {
3744
3745 return (USB_FAILURE);
3746 } else {
3747 secrt_data->secrt_n_encry =
3748 secrt_data->secrt_descr.bNumEncryptionTypes;
3749 len = sizeof (usb_encryption_descr_t) *
3750 secrt_data->secrt_n_encry;
3751
3752 secrt_data->secrt_encry_descr =
3753 (usb_encryption_descr_t *)kmem_alloc(len,
3754 KM_SLEEP);
3755
3756 for (j = 0; j < secrt_data->secrt_n_encry;
3757 j++) {
3758 cvs_data =
3759 &cfg_data->cfg_cvs[i + j + 1];
3760 if (cvs_data->cvs_buf[1] !=
3761 USB_DESCR_TYPE_ENCRYPTION) {
3762 kmem_free(secrt_data->
3763 secrt_encry_descr, len);
3764
3765 return (USB_FAILURE);
3766 }
3767
3768 /* Table 7-34 */
3769 count = usb_parse_data("ccccc",
3770 cvs_data->cvs_buf,
3771 cvs_data->cvs_buf_len,
3772 (void *)&secrt_data->
3773 secrt_encry_descr[j],
3774 USB_ENCRYPTION_DESCR_SIZE);
3775 if (count !=
3776 USB_ENCRYPTION_DESCR_SIZE) {
3777 kmem_free(secrt_data->
3778 secrt_encry_descr, len);
3779
3780 return (USB_FAILURE);
3781
3782 }
3783 }
3784 return (USB_SUCCESS);
3785 }
3786 }
3787 }
3788
3789 return (USB_FAILURE);
3790 }
3791
3792 /* initialize wusb_hc_data_t structure */
3793 static void
3794 hwahc_hc_data_init(hwahc_state_t *hwahcp)
3795 {
3796 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
3797
3798 hc_data->hc_dip = hwahcp->hwahc_dip;
3799 hc_data->hc_private_data = (void *)hwahcp;
3800
3801 (void) memset(hc_data->hc_chid, 0, sizeof (hc_data->hc_chid));
3802
3803 hc_data->hc_num_mmcies = hwahcp->hwahc_wa_data.wa_descr.bNumMMCIEs;
3804
3805 ASSERT(hc_data->hc_num_mmcies != 0);
3806
3807 hc_data->hc_mmcie_list = kmem_zalloc((hc_data->hc_num_mmcies *
3808 sizeof (wusb_ie_header_t *)), KM_SLEEP);
3809
3810 /* initialize frequently used IE */
3811 hc_data->hc_alive_ie.bIEIdentifier = WUSB_IE_DEV_KEEPALIVE;
3812
3813 /* register callbacks */
3814 hc_data->disconnect_dev = hwahc_disconnect_dev;
3815 hc_data->reconnect_dev = hwahc_reconnect_dev;
3816 hc_data->create_child = hwahc_create_child;
3817 hc_data->destroy_child = hwahc_destroy_child;
3818
3819 /* HWA HC operation functions */
3820 hc_data->set_encrypt = hwahc_set_encrypt;
3821 hc_data->set_ptk = hwahc_set_ptk;
3822 hc_data->set_gtk = hwahc_set_gtk;
3823 hc_data->set_device_info = hwahc_set_device_info;
3824 hc_data->set_cluster_id = hwahc_set_cluster_id;
3825 hc_data->set_stream_idx = hwahc_set_stream_idx;
3826 hc_data->set_wusb_mas = hwahc_set_wusb_mas;
3827 hc_data->add_mmc_ie = hwahc_add_mmc_ie;
3828 hc_data->rem_mmc_ie = hwahc_remove_mmc_ie;
3829 hc_data->stop_ch = hwahc_stop_ch;
3830 hc_data->set_num_dnts = hwahc_set_num_dnts;
3831 hc_data->get_time = hwahc_get_time;
3832
3833 hc_data->hc_num_ports = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
3834
3835 hc_data->hc_cd_list_length = (sizeof (dev_info_t **)) *
3836 (hc_data->hc_num_ports + 1);
3837
3838 hc_data->hc_children_dips = (dev_info_t **)kmem_zalloc(
3839 hc_data->hc_cd_list_length, KM_SLEEP);
3840 hc_data->hc_usba_devices = (usba_device_t **)kmem_zalloc(
3841 hc_data->hc_cd_list_length, KM_SLEEP);
3842 hc_data->hc_dev_infos = (wusb_dev_info_t **)kmem_zalloc(
3843 hc_data->hc_cd_list_length, KM_SLEEP);
3844
3845 mutex_init(&hc_data->hc_mutex, NULL, MUTEX_DRIVER, NULL);
3846 }
3847
3848 /* deinitialize wusb_hc_data_t structure */
3849 static void
3850 hwahc_hc_data_fini(hwahc_state_t *hwahcp)
3851 {
3852 int i;
3853 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
3854 wusb_ie_header_t *hdr;
3855
3856 #ifdef DEBUG
3857 usb_port_t port;
3858 #endif
3859
3860 if (hc_data->hc_mmcie_list) {
3861 /* Free all recorded IEs except statically allocated IEs */
3862 for (i = 0; i < hc_data->hc_num_mmcies; i++) {
3863 if (hc_data->hc_mmcie_list[i] != NULL) {
3864 hdr = hc_data->hc_mmcie_list[i];
3865 if ((hdr->bIEIdentifier !=
3866 WUSB_IE_DEV_KEEPALIVE)) {
3867 kmem_free(hdr, hdr->bLength);
3868 }
3869 hc_data->hc_mmcie_list[i] = NULL;
3870 }
3871 }
3872
3873 kmem_free(hc_data->hc_mmcie_list,
3874 hc_data->hc_num_mmcies * sizeof (wusb_ie_header_t *));
3875 }
3876
3877 if (hc_data->hc_cluster_id) {
3878 wusb_hc_free_cluster_id(hc_data->hc_cluster_id);
3879 }
3880
3881 if (hc_data->hc_cc_list) {
3882 wusb_hc_free_cc_list(hc_data->hc_cc_list);
3883 }
3884
3885 #ifdef DEBUG
3886 for (port = 1; port <= hc_data->hc_num_ports; port++) {
3887 ASSERT(hc_data->hc_usba_devices[port] == NULL);
3888 ASSERT(hc_data->hc_children_dips[port] == NULL);
3889 ASSERT(hc_data->hc_dev_infos[port] == NULL);
3890 }
3891 #endif
3892
3893 kmem_free(hc_data->hc_children_dips, hc_data->hc_cd_list_length);
3894 kmem_free(hc_data->hc_usba_devices, hc_data->hc_cd_list_length);
3895 kmem_free(hc_data->hc_dev_infos, hc_data->hc_cd_list_length);
3896
3897 mutex_destroy(&hc_data->hc_mutex);
3898 }
3899
3900 /* fully start the HWA hw */
3901 static int
3902 hwahc_hc_initial_start(hwahc_state_t *hwahcp)
3903 {
3904 uint8_t stream_idx;
3905 uint8_t mas[WUSB_SET_WUSB_MAS_LEN];
3906 int rval;
3907 uint8_t cluster_id = 0;
3908
3909 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3910 "hwahc_hc_initial_start:");
3911
3912 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3913
3914 if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
3915 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3916 "hwahc_hc_initial_start: invalid dev state = %d",
3917 hwahcp->hwahc_dev_state);
3918
3919 return (USB_INVALID_REQUEST);
3920 }
3921
3922 if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
3923 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3924 "hwahc_hc_initial_start: invalid hw state");
3925
3926 return (USB_INVALID_REQUEST);
3927 }
3928
3929 /*
3930 * start beacon of radio layer
3931 * We're not sure if previouse channel is occupied or not. So, let
3932 * UWB allocates a free channel for this hwa. Then we can start
3933 * beacon.
3934 */
3935 hwahcp->hwahc_hc_data.hc_channel =
3936 uwb_allocate_channel(hwahcp->hwahc_dip);
3937 if (hwahcp->hwahc_hc_data.hc_channel == 0) {
3938 USB_DPRINTF_L2(PRINT_MASK_ATTA,
3939 hwahcp->hwahc_log_handle,
3940 "wusb_hc_initial_start: channel = %d",
3941 hwahcp->hwahc_hc_data.hc_channel);
3942 return (USB_FAILURE);
3943 }
3944
3945 if ((rval = uwb_start_beacon(hwahcp->hwahc_dip,
3946 hwahcp->hwahc_hc_data.hc_channel)) != USB_SUCCESS) {
3947 USB_DPRINTF_L2(PRINT_MASK_ATTA,
3948 hwahcp->hwahc_log_handle,
3949 "wusb_hc_initial_start: start uwb beacon failed");
3950
3951 return (rval);
3952 }
3953
3954 mutex_exit(&hwahcp->hwahc_mutex);
3955 /* reset wire adapter */
3956 rval = wusb_wa_reset(&hwahcp->hwahc_wa_data,
3957 hwahcp->hwahc_default_pipe);
3958 mutex_enter(&hwahcp->hwahc_mutex);
3959 if (rval != SUCCESS) {
3960 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3961 "hwahc_hc_initial_start: reset wa fails");
3962
3963 goto err;
3964 }
3965
3966 /* reuse the old cluster id or assign one */
3967 if (hwahcp->hwahc_hc_data.hc_cluster_id) {
3968 cluster_id = hwahcp->hwahc_hc_data.hc_cluster_id;
3969 } else {
3970 cluster_id = wusb_hc_get_cluster_id();
3971 if (cluster_id == 0) {
3972 USB_DPRINTF_L2(PRINT_MASK_ATTA,
3973 hwahcp->hwahc_log_handle,
3974 "hwahc_hc_initial_start: cannot get cluster id");
3975 rval = USB_NO_RESOURCES;
3976
3977 goto err;
3978 }
3979 }
3980
3981 mutex_exit(&hwahcp->hwahc_mutex);
3982 /* set cluster id for the wusb channel */
3983 rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data, cluster_id);
3984 if (rval != USB_SUCCESS) {
3985 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3986 "hwahc_hc_initial_start: set cluster id %d fails",
3987 cluster_id);
3988 mutex_enter(&hwahcp->hwahc_mutex);
3989
3990 goto err;
3991 }
3992
3993 /* UWB should be responsible for assigning stream index */
3994 stream_idx = 1;
3995
3996 rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
3997 if (rval != USB_SUCCESS) {
3998 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3999 "hwahc_hc_initial_start: set stream idx %d fails",
4000 stream_idx);
4001 mutex_enter(&hwahcp->hwahc_mutex);
4002
4003 goto err;
4004 }
4005
4006 /* set dnts slot */
4007 rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
4008 HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
4009
4010 if (rval != USB_SUCCESS) {
4011 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4012 "hwahc_hc_initial_start: set num dnts fails");
4013 mutex_enter(&hwahcp->hwahc_mutex);
4014
4015 goto err;
4016 }
4017
4018 /* set host info IE */
4019 rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
4020 if (rval != USB_SUCCESS) {
4021 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4022 "hwahc_hc_initial_start: add hostinfo ie fails");
4023 mutex_enter(&hwahcp->hwahc_mutex);
4024
4025 goto err;
4026 }
4027
4028 /* reserve MAS slots for the host, need a way to assign */
4029 (void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
4030 mas[0] = 0xf0; /* the first 4 slots are for beacons */
4031 rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4032 mutex_enter(&hwahcp->hwahc_mutex);
4033 if (rval != USB_SUCCESS) {
4034 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4035 "hwahc_hc_initial_start: set wusb mas fails");
4036
4037 goto err;
4038 }
4039
4040 /* record the available MAS slots */
4041 (void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4042
4043 /* Set initial GTK/TKID to random values */
4044 (void) random_get_pseudo_bytes(dft_gtk, 16);
4045 (void) random_get_pseudo_bytes(dft_gtkid, 3);
4046
4047 /* set default GTK, need a way to dynamically compute it */
4048 mutex_exit(&hwahcp->hwahc_mutex);
4049 rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
4050 if (rval != USB_SUCCESS) {
4051 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4052 "hwahc_hc_initial_start: set gtk fails");
4053 mutex_enter(&hwahcp->hwahc_mutex);
4054
4055 goto err;
4056 }
4057
4058 /* enable wire adapter */
4059 rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
4060 hwahcp->hwahc_default_pipe);
4061 if (rval != USB_SUCCESS) {
4062 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4063 "hwahc_hc_initial_start: enable wa fails");
4064 mutex_enter(&hwahcp->hwahc_mutex);
4065
4066 goto err;
4067 }
4068
4069 /* Start Notification endpoint */
4070 rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
4071
4072 if (rval != USB_SUCCESS) {
4073 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4074 "hwahc_hc_initial_start: start notification ep fails");
4075 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4076 hwahcp->hwahc_default_pipe);
4077
4078 mutex_enter(&hwahcp->hwahc_mutex);
4079
4080 goto err;
4081 }
4082
4083 mutex_enter(&hwahcp->hwahc_mutex);
4084
4085 /*
4086 * Handle transfer results on bulk-in ep
4087 * The bulk-in ep needs to be polled no matter the completion
4088 * notification is received or not to avoid miss result.
4089 */
4090 rval = hwahc_start_result_thread(hwahcp);
4091 if (rval != USB_SUCCESS) {
4092 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4093 "hwahc_hc_initial_start: start result thread fails, "
4094 "rval = %d", rval);
4095 mutex_exit(&hwahcp->hwahc_mutex);
4096 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
4097 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4098 hwahcp->hwahc_default_pipe);
4099 mutex_enter(&hwahcp->hwahc_mutex);
4100
4101 goto err;
4102 }
4103 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4104 "hwahc_hc_initial_start: start result thread success");
4105
4106 hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
4107 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
4108
4109 /* Don't do PM on an active beacon hwa until explicitly stopped */
4110 mutex_exit(&hwahcp->hwahc_mutex);
4111 hwahc_pm_busy_component(hwahcp);
4112 mutex_enter(&hwahcp->hwahc_mutex);
4113
4114 return (USB_SUCCESS);
4115
4116 err:
4117 if (cluster_id != 0) {
4118 wusb_hc_free_cluster_id(cluster_id);
4119 }
4120
4121 mutex_exit(&hwahcp->hwahc_mutex);
4122 (void) uwb_stop_beacon(hwahcp->hwahc_dip);
4123 mutex_enter(&hwahcp->hwahc_mutex);
4124
4125 return (rval);
4126 }
4127
4128 /* entirely stop the HWA from working */
4129 static int
4130 hwahc_hc_final_stop(hwahc_state_t *hwahcp)
4131 {
4132 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4133 "hwahc_hc_final_stop:");
4134
4135 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4136
4137 if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
4138 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4139 "hwahc_hc_final_stop: already stopped");
4140
4141 return (USB_SUCCESS);
4142 }
4143
4144 if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
4145 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4146 "hwahc_hc_final_stop: invalid dev state = %d",
4147 hwahcp->hwahc_dev_state);
4148
4149 return (USB_INVALID_REQUEST);
4150 }
4151
4152 /* might have been powered down before detaching */
4153 mutex_exit(&hwahcp->hwahc_mutex);
4154 (void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
4155 mutex_enter(&hwahcp->hwahc_mutex);
4156
4157 if (hwahcp->hwahc_dev_state != USB_DEV_DISCONNECTED) {
4158 /* notify children the host is going to stop */
4159 (void) hwahc_hc_channel_suspend(hwahcp);
4160
4161 /* release mutex here to avoid deadlock with exc_cb */
4162 mutex_exit(&hwahcp->hwahc_mutex);
4163
4164 /* stop notification endpoint */
4165 wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
4166 mutex_enter(&hwahcp->hwahc_mutex);
4167
4168 /* stop bulk-in ept from listening result */
4169 hwahc_stop_result_thread(hwahcp);
4170
4171 /* drain the device notifications */
4172 hwahc_drain_notif_queue(hwahcp);
4173
4174 /* disable wire adapter */
4175 mutex_exit(&hwahcp->hwahc_mutex);
4176 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4177 hwahcp->hwahc_default_pipe);
4178
4179 /* stop beaconing. Not necessary to unreserve mas */
4180 (void) uwb_stop_beacon(hwahcp->hwahc_dip);
4181
4182 wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
4183
4184 /* Manually remove all connected children */
4185 hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
4186
4187 /* delete all the children */
4188 (void) hwahc_cleanup_child(hwahcp->hwahc_dip);
4189 mutex_enter(&hwahcp->hwahc_mutex);
4190 }
4191
4192 /*
4193 * we make it busy at hwahc_hc_initial_start(). This idle operation
4194 * is to match that busy operation.
4195 * All other busy/idle operations should have been matched.
4196 */
4197 if ((hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) &&
4198 (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE)) {
4199 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4200 "hwahc_hc_final_stop: pm_busy=%d",
4201 hwahcp->hwahc_pm->hwahc_pm_busy);
4202 mutex_exit(&hwahcp->hwahc_mutex);
4203 hwahc_pm_idle_component(hwahcp);
4204 mutex_enter(&hwahcp->hwahc_mutex);
4205 }
4206
4207 hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
4208 if (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE) {
4209 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
4210 }
4211
4212 return (USB_SUCCESS);
4213 }
4214
4215 /*
4216 * init WUSB channel, this is only part of the full hw start operations
4217 * including setting wusb channel stream idx, wusb MAS slots reservation
4218 * and adding host info IE
4219 */
4220 static int
4221 hwahc_hc_channel_start(hwahc_state_t *hwahcp)
4222 {
4223 uint8_t stream_idx;
4224 uint8_t mas[WUSB_SET_WUSB_MAS_LEN];
4225 int rval;
4226
4227 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4228 "hwahc_hc_channel_start:");
4229
4230 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4231
4232 if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
4233 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4234 "hwahc_hc_channel_start: invalid dev_state = %d",
4235 hwahcp->hwahc_dev_state);
4236
4237 return (USB_INVALID_REQUEST);
4238 }
4239
4240 if (hwahcp->hwahc_hw_state != HWAHC_HW_CH_STOPPED) {
4241 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4242 "hwahc_hc_channel_start: invalid hw state");
4243
4244 return (USB_INVALID_REQUEST);
4245 }
4246
4247 /* set stream idx */
4248 stream_idx = 1;
4249
4250 mutex_exit(&hwahcp->hwahc_mutex);
4251 rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
4252 if (rval != USB_SUCCESS) {
4253 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4254 "hwahc_hc_channel_start: set stream idx %d fails",
4255 stream_idx);
4256 mutex_enter(&hwahcp->hwahc_mutex);
4257
4258 return (rval);
4259 }
4260
4261 /* reserve MAS slots for the host. Should be allocated by UWB */
4262 (void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
4263 mas[0] = 0xf0; /* for beacons */
4264 rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4265
4266 if (rval != USB_SUCCESS) {
4267 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4268 "hwahc_hc_channel_start: set wusb mas fails");
4269 mutex_enter(&hwahcp->hwahc_mutex);
4270
4271 return (rval);
4272 }
4273 (void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4274
4275 /* set host info IE */
4276 rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
4277
4278 if (rval != USB_SUCCESS) {
4279 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4280 "hwahc_hc_channel_start: add hostinfo ie fails");
4281 mutex_enter(&hwahcp->hwahc_mutex);
4282
4283 return (rval);
4284 }
4285
4286 mutex_enter(&hwahcp->hwahc_mutex);
4287 hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
4288 hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
4289
4290 /* do not PM this device, once we're ready to accept DN */
4291 mutex_exit(&hwahcp->hwahc_mutex);
4292 hwahc_pm_busy_component(hwahcp);
4293 mutex_enter(&hwahcp->hwahc_mutex);
4294
4295 return (USB_SUCCESS);
4296 }
4297
4298 /*
4299 * stop WUSB channel, this only stops part of the hw function
4300 * it mainly unreserve the MAS slots and remove the host info IE
4301 */
4302 static int
4303 hwahc_hc_channel_stop(hwahc_state_t *hwahcp)
4304 {
4305 uint8_t stream_idx;
4306 uint8_t mas[WUSB_SET_WUSB_MAS_LEN];
4307 int rval;
4308
4309 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4310 "hwahc_hc_channel_stop:");
4311
4312 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4313
4314 if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
4315 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4316 "hwahc_hc_channel_stop: invalid dev state %d",
4317 hwahcp->hwahc_dev_state);
4318
4319 return (USB_INVALID_REQUEST);
4320 }
4321
4322 if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED) {
4323 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4324 "hwahc_hc_channel_stop: already partially stopped");
4325
4326 return (USB_SUCCESS);
4327 }
4328
4329 if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
4330 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4331 "hwahc_hc_channel_stop: already stopped, invalid state");
4332
4333 return (USB_INVALID_REQUEST);
4334 }
4335
4336 /* send host disconect IE so that the children know to disconnect */
4337 mutex_exit(&hwahcp->hwahc_mutex);
4338 rval = wusb_hc_send_host_disconnect(&hwahcp->hwahc_hc_data);
4339 if (rval != USB_SUCCESS) {
4340 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4341 "hwahc_hc_channel_stop: send host disconnect ie fails");
4342
4343 mutex_enter(&hwahcp->hwahc_mutex);
4344
4345 return (rval);
4346 }
4347
4348 /* remove host info IE */
4349 wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
4350
4351 /* unset stream idx */
4352 stream_idx = 0;
4353
4354 rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
4355 if (rval != USB_SUCCESS) {
4356 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4357 "hwahc_hc_channel_stop: set stream idx 0 fails");
4358 mutex_enter(&hwahcp->hwahc_mutex);
4359
4360 return (rval);
4361 }
4362
4363 /* unreserve MAS slots */
4364 (void) memset(mas, 0, WUSB_SET_WUSB_MAS_LEN);
4365 rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4366
4367 if (rval != USB_SUCCESS) {
4368 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4369 "hwahc_hc_channel_stop: set null wusb mas fails");
4370 mutex_enter(&hwahcp->hwahc_mutex);
4371
4372 return (rval);
4373 }
4374
4375 mutex_enter(&hwahcp->hwahc_mutex);
4376 (void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4377
4378 hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
4379
4380 /* Channel is stopped, can be PM'ed */
4381 mutex_exit(&hwahcp->hwahc_mutex);
4382 hwahc_pm_idle_component(hwahcp);
4383 mutex_enter(&hwahcp->hwahc_mutex);
4384
4385 return (USB_SUCCESS);
4386 }
4387
4388 /* initialize data transfer related resources */
4389 static int
4390 hwahc_wa_start(hwahc_state_t *hwahcp)
4391 {
4392 int rval;
4393
4394 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4395 "hwahc_wa_start:");
4396
4397 /* get all rpipe descrs */
4398 if ((rval = wusb_wa_get_rpipe_descrs(&hwahcp->hwahc_wa_data,
4399 hwahcp->hwahc_default_pipe, PRINT_MASK_ATTA,
4400 hwahcp->hwahc_log_handle)) != USB_SUCCESS) {
4401 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4402 "hwahc_wa_start: get rpipe descrs fails, rval=%d", rval);
4403
4404 return (rval);
4405 }
4406
4407 /* open all data transfer epts */
4408 if ((rval = wusb_wa_open_pipes(&hwahcp->hwahc_wa_data)) !=
4409 USB_SUCCESS) {
4410 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4411 "hwahc_wa_start: open pipes fails, rval=%d", rval);
4412 (void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4413 hwahcp->hwahc_default_pipe);
4414
4415 return (rval);
4416 }
4417
4418 /* init notification list */
4419 usba_init_list(&hwahcp->hwahc_dn_notif_queue, NULL,
4420 hwahcp->hwahc_dev_data->dev_iblock_cookie);
4421
4422 return (USB_SUCCESS);
4423 }
4424
4425 /* deinitialize data transfer related resources */
4426 static void
4427 hwahc_wa_stop(hwahc_state_t *hwahcp)
4428 {
4429 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4430 "hwahc_wa_stop:");
4431
4432 usba_destroy_list(&hwahcp->hwahc_dn_notif_queue);
4433 wusb_wa_close_pipes(&hwahcp->hwahc_wa_data);
4434 }
4435
4436 /*
4437 * HUBD related initialization
4438 * To mimic standard hub attach process to create a fake "root hub"
4439 * for HWA
4440 */
4441 static int
4442 hwahc_hub_attach(hwahc_state_t *hwahcp)
4443 {
4444 hubd_t *hubd = NULL;
4445 dev_info_t *dip = hwahcp->hwahc_dip;
4446 int instance = ddi_get_instance(dip);
4447 int i;
4448 int rval;
4449
4450 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4451 "hwahc_hub_attach:");
4452
4453 if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
4454 "wire-adapter") != NDI_SUCCESS) {
4455
4456 return (USB_FAILURE);
4457 }
4458
4459 /* allocate hubd structure */
4460 hubd = hwahcp->hwahc_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
4461
4462 hubd->h_log_handle = usb_alloc_log_hdl(dip, "husb", &hubd_errlevel,
4463 &hubd_errmask, &hubd_instance_debug, 0);
4464 hubd->h_usba_device = usba_get_usba_device(dip);
4465 hubd->h_usba_device->usb_is_wa = TRUE;
4466 hubd->h_dip = dip;
4467 hubd->h_instance = instance;
4468 hubd->h_ignore_pwr_budget = B_TRUE;
4469 hubd->h_cleanup_child = hwahc_cleanup_child;
4470
4471 mutex_enter(&hubd->h_usba_device->usb_mutex);
4472 hubd->h_usba_device->usb_root_hubd = hubd;
4473 mutex_exit(&hubd->h_usba_device->usb_mutex);
4474
4475 if (usb_get_dev_data(dip, &hubd->h_dev_data,
4476 USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
4477 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4478 "cannot get dev_data");
4479
4480 goto fail;
4481 }
4482
4483 /* init hubd mutex */
4484 mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
4485 hubd->h_dev_data->dev_iblock_cookie);
4486
4487 usb_free_descr_tree(dip, hubd->h_dev_data);
4488
4489 hubd->h_init_state |= HUBD_LOCKS_DONE;
4490
4491 /* register the instance to usba HUBDI */
4492 rval = usba_hubdi_register(dip, 0);
4493 if (rval != USB_SUCCESS) {
4494 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4495 "usba_hubdi_register failed");
4496
4497 goto fail;
4498 }
4499
4500 mutex_enter(HUBD_MUTEX(hubd));
4501 hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
4502
4503 hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN,
4504 KM_SLEEP);
4505 hubd_get_ancestry_str(hubd);
4506
4507 /* create cfgadm minor nodes */
4508 for (i = 1; i <= hwahcp->hwahc_wa_data.wa_descr.bNumPorts; i++) {
4509 char ap_name[HUBD_APID_NAMELEN];
4510
4511 (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
4512 hubd->h_ancestry_str, i);
4513 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4514 "ap_name=%s", ap_name);
4515
4516 if (ddi_create_minor_node(dip, ap_name, S_IFCHR,
4517 (instance << HWAHC_MINOR_INSTANCE_SHIFT) | i,
4518 DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
4519 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4520 "cannot create attachment point node (%d)",
4521 instance);
4522 mutex_exit(HUBD_MUTEX(hubd));
4523
4524 goto fail;
4525 }
4526 }
4527 i = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
4528 mutex_exit(HUBD_MUTEX(hubd));
4529
4530 /* create hubd minor node */
4531 if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
4532 instance << HWAHC_MINOR_INSTANCE_SHIFT,
4533 DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
4534 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4535 "cannot create devctl minor node (%d)", instance);
4536
4537 goto fail;
4538 }
4539
4540 mutex_enter(HUBD_MUTEX(hubd));
4541 hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
4542 mutex_exit(HUBD_MUTEX(hubd));
4543
4544 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
4545 "usb-port-count", i) != DDI_PROP_SUCCESS) {
4546 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4547 "usb-port-count update failed");
4548 }
4549
4550 return (USB_SUCCESS);
4551
4552 fail:
4553 if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
4554 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4555 "fail to cleanup after hub attach failure");
4556 }
4557
4558 return (USB_FAILURE);
4559 }
4560
4561 /* HUBD related deinitialization */
4562 static int
4563 hwahc_hub_detach(hwahc_state_t *hwahcp)
4564 {
4565 hubd_t *hubd = hwahcp->hwahc_hubd;
4566 dev_info_t *dip = hwahcp->hwahc_dip;
4567
4568 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4569 "hwahc_hub_detach:");
4570
4571 if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
4572 goto done;
4573 }
4574
4575 if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
4576 /* remove minor nodes */
4577 ddi_remove_minor_node(dip, NULL);
4578 }
4579
4580 if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
4581 /* unregister with usba HUBDI */
4582 (void) usba_hubdi_unregister(dip);
4583 }
4584
4585 if (hubd->h_init_state & HUBD_LOCKS_DONE) {
4586 mutex_destroy(HUBD_MUTEX(hubd));
4587 }
4588
4589 if (hubd->h_ancestry_str) {
4590 kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
4591 }
4592
4593 done:
4594 if (hubd->h_dev_data) {
4595 /* unregister client from usba */
4596 usb_client_detach(dip, hubd->h_dev_data);
4597 }
4598
4599 usb_free_log_hdl(hubd->h_log_handle);
4600 kmem_free(hubd, sizeof (hubd_t));
4601 ddi_prop_remove_all(dip);
4602
4603 return (USB_SUCCESS);
4604 }
4605
4606 /* print security descrs */
4607 static void
4608 hwahc_print_secrt_data(hwahc_state_t *hwahcp)
4609 {
4610 int i;
4611 wusb_secrt_data_t *secrt_data = &hwahcp->hwahc_secrt_data;
4612
4613 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4614 "The Host Wire Adapter security descriptor:");
4615 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4616 "bLength = 0x%x\t\t bDescriptorType = 0x%x",
4617 secrt_data->secrt_descr.bLength,
4618 secrt_data->secrt_descr.bDescriptorType);
4619 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4620 "wTotalLength = 0x%x\t bNumEncryptionTypes = 0x%x",
4621 secrt_data->secrt_descr.wTotalLength,
4622 secrt_data->secrt_descr.bNumEncryptionTypes);
4623
4624 for (i = 0; i < secrt_data->secrt_n_encry; i++) {
4625 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4626 "The Host Wire Adapter encryption descriptor %d:", i + 1);
4627 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4628 "bLength = 0x%x\t\t bDescriptorType = 0x%x",
4629 secrt_data->secrt_encry_descr[i].bLength,
4630 secrt_data->secrt_encry_descr[i].bDescriptorType);
4631 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4632 "bEncryptionType = 0x%x\t bEncryptionValue = 0x%x",
4633 secrt_data->secrt_encry_descr[i].bEncryptionType,
4634 secrt_data->secrt_encry_descr[i].bEncryptionValue);
4635 USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4636 "bAuthKeyIndex = 0x%x",
4637 secrt_data->secrt_encry_descr[i].bAuthKeyIndex);
4638 }
4639 }
4640
4641 /* drain device notifications */
4642 static void
4643 hwahc_drain_notif_queue(hwahc_state_t *hwahcp)
4644 {
4645 int i;
4646
4647 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4648
4649 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4650 "hwahc_drain_notif_queue: started");
4651
4652 if ((hwahcp->hwahc_notif_thread_id == NULL) &&
4653 (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0)) {
4654 /* kick off a notif thread to drain the queue */
4655 if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
4656 (void *)hwahcp, 0) != USB_SUCCESS) {
4657 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4658 hwahcp->hwahc_log_handle,
4659 "hwahc_drain_notif_queue: no notif thread started");
4660 } else {
4661 hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
4662 }
4663 }
4664
4665 for (i = 0; i < HWAHC_NOTIF_DRAIN_TIMEOUT; i++) {
4666 /* loop until the queue is completed or it timeouts */
4667 if ((hwahcp->hwahc_notif_thread_id == NULL) &&
4668 (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) ==
4669 0)) {
4670
4671 break;
4672 }
4673 mutex_exit(&hwahcp->hwahc_mutex);
4674 delay(drv_usectohz(1000000));
4675 mutex_enter(&hwahcp->hwahc_mutex);
4676 }
4677
4678 /* cleanup the queue if not completed */
4679 while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
4680 hwahc_dn_notif_list_t *nlist;
4681
4682 nlist = (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
4683 &hwahcp->hwahc_dn_notif_queue);
4684 ASSERT(nlist != NULL);
4685 ASSERT(nlist->dn_notif != NULL);
4686 usba_destroy_list(&nlist->notif_list);
4687 kmem_free(nlist->dn_notif, nlist->dn_notif->bLength);
4688 kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
4689 }
4690
4691 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4692 "hwahc_drain_notif_queue: ended");
4693 }
4694
4695
4696 /* normal callback for notification ept */
4697 static void
4698 hwahc_intr_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
4699 {
4700 dev_info_t *dip = (USBA_REQ2WRP(reqp))->wr_dip;
4701 hwahc_state_t *hwahcp;
4702 mblk_t *data = reqp->intr_data;
4703
4704 ASSERT(dip != NULL);
4705 hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
4706 ASSERT(hwahcp != NULL);
4707
4708 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4709 "hwahc_intr_cb: ph = 0x%p reqp = 0x%p", (void *)ph,
4710 (void *)reqp);
4711
4712 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
4713
4714 if (data == NULL) {
4715 usb_free_intr_req(reqp);
4716
4717 return;
4718 }
4719
4720 /* handle the notification */
4721 hwahc_handle_notif(hwahcp, data);
4722
4723 usb_free_intr_req(reqp);
4724 }
4725
4726 /*
4727 * See Section 8.3.3.3 for Transfer Notification format and
4728 * Section 8.5.4 for HWA specific notifications.
4729 * Three kinds of Notifications:
4730 * - Transfer Completion
4731 * - DN Received
4732 * - BPST ADJ
4733 */
4734 /* handle the notification according to notification type */
4735 static void
4736 hwahc_handle_notif(hwahc_state_t *hwahcp, mblk_t *data)
4737 {
4738 int len;
4739 uint8_t *p;
4740 wa_notif_header_t *hdr;
4741
4742 if (data == NULL) {
4743
4744 return;
4745 }
4746
4747 len = MBLKL(data);
4748 p = data->b_rptr;
4749 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4750 "hwahc_handle_notif: data len = %d", len);
4751
4752 /*
4753 * according to WUSB 1.0/8.1.2, multiple notifications might be sent
4754 * at a time, need to parse one by one
4755 */
4756 while (len > 0) {
4757 if (len < 2) {
4758 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4759 hwahcp->hwahc_log_handle,
4760 "hwahc_handle_notif: short packet len = %d",
4761 len);
4762
4763 break;
4764 }
4765
4766 hdr = (wa_notif_header_t *)p;
4767 if (len < hdr->bLength) {
4768 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4769 hwahcp->hwahc_log_handle,
4770 "hwahc_handle_notif: length not match, "
4771 "hdr length = %d, actual length = %d",
4772 hdr->bLength, len);
4773
4774 break;
4775 }
4776
4777 switch (hdr->bNotifyType) {
4778 case WA_NOTIF_TYPE_TRANSFER:
4779 {
4780 uint8_t ept = p[2];
4781
4782 /* deal with transfer completion notification */
4783 hwahc_handle_xfer_result(hwahcp, ept);
4784
4785 break;
4786 }
4787 case HWA_NOTIF_TYPE_DN_RECEIVED:
4788 {
4789 hwa_notif_dn_recvd_t *dn_notif;
4790
4791 dn_notif = kmem_alloc(hdr->bLength, KM_NOSLEEP);
4792 (void) memcpy(dn_notif, p, hdr->bLength);
4793
4794 /* deal with device notification */
4795 hwahc_handle_dn_notif(hwahcp, dn_notif);
4796
4797 break;
4798 }
4799 case HWA_NOTIF_TYPE_BPST_ADJ:
4800 USB_DPRINTF_L3(PRINT_MASK_CBOPS,
4801 hwahcp->hwahc_log_handle,
4802 "hwahc_handle_notif: received BPST adjust "
4803 "notification, bAdjustment = %d", p[2]);
4804
4805 break;
4806 default:
4807 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4808 hwahcp->hwahc_log_handle,
4809 "hwahc_handle_notif: unknown notification 0x%x",
4810 hdr->bNotifyType);
4811
4812 break;
4813 }
4814 p += hdr->bLength;
4815 len -= hdr->bLength;
4816 }
4817 }
4818
4819 /*
4820 * start listening on bulk-in ept for transfer result
4821 *
4822 * Dispatches a task to read the BULK IN endpoint to get the result of
4823 * last request. usb_async_req() will have system_taskq to process the tasks.
4824 */
4825 int
4826 hwahc_start_result_thread(hwahc_state_t *hwahcp)
4827 {
4828 wusb_wa_data_t *wa_data;
4829
4830 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4831 "hwahc_start_result_thread:");
4832
4833 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4834
4835 if (hwahcp->hwahc_result_thread_id != 0) {
4836 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4837 "hwahc_start_result_thread: already started");
4838
4839 return (USB_SUCCESS);
4840 }
4841
4842 wa_data = &hwahcp->hwahc_wa_data;
4843
4844 mutex_enter(&wa_data->wa_mutex);
4845 if ((wa_data->wa_bulkin_ph != NULL) &&
4846 (wa_data->wa_bulkin_pipe_state != WA_PIPE_STOPPED)) {
4847 mutex_exit(&wa_data->wa_mutex);
4848
4849 return (USB_INVALID_PIPE);
4850 }
4851 mutex_exit(&wa_data->wa_mutex);
4852
4853 if (wa_data->wa_bulkin_ph == NULL) {
4854 mutex_exit(&hwahcp->hwahc_mutex);
4855 if (usb_pipe_open(wa_data->wa_dip, &wa_data->wa_bulkin_ept,
4856 &wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
4857 &wa_data->wa_bulkin_ph) != USB_SUCCESS) {
4858 USB_DPRINTF_L2(PRINT_MASK_ATTA,
4859 hwahcp->hwahc_log_handle,
4860 "hwahc_start_result_thread: open pipe failed");
4861
4862
4863 mutex_enter(&hwahcp->hwahc_mutex);
4864 return (USB_FAILURE);
4865 }
4866 mutex_enter(&hwahcp->hwahc_mutex);
4867
4868 mutex_enter(&wa_data->wa_mutex);
4869 wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4870 mutex_exit(&wa_data->wa_mutex);
4871 }
4872
4873 /* kick off an asynchronous thread to handle transfer result */
4874 if (usb_async_req(hwahcp->hwahc_dip, hwahc_result_thread,
4875 (void *)hwahcp, 0) != USB_SUCCESS) {
4876 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4877 "hwahc_start_result_thread: failed to start result thread");
4878
4879 return (USB_FAILURE);
4880 }
4881 hwahcp->hwahc_result_thread_id = (kthread_t *)1;
4882
4883 /* pipe state is active while the result thread is on */
4884 mutex_enter(&wa_data->wa_mutex);
4885 wa_data->wa_bulkin_pipe_state = WA_PIPE_ACTIVE;
4886 mutex_exit(&wa_data->wa_mutex);
4887
4888 return (USB_SUCCESS);
4889 }
4890
4891 /* stop the bulk-in ept from listening */
4892 static void
4893 hwahc_stop_result_thread(hwahc_state_t *hwahcp)
4894 {
4895 wusb_wa_data_t *wa_data;
4896
4897 ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4898
4899 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4900 "hwahc_stop_result_thread:");
4901
4902 if (hwahcp->hwahc_result_thread_id == 0) {
4903 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4904 "hwahc_stop_result_thread: already stopped");
4905
4906 return;
4907 }
4908
4909 wa_data = &hwahcp->hwahc_wa_data;
4910 mutex_enter(&wa_data->wa_mutex);
4911 if ((wa_data->wa_bulkin_ph == NULL) ||
4912 (wa_data->wa_bulkin_pipe_state != WA_PIPE_ACTIVE)) {
4913 USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4914 "hwahc_stop_result_thread: invalid pipe state");
4915
4916 mutex_exit(&wa_data->wa_mutex);
4917
4918 return;
4919 }
4920 mutex_exit(&wa_data->wa_mutex);
4921
4922 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4923 "hwahc_stop_result_thread: reset hwa bulk-in pipe");
4924 mutex_exit(&hwahcp->hwahc_mutex);
4925 usb_pipe_reset(wa_data->wa_dip, wa_data->wa_bulkin_ph,
4926 USB_FLAGS_SLEEP, NULL, NULL);
4927
4928 /*
4929 * have to close pipe here to fail the bulk-in transfer
4930 * that never timeouts
4931 */
4932 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4933 "hwahc_stop_result_thread: close hwa bulk-in pipe");
4934 usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
4935 USB_FLAGS_SLEEP, NULL, NULL);
4936 mutex_enter(&hwahcp->hwahc_mutex);
4937
4938 mutex_enter(&wa_data->wa_mutex);
4939 wa_data->wa_bulkin_ph = NULL;
4940 wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4941 mutex_exit(&wa_data->wa_mutex);
4942
4943 while (hwahcp->hwahc_result_thread_id != 0) {
4944 /* wait the result thread to exit */
4945 cv_wait(&hwahcp->hwahc_result_thread_cv, &hwahcp->hwahc_mutex);
4946 }
4947 }
4948
4949 /*
4950 * keep listening for transfer result by setting timeout to 0 while the
4951 * bulk-in pipe is active
4952 * the thread would be stopped by closing bulk-in pipe or encountering
4953 * transaction error, eg, hot-removal of hwa device
4954 */
4955 static void
4956 hwahc_result_thread(void *arg)
4957 {
4958 hwahc_state_t *hwahcp = (hwahc_state_t *)arg;
4959 wusb_wa_data_t *wa_data = &hwahcp->hwahc_wa_data;
4960 int rval;
4961 uint8_t retry = 0;
4962
4963 mutex_enter(&hwahcp->hwahc_mutex);
4964 ASSERT(hwahcp->hwahc_result_thread_id == (kthread_t *)1);
4965 hwahcp->hwahc_result_thread_id = curthread;
4966 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4967 "hwahc_result_thread: started, thread_id=0x%p",
4968 (void *)hwahcp->hwahc_result_thread_id);
4969
4970 /* keep polling the bulk IN endpoint to get the result */
4971 mutex_enter(&wa_data->wa_mutex);
4972 while (wa_data->wa_bulkin_pipe_state == WA_PIPE_ACTIVE) {
4973 mutex_exit(&wa_data->wa_mutex);
4974 mutex_exit(&hwahcp->hwahc_mutex);
4975
4976 if ((rval = wusb_wa_get_xfer_result(wa_data)) != USB_SUCCESS) {
4977 retry++;
4978 USB_DPRINTF_L2(PRINT_MASK_ATTA,
4979 hwahcp->hwahc_log_handle,
4980 "hwahc_result_thread: get xfer result failed, "
4981 "rval = %d, retry = %d", rval, retry);
4982
4983 /* retry 3 times upon failure */
4984 if (retry >= 3) {
4985 mutex_enter(&hwahcp->hwahc_mutex);
4986 mutex_enter(&wa_data->wa_mutex);
4987
4988 break;
4989 }
4990 }
4991
4992 mutex_enter(&hwahcp->hwahc_mutex);
4993 mutex_enter(&wa_data->wa_mutex);
4994 }
4995
4996 hwahcp->hwahc_result_thread_id = 0;
4997 wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4998 mutex_exit(&wa_data->wa_mutex);
4999
5000 /* signal to the thread requesting stopping if any */
5001 cv_signal(&hwahcp->hwahc_result_thread_cv);
5002
5003 USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5004 "hwahc_result_thread: ended");
5005
5006 mutex_exit(&hwahcp->hwahc_mutex);
5007 }
5008
5009 /*
5010 * nothing to do here, just check if the ept number in the transfer
5011 * completion notification is valid
5012 * the actual handling of transfer result is performed by the result thread
5013 */
5014 static void
5015 hwahc_handle_xfer_result(hwahc_state_t *hwahcp, uint8_t ept)
5016 {
5017 usb_ep_descr_t *epdt;
5018
5019 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5020 "hwahc_handle_xfer_result: result on ept %d", ept);
5021
5022 epdt = &hwahcp->hwahc_wa_data.wa_bulkin_ept;
5023
5024 /* the result should be on the bulk-in ept */
5025 if ((epdt->bEndpointAddress & USB_EP_NUM_MASK) != ept) {
5026 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5027 "hwahc_handle_xfer_result: ept number not match");
5028
5029 return;
5030 }
5031 }
5032
5033
5034 /*
5035 * Section 8.5.4.2.
5036 * Copy the DN Notification and add it to the instance's global
5037 * nofication list. If the worker thread is not started yet, start
5038 * it.
5039 */
5040 static void
5041 hwahc_handle_dn_notif(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
5042 {
5043 hwahc_dn_notif_list_t *nlist;
5044
5045 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5046 "hwahc_handle_dn_notif: notif = 0x%p", (void *)dn_notif);
5047
5048 nlist = kmem_zalloc(sizeof (hwahc_dn_notif_list_t), KM_NOSLEEP);
5049
5050 mutex_enter(&hwahcp->hwahc_mutex);
5051 nlist->dn_notif = dn_notif;
5052
5053 usba_init_list(&nlist->notif_list, (usb_opaque_t)nlist,
5054 hwahcp->hwahc_dev_data->dev_iblock_cookie);
5055
5056 /* queue the new notification to the list */
5057 usba_add_to_list(&hwahcp->hwahc_dn_notif_queue, &nlist->notif_list);
5058
5059 /* handle the notification queue with an asynchronous thread */
5060 if (hwahcp->hwahc_notif_thread_id == 0) {
5061 if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
5062 (void *)hwahcp, 0) != USB_SUCCESS) {
5063 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5064 hwahcp->hwahc_log_handle,
5065 "hwahc_handle_dn_notif: no notif thread started");
5066 mutex_exit(&hwahcp->hwahc_mutex);
5067
5068 return;
5069 }
5070 hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
5071 }
5072
5073 mutex_exit(&hwahcp->hwahc_mutex);
5074 }
5075
5076 /* handle the notifications in the notification queue in sequence */
5077 static void
5078 hwahc_notif_thread(void *arg)
5079 {
5080 hwahc_state_t *hwahcp = (hwahc_state_t *)arg;
5081 hwahc_dn_notif_list_t *nlist;
5082 hwa_notif_dn_recvd_t *dn_notif;
5083
5084 mutex_enter(&hwahcp->hwahc_mutex);
5085 ASSERT(hwahcp->hwahc_notif_thread_id == (kthread_t *)1);
5086 hwahcp->hwahc_notif_thread_id = curthread;
5087
5088 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5089 "hwahc_notif_thread: started, thread_id=0x%p",
5090 (void *)hwahcp->hwahc_notif_thread_id);
5091
5092 while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
5093 /*
5094 * first in first out, only one notification will be handled
5095 * at a time, so it assures no racing in attach or detach
5096 */
5097 if ((nlist =
5098 (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
5099 &hwahcp->hwahc_dn_notif_queue)) == NULL) {
5100
5101 continue;
5102 }
5103 dn_notif = nlist->dn_notif;
5104 mutex_exit(&hwahcp->hwahc_mutex);
5105 hwahc_handle_dn(hwahcp, dn_notif);
5106 usba_destroy_list(&nlist->notif_list);
5107 kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
5108 mutex_enter(&hwahcp->hwahc_mutex);
5109 }
5110
5111 hwahcp->hwahc_notif_thread_id = 0;
5112
5113 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5114 "hwahc_notif_thread: ended");
5115
5116 mutex_exit(&hwahcp->hwahc_mutex);
5117 }
5118
5119 /* Set the child device's active bit to 1 */
5120 static void
5121 hwahc_set_device_active(hwahc_state_t *hwahcp, uint8_t devaddr)
5122 {
5123 wusb_dev_info_t *dev_info;
5124 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
5125 int i;
5126
5127 mutex_enter(&hc_data->hc_mutex);
5128 for (i = 1; i <= hc_data->hc_num_ports; i++) {
5129 dev_info = hc_data->hc_dev_infos[i];
5130 if ((dev_info != NULL) && (dev_info->wdev_addr == devaddr)) {
5131 dev_info->wdev_active = 1;
5132 USB_DPRINTF_L3(DPRINT_MASK_EVENTS,
5133 hwahcp->hwahc_log_handle,
5134 "hwahc_set_device_active:device(%p) updated ",
5135 (void *)dev_info);
5136
5137 break;
5138 }
5139 }
5140 mutex_exit(&hc_data->hc_mutex);
5141 }
5142
5143 /*
5144 * handle a specific device notification
5145 * assuming the raw data in HWA DN_RECEIVED notification pkt includes
5146 * no more than one dn pkt
5147 */
5148 static void
5149 hwahc_handle_dn(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
5150 {
5151 uint8_t *p;
5152 size_t len;
5153 uint8_t dntype;
5154 int circ;
5155 wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
5156
5157 if (dn_notif->bLength < 4) {
5158 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5159 "hwahc_handle_dn: bLength too short %d", dn_notif->bLength);
5160 kmem_free(dn_notif, dn_notif->bLength);
5161
5162 return;
5163 }
5164
5165 p = dn_notif->notifdata;
5166 len = dn_notif->bLength - 4;
5167
5168 /*
5169 * WUSB Errata 06.12 specifies that the raw data in the DN_RECEIVED
5170 * notification must not include the WUSB header, but only the bType
5171 * and Notification specific data
5172 */
5173 if (len == 0) {
5174 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5175 "hwahc_handle_dn: no raw data");
5176 kmem_free(dn_notif, dn_notif->bLength);
5177
5178 return;
5179 }
5180 dntype = *p;
5181
5182 /* update the device's status bit, no matter what the DN is */
5183 hwahc_set_device_active(hwahcp, dn_notif->bSourceDeviceAddr);
5184
5185 ndi_devi_enter(hwahcp->hwahc_dip, &circ);
5186 switch (dntype) {
5187 case WUSB_DN_CONNECT:
5188 /* DN_Connect */
5189 wusb_hc_handle_dn_connect(
5190 hc_data, hwahcp->hwahc_default_pipe,
5191 hwahcp->hwahc_wa_data.wa_ifno, p, len,
5192 &hwahcp->hwahc_secrt_data);
5193
5194 break;
5195 case WUSB_DN_DISCONNECT:
5196 /* DN_Disconnect */
5197 wusb_hc_handle_dn_disconnect(
5198 hc_data, dn_notif->bSourceDeviceAddr,
5199 p, len);
5200
5201 break;
5202 case WUSB_DN_ALIVE:
5203 /* We only send KeepAlive IE to one device at a comment */
5204 mutex_enter(&hc_data->hc_mutex);
5205 if (dn_notif->bSourceDeviceAddr ==
5206 hc_data->hc_alive_ie.bDeviceAddress[0]) {
5207 mutex_exit(&hc_data->hc_mutex);
5208 wusb_hc_rem_ie(hc_data,
5209 (wusb_ie_header_t *)&hc_data->hc_alive_ie);
5210 mutex_enter(&hc_data->hc_mutex);
5211 }
5212 mutex_exit(&hc_data->hc_mutex);
5213
5214 break;
5215 case WUSB_DN_EPRDY:
5216 case WUSB_DN_MASAVAILCHANGED:
5217 case WUSB_DN_REMOTEWAKEUP:
5218 case WUSB_DN_SLEEP:
5219 default:
5220 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5221 "hwahc_handle_dn: dn type 0x%x not supported yet",
5222 dntype);
5223
5224 break;
5225 }
5226
5227 kmem_free(dn_notif, dn_notif->bLength);
5228 ndi_devi_exit(hwahcp->hwahc_dip, circ);
5229 }
5230
5231 /* exceptional callback for notification ept */
5232 /* ARGSUSED */
5233 static void
5234 hwahc_intr_exc_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
5235 {
5236 dev_info_t *dip = (USBA_REQ2WRP(reqp))->wr_dip;
5237 hwahc_state_t *hwahcp;
5238
5239 ASSERT(dip != NULL);
5240 hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
5241 ASSERT(hwahcp != NULL);
5242
5243 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5244 "hwahc_intr_exc_cb: receive intr exception cb, cr=%d",
5245 reqp->intr_completion_reason);
5246
5247 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
5248
5249 mutex_enter(&hwahcp->hwahc_mutex);
5250
5251 switch (reqp->intr_completion_reason) {
5252 case USB_CR_PIPE_RESET:
5253 /* only restart nep after autoclearing */
5254 if (hwahcp->hwahc_dev_state == USB_DEV_ONLINE) {
5255 hwahcp->hwahc_wa_data.wa_intr_pipe_state =
5256 WA_PIPE_STOPPED;
5257 mutex_exit(&hwahcp->hwahc_mutex);
5258 (void) wusb_wa_start_nep(&hwahcp->hwahc_wa_data,
5259 USB_FLAGS_NOSLEEP);
5260 mutex_enter(&hwahcp->hwahc_mutex);
5261 }
5262
5263 break;
5264 case USB_CR_DEV_NOT_RESP:
5265 case USB_CR_STOPPED_POLLING:
5266 case USB_CR_PIPE_CLOSING:
5267 case USB_CR_UNSPECIFIED_ERR:
5268 /* never restart nep on these conditions */
5269 default:
5270 /* for all others, wait for the autoclearing PIPE_RESET cb */
5271
5272 break;
5273 }
5274
5275 usb_free_intr_req(reqp);
5276 mutex_exit(&hwahcp->hwahc_mutex);
5277 }
5278
5279 /*
5280 * callback function called by WA to resubmit a periodic request for
5281 * interrupt polling or isochronous transfer.
5282 */
5283 static int
5284 hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
5285 usba_pipe_handle_data_t *ph)
5286 {
5287 hwahc_state_t *hwahcp = wa_data->wa_private_data;
5288 hwahc_pipe_private_t *pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
5289 int rval;
5290
5291 mutex_enter(&hwahcp->hwahc_mutex);
5292
5293 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5294 "hwahc_pipe_submit_periodic_req: hwahcp=0x%p, pp=0x%p,"
5295 " pipe state = %d", (void *)hwahcp, (void *)pp, pp->pp_state);
5296
5297 if (pp->pp_state != HWAHC_PIPE_STATE_ACTIVE) {
5298 /* pipe error or pipe closing, don't resubmit any more */
5299 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5300 "hwahc_pipe_submit_periodic_req: pipe not active = %d",
5301 pp->pp_state);
5302
5303 mutex_exit(&hwahcp->hwahc_mutex);
5304
5305 return (USB_PIPE_ERROR);
5306 }
5307
5308 mutex_exit(&hwahcp->hwahc_mutex);
5309
5310 /* re-submit the original request */
5311 rval = wusb_wa_intr_xfer(wa_data, pp->pp_rp, ph,
5312 (usb_intr_req_t *)pp->pp_client_periodic_in_reqp, 0);
5313
5314 return (rval);
5315 }
5316
5317 /* call HCD callback for completion handling */
5318 static void
5319 hwahc_rpipe_xfer_cb(dev_info_t *dip, usba_pipe_handle_data_t *ph,
5320 wusb_wa_trans_wrapper_t *wr, usb_cr_t cr)
5321 {
5322 hwahc_state_t *hwahcp;
5323 hwahc_pipe_private_t *pp;
5324 usb_opaque_t req;
5325 wusb_hc_data_t *hc_data;
5326
5327 hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
5328 if (hwahcp == NULL) {
5329
5330 return;
5331 }
5332
5333 hc_data = &hwahcp->hwahc_hc_data;
5334
5335 mutex_enter(&hwahcp->hwahc_mutex);
5336 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5337 "hwahc_rpipe_xfer_cb: ph = 0x%p, wr = 0x%p cr = 0x%x",
5338 (void *)ph, (void *)wr, cr);
5339
5340 pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
5341
5342 mutex_enter(&hc_data->hc_mutex);
5343 pp->pp_wdev->wdev_active = 1; /* this device is active on xfer */
5344 mutex_exit(&hc_data->hc_mutex);
5345
5346 switch (cr) {
5347 case USB_CR_OK:
5348 break;
5349 case USB_CR_NOT_SUPPORTED:
5350 case USB_CR_NO_RESOURCES:
5351 case USB_CR_PIPE_RESET:
5352 case USB_CR_STOPPED_POLLING:
5353 pp->pp_state = HWAHC_PIPE_STATE_IDLE;
5354 break;
5355 case USB_CR_PIPE_CLOSING:
5356 break;
5357 default:
5358 pp->pp_state = HWAHC_PIPE_STATE_ERROR;
5359
5360 break;
5361 }
5362
5363 if (wr && wr->wr_reqp) {
5364 req = wr->wr_reqp;
5365
5366 mutex_enter(&wr->wr_rp->rp_mutex);
5367 wr->wr_reqp = NULL;
5368 mutex_exit(&wr->wr_rp->rp_mutex);
5369
5370 } else { /* periodic pipe cleanup */
5371
5372 /* the original request is cleared and returned to client */
5373 req = pp->pp_client_periodic_in_reqp;
5374 pp->pp_client_periodic_in_reqp = NULL;
5375 }
5376
5377 mutex_exit(&hwahcp->hwahc_mutex);
5378
5379 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5380 "hwahc_rpipe_xfer_cb: call usba_hcdi_cb for req= 0x%p",
5381 (void *)req);
5382
5383 usba_hcdi_cb(ph, req, cr);
5384 }
5385
5386 /* post disconnect event to child on a certain port */
5387 static void
5388 hwahc_disconnect_dev(dev_info_t *dip, usb_port_t port)
5389 {
5390 hwahc_state_t *hwahcp;
5391 int circ;
5392 dev_info_t *child_dip;
5393
5394 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5395 ddi_get_instance(dip))) == NULL) {
5396
5397 return;
5398 }
5399
5400 ndi_devi_enter(dip, &circ);
5401 mutex_enter(&hwahcp->hwahc_mutex);
5402
5403 child_dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
5404 if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) && child_dip) {
5405 mutex_exit(&hwahcp->hwahc_mutex);
5406
5407 /* if the child driver remains attached */
5408 if (i_ddi_devi_attached(child_dip)) {
5409 hwahc_post_event(hwahcp, port,
5410 USBA_EVENT_TAG_HOT_REMOVAL);
5411 }
5412 mutex_enter(&hwahcp->hwahc_mutex);
5413 }
5414
5415 mutex_exit(&hwahcp->hwahc_mutex);
5416 ndi_devi_exit(dip, circ);
5417 }
5418
5419 /* post reconect event to child on a certain port */
5420 static void
5421 hwahc_reconnect_dev(dev_info_t *dip, usb_port_t port)
5422 {
5423 hwahc_state_t *hwahcp;
5424 int circ;
5425
5426 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5427 ddi_get_instance(dip))) == NULL) {
5428
5429 return;
5430 }
5431 ndi_devi_enter(dip, &circ);
5432 mutex_enter(&hwahcp->hwahc_mutex);
5433
5434 if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) &&
5435 (hwahcp->hwahc_hc_data.hc_children_dips[port])) {
5436 mutex_exit(&hwahcp->hwahc_mutex);
5437 hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_INSERTION);
5438 mutex_enter(&hwahcp->hwahc_mutex);
5439 }
5440
5441 mutex_exit(&hwahcp->hwahc_mutex);
5442 ndi_devi_exit(dip, circ);
5443 }
5444
5445
5446 /*
5447 * Device TrustTimeout timer operations:
5448 * hwahc_start_trust_timer: start the trust timer for a newly connected device
5449 * hwahc_trust_timeout_handler: timer handler
5450 * hwahc_stop_trust_timer: stop a device's trust timer
5451 */
5452 static void
5453 hwahc_start_trust_timer(wusb_dev_info_t *dev)
5454 {
5455 if (hwahc_enable_trust_timeout == 0) {
5456
5457 return;
5458 }
5459
5460 if (dev->wdev_trust_timer == NULL) {
5461 dev->wdev_trust_timer = timeout(hwahc_trust_timeout_handler,
5462 (void *)dev, drv_usectohz(WUSB_TRUST_TIMEOUT_US));
5463 }
5464 }
5465
5466 /* timeout handler for device TrustTimeout. See section 4.14 */
5467 static void
5468 hwahc_trust_timeout_handler(void *arg)
5469 {
5470 wusb_dev_info_t *dev = (wusb_dev_info_t *)arg;
5471 usb_port_t port;
5472 uint16_t dev_addr;
5473 wusb_hc_data_t *hc_data = dev->wdev_hc;
5474 uint8_t retry = 3;
5475 int rval;
5476
5477 mutex_enter(&hc_data->hc_mutex);
5478
5479 dev->wdev_trust_timer = 0;
5480 dev_addr = dev->wdev_addr;
5481
5482 if (dev->wdev_active == 1) {
5483 /* device is active during the past period. Restart the timer */
5484 dev->wdev_active = 0; /* expect device DN set it to 1 */
5485 } else {
5486 /* send a KeepAlive IE to query the device */
5487 for (retry = 0; retry < 3; retry++) {
5488 mutex_exit(&hc_data->hc_mutex);
5489 rval = wusb_hc_send_keepalive_ie(hc_data,
5490 dev_addr);
5491 mutex_enter(&hc_data->hc_mutex);
5492
5493 if (rval == USB_SUCCESS) {
5494 break;
5495 }
5496 /* retry 3 times if fail to send KeepAlive IE */
5497 }
5498
5499 if (dev->wdev_active == 0) {
5500 /* still no activity! Delete this device */
5501 if (wusb_hc_is_dev_connected(hc_data, dev->wdev_cdid,
5502 &port)) {
5503 mutex_exit(&hc_data->hc_mutex);
5504 (void) hwahc_destroy_child(hc_data->hc_dip,
5505 port);
5506
5507 /* the device comes to the end of its life */
5508 return;
5509 }
5510 }
5511 }
5512
5513 /* active or we received DN during query */
5514 hwahc_start_trust_timer(dev);
5515
5516 mutex_exit(&hc_data->hc_mutex);
5517 }
5518
5519 /* stop a child device's trust timeout handler */
5520 void
5521 hwahc_stop_trust_timer(wusb_dev_info_t *dev)
5522 {
5523 timeout_id_t tid;
5524 wusb_hc_data_t *hc_data = dev->wdev_hc;
5525
5526 ASSERT(mutex_owned(&hc_data->hc_mutex));
5527
5528 if (hwahc_enable_trust_timeout == 0) {
5529 return;
5530 }
5531
5532 tid = dev->wdev_trust_timer;
5533
5534 dev->wdev_trust_timer = NULL;
5535 mutex_exit(&hc_data->hc_mutex);
5536
5537 if (tid != NULL) {
5538 (void) untimeout(tid);
5539 }
5540
5541 mutex_enter(&hc_data->hc_mutex);
5542 }
5543
5544 /* configure child device and attach child on a certain port */
5545 static int
5546 hwahc_create_child(dev_info_t *dip, usb_port_t port)
5547 {
5548 hwahc_state_t *hwahcp;
5549 wusb_hc_data_t *hc_data;
5550 wusb_dev_info_t *dev_info;
5551 usb_pipe_handle_t ph;
5552 int rval;
5553 dev_info_t *child_dip;
5554 usba_device_t *child_ud = NULL;
5555 mblk_t *pdata = NULL;
5556 usb_cr_t completion_reason;
5557 usb_cb_flags_t cb_flags;
5558 size_t size;
5559 uint8_t address;
5560 int user_conf_index;
5561 uint_t config_index;
5562 int prh_circ, rh_circ, circ;
5563 dev_info_t *rh_dip;
5564 usb_dev_descr_t usb_dev_descr;
5565
5566 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5567 ddi_get_instance(dip))) == NULL) {
5568
5569 return (USB_INVALID_ARGS);
5570 }
5571
5572 rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
5573 ndi_hold_devi(dip); /* avoid racing with dev detach */
5574 /* exclude other threads */
5575 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
5576 ndi_devi_enter(rh_dip, &rh_circ);
5577 ndi_devi_enter(dip, &circ);
5578
5579 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*dev_info));
5580
5581 hc_data = &hwahcp->hwahc_hc_data;
5582 mutex_enter(&hc_data->hc_mutex);
5583 dev_info = hc_data->hc_dev_infos[port];
5584
5585 /* Created in whcdi.c before authed */
5586 child_dip = hc_data->hc_children_dips[port];
5587
5588 child_ud = usba_get_usba_device(child_dip);
5589 ph = dev_info->wdev_ph;
5590
5591 mutex_exit(&hc_data->hc_mutex);
5592 /*
5593 * HWA maintains the address space as a separate bus and
5594 * will not occupy parent's address space
5595 */
5596 address = child_ud->usb_addr;
5597 if (address < 0x80) {
5598 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5599 "hwahc_create_child: reconnecting, address = %d",
5600 address);
5601
5602 } else {
5603 /* SetAddress(0) */
5604 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5605 USB_DEV_REQ_HOST_TO_DEV,
5606 USB_REQ_SET_ADDRESS, /* bRequest */
5607 0, /* wValue */
5608 0, /* wIndex */
5609 0, /* wLength */
5610 NULL, 0,
5611 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5612 char buffer[64];
5613 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5614 hwahcp->hwahc_log_handle,
5615 "setting address failed (cr=%s cb_flags=%s "
5616 "rval=%d)", usb_str_cr(completion_reason),
5617 usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
5618 rval);
5619
5620 goto done;
5621 }
5622
5623 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5624 "set address 0 done");
5625
5626 usb_pipe_close(child_dip, ph,
5627 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5628
5629 child_ud->usb_addr = 0;
5630 dev_info->wdev_addr = 0;
5631 dev_info->wdev_ph = NULL;
5632
5633 /* need to be called each time dev addr is changed */
5634 if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
5635 port)) != USB_SUCCESS) {
5636 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5637 hwahcp->hwahc_log_handle,
5638 "update device info failed, rval = %d", rval);
5639
5640 goto done;
5641 }
5642
5643 /* new ph is stored in usba_device */
5644 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5645 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5646 USB_SUCCESS) {
5647 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5648 hwahcp->hwahc_log_handle,
5649 "usb_pipe_open failed (%d)", rval);
5650
5651 goto done;
5652 }
5653
5654 /* provide at least 2ms time for address change, 7.3.1.3 */
5655 delay(drv_usectohz(2000));
5656
5657 /* start normal enumeration process */
5658 /*
5659 * wusb bus address has 1:1 relationship with port number
5660 * and wusb bus address starts from 2, so as to follow
5661 * the convention that USB bus address 1 is reserved for
5662 * host controller device. As such, only 126 WUSB devices
5663 * are supported on a WUSB host
5664 */
5665 address = port + 1;
5666 if (address >= 0x80) {
5667 USB_DPRINTF_L3(PRINT_MASK_CBOPS,
5668 hwahcp->hwahc_log_handle,
5669 "hwahc_create_child: address for port %d exceeds "
5670 "0x80", port);
5671 rval = USB_FAILURE;
5672
5673 goto done;
5674 }
5675 /* Set the address of the device */
5676 if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5677 USB_DEV_REQ_HOST_TO_DEV,
5678 USB_REQ_SET_ADDRESS, /* bRequest */
5679 address, /* wValue */
5680 0, /* wIndex */
5681 0, /* wLength */
5682 NULL, 0,
5683 &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5684 char buffer[64];
5685 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5686 hwahcp->hwahc_log_handle,
5687 "setting address failed (cr=%s cb_flags=%s "
5688 "rval=%d)", usb_str_cr(completion_reason),
5689 usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
5690 rval);
5691
5692 goto done;
5693 }
5694
5695 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5696 "set address 0x%x done", address);
5697
5698 usb_pipe_close(child_dip, ph,
5699 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5700
5701 child_ud->usb_addr = address;
5702 dev_info->wdev_addr = address;
5703 dev_info->wdev_ph = NULL;
5704
5705 if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
5706 port)) != USB_SUCCESS) {
5707 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5708 hwahcp->hwahc_log_handle,
5709 "update device info failed, rval = %d", rval);
5710
5711 goto done;
5712 }
5713
5714 /* new ph is stored in usba_device */
5715 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5716 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5717 USB_SUCCESS) {
5718 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5719 hwahcp->hwahc_log_handle,
5720 "usb_pipe_open failed (%d)", rval);
5721
5722 goto done;
5723 }
5724
5725 /* provide at least 2ms time for address change, 7.3.1.3 */
5726 delay(drv_usectohz(2000));
5727 }
5728
5729 /* get device descriptor ignoring device reconnection */
5730 rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5731 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5732 USB_REQ_GET_DESCR, /* bRequest */
5733 USB_DESCR_TYPE_SETUP_DEV, /* wValue */
5734 0, /* wIndex */
5735 512, /* wLength */
5736 &pdata, USB_ATTRS_SHORT_XFER_OK,
5737 &completion_reason, &cb_flags, 0);
5738
5739 if (rval != USB_SUCCESS) {
5740 if (pdata) {
5741 freemsg(pdata);
5742 pdata = NULL;
5743 }
5744
5745 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5746 "hwahc_create_child: get device descriptor failed "
5747 "(%s 0x%x %d)", usb_str_cr(completion_reason),
5748 cb_flags, rval);
5749
5750 goto done;
5751 }
5752
5753 ASSERT(pdata != NULL);
5754 size = usb_parse_dev_descr(
5755 pdata->b_rptr,
5756 MBLKL(pdata),
5757 &usb_dev_descr,
5758 sizeof (usb_dev_descr_t));
5759 freemsg(pdata);
5760
5761 if (size < USB_DEV_DESCR_SIZE) {
5762 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5763 "hwahc_create_child: get device descriptor size = %lu "
5764 "expected size = %u", size, USB_DEV_DESCR_SIZE);
5765 rval = USB_FAILURE;
5766
5767 goto done;
5768 }
5769
5770 bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
5771 sizeof (usb_dev_descr_t));
5772 child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
5773
5774 if (usb_dev_descr.bNumConfigurations == 0) {
5775 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5776 "device descriptor:\n\t"
5777 "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
5778 "protocol=0x%x maxpktsize=0x%x "
5779 "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
5780 "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
5781 usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
5782 usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
5783 usb_dev_descr.bDeviceSubClass,
5784 usb_dev_descr.bDeviceProtocol,
5785 usb_dev_descr.bMaxPacketSize0,
5786 usb_dev_descr.idVendor,
5787 usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
5788 usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
5789 usb_dev_descr.iSerialNumber,
5790 usb_dev_descr.bNumConfigurations);
5791
5792 rval = USB_FAILURE;
5793
5794 goto done;
5795 }
5796
5797 /* get the device string descriptor(s) */
5798 usba_get_dev_string_descrs(child_dip, child_ud);
5799
5800 /* retrieve config cloud for all configurations */
5801 rval = hubd_get_all_device_config_cloud(hwahcp->hwahc_hubd,
5802 child_dip, child_ud);
5803 if (rval != USB_SUCCESS) {
5804 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5805 "failed to get configuration descriptor(s)");
5806
5807 goto done;
5808 }
5809
5810 /* get the preferred configuration for this device */
5811 user_conf_index = hubd_select_device_configuration(hwahcp->hwahc_hubd,
5812 port, child_dip, child_ud);
5813
5814 /* Check if the user selected configuration index is in range */
5815 if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
5816 (user_conf_index < 0)) {
5817 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5818 "Configuration index for device idVendor=%d "
5819 "idProduct=%d is=%d, and is out of range[0..%d]",
5820 usb_dev_descr.idVendor, usb_dev_descr.idProduct,
5821 user_conf_index, usb_dev_descr.bNumConfigurations - 1);
5822
5823 /* treat this as user didn't specify configuration */
5824 user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
5825 }
5826
5827 if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
5828 if (child_ud->usb_preferred_driver) {
5829 /*
5830 * It is the job of the "preferred driver" to put the
5831 * device in the desired configuration. Till then
5832 * put the device in config index 0.
5833 */
5834 /* h_ignore_pwr_budget = TRUE, not care the power */
5835 if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
5836 USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
5837
5838 goto done;
5839 }
5840
5841 child_dip = hubd_ready_device(hwahcp->hwahc_hubd,
5842 child_dip, child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
5843
5844 /*
5845 * Assign the dip before onlining to avoid race
5846 * with busctl
5847 */
5848 mutex_enter(&hc_data->hc_mutex);
5849 hc_data->hc_children_dips[port] = child_dip;
5850 mutex_exit(&hc_data->hc_mutex);
5851
5852 (void) usba_bind_driver(child_dip);
5853 } else {
5854 /*
5855 * loop through all the configurations to see if we
5856 * can find a driver for any one config. If not, set
5857 * the device in config_index 0
5858 */
5859 rval = USB_FAILURE;
5860 for (config_index = 0;
5861 (config_index < usb_dev_descr.bNumConfigurations) &&
5862 (rval != USB_SUCCESS); config_index++) {
5863
5864 child_dip = hubd_ready_device(
5865 hwahcp->hwahc_hubd,
5866 child_dip, child_ud, config_index);
5867
5868 /*
5869 * Assign the dip before onlining to avoid race
5870 * with busctl
5871 */
5872 mutex_enter(&hc_data->hc_mutex);
5873 hc_data->hc_children_dips[port] = child_dip;
5874 mutex_exit(&hc_data->hc_mutex);
5875
5876 rval = usba_bind_driver(child_dip);
5877
5878 if (rval == USB_SUCCESS) {
5879 /* always succeed for WUSB device */
5880 if ((usba_hubdi_check_power_budget(dip,
5881 child_ud, config_index)) !=
5882 USB_SUCCESS) {
5883 rval = USB_FAILURE;
5884
5885 goto done;
5886 }
5887 }
5888 }
5889
5890 if (rval != USB_SUCCESS) {
5891 if ((usba_hubdi_check_power_budget(dip,
5892 child_ud, 0)) != USB_SUCCESS) {
5893
5894 goto done;
5895 }
5896
5897 child_dip = hubd_ready_device(
5898 hwahcp->hwahc_hubd,
5899 child_dip, child_ud, 0);
5900 mutex_enter(&hc_data->hc_mutex);
5901 hc_data->hc_children_dips[port] = child_dip;
5902 mutex_exit(&hc_data->hc_mutex);
5903 }
5904 } /* end else loop all configs */
5905 } else {
5906 if ((usba_hubdi_check_power_budget(dip, child_ud,
5907 (uint_t)user_conf_index)) != USB_SUCCESS) {
5908 rval = USB_FAILURE;
5909
5910 goto done;
5911 }
5912
5913 child_dip = hubd_ready_device(hwahcp->hwahc_hubd, child_dip,
5914 child_ud, (uint_t)user_conf_index);
5915
5916 /*
5917 * Assign the dip before onlining to avoid race
5918 * with busctl
5919 */
5920 mutex_enter(&hc_data->hc_mutex);
5921 hc_data->hc_children_dips[port] = child_dip;
5922 mutex_exit(&hc_data->hc_mutex);
5923
5924 (void) usba_bind_driver(child_dip);
5925
5926 rval = USB_SUCCESS;
5927 }
5928
5929 /* workaround for non response after ctrl write */
5930 usb_pipe_close(child_dip, ph,
5931 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5932
5933 if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5934 USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5935 USB_SUCCESS) {
5936 USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5937 hwahcp->hwahc_log_handle,
5938 "usb_pipe_open failed (%d)", rval);
5939
5940 goto done;
5941 }
5942
5943 done:
5944 _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*dev_info));
5945
5946 ndi_devi_exit(dip, circ);
5947 ndi_devi_exit(rh_dip, rh_circ);
5948 ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
5949
5950 (void) devfs_clean(rh_dip, NULL, 0);
5951
5952 if (rval == USB_SUCCESS) {
5953 (void) ndi_devi_online(child_dip, 0);
5954
5955 USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5956 "hwahc_create_child: create timer for child %p",
5957 (void *)dev_info);
5958
5959 mutex_enter(&hc_data->hc_mutex);
5960 hwahc_start_trust_timer(dev_info);
5961 mutex_exit(&hc_data->hc_mutex);
5962 }
5963
5964 ndi_rele_devi(dip);
5965
5966 return (rval);
5967 }
5968
5969 /* offline child on a certain port */
5970 static int
5971 hwahc_destroy_child(dev_info_t *dip, usb_port_t port)
5972 {
5973 hwahc_state_t *hwahcp;
5974
5975 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5976 ddi_get_instance(dip))) == NULL) {
5977
5978 return (USB_INVALID_ARGS);
5979 }
5980
5981 hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_REMOVAL);
5982
5983 USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5984 "hwahc_destroy_child: scheduling cleanup");
5985
5986 /* schedule cleanup thread */
5987 hubd_schedule_cleanup(hwahcp->hwahc_hubd->h_usba_device->
5988 usb_root_hub_dip);
5989
5990 return (USB_SUCCESS);
5991 }
5992
5993 /*
5994 * called by cleanup thread to offline child and cleanup child resources
5995 * Child's callback functions have been called before calling this routine.
5996 * dip - hwahc's dip
5997 */
5998 static int
5999 hwahc_cleanup_child(dev_info_t *dip)
6000 {
6001 hwahc_state_t *hwahcp;
6002 wusb_hc_data_t *hc_data;
6003 usb_port_t port;
6004
6005 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
6006 ddi_get_instance(dip))) == NULL) {
6007
6008 return (USB_INVALID_ARGS);
6009 }
6010
6011 hc_data = &hwahcp->hwahc_hc_data;
6012 mutex_enter(&hc_data->hc_mutex);
6013 for (port = 1; port <= hc_data->hc_num_ports; port++) {
6014 dev_info_t *cdip = hc_data->hc_children_dips[port];
6015
6016 if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
6017
6018 continue;
6019 }
6020
6021 /*
6022 * child's callback has been called and its dip has been
6023 * marked REMOVED. Do further cleanup in hwa driver for
6024 * this child.
6025 */
6026 mutex_exit(&hc_data->hc_mutex);
6027 (void) hwahc_delete_child(dip, port, NDI_DEVI_REMOVE, B_TRUE);
6028 mutex_enter(&hc_data->hc_mutex);
6029 }
6030 mutex_exit(&hc_data->hc_mutex);
6031
6032 return (USB_SUCCESS);
6033 }
6034
6035 /* offline child and cleanup child resources */
6036 static int
6037 hwahc_delete_child(dev_info_t *dip, usb_port_t port, uint_t flag,
6038 boolean_t retry)
6039 {
6040 hwahc_state_t *hwahcp;
6041 dev_info_t *child_dip;
6042 usba_device_t *usba_device;
6043 wusb_hc_data_t *hc_data;
6044 int rval;
6045
6046 if ((hwahcp = ddi_get_soft_state(hwahc_statep,
6047 ddi_get_instance(dip))) == NULL) {
6048
6049 return (USB_INVALID_ARGS);
6050 }
6051
6052 child_dip = hwahc_get_child_dip(hwahcp, port);
6053 if (child_dip == NULL) {
6054
6055 return (USB_SUCCESS);
6056 }
6057
6058 usba_device = usba_get_usba_device(child_dip);
6059 hc_data = &hwahcp->hwahc_hc_data;
6060
6061 USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
6062 "hwahc_delete_child: port=%d, dip=0x%p usba_device=0x%p",
6063 port, (void *)child_dip, (void *)usba_device);
6064
6065 if (usba_device) {
6066 usba_hubdi_incr_power_budget(dip, usba_device);
6067 }
6068
6069 /* remove this child's dip. If it's <DS_INITIALIZED, free it */
6070 rval = usba_destroy_child_devi(child_dip, flag);
6071
6072 if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
6073 /*
6074 * if the child was still < DS_INITIALIZED
6075 * then our bus_unconfig was not called and
6076 * we have to zap the child here
6077 */
6078 mutex_enter(&hc_data->hc_mutex);
6079 if (hc_data->hc_children_dips[port] == child_dip) {
6080 usba_device_t *ud = hc_data->hc_usba_devices[port];
6081 wusb_dev_info_t *dev_info = hc_data->hc_dev_infos[port];
6082
6083 hc_data->hc_children_dips[port] = NULL;
6084 if (ud) {
6085 mutex_exit(&hc_data->hc_mutex);
6086
6087 mutex_enter(&ud->usb_mutex);
6088 ud->usb_ref_count = 0;
6089 mutex_exit(&ud->usb_mutex);
6090
6091 usba_free_usba_device(ud);
6092 mutex_enter(&hc_data->hc_mutex);
6093 hc_data->hc_usba_devices[port] = NULL;
6094 }
6095
6096 /* free the child's wusb_dev_info data */
6097 if (dev_info) {
6098 wusb_secrt_data_t *secrt_data;
6099
6100 if (dev_info->
6101 wdev_secrt_data.secrt_encry_descr) {
6102 secrt_data = &dev_info->wdev_secrt_data;
6103 kmem_free(secrt_data->secrt_encry_descr,
6104 sizeof (usb_encryption_descr_t) *
6105 secrt_data->secrt_n_encry);
6106 }
6107 if (dev_info->wdev_uwb_descr) {
6108 kmem_free(dev_info->wdev_uwb_descr,
6109 sizeof (usb_uwb_cap_descr_t));
6110 }
6111 kmem_free(dev_info, sizeof (wusb_dev_info_t));
6112 hc_data->hc_dev_infos[port] = NULL;
6113 }
6114 }
6115 mutex_exit(&hc_data->hc_mutex);
6116 }
6117
6118 if ((rval != USB_SUCCESS) && retry) {
6119
6120 hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
6121 }
6122
6123 return (rval);
6124 }
6125
6126 /*
6127 * Set encryption type for WUSB host, refer to WUSB 1.0/8.5.3.6
6128 * index = port number - 1
6129 */
6130 int
6131 hwahc_set_dev_encrypt(usb_pipe_handle_t ph, uint8_t ifc,
6132 usb_port_t index, wusb_secrt_data_t *secrt_data, uint8_t type)
6133 {
6134 int16_t value;
6135 usb_ctrl_setup_t setup;
6136 usb_cr_t cr;
6137 usb_cb_flags_t cb_flags;
6138
6139 USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
6140 "hwahc_set_dev_encrypt: device index = %d", index);
6141
6142 if (type == USB_ENC_TYPE_UNSECURE) {
6143 value = 0;
6144 } else if (type == USB_ENC_TYPE_CCM_1) {
6145 if (secrt_data == NULL) {
6146
6147 return (USB_INVALID_ARGS);
6148 }
6149
6150 value = wusb_get_ccm_encryption_value(secrt_data);
6151 if (value == -1) {
6152 USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6153 "hwahc_set_dev_encrypt: cannot find ccm "
6154 "encryption type");
6155
6156 return (USB_FAILURE);
6157 }
6158 USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6159 "hwahc_set_dev_encrypt: ccm encryption value is %d",
6160 value);
6161 } else {
6162 USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6163 "hwahc_set_dev_encrypt: unsupported encryption type %d",
6164 type);
6165
6166 return (USB_INVALID_ARGS);
6167 }
6168
6169 setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
6170 USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
6171 setup.bRequest = USB_REQ_SET_ENCRYPTION;
6172 setup.wValue = (uint16_t)value;
6173 setup.wIndex = (index << 8) | ifc;
6174 setup.wLength = 0;
6175 setup.attrs = USB_ATTRS_NONE;
6176
6177 USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6178 "bmRequestType=0x%x, bRequest=0x%x, wValue=0x%x, wIndex=0x%x",
6179 setup.bmRequestType, setup.bRequest, setup.wValue, setup.wIndex);
6180
6181 return (usb_pipe_ctrl_xfer_wait(ph, &setup, NULL,
6182 &cr, &cb_flags, USB_FLAGS_SLEEP));
6183 }