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