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 /*
28 * This driver is to be used for download firmware for devices.
29 */
30
31 #if defined(lint) && !defined(DEBUG)
32 #define DEBUG
33 #endif
34
35 #define USBDRV_MAJOR_VER 2
36 #define USBDRV_MINOR_VER 0
37
38 #include <sys/kobj.h>
39 #include <sys/usb/usba.h>
40 #include <sys/usb/usba/usbai_private.h>
41 #include <sys/usb/clients/wusb_df/wusb_df.h>
42
43 #define WUSB_DF_INITIAL_SOFT_SPACE 1
44 #define MAX_DAT_FILE_SIZE (512 * 1024)
45
46 uint_t wusb_df_errlevel = USB_LOG_L4;
47 uint_t wusb_df_errmask = (uint_t)PRINT_MASK_ALL;
48 uint_t wusb_df_instance_debug = (uint_t)-1;
49 static char *name = "wusb_df"; /* Driver name, used all over. */
50 static char wusb_fwmod[] = "hwa1480_fw";
51 static char wusb_fwsym1[] = "hwa1480_fw";
52
53 /* Soft state structures */
54 static void *wusb_df_statep;
55
56 /*
57 * Function Prototypes
58 */
59 static int wusb_df_attach(dev_info_t *, ddi_attach_cmd_t);
60 static int wusb_df_detach(dev_info_t *, ddi_detach_cmd_t);
61 static int wusb_df_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
62 static int wusb_df_cleanup(dev_info_t *, wusb_df_state_t *);
63 static int wusb_df_disconnect_callback(dev_info_t *);
64 static int wusb_df_reconnect_callback(dev_info_t *);
65 static void wusb_df_restore_device_state(dev_info_t *, wusb_df_state_t *);
66 static int wusb_df_cpr_suspend(dev_info_t *);
67 static void wusb_df_cpr_resume(dev_info_t *);
68 static void wusb_df_pm_busy_component(wusb_df_state_t *);
69 static void wusb_df_pm_idle_component(wusb_df_state_t *);
70 static int wusb_df_power(dev_info_t *, int, int);
71 static void wusb_df_init_power_mgmt(wusb_df_state_t *);
72 static void wusb_df_destroy_power_mgmt(wusb_df_state_t *);
73 static int wusb_df_serialize_access(wusb_df_state_t *, boolean_t);
74 static void wusb_df_release_access(wusb_df_state_t *);
75 static int wusb_df_firmware_download(wusb_df_state_t *wusbp);
76
77
78 /* _NOTE is an advice for locklint. Locklint checks lock use for deadlocks. */
79 _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_ctrl_req))
80 _NOTE(SCHEME_PROTECTS_DATA("unique per call", buf))
81
82 /* module loading stuff */
83 struct cb_ops wusb_df_cb_ops = {
84 nodev, /* open */
85 nodev, /* close */
86 nodev, /* strategy */
87 nulldev, /* print */
88 nulldev, /* dump */
89 nodev, /* read */
90 nodev, /* write */
91 nodev, /* ioctl */
92 nulldev, /* devmap */
93 nodev, /* mmap */
94 nodev, /* segmap */
95 nochpoll, /* poll */
96 ddi_prop_op, /* cb_prop_op */
97 NULL, /* streamtab */
98 D_MP
99 };
100
101 static struct dev_ops wusb_df_ops = {
102 DEVO_REV, /* devo_rev, */
103 0, /* refcnt */
104 wusb_df_info, /* info */
105 nulldev, /* identify */
106 nulldev, /* probe */
107 wusb_df_attach, /* attach */
108 wusb_df_detach, /* detach */
109 nodev, /* reset */
110 &wusb_df_cb_ops, /* driver operations */
111 NULL, /* bus operations */
112 wusb_df_power /* power */
113 };
114
115 static struct modldrv wusb_df_modldrv = {
116 &mod_driverops,
117 "WUSB firmware download",
118 &wusb_df_ops
119 };
120
121 static struct modlinkage modlinkage = {
122 MODREV_1,
123 &wusb_df_modldrv,
124 NULL
125 };
126
127 /*
128 * Descriptor for a record of firmware
129 */
130 typedef struct fw_dsc {
131 uint32_t addr;
132 size_t size;
133 uint8_t *data;
134 struct fw_dsc *next;
135 } fw_dsc_t;
136
137
138 /*
139 * Module-wide initialization routine.
140 */
141 int
_init(void)142 _init(void)
143 {
144 int rval;
145
146
147 if ((rval = ddi_soft_state_init(&wusb_df_statep,
148 sizeof (wusb_df_state_t), WUSB_DF_INITIAL_SOFT_SPACE)) != 0) {
149
150 return (rval);
151 }
152
153 if ((rval = mod_install(&modlinkage)) != 0) {
154 ddi_soft_state_fini(&wusb_df_statep);
155 }
156
157
158 return (rval);
159 }
160
161
162 /*
163 * Module-wide tear-down routine.
164 */
165 int
_fini(void)166 _fini(void)
167 {
168 int rval;
169
170 if ((rval = mod_remove(&modlinkage)) != 0) {
171
172 return (rval);
173 }
174
175 ddi_soft_state_fini(&wusb_df_statep);
176
177 return (rval);
178 }
179
180
181 int
_info(struct modinfo * modinfop)182 _info(struct modinfo *modinfop)
183 {
184 return (mod_info(&modlinkage, modinfop));
185 }
186
187 /*
188 * wusb_df_attach:
189 * Attach or resume.
190 *
191 * For attach, initialize state and device, including:
192 * state variables, locks, device node
193 * device registration with system
194 * power management, hotplugging
195 * For resume, restore device and state
196 */
197 static int
wusb_df_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)198 wusb_df_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
199 {
200 int instance = ddi_get_instance(dip);
201 char *devinst;
202 int devinstlen;
203 wusb_df_state_t *wusb_dfp = NULL;
204 usb_ep_data_t *ep_datap;
205 int status;
206
207 switch (cmd) {
208 case DDI_ATTACH:
209 break;
210
211 case DDI_RESUME:
212 wusb_df_cpr_resume(dip);
213
214 /*
215 * Always return success to work around enumeration failures.
216 * This works around an issue where devices which are present
217 * before a suspend and absent upon resume could cause a system
218 * panic on resume.
219 */
220 return (DDI_SUCCESS);
221 default:
222 return (DDI_FAILURE);
223 }
224
225 if (ddi_soft_state_zalloc(wusb_df_statep, instance) == DDI_SUCCESS) {
226 wusb_dfp = ddi_get_soft_state(wusb_df_statep, instance);
227 }
228 if (wusb_dfp == NULL) {
229
230 return (DDI_FAILURE);
231 }
232
233 wusb_dfp->wusb_df_dip = dip;
234
235 devinst = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
236 devinstlen = snprintf(devinst, USB_MAXSTRINGLEN, "%s%d: ",
237 ddi_driver_name(dip), instance);
238
239 wusb_dfp->wusb_df_devinst = kmem_zalloc(devinstlen + 1, KM_SLEEP);
240 (void) strncpy(wusb_dfp->wusb_df_devinst, devinst, devinstlen);
241 kmem_free(devinst, USB_MAXSTRINGLEN);
242
243 wusb_dfp->wusb_df_log_hdl = usb_alloc_log_hdl(dip, "wusb_df",
244 &wusb_df_errlevel, &wusb_df_errmask, &wusb_df_instance_debug, 0);
245
246 USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
247 "Attach: enter for attach");
248
249 if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
250 USB_SUCCESS) {
251 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
252 "attach: usb_client_attach failed, error code:%d", status);
253 goto fail;
254 }
255
256 if ((status = usb_get_dev_data(dip, &wusb_dfp->wusb_df_reg,
257 USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) {
258 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
259 "attach: usb_get_dev_data failed, error code:%d", status);
260 goto fail;
261 }
262
263
264 /*
265 * Get the descriptor for an intr pipe at alt 0 of current interface.
266 * This will be used later to open the pipe.
267 */
268 if ((ep_datap = usb_lookup_ep_data(dip, wusb_dfp->wusb_df_reg,
269 wusb_dfp->wusb_df_reg->dev_curr_if, 0, 0,
270 USB_EP_ATTR_INTR, USB_EP_DIR_IN)) == NULL) {
271 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
272 "attach: Error getting intr endpoint descriptor");
273 goto fail;
274 }
275 wusb_dfp->wusb_df_intr_ep_descr = ep_datap->ep_descr;
276
277 usb_free_descr_tree(dip, wusb_dfp->wusb_df_reg);
278
279 mutex_init(&wusb_dfp->wusb_df_mutex, NULL, MUTEX_DRIVER,
280 wusb_dfp->wusb_df_reg->dev_iblock_cookie);
281
282 cv_init(&wusb_dfp->wusb_df_serial_cv, NULL, CV_DRIVER, NULL);
283 wusb_dfp->wusb_df_serial_inuse = B_FALSE;
284
285 wusb_dfp->wusb_df_locks_initialized = B_TRUE;
286
287 /* create minor node */
288 if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
289 "wusb_df", 0) != DDI_SUCCESS) {
290 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
291 "attach: Error creating minor node");
292 goto fail;
293 }
294
295 /* Put online before PM init as can get power managed afterward. */
296 wusb_dfp->wusb_df_dev_state = USB_DEV_ONLINE;
297
298 /* initialize power management */
299 wusb_df_init_power_mgmt(wusb_dfp);
300
301 if (usb_register_hotplug_cbs(dip, wusb_df_disconnect_callback,
302 wusb_df_reconnect_callback) != USB_SUCCESS) {
303
304 goto fail;
305 }
306
307 /* Report device */
308 ddi_report_dev(dip);
309
310 (void) wusb_df_firmware_download(wusb_dfp);
311
312 if (usb_reset_device(dip, USB_RESET_LVL_REATTACH) != USB_SUCCESS) {
313 USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
314 "reset device failed");
315
316 return (USB_FAILURE);
317 }
318
319 return (DDI_SUCCESS);
320
321 fail:
322 if (wusb_dfp) {
323 (void) wusb_df_cleanup(dip, wusb_dfp);
324 }
325
326 return (DDI_FAILURE);
327 }
328
329
330 /*
331 * wusb_df_detach:
332 * detach or suspend driver instance
333 *
334 * Note: in detach, only contention threads is from pm and disconnnect.
335 */
336 static int
wusb_df_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)337 wusb_df_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
338 {
339 int instance = ddi_get_instance(dip);
340 wusb_df_state_t *wusb_dfp =
341 ddi_get_soft_state(wusb_df_statep, instance);
342 int rval = DDI_FAILURE;
343
344 switch (cmd) {
345 case DDI_DETACH:
346
347 USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
348 "Detach: enter for detach");
349
350 rval = wusb_df_cleanup(dip, wusb_dfp);
351
352 break;
353 case DDI_SUSPEND:
354 USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
355 "Detach: enter for suspend");
356
357 rval = wusb_df_cpr_suspend(dip);
358 default:
359
360 break;
361 }
362
363 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
364 }
365
366
367 /*
368 * wusb_df_cleanup:
369 * clean up the driver state for detach
370 */
371 static int
wusb_df_cleanup(dev_info_t * dip,wusb_df_state_t * wusb_dfp)372 wusb_df_cleanup(dev_info_t *dip, wusb_df_state_t *wusb_dfp)
373 {
374
375 USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
376 "Cleanup: enter");
377
378 if (wusb_dfp->wusb_df_locks_initialized) {
379
380 /* This must be done 1st to prevent more events from coming. */
381 usb_unregister_hotplug_cbs(dip);
382
383 /*
384 * At this point, no new activity can be initiated. The driver
385 * has disabled hotplug callbacks. The Solaris framework has
386 * disabled new opens on a device being detached, and does not
387 * allow detaching an open device.
388 *
389 * The following ensures that all driver activity has drained.
390 */
391 mutex_enter(&wusb_dfp->wusb_df_mutex);
392 (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG);
393 wusb_df_release_access(wusb_dfp);
394 mutex_exit(&wusb_dfp->wusb_df_mutex);
395
396 /* All device activity has died down. */
397 wusb_df_destroy_power_mgmt(wusb_dfp);
398
399 /* start dismantling */
400 ddi_remove_minor_node(dip, NULL);
401
402 cv_destroy(&wusb_dfp->wusb_df_serial_cv);
403 mutex_destroy(&wusb_dfp->wusb_df_mutex);
404 }
405
406 usb_client_detach(dip, wusb_dfp->wusb_df_reg);
407
408 usb_free_log_hdl(wusb_dfp->wusb_df_log_hdl);
409
410 if (wusb_dfp->wusb_df_devinst != NULL) {
411 kmem_free(wusb_dfp->wusb_df_devinst,
412 strlen(wusb_dfp->wusb_df_devinst) + 1);
413 }
414
415 ddi_soft_state_free(wusb_df_statep, ddi_get_instance(dip));
416 ddi_prop_remove_all(dip);
417
418 return (USB_SUCCESS);
419 }
420
421
422 /*
423 * wusb_df_disconnect_callback:
424 * Called when device hotplug-removed.
425 * Close pipes. (This does not attempt to contact device.)
426 * Set state to DISCONNECTED
427 */
428 static int
wusb_df_disconnect_callback(dev_info_t * dip)429 wusb_df_disconnect_callback(dev_info_t *dip)
430 {
431 int instance = ddi_get_instance(dip);
432 wusb_df_state_t *wusb_dfp =
433 ddi_get_soft_state(wusb_df_statep, instance);
434
435
436 USB_DPRINTF_L4(PRINT_MASK_CB, wusb_dfp->wusb_df_log_hdl,
437 "disconnect: enter");
438
439 mutex_enter(&wusb_dfp->wusb_df_mutex);
440 (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG);
441
442 /*
443 * Save any state of device or IO in progress required by
444 * wusb_df_restore_device_state for proper device "thawing" later.
445 */
446 wusb_dfp->wusb_df_dev_state = USB_DEV_DISCONNECTED;
447
448 wusb_df_release_access(wusb_dfp);
449 mutex_exit(&wusb_dfp->wusb_df_mutex);
450
451 return (USB_SUCCESS);
452 }
453
454
455 /*
456 * wusb_df_reconnect_callback:
457 * Called with device hotplug-inserted
458 * Restore state
459 */
460 static int
wusb_df_reconnect_callback(dev_info_t * dip)461 wusb_df_reconnect_callback(dev_info_t *dip)
462 {
463 int instance = ddi_get_instance(dip);
464 wusb_df_state_t *wusb_dfp =
465 ddi_get_soft_state(wusb_df_statep, instance);
466
467 USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
468 "reconnect: enter");
469
470 wusb_df_pm_busy_component(wusb_dfp);
471 (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR);
472
473 mutex_enter(&wusb_dfp->wusb_df_mutex);
474 (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG);
475 wusb_df_restore_device_state(dip, wusb_dfp);
476 wusb_df_release_access(wusb_dfp);
477 mutex_exit(&wusb_dfp->wusb_df_mutex);
478
479 wusb_df_pm_idle_component(wusb_dfp);
480
481 return (USB_SUCCESS);
482 }
483
484
485 /*
486 * wusb_df_restore_device_state:
487 * Called during hotplug-reconnect and resume.
488 * reenable power management
489 * Verify the device is the same as before the disconnect/suspend.
490 * Restore device state
491 * Thaw any IO which was frozen.
492 * Quiesce device. (Other routines will activate if thawed IO.)
493 * Set device online.
494 * Leave device disconnected if there are problems.
495 */
496 static void
wusb_df_restore_device_state(dev_info_t * dip,wusb_df_state_t * wusb_dfp)497 wusb_df_restore_device_state(dev_info_t *dip, wusb_df_state_t *wusb_dfp)
498 {
499 USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
500 "wusb_df_restore_device_state: enter");
501
502 ASSERT(mutex_owned(&wusb_dfp->wusb_df_mutex));
503
504 ASSERT((wusb_dfp->wusb_df_dev_state == USB_DEV_DISCONNECTED) ||
505 (wusb_dfp->wusb_df_dev_state == USB_DEV_SUSPENDED));
506
507 mutex_exit(&wusb_dfp->wusb_df_mutex);
508
509
510 /* Check if we are talking to the same device */
511 if (usb_check_same_device(dip, wusb_dfp->wusb_df_log_hdl,
512 USB_LOG_L0, PRINT_MASK_ALL,
513 USB_CHK_ALL, NULL) != USB_SUCCESS) {
514
515 /* change the device state from suspended to disconnected */
516 mutex_enter(&wusb_dfp->wusb_df_mutex);
517 wusb_dfp->wusb_df_dev_state = USB_DEV_SUSPENDED;
518 USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
519 "wusb_df_restore_device_state: check same device failed");
520 return;
521 }
522
523 mutex_enter(&wusb_dfp->wusb_df_mutex);
524 wusb_dfp->wusb_df_dev_state = USB_DEV_ONLINE;
525
526 if (wusb_dfp->wusb_df_pm &&
527 wusb_dfp->wusb_df_pm->wusb_df_wakeup_enabled) {
528
529 /* Failure here means device disappeared again. */
530 mutex_exit(&wusb_dfp->wusb_df_mutex);
531 if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) !=
532 USB_SUCCESS) {
533 USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
534 "device may or may not be accessible. "
535 "Please verify reconnection");
536 }
537 mutex_enter(&wusb_dfp->wusb_df_mutex);
538 }
539
540
541 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
542 "wusb_df_restore_device_state: end");
543
544 }
545
546
547 /*
548 * wusb_df_cpr_suspend:
549 * Clean up device.
550 * Wait for any IO to finish, then close pipes.
551 * Quiesce device.
552 */
553 static int
wusb_df_cpr_suspend(dev_info_t * dip)554 wusb_df_cpr_suspend(dev_info_t *dip)
555 {
556 int instance = ddi_get_instance(dip);
557 wusb_df_state_t *wusb_dfp =
558 ddi_get_soft_state(wusb_df_statep, instance);
559
560 USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
561 "suspend enter");
562
563 /* Serialize to prevent races with detach, open, device access. */
564 mutex_enter(&wusb_dfp->wusb_df_mutex);
565 (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG);
566 mutex_exit(&wusb_dfp->wusb_df_mutex);
567
568 wusb_df_pm_busy_component(wusb_dfp);
569
570 mutex_enter(&wusb_dfp->wusb_df_mutex);
571
572 /* Access device here to clean it up. */
573
574 wusb_dfp->wusb_df_dev_state = USB_DEV_SUSPENDED;
575
576 /*
577 * Save any state of device required by wusb_df_restore_device_state
578 * for proper device "thawing" later.
579 */
580
581 wusb_df_release_access(wusb_dfp);
582 mutex_exit(&wusb_dfp->wusb_df_mutex);
583
584 wusb_df_pm_idle_component(wusb_dfp);
585
586 USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
587 "suspend: success");
588
589 return (USB_SUCCESS);
590 }
591
592
593 /*
594 * wusb_df_cpr_resume:
595 *
596 * wusb_df_restore_device_state marks success by putting device back online
597 */
598 static void
wusb_df_cpr_resume(dev_info_t * dip)599 wusb_df_cpr_resume(dev_info_t *dip)
600 {
601 int instance = ddi_get_instance(dip);
602 wusb_df_state_t *wusb_dfp =
603 ddi_get_soft_state(wusb_df_statep, instance);
604
605 USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_dfp->wusb_df_log_hdl,
606 "resume: enter");
607
608 /*
609 * NOTE: A pm_raise_power in wusb_df_restore_device_state will bring
610 * the power-up state of device into synch with the system.
611 */
612 wusb_df_pm_busy_component(wusb_dfp);
613 (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR);
614 mutex_enter(&wusb_dfp->wusb_df_mutex);
615 wusb_df_restore_device_state(dip, wusb_dfp);
616 mutex_exit(&wusb_dfp->wusb_df_mutex);
617 wusb_df_pm_idle_component(wusb_dfp);
618 }
619
620 static void
wusb_df_pm_busy_component(wusb_df_state_t * wusb_dfp)621 wusb_df_pm_busy_component(wusb_df_state_t *wusb_dfp)
622 {
623 ASSERT(!mutex_owned(&wusb_dfp->wusb_df_mutex));
624
625 mutex_enter(&wusb_dfp->wusb_df_mutex);
626 if (wusb_dfp->wusb_df_pm == NULL) {
627 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
628 "wusb_df_pm_busy_component: pm = NULL");
629 goto done;
630 }
631
632 wusb_dfp->wusb_df_pm->wusb_df_pm_busy++;
633 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
634 "wusb_df_pm_busy_component: %d",
635 wusb_dfp->wusb_df_pm->wusb_df_pm_busy);
636
637 mutex_exit(&wusb_dfp->wusb_df_mutex);
638
639 if (pm_busy_component(wusb_dfp->wusb_df_dip, 0) != DDI_SUCCESS) {
640 mutex_enter(&wusb_dfp->wusb_df_mutex);
641 wusb_dfp->wusb_df_pm->wusb_df_pm_busy--;
642
643 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
644 "wusb_df_pm_busy_component: %d",
645 wusb_dfp->wusb_df_pm->wusb_df_pm_busy);
646 mutex_exit(&wusb_dfp->wusb_df_mutex);
647
648
649 }
650 return;
651 done:
652 mutex_exit(&wusb_dfp->wusb_df_mutex);
653
654 }
655
656 static void
wusb_df_pm_idle_component(wusb_df_state_t * wusb_dfp)657 wusb_df_pm_idle_component(wusb_df_state_t *wusb_dfp)
658 {
659 ASSERT(!mutex_owned(&wusb_dfp->wusb_df_mutex));
660 mutex_enter(&wusb_dfp->wusb_df_mutex);
661 if (wusb_dfp->wusb_df_pm == NULL) {
662 mutex_exit(&wusb_dfp->wusb_df_mutex);
663 return;
664 }
665 mutex_exit(&wusb_dfp->wusb_df_mutex);
666
667
668 if (pm_idle_component(wusb_dfp->wusb_df_dip, 0) == DDI_SUCCESS) {
669 mutex_enter(&wusb_dfp->wusb_df_mutex);
670 ASSERT(wusb_dfp->wusb_df_pm->wusb_df_pm_busy > 0);
671 wusb_dfp->wusb_df_pm->wusb_df_pm_busy--;
672
673 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
674 "wusb_df_pm_idle_component: %d",
675 wusb_dfp->wusb_df_pm->wusb_df_pm_busy);
676
677 mutex_exit(&wusb_dfp->wusb_df_mutex);
678 }
679 }
680
681 /*
682 * wusb_df_power :
683 * Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
684 * usb_req_raise_power and usb_req_lower_power.
685 */
686 /* ARGSUSED */
687 static int
wusb_df_power(dev_info_t * dip,int comp,int level)688 wusb_df_power(dev_info_t *dip, int comp, int level)
689 {
690 wusb_df_state_t *wusb_dfp;
691 wusb_df_power_t *pm;
692 int rval = USB_SUCCESS;
693
694 wusb_dfp = ddi_get_soft_state(wusb_df_statep, ddi_get_instance(dip));
695
696 USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
697 "wusb_df_power: enter: level = %d", level);
698
699 mutex_enter(&wusb_dfp->wusb_df_mutex);
700 (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG);
701
702
703 /*
704 * If we are disconnected/suspended, return success. Note that if we
705 * return failure, bringing down the system will hang when
706 * PM tries to power up all devices
707 */
708 if ((wusb_dfp->wusb_df_dev_state == USB_DEV_DISCONNECTED) ||
709 (wusb_dfp->wusb_df_dev_state == USB_DEV_SUSPENDED)) {
710
711 USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
712 "wusb_df_power: disconnected/suspended "
713 "dev_state=%d", wusb_dfp->wusb_df_dev_state);
714 rval = USB_SUCCESS;
715
716 goto done;
717 }
718
719 if (wusb_dfp->wusb_df_pm == NULL) {
720
721 goto done;
722 }
723
724 pm = wusb_dfp->wusb_df_pm;
725
726 /* Check if we are transitioning to a legal power level */
727 if (USB_DEV_PWRSTATE_OK(pm->wusb_df_pwr_states, level)) {
728 USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
729 "wusb_df_power: illegal power level = %d "
730 "pwr_states: %x", level, pm->wusb_df_pwr_states);
731
732 goto done;
733 }
734
735 switch (level) {
736 case USB_DEV_OS_PWR_OFF :
737 /* fail attempt to go to low power if busy */
738 if (pm->wusb_df_pm_busy) {
739
740 goto done;
741 }
742 if (wusb_dfp->wusb_df_dev_state == USB_DEV_ONLINE) {
743 wusb_dfp->wusb_df_dev_state = USB_DEV_PWRED_DOWN;
744 wusb_dfp->wusb_df_pm->wusb_df_current_power =
745 USB_DEV_OS_PWR_OFF;
746 } else {
747 rval = USB_SUCCESS;
748 }
749 break;
750
751 case USB_DEV_OS_FULL_PWR :
752 /*
753 * PM framework tries to put us in full power during system
754 * shutdown.
755 */
756 wusb_dfp->wusb_df_dev_state = USB_DEV_ONLINE;
757 wusb_dfp->wusb_df_pm->wusb_df_current_power =
758 USB_DEV_OS_FULL_PWR;
759 break;
760
761 /* Levels 1 and 2 are not supported by this driver to keep it simple. */
762 default:
763 USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
764 "wusb_df_power: power level %d not supported", level);
765 break;
766 }
767 done:
768 wusb_df_release_access(wusb_dfp);
769 mutex_exit(&wusb_dfp->wusb_df_mutex);
770
771 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
772 }
773
774
775 /*
776 * wusb_df_init_power_mgmt:
777 * Initialize power management and remote wakeup functionality.
778 * No mutex is necessary in this function as it's called only by attach.
779 */
780 static void
wusb_df_init_power_mgmt(wusb_df_state_t * wusb_dfp)781 wusb_df_init_power_mgmt(wusb_df_state_t *wusb_dfp)
782 {
783 wusb_df_power_t *wusb_dfpm;
784 uint_t pwr_states;
785
786 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
787 "init_power_mgmt enter");
788
789 /*
790 * If remote wakeup is not available you may not want to do
791 * power management.
792 */
793 /* Allocate the state structure */
794 wusb_dfpm = kmem_zalloc(sizeof (wusb_df_power_t), KM_SLEEP);
795 wusb_dfp->wusb_df_pm = wusb_dfpm;
796 wusb_dfpm->wusb_df_state = wusb_dfp;
797 wusb_dfpm->wusb_df_pm_capabilities = 0;
798 wusb_dfpm->wusb_df_current_power = USB_DEV_OS_FULL_PWR;
799
800 if (usb_create_pm_components(wusb_dfp->wusb_df_dip, &pwr_states) ==
801 USB_SUCCESS) {
802
803 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
804 "wusb_df_init_power_mgmt: created PM components");
805
806 wusb_dfpm->wusb_df_pwr_states = (uint8_t)pwr_states;
807 (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0,
808 USB_DEV_OS_FULL_PWR);
809
810 if (usb_handle_remote_wakeup(wusb_dfp->wusb_df_dip,
811 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
812 wusb_dfpm->wusb_df_wakeup_enabled = 1;
813 } else {
814 USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
815 "wusb_df_init_power_mgmt:"
816 "fail to enable remote wakeup");
817 }
818
819 } else {
820 USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
821 "wusb_df_init_power_mgmt: create_pm_compts failed");
822 }
823 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
824 "wusb_df_init_power_mgmt: end");
825
826 }
827
828
829 /*
830 * wusb_df_destroy_power_mgmt:
831 * Shut down and destroy power management and remote wakeup functionality.
832 */
833 static void
wusb_df_destroy_power_mgmt(wusb_df_state_t * wusb_dfp)834 wusb_df_destroy_power_mgmt(wusb_df_state_t *wusb_dfp)
835 {
836 USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl,
837 "destroy_power_mgmt enter");
838
839 ASSERT(!mutex_owned(&wusb_dfp->wusb_df_mutex));
840
841 mutex_enter(&wusb_dfp->wusb_df_mutex);
842 if (!wusb_dfp->wusb_df_pm) {
843 mutex_exit(&wusb_dfp->wusb_df_mutex);
844 return;
845 }
846 mutex_exit(&wusb_dfp->wusb_df_mutex);
847
848 (void) wusb_df_pm_busy_component(wusb_dfp);
849
850 mutex_enter(&wusb_dfp->wusb_df_mutex);
851 if (wusb_dfp->wusb_df_dev_state != USB_DEV_DISCONNECTED) {
852
853 if (wusb_dfp->wusb_df_pm->wusb_df_wakeup_enabled) {
854 mutex_exit(&wusb_dfp->wusb_df_mutex);
855
856 (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0,
857 USB_DEV_OS_FULL_PWR);
858 if (usb_handle_remote_wakeup(wusb_dfp->wusb_df_dip,
859 USB_REMOTE_WAKEUP_DISABLE) != USB_SUCCESS) {
860 USB_DPRINTF_L2(PRINT_MASK_PM,
861 wusb_dfp->wusb_df_log_hdl,
862 "wusb_df_destroy_power_mgmt: "
863 "Error disabling rmt wakeup");
864 }
865 mutex_enter(&wusb_dfp->wusb_df_mutex);
866
867 }
868 }
869 mutex_exit(&wusb_dfp->wusb_df_mutex);
870
871 /*
872 * Since remote wakeup is disabled now,
873 * no one can raise power
874 * and get to device once power is lowered here.
875 */
876 (void) pm_lower_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_PWR_OFF);
877 wusb_df_pm_idle_component(wusb_dfp);
878
879 mutex_enter(&wusb_dfp->wusb_df_mutex);
880 kmem_free(wusb_dfp->wusb_df_pm, sizeof (wusb_df_power_t));
881 wusb_dfp->wusb_df_pm = NULL;
882 mutex_exit(&wusb_dfp->wusb_df_mutex);
883 }
884
885
886 /*
887 * wusb_df_serialize_access:
888 * Get the serial synchronization object before returning.
889 *
890 * Arguments:
891 * wusb_dfp - Pointer to wusb_df state structure
892 * waitsig - Set to:
893 * WUSB_DF_SER_SIG - to wait such that a signal can interrupt
894 * WUSB_DF_SER_NOSIG - to wait such that a signal cannot interrupt
895 */
896 static int
wusb_df_serialize_access(wusb_df_state_t * wusb_dfp,boolean_t waitsig)897 wusb_df_serialize_access(wusb_df_state_t *wusb_dfp, boolean_t waitsig)
898 {
899 int rval = 1;
900
901 ASSERT(mutex_owned(&wusb_dfp->wusb_df_mutex));
902
903 while (wusb_dfp->wusb_df_serial_inuse) {
904 if (waitsig == WUSB_DF_SER_SIG) {
905 rval = cv_wait_sig(&wusb_dfp->wusb_df_serial_cv,
906 &wusb_dfp->wusb_df_mutex);
907 } else {
908 cv_wait(&wusb_dfp->wusb_df_serial_cv,
909 &wusb_dfp->wusb_df_mutex);
910 }
911 }
912 wusb_dfp->wusb_df_serial_inuse = B_TRUE;
913
914 return (rval);
915 }
916
917
918 /*
919 * wusb_df_release_access:
920 * Release the serial synchronization object.
921 */
922 static void
wusb_df_release_access(wusb_df_state_t * wusb_dfp)923 wusb_df_release_access(wusb_df_state_t *wusb_dfp)
924 {
925 ASSERT(mutex_owned(&wusb_dfp->wusb_df_mutex));
926 wusb_dfp->wusb_df_serial_inuse = B_FALSE;
927 cv_broadcast(&wusb_dfp->wusb_df_serial_cv);
928 }
929
930 /*
931 * wusb_df_info:
932 * Get minor number, soft state structure, etc.
933 */
934 /*ARGSUSED*/
935 static int
wusb_df_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)936 wusb_df_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
937 void *arg, void **result)
938 {
939 wusb_df_state_t *wusb_dfp;
940 int error = DDI_FAILURE;
941
942 switch (infocmd) {
943 case DDI_INFO_DEVT2DEVINFO:
944 if ((wusb_dfp = ddi_get_soft_state(wusb_df_statep,
945 getminor((dev_t)arg))) != NULL) {
946 *result = wusb_dfp->wusb_df_dip;
947 if (*result != NULL) {
948 error = DDI_SUCCESS;
949 }
950 } else {
951 *result = NULL;
952 }
953 break;
954 case DDI_INFO_DEVT2INSTANCE:
955 *result = (void *)(uintptr_t)getminor((dev_t)arg);
956 error = DDI_SUCCESS;
957 break;
958 default:
959 break;
960 }
961
962 return (error);
963 }
964
965 /* Free a chain of firmware headers */
966 static void
free_fw_dscs(struct fw_dsc * head)967 free_fw_dscs(struct fw_dsc *head)
968 {
969 struct fw_dsc *next;
970
971 while (head) {
972 next = head->next;
973 kmem_free(head, sizeof (fw_dsc_t));
974 head = next;
975 }
976 }
977
978 static unsigned int
char2int32(unsigned char * input)979 char2int32(unsigned char *input)
980 {
981 return ((*input) |
982 (*(input + 1) << 8) |
983 (*(input + 2) << 16) |
984 (*(input + 3) << 24));
985 }
986
987 /*
988 * download firmware or command by control pipe
989 */
990 static int
wusb_df_send_data(wusb_df_state_t * wusbp,unsigned int address,const unsigned char * buffer,unsigned int size)991 wusb_df_send_data(wusb_df_state_t *wusbp,
992 unsigned int address,
993 const unsigned char *buffer,
994 unsigned int size)
995 {
996 int error = DDI_FAILURE;
997 usb_ctrl_setup_t setup;
998 usb_cb_flags_t cb_flags;
999 usb_cr_t cr;
1000 mblk_t *data = NULL; /* data for USBA */
1001 uint16_t data_len; /* # of bytes want to write */
1002 uint_t cnt; /* # of xfered bytes */
1003
1004 setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR |
1005 USB_DEV_REQ_HOST_TO_DEV | USB_DEV_REQ_RCPT_DEV;
1006 setup.bRequest = 0xf0;
1007 setup.attrs = 0;
1008
1009 for (cnt = 0; cnt < size; cnt += data_len) {
1010 data_len = min(size - cnt, 512);
1011
1012 /* reuse previous mblk if possible */
1013 if ((data = reallocb(data, data_len, 0)) == NULL) {
1014
1015 return (USB_FAILURE);
1016 }
1017 bcopy(buffer + cnt, data->b_rptr, data_len);
1018 data->b_wptr += data_len;
1019
1020 setup.wValue = (address + cnt) & 0xffff;
1021 setup.wIndex = ((address + cnt) >> 16) & 0xffff;
1022 setup.wLength = data_len;
1023 error = usb_pipe_ctrl_xfer_wait(
1024 wusbp->wusb_df_reg->dev_default_ph, &setup, &data,
1025 &cr, &cb_flags, 0);
1026 if (error != USB_SUCCESS) {
1027 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusbp->wusb_df_log_hdl,
1028 "wusb_df_send_data: "
1029 "send failed rval=%d, cr=%d, cb=0x%x\n",
1030 error, cr, cb_flags);
1031
1032 break;
1033 }
1034 }
1035
1036 if (data) {
1037 freemsg(data);
1038 }
1039
1040 return (error);
1041 }
1042
1043 /*
1044 * find the firmware module's "_start", "_end" symbols
1045 * and get the size of firmware.
1046 */
1047 static int
wusbdf_mod_loadsym(wusb_df_state_t * dfp,ddi_modhandle_t modp,char * mod,char * sym,char ** start,size_t * len)1048 wusbdf_mod_loadsym(wusb_df_state_t *dfp, ddi_modhandle_t modp, char *mod,
1049 char *sym, char **start, size_t *len)
1050 {
1051 char start_sym[256];
1052 char end_sym[256];
1053 char *p, *end;
1054 int rv;
1055 size_t n;
1056
1057 (void) snprintf(start_sym, sizeof (start_sym), "%s_start", sym);
1058 (void) snprintf(end_sym, sizeof (end_sym), "%s_end", sym);
1059
1060 p = (char *)ddi_modsym(modp, start_sym, &rv);
1061 if (p == NULL || rv != 0) {
1062 USB_DPRINTF_L2(PRINT_MASK_ATTA, dfp->wusb_df_log_hdl,
1063 "mod %s: symbol %s not found\n", mod, start_sym);
1064 return (-1);
1065 }
1066 end = (char *)ddi_modsym(modp, end_sym, &rv);
1067 if (end == NULL || rv != 0) {
1068 USB_DPRINTF_L2(PRINT_MASK_ATTA, dfp->wusb_df_log_hdl,
1069 "mod %s: symbol %s not found\n", mod, end_sym);
1070 return (-1);
1071 }
1072 n = end - p;
1073
1074 *start = p;
1075 *len = n;
1076
1077 return (0);
1078 }
1079
1080 /* write firmware segments into device through control endpoint */
1081 static int
wusb_df_fw_download(wusb_df_state_t * wusb_dfp)1082 wusb_df_fw_download(wusb_df_state_t *wusb_dfp)
1083 {
1084 int error = USB_SUCCESS;
1085 size_t size = 0, record_cnt = 0;
1086 unsigned char *pdata, *data_end;
1087 unsigned char *firmware_image;
1088 fw_dsc_t *pdsc = NULL, *rcd_head = NULL, *tmpr = NULL;
1089 unsigned int remaining_size;
1090 int rv = 0;
1091 ddi_modhandle_t modp;
1092 char *firm_start;
1093
1094 USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1095 "Download firmware: %s", wusb_fwmod);
1096
1097 /* allow user specify firmware in .conf? */
1098
1099 /* see elfwrap(1) for how to turn firmware into loadable module */
1100 modp = ddi_modopen(wusb_fwmod, KRTLD_MODE_FIRST, &rv);
1101 if (modp == NULL) {
1102 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1103 "module %s not found", wusb_fwmod);
1104
1105 error = USB_FAILURE;
1106 goto checkstatus;
1107 }
1108
1109 rv = wusbdf_mod_loadsym(wusb_dfp, modp, wusb_fwmod, wusb_fwsym1,
1110 &firm_start, &size);
1111 if (rv != 0) {
1112 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1113 "module(%s) loadsym error", wusb_fwmod);
1114
1115 error = USB_FAILURE;
1116 goto checkstatus;
1117 }
1118
1119 if (size >= MAX_DAT_FILE_SIZE) {
1120 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1121 "file size too big");
1122
1123 error = USB_FAILURE;
1124 goto checkstatus;
1125 } else {
1126 firmware_image = (unsigned char *)kmem_alloc(size, KM_SLEEP);
1127
1128 if (!firmware_image) {
1129 USB_DPRINTF_L2(PRINT_MASK_ATTA,
1130 wusb_dfp->wusb_df_log_hdl, "malloc failed");
1131
1132 error = USB_FAILURE;
1133 goto checkstatus;
1134 }
1135
1136 (void) memcpy(firmware_image, firm_start, size);
1137 }
1138
1139 USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1140 "file size = %d", (int)size);
1141
1142 /*
1143 * close the module, return if 1) fail to close or 2) encounter error
1144 * when getting above symbol
1145 */
1146 checkstatus:
1147 if (modp != NULL)
1148 rv = ddi_modclose(modp);
1149
1150 if ((rv != 0) || (error != USB_SUCCESS)) {
1151 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1152 "modclose(%s) error", wusb_fwmod);
1153
1154 return (USB_FAILURE);
1155 }
1156
1157 /*
1158 * BIN firmware has this format:
1159 * address(4B) + length(4B) + data(Length Bytes) ... repeat
1160 */
1161 pdata = firmware_image;
1162 data_end = firmware_image + size;
1163 while (pdata < data_end) {
1164 error = USB_FAILURE;
1165 pdsc = (fw_dsc_t *)kmem_zalloc(sizeof (fw_dsc_t), KM_SLEEP);
1166
1167 /* hdr_offset = pdata - firmware_image; */
1168 remaining_size = data_end - pdata;
1169
1170 if ((pdata + 8) > data_end) {
1171 kmem_free(pdsc, sizeof (fw_dsc_t));
1172 free_fw_dscs(rcd_head);
1173 break;
1174 }
1175
1176 pdsc->next = NULL;
1177 pdsc->addr = char2int32(pdata);
1178 pdsc->size = 4 * char2int32(pdata + 4);
1179 pdsc->data = pdata + 8;
1180 if (pdsc->size > remaining_size) {
1181 kmem_free(pdsc, sizeof (fw_dsc_t));
1182 free_fw_dscs(rcd_head);
1183 break;
1184 }
1185 USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1186 "address = 0x%x, length = 0x%x, "
1187 "first 4 byte is : 0x%02x 0x%02x 0x%02x 0x%02x",
1188 pdsc->addr, (int)pdsc->size, pdsc->data[0], pdsc->data[1],
1189 pdsc->data[2], pdsc->data[3]);
1190
1191 pdata += 8 + pdsc->size;
1192 if (rcd_head == NULL) {
1193 rcd_head = pdsc;
1194 } else {
1195 tmpr->next = pdsc;
1196 }
1197
1198 tmpr = pdsc; /* tmp record */
1199 record_cnt ++;
1200 error = USB_SUCCESS;
1201 }
1202
1203 USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1204 "Start download firmware ...");
1205 for (pdsc = rcd_head; pdsc != NULL; pdsc = pdsc->next) {
1206 error = wusb_df_send_data(wusb_dfp, pdsc->addr,
1207 pdsc->data, pdsc->size);
1208 if (error != USB_SUCCESS) {
1209
1210 USB_DPRINTF_L2(PRINT_MASK_ATTA,
1211 wusb_dfp->wusb_df_log_hdl, "Download failure!");
1212 break;
1213 }
1214
1215 delay(drv_usectohz(1000));
1216 }
1217
1218 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl,
1219 "Download firmware end.");
1220
1221 free_fw_dscs(rcd_head);
1222 kmem_free(firmware_image, size);
1223
1224 return (error);
1225 }
1226
1227
1228 /*
1229 * Firmware download. Program device special registers and then call
1230 * wusb_df_fw_download() to download the true data.
1231 */
1232 static int
wusb_df_firmware_download(wusb_df_state_t * wusbp)1233 wusb_df_firmware_download(wusb_df_state_t *wusbp)
1234 {
1235 int error = USB_FAILURE;
1236 unsigned char buf[4];
1237
1238 (void) memset(buf, 0, 4);
1239 /* program the device register to make it ready to accept fw */
1240 error = wusb_df_send_data(wusbp, 0x800000c0, buf, 4);
1241 if (error != USB_SUCCESS) {
1242 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusbp->wusb_df_log_hdl,
1243 "Fail init");
1244 return (error);
1245 }
1246
1247 error = wusb_df_fw_download(wusbp);
1248 if (error != USB_SUCCESS) {
1249 USB_DPRINTF_L2(PRINT_MASK_ATTA, wusbp->wusb_df_log_hdl,
1250 "Fail to download firmware");
1251 return (error);
1252 }
1253
1254 buf[0] = 0x48;
1255 buf[1] = 0x56;
1256 buf[2] = 0x2c;
1257 buf[3] = 0x00;
1258 error = wusb_df_send_data(wusbp, 0x80008060, buf, 4);
1259 if (error != USB_SUCCESS) {
1260 return (error);
1261 }
1262
1263 (void) memset(buf, 0, 4);
1264 buf[0] = 0x18;
1265 /* firmware download finished, program the device to lock fw */
1266 error = wusb_df_send_data(wusbp, 0x800000c0, buf, 4);
1267
1268 return (error);
1269 }
1270