Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / usb / usba / usba_ugen.c
blob717b852549e066e4275c2acbeb7da9f27817fe57
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
21 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
23 * Copyright (c) 2016 by Delphix. All rights reserved.
27 * Copyright 2016 Joyent, Inc.
31 * UGEN: USB Generic Driver support code
33 * This code provides entry points called by the ugen driver or other
34 * drivers that want to export a ugen interface
36 * The "Universal Generic Driver" (UGEN) for USB devices provides interfaces
37 * to talk to USB devices. This is very useful for Point of Sale sale
38 * devices and other simple devices like USB scanner, USB palm pilot.
39 * The UGEN provides a system call interface to USB devices enabling
40 * a USB device vendor to write an application for their
41 * device instead of writing a driver. This facilitates the vendor to write
42 * device management s/w quickly in userland.
44 * UGEN supports read/write/poll entry points. An application can be written
45 * using read/write/aioread/aiowrite/poll system calls to communicate
46 * with the device.
48 * XXX Theory of Operations
50 #include <sys/usb/usba/usbai_version.h>
51 #include <sys/usb/usba.h>
52 #include <sys/sysmacros.h>
53 #include <sys/strsun.h>
55 #include "sys/usb/clients/ugen/usb_ugen.h"
56 #include "sys/usb/usba/usba_ugen.h"
57 #include "sys/usb/usba/usba_ugend.h"
59 /* Debugging information */
60 uint_t ugen_errmask = (uint_t)UGEN_PRINT_ALL;
61 uint_t ugen_errlevel = USB_LOG_L4;
62 uint_t ugen_instance_debug = (uint_t)-1;
64 /* default endpoint descriptor */
65 static usb_ep_descr_t ugen_default_ep_descr =
66 {7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0};
68 /* tunables */
69 int ugen_busy_loop = 60; /* secs */
70 int ugen_ctrl_timeout = 10;
71 int ugen_bulk_timeout = 10;
72 int ugen_intr_timeout = 10;
73 int ugen_enable_pm = 0;
74 int ugen_isoc_buf_limit = 1000; /* ms */
77 /* local function prototypes */
78 static int ugen_cleanup(ugen_state_t *);
79 static int ugen_cpr_suspend(ugen_state_t *);
80 static void ugen_cpr_resume(ugen_state_t *);
82 static void ugen_restore_state(ugen_state_t *);
83 static int ugen_check_open_flags(ugen_state_t *, dev_t, int);
84 static int ugen_strategy(struct buf *);
85 static void ugen_minphys(struct buf *);
87 static void ugen_pm_init(ugen_state_t *);
88 static void ugen_pm_destroy(ugen_state_t *);
89 static void ugen_pm_busy_component(ugen_state_t *);
90 static void ugen_pm_idle_component(ugen_state_t *);
92 /* endpoint xfer and status management */
93 static int ugen_epxs_init(ugen_state_t *);
94 static void ugen_epxs_destroy(ugen_state_t *);
95 static int ugen_epxs_data_init(ugen_state_t *, usb_ep_data_t *,
96 uchar_t, uchar_t, uchar_t, uchar_t);
97 static void ugen_epxs_data_destroy(ugen_state_t *, ugen_ep_t *);
98 static int ugen_epxs_minor_nodes_create(ugen_state_t *,
99 usb_ep_descr_t *, uchar_t,
100 uchar_t, uchar_t, uchar_t);
101 static int ugen_epxs_check_open_nodes(ugen_state_t *);
103 static int ugen_epx_open(ugen_state_t *, dev_t, int);
104 static void ugen_epx_close(ugen_state_t *, dev_t, int);
105 static void ugen_epx_shutdown(ugen_state_t *);
107 static int ugen_epx_open_pipe(ugen_state_t *, ugen_ep_t *, int);
108 static void ugen_epx_close_pipe(ugen_state_t *, ugen_ep_t *);
110 static int ugen_epx_req(ugen_state_t *, struct buf *);
111 static int ugen_epx_ctrl_req(ugen_state_t *, ugen_ep_t *,
112 struct buf *, boolean_t *);
113 static void ugen_epx_ctrl_req_cb(usb_pipe_handle_t, usb_ctrl_req_t *);
114 static int ugen_epx_bulk_req(ugen_state_t *, ugen_ep_t *,
115 struct buf *, boolean_t *);
116 static void ugen_epx_bulk_req_cb(usb_pipe_handle_t, usb_bulk_req_t *);
117 static int ugen_epx_intr_IN_req(ugen_state_t *, ugen_ep_t *,
118 struct buf *, boolean_t *);
119 static int ugen_epx_intr_IN_start_polling(ugen_state_t *, ugen_ep_t *);
120 static void ugen_epx_intr_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
121 static void ugen_epx_intr_IN_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
122 static int ugen_epx_intr_OUT_req(ugen_state_t *, ugen_ep_t *,
123 struct buf *, boolean_t *);
124 static void ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
125 static int ugen_epx_isoc_IN_req(ugen_state_t *, ugen_ep_t *,
126 struct buf *, boolean_t *);
127 static int ugen_epx_isoc_IN_start_polling(ugen_state_t *, ugen_ep_t *);
128 static void ugen_epx_isoc_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
129 static void ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
130 static int ugen_epx_isoc_OUT_req(ugen_state_t *, ugen_ep_t *,
131 struct buf *, boolean_t *);
132 static void ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
134 static int ugen_eps_open(ugen_state_t *, dev_t, int);
135 static void ugen_eps_close(ugen_state_t *, dev_t, int);
136 static int ugen_eps_req(ugen_state_t *, struct buf *);
137 static void ugen_update_ep_descr(ugen_state_t *, ugen_ep_t *);
139 /* device status management */
140 static int ugen_ds_init(ugen_state_t *);
141 static void ugen_ds_destroy(ugen_state_t *);
142 static int ugen_ds_open(ugen_state_t *, dev_t, int);
143 static void ugen_ds_close(ugen_state_t *, dev_t, int);
144 static int ugen_ds_req(ugen_state_t *, struct buf *);
145 static void ugen_ds_change(ugen_state_t *);
146 static int ugen_ds_minor_nodes_create(ugen_state_t *);
147 static void ugen_ds_poll_wakeup(ugen_state_t *);
149 /* utility functions */
150 static int ugen_minor_index_create(ugen_state_t *, ugen_minor_t);
151 static ugen_minor_t ugen_devt2minor(ugen_state_t *, dev_t);
152 static void ugen_minor_node_table_create(ugen_state_t *);
153 static void ugen_minor_node_table_destroy(ugen_state_t *);
154 static void ugen_minor_node_table_shrink(ugen_state_t *);
155 static int ugen_cr2lcstat(int);
156 static void ugen_check_mask(uint_t, uint_t *, uint_t *);
157 static int ugen_is_valid_minor_node(ugen_state_t *, dev_t);
159 static kmutex_t ugen_devt_list_mutex;
160 static ugen_devt_list_entry_t ugen_devt_list;
161 static ugen_devt_cache_entry_t ugen_devt_cache[UGEN_DEVT_CACHE_SIZE];
162 static uint_t ugen_devt_cache_index;
163 static void ugen_store_devt(ugen_state_t *, minor_t);
164 static ugen_state_t *ugen_devt2state(dev_t);
165 static void ugen_free_devt(ugen_state_t *);
168 * usb_ugen entry points
170 * usb_ugen_get_hdl:
171 * allocate and initialize handle
173 usb_ugen_hdl_t
174 usb_ugen_get_hdl(dev_info_t *dip, usb_ugen_info_t *usb_ugen_info)
176 usb_ugen_hdl_impl_t *hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
177 ugen_state_t *ugenp = kmem_zalloc(sizeof (ugen_state_t),
178 KM_SLEEP);
179 uint_t len, shift, limit;
180 int rval;
182 hdl->hdl_ugenp = ugenp;
184 /* masks may not overlap */
185 if (usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask &
186 usb_ugen_info->usb_ugen_minor_node_instance_mask) {
187 usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
189 return (NULL);
192 if ((rval = usb_get_dev_data(dip, &ugenp->ug_dev_data,
193 usb_owns_device(dip) ? USB_PARSE_LVL_ALL : USB_PARSE_LVL_IF,
194 0)) != USB_SUCCESS) {
195 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
196 "usb_ugen_attach: usb_get_dev_data failed, rval=%d", rval);
198 return (NULL);
201 /* Initialize state structure for this instance */
202 mutex_init(&ugenp->ug_mutex, NULL, MUTEX_DRIVER,
203 ugenp->ug_dev_data->dev_iblock_cookie);
205 mutex_enter(&ugenp->ug_mutex);
206 ugenp->ug_dip = dip;
207 ugenp->ug_instance = ddi_get_instance(dip);
208 ugenp->ug_hdl = hdl;
210 /* Allocate a log handle for debug/error messages */
211 if (strcmp(ddi_driver_name(dip), "ugen") != 0) {
212 char *name;
214 len = strlen(ddi_driver_name(dip)) + sizeof ("_ugen") + 1;
215 name = kmem_alloc(len, KM_SLEEP);
216 (void) snprintf(name, len, "%s_ugen", ddi_driver_name(dip));
218 ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, name, &ugen_errlevel,
219 &ugen_errmask, &ugen_instance_debug, 0);
220 hdl->hdl_log_name = name;
221 hdl->hdl_log_name_length = len;
222 } else {
223 ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, "ugen",
224 &ugen_errlevel,
225 &ugen_errmask, &ugen_instance_debug, 0);
228 hdl->hdl_dip = dip;
229 hdl->hdl_flags = usb_ugen_info->usb_ugen_flags;
231 ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask,
232 &shift, &limit);
233 if (limit == 0) {
234 usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
235 mutex_exit(&ugenp->ug_mutex);
237 return (NULL);
239 hdl->hdl_minor_node_ugen_bits_mask = usb_ugen_info->
240 usb_ugen_minor_node_ugen_bits_mask;
241 hdl->hdl_minor_node_ugen_bits_shift = shift;
242 hdl->hdl_minor_node_ugen_bits_limit = limit;
244 ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_instance_mask,
245 &shift, &limit);
246 if (limit == 0) {
247 usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
248 mutex_exit(&ugenp->ug_mutex);
250 return (NULL);
253 hdl->hdl_minor_node_instance_mask = usb_ugen_info->
254 usb_ugen_minor_node_instance_mask;
255 hdl->hdl_minor_node_instance_shift = shift;
256 hdl->hdl_minor_node_instance_limit = limit;
258 USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
259 "usb_ugen_get_hdl: instance shift=%d instance limit=%d",
260 hdl->hdl_minor_node_instance_shift,
261 hdl->hdl_minor_node_instance_limit);
263 USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
264 "usb_ugen_get_hdl: bits shift=%d bits limit=%d",
265 hdl->hdl_minor_node_ugen_bits_shift,
266 hdl->hdl_minor_node_ugen_bits_limit);
268 mutex_exit(&ugenp->ug_mutex);
270 return ((usb_ugen_hdl_t)hdl);
275 * usb_ugen_release_hdl:
276 * deallocate a handle
278 void
279 usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)
281 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
282 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
284 if (usb_ugen_hdl_impl) {
285 ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
287 if (ugenp) {
288 mutex_destroy(&ugenp->ug_mutex);
289 usb_free_log_hdl(ugenp->ug_log_hdl);
290 usb_free_dev_data(usb_ugen_hdl_impl->hdl_dip,
291 ugenp->ug_dev_data);
292 kmem_free(ugenp, sizeof (*ugenp));
294 if (usb_ugen_hdl_impl->hdl_log_name) {
295 kmem_free(usb_ugen_hdl_impl->hdl_log_name,
296 usb_ugen_hdl_impl->hdl_log_name_length);
298 kmem_free(usb_ugen_hdl_impl, sizeof (*usb_ugen_hdl_impl));
304 * usb_ugen_attach()
307 usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl, ddi_attach_cmd_t cmd)
309 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
310 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
311 ugen_state_t *ugenp;
312 dev_info_t *dip;
314 if (usb_ugen_hdl == NULL) {
316 return (USB_FAILURE);
319 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
320 dip = usb_ugen_hdl_impl->hdl_dip;
323 USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
324 "usb_ugen_attach: cmd=%d", cmd);
326 switch (cmd) {
327 case DDI_ATTACH:
329 break;
330 case DDI_RESUME:
331 ugen_cpr_resume(ugenp);
333 return (USB_SUCCESS);
334 default:
335 USB_DPRINTF_L2(UGEN_PRINT_ATTA, NULL,
336 "usb_ugen_attach: unknown command");
338 return (USB_FAILURE);
341 mutex_enter(&ugenp->ug_mutex);
342 ugenp->ug_ser_cookie =
343 usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD);
344 ugenp->ug_cleanup_flags |= UGEN_INIT_LOCKS;
346 /* Get maximum bulk transfer size supported by the HCD */
347 if (usb_pipe_get_max_bulk_transfer_size(dip,
348 &ugenp->ug_max_bulk_xfer_sz) != USB_SUCCESS) {
349 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
350 "usb_ugen_attach: Getting max bulk xfer sz failed");
351 mutex_exit(&ugenp->ug_mutex);
353 goto fail;
356 /* table for mapping 48 bit minor codes to 9 bit index (for ugen) */
357 ugen_minor_node_table_create(ugenp);
359 /* prepare device status node handling */
360 if (ugen_ds_init(ugenp) != USB_SUCCESS) {
361 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
362 "usb_ugen_attach: preparing dev status failed");
363 mutex_exit(&ugenp->ug_mutex);
365 goto fail;
368 /* prepare all available xfer and status endpoints nodes */
369 if (ugen_epxs_init(ugenp) != USB_SUCCESS) {
370 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
371 "usb_ugen_attach: preparing endpoints failed");
372 mutex_exit(&ugenp->ug_mutex);
374 goto fail;
377 /* reduce table size if not all entries are used */
378 ugen_minor_node_table_shrink(ugenp);
380 /* we are ready to go */
381 ugenp->ug_dev_state = USB_DEV_ONLINE;
383 mutex_exit(&ugenp->ug_mutex);
385 /* prepare PM */
386 if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
387 ugen_pm_init(ugenp);
391 * if ugen driver, kill all child nodes otherwise set cfg fails
392 * if requested
394 if (usb_owns_device(dip) &&
395 (usb_ugen_hdl_impl->hdl_flags & USB_UGEN_REMOVE_CHILDREN)) {
396 dev_info_t *cdip;
398 /* save cfgidx so we can restore on detach */
399 mutex_enter(&ugenp->ug_mutex);
400 ugenp->ug_initial_cfgidx = usb_get_current_cfgidx(dip);
401 mutex_exit(&ugenp->ug_mutex);
403 for (cdip = ddi_get_child(dip); cdip; ) {
404 dev_info_t *next = ddi_get_next_sibling(cdip);
405 (void) ddi_remove_child(cdip, 0);
406 cdip = next;
410 return (DDI_SUCCESS);
411 fail:
412 if (ugenp) {
413 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
414 "attach fail");
415 (void) ugen_cleanup(ugenp);
418 return (DDI_FAILURE);
423 * usb_ugen_detach()
426 usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl, ddi_detach_cmd_t cmd)
428 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
429 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
430 int rval = USB_FAILURE;
432 if (usb_ugen_hdl) {
433 ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
435 USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
436 "usb_ugen_detach cmd %d", cmd);
438 switch (cmd) {
439 case DDI_DETACH:
440 rval = ugen_cleanup(ugenp);
442 break;
443 case DDI_SUSPEND:
444 rval = ugen_cpr_suspend(ugenp);
446 break;
447 default:
449 break;
453 return (rval);
458 * ugen_cleanup()
460 static int
461 ugen_cleanup(ugen_state_t *ugenp)
463 dev_info_t *dip = ugenp->ug_dip;
465 USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl, "ugen_cleanup");
467 if (ugenp->ug_cleanup_flags & UGEN_INIT_LOCKS) {
469 /* shutdown all endpoints */
470 ugen_epx_shutdown(ugenp);
473 * At this point, no new activity can be initiated.
474 * The driver has disabled hotplug callbacks.
475 * The Solaris framework has disabled
476 * new opens on a device being detached, and does not
477 * allow detaching an open device. PM should power
478 * down while we are detaching
480 * The following ensures that any other driver
481 * activity must have drained (paranoia)
483 (void) usb_serialize_access(ugenp->ug_ser_cookie,
484 USB_WAIT, 0);
485 usb_release_access(ugenp->ug_ser_cookie);
487 mutex_enter(&ugenp->ug_mutex);
488 ASSERT(ugenp->ug_open_count == 0);
489 ASSERT(ugenp->ug_pending_cmds == 0);
491 /* dismantle in reverse order */
492 ugen_pm_destroy(ugenp);
493 ugen_epxs_destroy(ugenp);
494 ugen_ds_destroy(ugenp);
495 ugen_minor_node_table_destroy(ugenp);
498 /* restore to initial configuration */
499 if (usb_owns_device(dip) &&
500 (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
501 int idx = ugenp->ug_initial_cfgidx;
502 mutex_exit(&ugenp->ug_mutex);
503 (void) usb_set_cfg(dip, idx,
504 USB_FLAGS_SLEEP, NULL, NULL);
505 } else {
506 mutex_exit(&ugenp->ug_mutex);
509 usb_fini_serialization(ugenp->ug_ser_cookie);
512 ddi_prop_remove_all(dip);
513 ddi_remove_minor_node(dip, NULL);
515 ugen_free_devt(ugenp);
517 return (USB_SUCCESS);
522 * ugen_cpr_suspend
524 static int
525 ugen_cpr_suspend(ugen_state_t *ugenp)
527 int rval = USB_FAILURE;
528 int i;
529 int prev_state;
531 USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
532 "ugen_cpr_suspend:");
534 mutex_enter(&ugenp->ug_mutex);
535 switch (ugenp->ug_dev_state) {
536 case USB_DEV_ONLINE:
537 case USB_DEV_DISCONNECTED:
538 USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
539 "ugen_cpr_suspend:");
541 prev_state = ugenp->ug_dev_state;
542 ugenp->ug_dev_state = USB_DEV_SUSPENDED;
544 if (ugenp->ug_open_count) {
545 /* drain outstanding cmds */
546 for (i = 0; i < ugen_busy_loop; i++) {
547 if (ugenp->ug_pending_cmds == 0) {
549 break;
551 mutex_exit(&ugenp->ug_mutex);
552 delay(drv_usectohz(100000));
553 mutex_enter(&ugenp->ug_mutex);
556 /* if still outstanding cmds, fail suspend */
557 if (ugenp->ug_pending_cmds) {
558 ugenp->ug_dev_state = prev_state;
560 USB_DPRINTF_L2(UGEN_PRINT_CPR,
561 ugenp->ug_log_hdl,
562 "ugen_cpr_suspend: pending %d",
563 ugenp->ug_pending_cmds);
565 rval = USB_FAILURE;
566 break;
569 mutex_exit(&ugenp->ug_mutex);
570 (void) usb_serialize_access(ugenp->ug_ser_cookie,
571 USB_WAIT, 0);
572 /* close all pipes */
573 ugen_epx_shutdown(ugenp);
575 usb_release_access(ugenp->ug_ser_cookie);
577 mutex_enter(&ugenp->ug_mutex);
580 /* wakeup devstat reads and polls */
581 ugen_ds_change(ugenp);
582 ugen_ds_poll_wakeup(ugenp);
584 rval = USB_SUCCESS;
585 break;
586 case USB_DEV_SUSPENDED:
587 case USB_UGEN_DEV_UNAVAILABLE_RESUME:
588 case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
589 default:
591 break;
593 mutex_exit(&ugenp->ug_mutex);
595 return (rval);
599 * ugen_cpr_resume
601 static void
602 ugen_cpr_resume(ugen_state_t *ugenp)
604 USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
605 "ugen_cpr_resume:");
607 ugen_restore_state(ugenp);
611 * usb_ugen_disconnect_ev_cb:
614 usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
616 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
617 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
618 ugen_state_t *ugenp;
620 if (usb_ugen_hdl_impl == NULL) {
622 return (USB_FAILURE);
625 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
627 USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
628 "usb_ugen_disconnect_ev_cb:");
630 /* get exclusive access */
631 (void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
633 mutex_enter(&ugenp->ug_mutex);
634 ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
635 if (ugenp->ug_open_count) {
636 mutex_exit(&ugenp->ug_mutex);
638 /* close all pipes */
639 (void) ugen_epx_shutdown(ugenp);
641 mutex_enter(&ugenp->ug_mutex);
645 /* wakeup devstat reads and polls */
646 ugen_ds_change(ugenp);
647 ugen_ds_poll_wakeup(ugenp);
649 mutex_exit(&ugenp->ug_mutex);
650 usb_release_access(ugenp->ug_ser_cookie);
652 return (USB_SUCCESS);
657 * usb_ugen_reconnect_ev_cb:
660 usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
662 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
663 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
664 ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
666 USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
667 "usb_ugen_reconnect_ev_cb:");
669 ugen_restore_state(ugenp);
671 return (USB_SUCCESS);
676 * ugen_restore_state:
677 * Check for same device; if a different device is attached, set
678 * the device status to disconnected.
679 * If we were open, then set to UNAVAILABLE until all endpoints have
680 * be closed.
682 static void
683 ugen_restore_state(ugen_state_t *ugenp)
685 dev_info_t *dip = ugenp->ug_dip;
687 USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
688 "ugen_restore_state");
690 /* first raise power */
691 if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
692 ugen_pm_busy_component(ugenp);
693 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
696 /* Check if we are talking to the same device */
697 if (usb_check_same_device(dip, ugenp->ug_log_hdl,
698 USB_LOG_L0, UGEN_PRINT_HOTPLUG, USB_CHK_ALL, NULL) ==
699 USB_FAILURE) {
700 mutex_enter(&ugenp->ug_mutex);
701 ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
703 /* wakeup devstat reads and polls */
704 ugen_ds_change(ugenp);
705 ugen_ds_poll_wakeup(ugenp);
707 mutex_exit(&ugenp->ug_mutex);
709 if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
710 ugen_pm_idle_component(ugenp);
713 return;
717 * get exclusive access, we don't want to change state in the
718 * middle of some other actions
720 (void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
722 mutex_enter(&ugenp->ug_mutex);
723 switch (ugenp->ug_dev_state) {
724 case USB_DEV_DISCONNECTED:
725 ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
726 USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RECONNECT;
728 break;
729 case USB_DEV_SUSPENDED:
730 ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
731 USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RESUME;
733 break;
735 USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
736 "ugen_restore_state: state=%d, opencount=%d",
737 ugenp->ug_dev_state, ugenp->ug_open_count);
739 /* wakeup devstat reads and polls */
740 ugen_ds_change(ugenp);
741 ugen_ds_poll_wakeup(ugenp);
743 mutex_exit(&ugenp->ug_mutex);
744 usb_release_access(ugenp->ug_ser_cookie);
746 if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
747 ugen_pm_idle_component(ugenp);
753 * usb_ugen_open:
755 /* ARGSUSED */
757 usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl, dev_t *devp, int flag, int sflag,
758 cred_t *cr)
760 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
761 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
762 ugen_state_t *ugenp;
763 int rval;
764 int minor_node_type;
766 if (usb_ugen_hdl == NULL) {
768 return (EINVAL);
771 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
773 if (ugen_is_valid_minor_node(ugenp, *devp) != USB_SUCCESS) {
775 return (EINVAL);
778 minor_node_type = UGEN_MINOR_TYPE(ugenp, *devp);
780 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
781 "usb_ugen_open: minor=%u", getminor(*devp));
782 USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
783 "cfgval=%" PRIu64 " cfgidx=%" PRIu64 " if=%" PRIu64
784 " alt=%" PRIu64 " epidx=%" PRIu64 " type=0x%" PRIx64,
785 UGEN_MINOR_CFGVAL(ugenp, *devp), UGEN_MINOR_CFGIDX(ugenp, *devp),
786 UGEN_MINOR_IF(ugenp, *devp), UGEN_MINOR_ALT(ugenp, *devp),
787 UGEN_MINOR_EPIDX(ugenp, *devp), UGEN_MINOR_TYPE(ugenp, *devp));
789 /* first check for legal open flags */
790 if ((rval = ugen_check_open_flags(ugenp, *devp, flag)) != 0) {
791 USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
792 "usb_ugen_open: check failed, rval=%d", rval);
794 return (rval);
797 /* exclude other threads including other opens */
798 if (usb_serialize_access(ugenp->ug_ser_cookie,
799 USB_WAIT_SIG, 0) <= 0) {
800 USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
801 "usb_ugen_open: interrupted");
803 return (EINTR);
806 mutex_enter(&ugenp->ug_mutex);
808 /* always allow open of dev stat node */
809 if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
811 /* if we are not online or powered down, fail open */
812 switch (ugenp->ug_dev_state) {
813 case USB_DEV_ONLINE:
815 break;
816 case USB_DEV_DISCONNECTED:
817 rval = ENODEV;
818 mutex_exit(&ugenp->ug_mutex);
820 goto done;
821 case USB_DEV_SUSPENDED:
822 case USB_UGEN_DEV_UNAVAILABLE_RESUME:
823 case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
824 default:
825 rval = EBADF;
826 mutex_exit(&ugenp->ug_mutex);
828 goto done;
831 mutex_exit(&ugenp->ug_mutex);
833 /* open node depending on type */
834 switch (minor_node_type) {
835 case UGEN_MINOR_EP_XFER_NODE:
836 if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
837 ugen_pm_busy_component(ugenp);
838 (void) pm_raise_power(ugenp->ug_dip, 0,
839 USB_DEV_OS_FULL_PWR);
842 rval = ugen_epx_open(ugenp, *devp, flag);
843 if (rval == 0) {
844 mutex_enter(&ugenp->ug_mutex);
845 ugenp->ug_open_count++;
846 mutex_exit(&ugenp->ug_mutex);
847 } else {
848 if (ugenp->ug_hdl->hdl_flags &
849 USB_UGEN_ENABLE_PM) {
850 ugen_pm_idle_component(ugenp);
854 break;
855 case UGEN_MINOR_EP_STAT_NODE:
856 rval = ugen_eps_open(ugenp, *devp, flag);
857 if (rval == 0) {
858 mutex_enter(&ugenp->ug_mutex);
859 ugenp->ug_open_count++;
860 mutex_exit(&ugenp->ug_mutex);
863 break;
864 case UGEN_MINOR_DEV_STAT_NODE:
865 rval = ugen_ds_open(ugenp, *devp, flag);
867 break;
868 default:
869 rval = EINVAL;
871 break;
873 done:
874 mutex_enter(&ugenp->ug_mutex);
876 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
877 "usb_ugen_open: minor=0x%x rval=%d state=%d cnt=%d",
878 getminor(*devp), rval, ugenp->ug_dev_state,
879 ugenp->ug_open_count);
881 mutex_exit(&ugenp->ug_mutex);
883 usb_release_access(ugenp->ug_ser_cookie);
885 return (rval);
890 * usb_ugen_close()
892 /* ARGSUSED */
894 usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, int flag, int otype,
895 cred_t *cr)
897 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
898 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
899 ugen_state_t *ugenp;
900 int minor_node_type;
902 if (usb_ugen_hdl == NULL) {
904 return (EINVAL);
907 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
908 if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
910 return (EINVAL);
913 minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
915 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
916 "usb_ugen_close: minor=0x%x", getminor(dev));
918 /* exclude other threads, including other opens */
919 if (usb_serialize_access(ugenp->ug_ser_cookie,
920 USB_WAIT_SIG, 0) <= 0) {
921 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
922 "usb_ugen_close: interrupted");
924 return (EINTR);
927 /* close node depending on type */
928 switch (minor_node_type) {
929 case UGEN_MINOR_EP_XFER_NODE:
930 ugen_epx_close(ugenp, dev, flag);
931 if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
932 ugen_pm_idle_component(ugenp);
935 break;
936 case UGEN_MINOR_EP_STAT_NODE:
937 ugen_eps_close(ugenp, dev, flag);
939 break;
940 case UGEN_MINOR_DEV_STAT_NODE:
941 ugen_ds_close(ugenp, dev, flag);
943 break;
944 default:
945 usb_release_access(ugenp->ug_ser_cookie);
947 return (EINVAL);
950 mutex_enter(&ugenp->ug_mutex);
951 if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
952 ASSERT(ugenp->ug_open_count > 0);
953 if ((--ugenp->ug_open_count == 0) &&
954 ((ugenp->ug_dev_state == USB_UGEN_DEV_UNAVAILABLE_RESUME) ||
955 (ugenp->ug_dev_state ==
956 USB_UGEN_DEV_UNAVAILABLE_RECONNECT))) {
957 ugenp->ug_dev_state = USB_DEV_ONLINE;
959 /* wakeup devstat reads and polls */
960 ugen_ds_change(ugenp);
961 ugen_ds_poll_wakeup(ugenp);
965 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
966 "usb_ugen_close: minor=0x%x state=%d cnt=%d",
967 getminor(dev), ugenp->ug_dev_state, ugenp->ug_open_count);
969 if (ugenp->ug_open_count == 0) {
970 ASSERT(ugen_epxs_check_open_nodes(ugenp) == USB_FAILURE);
973 mutex_exit(&ugenp->ug_mutex);
975 usb_release_access(ugenp->ug_ser_cookie);
977 return (0);
982 * usb_ugen_read/write()
984 /*ARGSUSED*/
986 usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
987 cred_t *credp)
989 ugen_state_t *ugenp;
990 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
991 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
993 if (usb_ugen_hdl == NULL) {
995 return (EINVAL);
997 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
999 if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1001 return (EINVAL);
1004 return (physio(ugen_strategy,
1005 NULL, dev, B_READ, ugen_minphys, uiop));
1009 /*ARGSUSED*/
1011 usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
1012 cred_t *credp)
1014 ugen_state_t *ugenp;
1015 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
1016 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
1018 if (usb_ugen_hdl == NULL) {
1020 return (EINVAL);
1022 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
1024 if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1026 return (EINVAL);
1029 return (physio(ugen_strategy,
1030 NULL, dev, B_WRITE, ugen_minphys, uiop));
1035 * usb_ugen_poll
1038 usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, short events,
1039 int anyyet, short *reventsp, struct pollhead **phpp)
1041 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
1042 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
1043 ugen_state_t *ugenp;
1044 int minor_node_type;
1045 uint_t ep_index;
1046 ugen_ep_t *epp;
1048 if (usb_ugen_hdl == NULL) {
1050 return (EINVAL);
1053 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
1054 if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1056 return (EINVAL);
1059 minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1060 ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
1061 epp = &ugenp->ug_ep[ep_index];
1063 mutex_enter(&ugenp->ug_mutex);
1065 USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
1066 "usb_ugen_poll: "
1067 "dev=0x%lx events=0x%x anyyet=0x%x rev=0x%p type=%d "
1068 "devstat=0x%x devstate=0x%x",
1069 dev, events, anyyet, (void *)reventsp, minor_node_type,
1070 ugenp->ug_ds.dev_stat, ugenp->ug_ds.dev_state);
1072 *reventsp = 0;
1074 if (ugenp->ug_dev_state == USB_DEV_ONLINE) {
1075 switch (minor_node_type) {
1076 case UGEN_MINOR_EP_XFER_NODE:
1077 /* if interrupt IN ep and there is data, set POLLIN */
1078 if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
1079 (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
1082 * if we are not polling, force another
1083 * read to kick off polling
1085 mutex_enter(&epp->ep_mutex);
1086 if ((epp->ep_data) ||
1087 ((epp->ep_state &
1088 UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0)) {
1089 *reventsp |= POLLIN;
1092 if ((!*reventsp && !anyyet) ||
1093 (events & POLLET)) {
1094 *phpp = &epp->ep_pollhead;
1095 epp->ep_state |=
1096 UGEN_EP_STATE_INTR_IN_POLL_PENDING;
1098 mutex_exit(&epp->ep_mutex);
1100 } else if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
1101 (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
1104 * if we are not polling, force another
1105 * read to kick off polling
1107 mutex_enter(&epp->ep_mutex);
1108 if ((epp->ep_data) ||
1109 ((epp->ep_state &
1110 UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0)) {
1111 *reventsp |= POLLIN;
1114 if ((!*reventsp && !anyyet) ||
1115 (events & POLLET)) {
1116 *phpp = &epp->ep_pollhead;
1117 epp->ep_state |=
1118 UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
1120 mutex_exit(&epp->ep_mutex);
1122 } else {
1123 /* no poll on other ep nodes */
1124 *reventsp |= POLLERR;
1127 break;
1128 case UGEN_MINOR_DEV_STAT_NODE:
1129 if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED)
1130 *reventsp |= POLLIN;
1132 if ((!*reventsp && !anyyet) || (events & POLLET)) {
1133 *phpp = &ugenp->ug_ds.dev_pollhead;
1134 ugenp->ug_ds.dev_stat |=
1135 UGEN_DEV_STATUS_POLL_PENDING;
1138 break;
1139 case UGEN_MINOR_EP_STAT_NODE:
1140 default:
1141 *reventsp |= POLLERR;
1143 break;
1145 } else {
1146 if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED)
1147 *reventsp |= POLLHUP|POLLIN;
1149 if ((!*reventsp && !anyyet) || (events & POLLET)) {
1150 *phpp = &ugenp->ug_ds.dev_pollhead;
1151 ugenp->ug_ds.dev_stat |=
1152 UGEN_DEV_STATUS_POLL_PENDING;
1156 mutex_exit(&ugenp->ug_mutex);
1158 USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
1159 "usb_ugen_poll end: reventsp=0x%x", *reventsp);
1161 return (0);
1166 * ugen_strategy
1168 static int
1169 ugen_strategy(struct buf *bp)
1171 dev_t dev = bp->b_edev;
1172 int rval = 0;
1173 ugen_state_t *ugenp = ugen_devt2state(dev);
1174 int minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1176 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1177 "ugen_strategy: bp=0x%p minor=0x%x", (void *)bp, getminor(dev));
1179 if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
1181 return (EINVAL);
1184 mutex_enter(&ugenp->ug_mutex);
1185 ugenp->ug_pending_cmds++;
1186 mutex_exit(&ugenp->ug_mutex);
1188 bp_mapin(bp);
1190 switch (minor_node_type) {
1191 case UGEN_MINOR_EP_XFER_NODE:
1192 rval = ugen_epx_req(ugenp, bp);
1194 break;
1195 case UGEN_MINOR_EP_STAT_NODE:
1196 rval = ugen_eps_req(ugenp, bp);
1198 break;
1199 case UGEN_MINOR_DEV_STAT_NODE:
1200 rval = ugen_ds_req(ugenp, bp);
1202 break;
1203 default:
1204 rval = EINVAL;
1206 break;
1209 mutex_enter(&ugenp->ug_mutex);
1210 ugenp->ug_pending_cmds--;
1212 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1213 "ugen_strategy: "
1214 "bp=0x%p cnt=%lu resid=%lu err=%d minor=0x%x rval=%d #cmds=%d",
1215 (void *)bp, bp->b_bcount, bp->b_resid, geterror(bp),
1216 getminor(dev), rval, ugenp->ug_pending_cmds);
1218 mutex_exit(&ugenp->ug_mutex);
1220 if (rval) {
1221 if (geterror(bp) == 0) {
1222 bioerror(bp, rval);
1226 biodone(bp);
1228 return (0);
1233 * ugen_minphys:
1235 static void
1236 ugen_minphys(struct buf *bp)
1238 dev_t dev = bp->b_edev;
1239 ugen_state_t *ugenp = ugen_devt2state(dev);
1240 int minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1241 uint_t ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
1242 ugen_ep_t *epp = &ugenp->ug_ep[ep_index];
1244 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1245 "ugen_phys: bp=0x%p dev=0x%lx index=%d type=0x%x",
1246 (void *)bp, dev, ep_index, minor_node_type);
1248 switch (minor_node_type) {
1249 case UGEN_MINOR_EP_XFER_NODE:
1250 switch (UGEN_XFER_TYPE(epp)) {
1251 case USB_EP_ATTR_BULK:
1252 if (bp->b_bcount > ugenp->ug_max_bulk_xfer_sz) {
1253 bp->b_bcount = ugenp->ug_max_bulk_xfer_sz;
1256 break;
1257 case USB_EP_ATTR_INTR:
1258 case USB_EP_ATTR_CONTROL:
1259 case USB_EP_ATTR_ISOCH:
1260 default:
1262 break;
1264 break;
1265 case UGEN_MINOR_EP_STAT_NODE:
1266 case UGEN_MINOR_DEV_STAT_NODE:
1267 default:
1269 break;
1274 * Get bmAttributes and bAddress of the endpoint which is going to
1275 * be opened
1277 static int
1278 ugen_get_ep_descr(ugen_state_t *ugenp, dev_t dev, uint8_t *bmAttr,
1279 uint8_t *bAddr)
1281 uint_t alt = UGEN_MINOR_ALT(ugenp, dev);
1282 uint_t ifc = UGEN_MINOR_IF(ugenp, dev);
1283 uint_t cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
1284 usb_cfg_data_t *dev_cfg;
1285 usb_if_data_t *if_data;
1286 usb_alt_if_data_t *alt_if_data;
1287 usb_ep_data_t *ep_data;
1288 int ep;
1289 int epidx = UGEN_MINOR_EPIDX(ugenp, dev);
1291 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1292 "cfg=%d, if=%d, alt=%d, ep=0x%x", cfgidx, ifc,
1293 alt, epidx);
1295 dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
1296 if_data = &dev_cfg->cfg_if[ifc];
1297 alt_if_data = &if_data->if_alt[alt];
1298 for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
1299 ep_data = &alt_if_data->altif_ep[ep];
1301 if (usb_get_ep_index(ep_data->ep_descr.
1302 bEndpointAddress) == epidx) {
1304 *bmAttr = ep_data->ep_descr.bmAttributes;
1305 *bAddr = ep_data->ep_descr.bEndpointAddress;
1307 return (USB_SUCCESS);
1311 return (USB_FAILURE);
1315 * check whether flag is appropriate for node type
1317 static int
1318 ugen_check_open_flags(ugen_state_t *ugenp, dev_t dev, int flag)
1320 ugen_ep_t *epp;
1321 int minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
1322 int rval = 0;
1323 uint8_t bmAttribute;
1324 uint8_t bAddress;
1326 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1327 "ugen_check_open_flags: "
1328 "dev=0x%lx, type=0x%x flag=0x%x idx=%" PRIu64,
1329 dev, minor_node_type, flag, UGEN_MINOR_EPIDX(ugenp, dev));
1331 switch (minor_node_type) {
1332 case UGEN_MINOR_EP_XFER_NODE:
1333 epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
1336 * Endpoints in two altsetting happen to have the same
1337 * bEndpointAddress, but they are different type, e.g,
1338 * one is BULK and the other is ISOC. They use the same
1339 * slot of ug_ep array. It's OK after switch_alt, because
1340 * after alt switch, ep info is updated to the new endpoint.
1341 * But it's not right here to use the other EP's info for
1342 * checking.
1344 if (UGEN_MINOR_EPIDX(ugenp, dev) != 0) {
1345 if ((rval = ugen_get_ep_descr(ugenp, dev, &bmAttribute,
1346 &bAddress)) != USB_SUCCESS) {
1347 USB_DPRINTF_L2(UGEN_PRINT_XFER,
1348 ugenp->ug_log_hdl, "ugen_get_descr: fail");
1350 return (ENODEV);
1352 } else {
1353 bmAttribute = ugen_default_ep_descr.bmAttributes;
1354 bAddress = ugen_default_ep_descr.bEndpointAddress;
1357 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1358 "ugen_check_open_flags: epp = %p,"
1359 "epp type = %d, bmAttr =0x%x, bAddr = 0x%02x", (void *)epp,
1360 UGEN_XFER_TYPE(epp), bmAttribute, bAddress);
1362 switch (bmAttribute & USB_EP_ATTR_MASK) {
1363 case USB_EP_ATTR_CONTROL:
1364 /* read and write must be set, ndelay not allowed */
1365 if (((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) ||
1366 (flag & (FNDELAY | FNONBLOCK))) {
1367 rval = EACCES;
1370 break;
1371 case USB_EP_ATTR_ISOCH:
1372 /* read and write must be set */
1373 if ((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) {
1374 rval = EACCES;
1377 break;
1378 case USB_EP_ATTR_BULK:
1379 /* ndelay not allowed */
1380 if (flag & (FNDELAY | FNONBLOCK)) {
1381 rval = EACCES;
1383 break;
1385 /*FALLTHRU*/
1386 case USB_EP_ATTR_INTR:
1387 /* check flag versus direction */
1388 if ((flag & FWRITE) && (bAddress & USB_EP_DIR_IN)) {
1389 rval = EACCES;
1391 if ((flag & FREAD) &&
1392 ((bAddress & USB_EP_DIR_IN) == 0)) {
1393 rval = EACCES;
1396 break;
1397 default:
1398 rval = EINVAL;
1400 break;
1402 break;
1403 case UGEN_MINOR_DEV_STAT_NODE:
1404 /* only reads are supported */
1405 if (flag & FWRITE) {
1406 rval = EACCES;
1409 break;
1410 case UGEN_MINOR_EP_STAT_NODE:
1412 break;
1413 default:
1414 rval = EINVAL;
1416 break;
1419 return (rval);
1424 * endpoint management
1426 * create/initialize all endpoint xfer/stat structures
1428 static int
1429 ugen_epxs_init(ugen_state_t *ugenp)
1431 usb_cfg_data_t *dev_cfg = ugenp->ug_dev_data->dev_cfg;
1432 uchar_t cfgidx, cfgval, iface, alt, ep;
1433 usb_if_data_t *if_data;
1434 usb_alt_if_data_t *alt_if_data;
1435 usb_ep_data_t *ep_data;
1437 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1438 "ugen_epxs_init:");
1440 /* initialize each ep's mutex first */
1441 for (ep = 0; ep < UGEN_N_ENDPOINTS; ep++) {
1442 mutex_init(&ugenp->ug_ep[ep].ep_mutex, NULL, MUTEX_DRIVER,
1443 ugenp->ug_dev_data->dev_iblock_cookie);
1446 /* init default ep as it does not have a descriptor */
1447 if (ugen_epxs_data_init(ugenp, NULL, 0, 0,
1448 ugenp->ug_dev_data->dev_curr_if, 0) != USB_SUCCESS) {
1449 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
1450 "creating default endpoint failed");
1452 return (USB_FAILURE);
1456 * walk all endpoints of all alternates of all interfaces of
1457 * all cfs
1459 for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
1460 dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
1461 cfgval = dev_cfg->cfg_descr.bConfigurationValue;
1462 for (iface = 0; iface < dev_cfg->cfg_n_if; iface++) {
1463 if_data = &dev_cfg->cfg_if[iface];
1464 for (alt = 0; alt < if_data->if_n_alt; alt++) {
1465 alt_if_data = &if_data->if_alt[alt];
1466 for (ep = 0; ep < alt_if_data->altif_n_ep;
1467 ep++) {
1468 ep_data = &alt_if_data->altif_ep[ep];
1469 if (ugen_epxs_data_init(ugenp, ep_data,
1470 cfgval, cfgidx, iface, alt) !=
1471 USB_SUCCESS) {
1473 return (USB_FAILURE);
1480 return (USB_SUCCESS);
1485 * initialize one endpoint structure
1487 static int
1488 ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
1489 uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
1491 int ep_index;
1492 ugen_ep_t *epp;
1493 usb_ep_descr_t *ep_descr;
1495 /* is this the default endpoint */
1496 ep_index = (ep_data == NULL) ? 0 :
1497 usb_get_ep_index(ep_data->ep_descr.bEndpointAddress);
1498 epp = &ugenp->ug_ep[ep_index];
1500 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1501 "ugen_epxs_data_init: "
1502 "cfgval=%d cfgidx=%d iface=%d alt=%d ep_index=%d",
1503 cfgval, cfgidx, iface, alt, ep_index);
1505 ep_descr = (ep_data == NULL) ? &ugen_default_ep_descr :
1506 &ep_data->ep_descr;
1508 mutex_init(&epp->ep_mutex, NULL, MUTEX_DRIVER,
1509 ugenp->ug_dev_data->dev_iblock_cookie);
1511 mutex_enter(&epp->ep_mutex);
1513 /* initialize if not yet init'ed */
1514 if (epp->ep_state == UGEN_EP_STATE_NONE) {
1515 epp->ep_descr = *ep_descr;
1516 epp->ep_cfgidx = cfgidx;
1517 epp->ep_if = iface;
1518 epp->ep_alt = alt;
1519 epp->ep_state = UGEN_EP_STATE_ACTIVE;
1520 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
1521 epp->ep_pipe_policy.pp_max_async_reqs = 1;
1523 if (ep_data == NULL) {
1524 bzero(&epp->ep_xdescr, sizeof (usb_ep_xdescr_t));
1525 epp->ep_xdescr.uex_version =
1526 USB_EP_XDESCR_CURRENT_VERSION;
1527 epp->ep_xdescr.uex_ep = *ep_descr;
1528 } else {
1530 * The only way this could fail is we have a bad
1531 * version, which shouldn't be possible inside of the
1532 * usba module itself.
1534 (void) usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
1535 ugenp->ug_dip, ep_data, &epp->ep_xdescr);
1538 cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
1539 epp->ep_ser_cookie = usb_init_serialization(
1540 ugenp->ug_dip, 0);
1543 mutex_exit(&epp->ep_mutex);
1545 /* create minor nodes for all alts */
1547 return (ugen_epxs_minor_nodes_create(ugenp, ep_descr,
1548 cfgval, cfgidx, iface, alt));
1553 * undo all endpoint initializations
1555 static void
1556 ugen_epxs_destroy(ugen_state_t *ugenp)
1558 int i;
1560 for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
1561 ugen_epxs_data_destroy(ugenp, &ugenp->ug_ep[i]);
1566 static void
1567 ugen_epxs_data_destroy(ugen_state_t *ugenp, ugen_ep_t *epp)
1569 if (epp) {
1570 ASSERT(epp->ep_ph == NULL);
1571 mutex_enter(&epp->ep_mutex);
1572 if (epp->ep_state != UGEN_EP_STATE_NONE) {
1573 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1574 "ugen_epxs_destroy: addr=0x%x",
1575 UGEN_XFER_ADDR(epp));
1576 cv_destroy(&epp->ep_wait_cv);
1578 mutex_exit(&epp->ep_mutex);
1580 mutex_destroy(&epp->ep_mutex);
1581 usb_fini_serialization(epp->ep_ser_cookie);
1587 * create endpoint status and xfer minor nodes
1589 * The actual minor node needs more than 18 bits. We create a table
1590 * and store the full minor node in this table and use the
1591 * index in the table as minor node. This allows 256 minor nodes
1592 * and 1024 instances
1594 static int
1595 ugen_epxs_minor_nodes_create(ugen_state_t *ugenp, usb_ep_descr_t *ep_descr,
1596 uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
1598 char node_name[32], *type;
1599 int vid = ugenp->ug_dev_data->dev_descr->idVendor;
1600 int pid = ugenp->ug_dev_data->dev_descr->idProduct;
1601 minor_t minor;
1602 int minor_index;
1603 ugen_minor_t minor_code, minor_code_base;
1604 int owns_device = (usb_owns_device(ugenp->ug_dip) ?
1605 UGEN_OWNS_DEVICE : 0);
1606 int ep_index =
1607 usb_get_ep_index(ep_descr->bEndpointAddress);
1608 int ep_addr =
1609 ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
1610 int ep_type =
1611 ep_descr->bmAttributes & USB_EP_ATTR_MASK;
1612 int ep_dir =
1613 ep_descr->bEndpointAddress & USB_EP_DIR_IN;
1615 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1616 "ugen_epxs_minor_nodes_create: "
1617 "cfgval=%d cfgidx=%d if=%d alt=%d ep=0x%x",
1618 cfgval, cfgidx, iface, alt, ep_addr);
1620 if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
1621 USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1622 "instance number too high (%d)", ugenp->ug_instance);
1624 return (USB_FAILURE);
1627 /* create stat and xfer minor node */
1628 minor_code_base =
1629 ((ugen_minor_t)cfgval) << UGEN_MINOR_CFGVAL_SHIFT |
1630 ((ugen_minor_t)cfgidx) << UGEN_MINOR_CFGIDX_SHIFT |
1631 iface << UGEN_MINOR_IF_SHIFT |
1632 alt << UGEN_MINOR_ALT_SHIFT |
1633 ep_index << UGEN_MINOR_EPIDX_SHIFT | owns_device;
1634 minor_code = minor_code_base | UGEN_MINOR_EP_XFER_NODE;
1636 minor_index = ugen_minor_index_create(ugenp, minor_code);
1637 if (minor_index < 0) {
1638 USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1639 "too many minor nodes, "
1640 "cannot create %d.%d.%d.%x",
1641 cfgval, iface, alt, ep_addr);
1642 /* carry on regardless */
1644 return (USB_SUCCESS);
1646 minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
1647 ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
1649 if (ep_type == USB_EP_ATTR_CONTROL) {
1650 type = "cntrl";
1651 } else {
1652 type = (ep_dir & USB_EP_DIR_IN) ? "in" : "out";
1656 * xfer ep node name:
1657 * vid.pid.[in|out|cntrl].[<cfg>.][if<iface>.][<alt>.]<ep addr>
1659 if ((ep_addr == 0) && owns_device) {
1660 (void) sprintf(node_name, "%x.%x.%s%d",
1661 vid, pid, type, ep_addr);
1662 } else if (cfgidx == 0 && alt == 0) {
1663 (void) sprintf(node_name, "%x.%x.if%d%s%d",
1664 vid, pid, iface, type, ep_addr);
1665 } else if (cfgidx == 0 && alt != 0) {
1666 (void) sprintf(node_name, "%x.%x.if%d.%d%s%d",
1667 vid, pid, iface, alt, type, ep_addr);
1668 } else if (cfgidx != 0 && alt == 0) {
1669 (void) sprintf(node_name, "%x.%x.cfg%dif%d%s%d",
1670 vid, pid, cfgval, iface, type, ep_addr);
1671 } else if (cfgidx != 0 && alt != 0) {
1672 (void) sprintf(node_name, "%x.%x.cfg%dif%d.%d%s%d",
1673 vid, pid, cfgval, iface, alt,
1674 type, ep_addr);
1677 USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1678 "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
1679 minor, minor_index, minor_code, node_name);
1681 ASSERT(minor < L_MAXMIN);
1683 if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
1684 S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
1686 return (USB_FAILURE);
1689 ugen_store_devt(ugenp, minor);
1691 minor_code = minor_code_base | UGEN_MINOR_EP_STAT_NODE;
1692 minor_index = ugen_minor_index_create(ugenp, minor_code);
1693 if (minor_index < 0) {
1694 USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1695 "too many minor nodes, "
1696 "cannot create %d.%d.%d.%x stat",
1697 cfgval, iface, alt,
1698 ep_descr->bEndpointAddress);
1699 /* carry on regardless */
1701 return (USB_SUCCESS);
1703 minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
1704 ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
1706 (void) strcat(node_name, "stat");
1708 USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
1709 "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
1710 minor, minor_index, minor_code, node_name);
1712 ASSERT(minor < L_MAXMIN);
1714 if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
1715 S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
1717 return (USB_FAILURE);
1720 ugen_store_devt(ugenp, minor);
1722 return (USB_SUCCESS);
1727 * close all non-default pipes and drain default pipe
1729 static void
1730 ugen_epx_shutdown(ugen_state_t *ugenp)
1732 int i;
1734 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1735 "ugen_epx_shutdown:");
1737 for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
1738 ugen_ep_t *epp = &ugenp->ug_ep[i];
1739 mutex_enter(&epp->ep_mutex);
1740 if (epp->ep_state != UGEN_EP_STATE_NONE) {
1741 mutex_exit(&epp->ep_mutex);
1742 (void) usb_serialize_access(epp->ep_ser_cookie,
1743 USB_WAIT, 0);
1744 (void) ugen_epx_close_pipe(ugenp, epp);
1745 usb_release_access(epp->ep_ser_cookie);
1746 } else {
1747 mutex_exit(&epp->ep_mutex);
1754 * find cfg index corresponding to cfg value
1756 static int
1757 ugen_cfgval2idx(ugen_state_t *ugenp, uint_t cfgval)
1759 usb_cfg_data_t *dev_cfg = ugenp->ug_dev_data->dev_cfg;
1760 int cfgidx;
1762 for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
1763 dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
1764 if (cfgval == dev_cfg->cfg_descr.bConfigurationValue) {
1766 return (cfgidx);
1770 ASSERT(cfgidx < ugenp->ug_dev_data->dev_n_cfg);
1772 return (0);
1777 * check if any node is open
1779 static int
1780 ugen_epxs_check_open_nodes(ugen_state_t *ugenp)
1782 int i;
1784 for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
1785 ugen_ep_t *epp = &ugenp->ug_ep[i];
1787 mutex_enter(&epp->ep_mutex);
1789 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1790 "ugen_epxs_check_open_nodes: epp=%d, ep_state=0x%x",
1791 i, epp->ep_state);
1793 if (epp->ep_state & UGEN_EP_STATE_XS_OPEN) {
1794 mutex_exit(&epp->ep_mutex);
1796 return (USB_SUCCESS);
1798 mutex_exit(&epp->ep_mutex);
1801 return (USB_FAILURE);
1806 * check if we can switch alternate
1808 static int
1809 ugen_epxs_check_alt_switch(ugen_state_t *ugenp, uchar_t iface, uchar_t cfgidx)
1811 int i;
1813 for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
1814 ugen_ep_t *epp = &ugenp->ug_ep[i];
1816 mutex_enter(&epp->ep_mutex);
1818 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1819 "ugen_epxs_check_alt_switch: epp=%d, ep_state=0x%x",
1820 i, epp->ep_state);
1823 * if the endpoint is open and part of this cfg and interface
1824 * then we cannot switch alternates
1826 if ((epp->ep_state & UGEN_EP_STATE_XS_OPEN) &&
1827 (epp->ep_cfgidx == cfgidx) &&
1828 (epp->ep_if == iface)) {
1829 mutex_exit(&epp->ep_mutex);
1831 return (USB_FAILURE);
1833 mutex_exit(&epp->ep_mutex);
1836 return (USB_SUCCESS);
1841 * implicit switch to new cfg and alt
1842 * If a crummy device fails usb_get_cfg or usb_get_alt_if, we carry on
1843 * regardless so at least the device can be opened.
1845 static int
1846 ugen_epxs_switch_cfg_alt(ugen_state_t *ugenp, ugen_ep_t *epp, dev_t dev)
1848 int rval = USB_SUCCESS;
1849 uint_t alt;
1850 uint_t new_alt = UGEN_MINOR_ALT(ugenp, dev);
1851 uint_t new_if = UGEN_MINOR_IF(ugenp, dev);
1852 uint_t cur_if = epp->ep_if;
1853 uint_t new_cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
1854 uint_t cur_cfgidx;
1855 uint_t cfgval;
1856 int switched = 0;
1858 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1859 "ugen_epxs_switch_cfg_alt: old cfgidx=%d, if=%d alt=%d",
1860 epp->ep_cfgidx, epp->ep_if, epp->ep_alt);
1861 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1862 "new cfgidx=%d, if=%d alt=%d ep_state=0x%x",
1863 new_cfgidx, new_if, new_alt, epp->ep_state);
1865 /* no need to switch if there is only 1 cfg, 1 iface and no alts */
1866 if ((new_if == 0) && (new_alt == 0) &&
1867 (ugenp->ug_dev_data->dev_n_cfg == 1) &&
1868 (ugenp->ug_dev_data->dev_cfg[0].cfg_n_if == 1) &&
1869 (ugenp->ug_dev_data->
1870 dev_cfg[0].cfg_if[new_if].if_n_alt == 1)) {
1871 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1872 "no need for switching: n_cfg=%d n_alt=%d",
1873 ugenp->ug_dev_data->dev_n_cfg,
1874 ugenp->ug_dev_data->
1875 dev_cfg[0].cfg_if[new_if].if_n_alt);
1877 ASSERT(epp->ep_alt == new_alt);
1878 ASSERT(epp->ep_cfgidx == new_cfgidx);
1879 ASSERT(epp->ep_if == new_if);
1881 return (rval);
1884 /* no switch for default endpoint */
1885 if (epp->ep_descr.bEndpointAddress == 0) {
1887 return (rval);
1890 mutex_exit(&epp->ep_mutex);
1891 if ((ugenp->ug_dev_data->dev_n_cfg > 1) &&
1892 usb_get_cfg(ugenp->ug_dip, &cfgval,
1893 USB_FLAGS_SLEEP) == USB_SUCCESS) {
1895 mutex_enter(&epp->ep_mutex);
1897 cur_cfgidx = ugen_cfgval2idx(ugenp, cfgval);
1899 if (new_cfgidx != cur_cfgidx) {
1900 mutex_exit(&epp->ep_mutex);
1903 * we can't change config if any node
1904 * is open
1906 if (ugen_epxs_check_open_nodes(ugenp) ==
1907 USB_SUCCESS) {
1908 mutex_enter(&epp->ep_mutex);
1910 return (USB_BUSY);
1914 * we are going to do this synchronously to
1915 * keep it simple.
1916 * This should never hang forever.
1918 if ((rval = usb_set_cfg(ugenp->ug_dip,
1919 new_cfgidx, USB_FLAGS_SLEEP, NULL,
1920 NULL)) != USB_SUCCESS) {
1921 USB_DPRINTF_L2(UGEN_PRINT_XFER,
1922 ugenp->ug_log_hdl,
1923 "implicit set cfg (%" PRId64
1924 ") failed (%d)",
1925 UGEN_MINOR_CFGIDX(ugenp, dev), rval);
1926 mutex_enter(&epp->ep_mutex);
1928 return (rval);
1930 mutex_enter(&epp->ep_mutex);
1931 epp->ep_if = (uchar_t)new_if;
1932 switched++;
1934 epp->ep_cfgidx = (uchar_t)new_cfgidx;
1936 mutex_exit(&epp->ep_mutex);
1940 * implicitly switch to new alternate if
1941 * - we have not switched configuration (if we
1942 * we switched config, the alternate must be 0)
1943 * - n_alts is > 1
1944 * - if the device supports get_alternate iface
1946 if ((switched && (new_alt > 0)) ||
1947 ((ugenp->ug_dev_data->dev_cfg[new_cfgidx].
1948 cfg_if[new_if].if_n_alt > 1) &&
1949 (usb_get_alt_if(ugenp->ug_dip, new_if, &alt,
1950 USB_FLAGS_SLEEP) == USB_SUCCESS))) {
1951 if (switched || (alt != new_alt)) {
1952 if (ugen_epxs_check_alt_switch(ugenp, cur_if,
1953 new_cfgidx) != USB_SUCCESS) {
1954 mutex_enter(&epp->ep_mutex);
1956 return (USB_BUSY);
1958 if ((rval = usb_set_alt_if(ugenp->ug_dip, new_if,
1959 new_alt, USB_FLAGS_SLEEP, NULL, NULL)) !=
1960 USB_SUCCESS) {
1961 USB_DPRINTF_L2(UGEN_PRINT_XFER,
1962 ugenp->ug_log_hdl,
1963 "implicit set new alternate "
1964 "(%d) failed (%d)", new_alt, rval);
1965 mutex_enter(&epp->ep_mutex);
1967 return (rval);
1972 mutex_enter(&epp->ep_mutex);
1973 epp->ep_alt = (uchar_t)new_alt;
1974 ugen_update_ep_descr(ugenp, epp);
1976 return (rval);
1981 * update endpoint descriptor in ugen_ep structure after
1982 * switching configuration or alternate
1984 static void
1985 ugen_update_ep_descr(ugen_state_t *ugenp, ugen_ep_t *epp)
1987 usb_cfg_data_t *dev_cfg = ugenp->ug_dev_data->dev_cfg;
1988 usb_if_data_t *if_data;
1989 usb_alt_if_data_t *alt_if_data;
1990 usb_ep_data_t *ep_data;
1991 int ep;
1993 dev_cfg = &ugenp->ug_dev_data->dev_cfg[epp->ep_cfgidx];
1994 if_data = &dev_cfg->cfg_if[epp->ep_if];
1995 alt_if_data = &if_data->if_alt[epp->ep_alt];
1996 for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
1997 ep_data = &alt_if_data->altif_ep[ep];
1998 if (usb_get_ep_index(ep_data->ep_descr.
1999 bEndpointAddress) ==
2000 usb_get_ep_index(epp->ep_descr.
2001 bEndpointAddress)) {
2002 epp->ep_descr = ep_data->ep_descr;
2003 (void) usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
2004 ugenp->ug_dip, ep_data, &epp->ep_xdescr);
2006 break;
2013 * Xfer endpoint management
2015 * open an endpoint for xfers
2017 * Return values: errno
2019 static int
2020 ugen_epx_open(ugen_state_t *ugenp, dev_t dev, int flag)
2022 ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
2023 int rval;
2025 mutex_enter(&epp->ep_mutex);
2027 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2028 "ugen_epx_open: minor=0x%x flag=0x%x ep_state=0x%x",
2029 getminor(dev), flag, epp->ep_state);
2031 ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
2033 /* implicit switch to new cfg & alt */
2034 if ((epp->ep_state & UGEN_EP_STATE_XFER_OPEN) != 0) {
2035 mutex_exit(&epp->ep_mutex);
2037 return (EBUSY);
2039 if ((rval = ugen_epxs_switch_cfg_alt(ugenp, epp, dev)) ==
2040 USB_SUCCESS) {
2041 rval = ugen_epx_open_pipe(ugenp, epp, flag);
2044 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2045 "ugen_epx_open: state=0x%x", epp->ep_state);
2047 ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
2048 epp->ep_done = epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2050 mutex_exit(&epp->ep_mutex);
2052 return (usb_rval2errno(rval));
2057 * close an endpoint for xfers
2059 static void
2060 ugen_epx_close(ugen_state_t *ugenp, dev_t dev, int flag)
2062 ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
2064 mutex_enter(&epp->ep_mutex);
2065 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2066 "ugen_epx_close: dev=0x%lx flag=0x%x state=0x%x", dev, flag,
2067 epp->ep_state);
2068 mutex_exit(&epp->ep_mutex);
2070 ugen_epx_close_pipe(ugenp, epp);
2072 mutex_enter(&epp->ep_mutex);
2073 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2074 "ugen_epx_close: state=0x%x", epp->ep_state);
2075 ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
2076 ASSERT(epp->ep_bp == NULL);
2077 ASSERT(epp->ep_done == 0);
2078 ASSERT(epp->ep_data == NULL);
2079 mutex_exit(&epp->ep_mutex);
2084 * open pipe for this endpoint
2085 * If the pipe is an interrupt IN pipe, start polling immediately
2087 static int
2088 ugen_epx_open_pipe(ugen_state_t *ugenp, ugen_ep_t *epp, int flag)
2090 int rval = USB_SUCCESS;
2092 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2093 "ugen_epx_open_pipe: epp=0x%p flag=%d state=0x%x",
2094 (void *)epp, flag, epp->ep_state);
2096 epp->ep_state |= UGEN_EP_STATE_XFER_OPEN;
2097 epp->ep_xfer_oflag = flag;
2099 /* if default pipe, just copy the handle */
2100 if ((epp->ep_descr.bEndpointAddress & USB_EP_NUM_MASK) == 0) {
2101 epp->ep_ph = ugenp->ug_dev_data->dev_default_ph;
2102 } else {
2103 mutex_exit(&epp->ep_mutex);
2105 rval = usb_pipe_xopen(ugenp->ug_dip,
2106 &epp->ep_xdescr, &epp->ep_pipe_policy,
2107 USB_FLAGS_SLEEP, &epp->ep_ph);
2109 mutex_enter(&epp->ep_mutex);
2111 if (rval == USB_SUCCESS) {
2112 (void) usb_pipe_set_private(epp->ep_ph,
2113 (usb_opaque_t)epp);
2116 * if interrupt IN pipe, and one xfer mode
2117 * has not been set, start polling immediately
2119 if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
2120 (!(epp->ep_one_xfer)) &&
2121 (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
2122 if ((rval = ugen_epx_intr_IN_start_polling(
2123 ugenp, epp)) != USB_SUCCESS) {
2125 mutex_exit(&epp->ep_mutex);
2126 usb_pipe_close(ugenp->ug_dip,
2127 epp->ep_ph, USB_FLAGS_SLEEP,
2128 NULL, NULL);
2129 mutex_enter(&epp->ep_mutex);
2131 epp->ep_ph = NULL;
2132 } else {
2133 epp->ep_state |=
2134 UGEN_EP_STATE_INTR_IN_POLLING_ON;
2136 /* allow for about 1 sec of data */
2137 epp->ep_buf_limit =
2138 (1000/epp->ep_descr.bInterval) *
2139 epp->ep_descr.wMaxPacketSize;
2143 /* set ep_buf_limit for isoc IN pipe */
2144 if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
2145 (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
2146 uint16_t max_size;
2147 uint32_t framecnt;
2149 max_size =
2150 UGEN_PKT_SIZE(epp->ep_descr.wMaxPacketSize);
2153 * wMaxPacketSize bits 10..0 specifies maximum
2154 * packet size, which can hold 1024 bytes. If
2155 * bits 12..11 is non zero, max_size will be
2156 * greater than 1024 and the endpoint is a
2157 * high-bandwidth endpoint.
2159 if (max_size <= 1024) {
2161 * allowing about 1s data of highspeed and 8s
2162 * data of full speed device
2164 framecnt = ugen_isoc_buf_limit;
2165 epp->ep_buf_limit = framecnt *
2166 max_size * 8;
2167 } else {
2169 * allow for about 333 ms data for high-speed
2170 * high-bandwidth data
2172 framecnt = ugen_isoc_buf_limit/3;
2173 epp->ep_buf_limit =
2174 framecnt * max_size * 8;
2177 epp->ep_isoc_in_inited = 0;
2182 if (rval != USB_SUCCESS) {
2183 epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
2184 UGEN_EP_STATE_INTR_IN_POLLING_ON);
2187 return (rval);
2192 * close an endpoint pipe
2194 static void
2195 ugen_epx_close_pipe(ugen_state_t *ugenp, ugen_ep_t *epp)
2197 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2198 "ugen_epx_close_pipe: epp=0x%p", (void *)epp);
2200 mutex_enter(&epp->ep_mutex);
2201 if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
2203 /* free isoc pipe private data ep_isoc_info.isoc_pkt_descr. */
2204 if (UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) {
2205 int len;
2206 int n_pkt;
2208 if (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN &&
2209 (epp->ep_state &
2210 UGEN_EP_STATE_ISOC_IN_POLLING_ON)) {
2211 mutex_exit(&epp->ep_mutex);
2212 usb_pipe_stop_isoc_polling(epp->ep_ph,
2213 USB_FLAGS_SLEEP);
2214 mutex_enter(&epp->ep_mutex);
2217 if (epp->ep_isoc_info.isoc_pkt_descr) {
2218 n_pkt = epp->ep_isoc_info.
2219 isoc_pkts_count;
2220 len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
2222 kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
2223 len);
2225 epp->ep_isoc_info.isoc_pkt_descr = NULL;
2227 epp->ep_isoc_in_inited = 0;
2232 epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
2233 UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED |
2234 UGEN_EP_STATE_INTR_IN_POLLING_ON |
2235 UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED |
2236 UGEN_EP_STATE_ISOC_IN_POLLING_ON);
2238 if (epp->ep_ph == ugenp->ug_dev_data->dev_default_ph) {
2239 mutex_exit(&epp->ep_mutex);
2241 (void) usb_pipe_drain_reqs(ugenp->ug_dip,
2242 epp->ep_ph, 0, USB_FLAGS_SLEEP,
2243 NULL, NULL);
2244 mutex_enter(&epp->ep_mutex);
2245 } else {
2246 mutex_exit(&epp->ep_mutex);
2247 usb_pipe_close(ugenp->ug_dip,
2248 epp->ep_ph, USB_FLAGS_SLEEP, NULL, NULL);
2250 mutex_enter(&epp->ep_mutex);
2251 epp->ep_ph = NULL;
2254 freemsg(epp->ep_data);
2255 epp->ep_ph = NULL;
2256 epp->ep_data = NULL;
2258 ASSERT(epp->ep_ph == NULL);
2259 ASSERT(epp->ep_data == NULL);
2260 mutex_exit(&epp->ep_mutex);
2265 * start endpoint xfer
2267 * We first serialize at endpoint level for only one request at the time
2269 * Return values: errno
2271 static int
2272 ugen_epx_req(ugen_state_t *ugenp, struct buf *bp)
2274 dev_t dev = bp->b_edev;
2275 ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
2276 boolean_t wait = B_FALSE;
2277 int rval = 0;
2279 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2280 "ugen_epx_req: bp=0x%p dev=0x%lx", (void *)bp, dev);
2282 /* single thread per endpoint, one request at the time */
2283 if (usb_serialize_access(epp->ep_ser_cookie, USB_WAIT_SIG, 0) <=
2284 0) {
2286 return (EINTR);
2289 mutex_enter(&ugenp->ug_mutex);
2290 switch (ugenp->ug_dev_state) {
2291 case USB_DEV_ONLINE:
2293 break;
2294 case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
2295 case USB_DEV_DISCONNECTED:
2296 mutex_enter(&epp->ep_mutex);
2297 epp->ep_lcmd_status = USB_LC_STAT_DISCONNECTED;
2298 mutex_exit(&epp->ep_mutex);
2299 rval = ENODEV;
2301 break;
2302 case USB_UGEN_DEV_UNAVAILABLE_RESUME:
2303 case USB_DEV_SUSPENDED:
2304 mutex_enter(&epp->ep_mutex);
2305 epp->ep_lcmd_status = USB_LC_STAT_SUSPENDED;
2306 mutex_exit(&epp->ep_mutex);
2307 rval = EBADF;
2309 break;
2310 default:
2311 mutex_enter(&epp->ep_mutex);
2312 epp->ep_lcmd_status = USB_LC_STAT_HW_ERR;
2313 mutex_exit(&epp->ep_mutex);
2314 rval = EIO;
2316 break;
2319 #ifndef __lock_lint
2320 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2321 "ugen_epx_req: lcmd_status=0x%x", epp->ep_lcmd_status);
2322 #endif
2324 mutex_exit(&ugenp->ug_mutex);
2326 if (rval) {
2327 usb_release_access(epp->ep_ser_cookie);
2329 return (rval);
2332 mutex_enter(&epp->ep_mutex);
2333 ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
2334 epp->ep_done = 0;
2335 epp->ep_bp = bp;
2337 switch (epp->ep_descr.bmAttributes & USB_EP_ATTR_MASK) {
2338 case USB_EP_ATTR_CONTROL:
2339 rval = ugen_epx_ctrl_req(ugenp, epp, bp, &wait);
2341 break;
2342 case USB_EP_ATTR_BULK:
2343 rval = ugen_epx_bulk_req(ugenp, epp, bp, &wait);
2345 break;
2346 case USB_EP_ATTR_INTR:
2347 if (bp->b_flags & B_READ) {
2348 rval = ugen_epx_intr_IN_req(ugenp, epp, bp, &wait);
2349 } else {
2350 rval = ugen_epx_intr_OUT_req(ugenp, epp, bp, &wait);
2353 break;
2354 case USB_EP_ATTR_ISOCH:
2355 if (bp->b_flags & B_READ) {
2356 rval = ugen_epx_isoc_IN_req(ugenp, epp, bp, &wait);
2357 } else {
2358 rval = ugen_epx_isoc_OUT_req(ugenp, epp, bp, &wait);
2361 break;
2362 default:
2363 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2364 rval = USB_INVALID_REQUEST;
2367 /* if the xfer could not immediately be completed, block here */
2368 if ((rval == USB_SUCCESS) && wait) {
2369 while (!epp->ep_done) {
2370 if ((cv_wait_sig(&epp->ep_wait_cv,
2371 &epp->ep_mutex) <= 0) && !epp->ep_done) {
2372 USB_DPRINTF_L2(UGEN_PRINT_XFER,
2373 ugenp->ug_log_hdl,
2374 "ugen_epx_req: interrupted ep=0x%" PRIx64,
2375 UGEN_MINOR_EPIDX(ugenp, dev));
2378 * blow away the request except for dflt pipe
2379 * (this is prevented in USBA)
2381 mutex_exit(&epp->ep_mutex);
2382 usb_pipe_reset(ugenp->ug_dip, epp->ep_ph,
2383 USB_FLAGS_SLEEP, NULL, NULL);
2384 (void) usb_pipe_drain_reqs(ugenp->ug_dip,
2385 epp->ep_ph, 0,
2386 USB_FLAGS_SLEEP, NULL, NULL);
2388 mutex_enter(&epp->ep_mutex);
2390 if (geterror(bp) == 0) {
2391 bioerror(bp, EINTR);
2393 epp->ep_lcmd_status =
2394 USB_LC_STAT_INTERRUPTED;
2396 break;
2398 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2399 "ugen_epx_req: wakeup");
2403 /* always set lcmd_status if there was a failure */
2404 if ((rval != USB_SUCCESS) &&
2405 (epp->ep_lcmd_status == USB_LC_STAT_NOERROR)) {
2406 epp->ep_lcmd_status = USB_LC_STAT_UNSPECIFIED_ERR;
2409 epp->ep_done = 0;
2410 epp->ep_bp = NULL;
2411 mutex_exit(&epp->ep_mutex);
2413 usb_release_access(epp->ep_ser_cookie);
2414 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2415 "ugen_epx_req: done");
2417 return (usb_rval2errno(rval));
2422 * handle control xfers
2424 static int
2425 ugen_epx_ctrl_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2426 struct buf *bp, boolean_t *wait)
2428 usb_ctrl_req_t *reqp = NULL;
2429 uchar_t *setup = ((uchar_t *)(bp->b_un.b_addr));
2430 int rval;
2431 ushort_t wLength;
2433 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2434 "ugen_epx_ctrl_req: epp=0x%p state=0x%x bp=0x%p",
2435 (void *)epp, epp->ep_state, (void *)bp);
2437 /* is this a read following a write with setup data? */
2438 if (bp->b_flags & B_READ) {
2439 if (epp->ep_data) {
2440 int ep_len = MBLKL(epp->ep_data);
2441 int len = min(bp->b_bcount, ep_len);
2443 bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
2444 epp->ep_data->b_rptr += len;
2445 if (MBLKL(epp->ep_data) == 0) {
2446 freemsg(epp->ep_data);
2447 epp->ep_data = NULL;
2449 bp->b_resid = bp->b_bcount - len;
2450 } else {
2451 bp->b_resid = bp->b_bcount;
2454 return (USB_SUCCESS);
2457 /* discard old data if any */
2458 if (epp->ep_data) {
2459 freemsg(epp->ep_data);
2460 epp->ep_data = NULL;
2463 /* allocate and initialize request */
2464 wLength = (setup[7] << 8) | setup[6];
2465 reqp = usb_alloc_ctrl_req(ugenp->ug_dip, wLength, USB_FLAGS_NOSLEEP);
2466 if (reqp == NULL) {
2467 epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
2469 return (USB_NO_RESOURCES);
2472 /* assume an LE data stream */
2473 reqp->ctrl_bmRequestType = setup[0];
2474 reqp->ctrl_bRequest = setup[1];
2475 reqp->ctrl_wValue = (setup[3] << 8) | setup[2];
2476 reqp->ctrl_wIndex = (setup[5] << 8) | setup[4];
2477 reqp->ctrl_wLength = wLength;
2478 reqp->ctrl_timeout = ugen_ctrl_timeout;
2479 reqp->ctrl_attributes = USB_ATTRS_AUTOCLEARING |
2480 USB_ATTRS_SHORT_XFER_OK;
2481 reqp->ctrl_cb = ugen_epx_ctrl_req_cb;
2482 reqp->ctrl_exc_cb = ugen_epx_ctrl_req_cb;
2483 reqp->ctrl_client_private = (usb_opaque_t)ugenp;
2486 * is this a legal request? No accesses to device are
2487 * allowed if we don't own the device
2489 if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_RCPT_MASK) ==
2490 USB_DEV_REQ_RCPT_DEV) &&
2491 (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
2492 USB_DEV_REQ_HOST_TO_DEV) &&
2493 (usb_owns_device(ugenp->ug_dip) == B_FALSE))) {
2494 rval = USB_INVALID_PERM;
2495 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2497 goto fail;
2500 /* filter out set_cfg and set_if standard requests */
2501 if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_TYPE_MASK) ==
2502 USB_DEV_REQ_TYPE_STANDARD) {
2503 switch (reqp->ctrl_bRequest) {
2504 case USB_REQ_SET_CFG:
2505 case USB_REQ_SET_IF:
2506 rval = USB_INVALID_REQUEST;
2507 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2509 goto fail;
2510 default:
2512 break;
2516 /* is this from host to device? */
2517 if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
2518 USB_DEV_REQ_HOST_TO_DEV) && reqp->ctrl_wLength) {
2519 if (((bp->b_bcount - UGEN_SETUP_PKT_SIZE) - wLength) != 0) {
2520 rval = USB_INVALID_REQUEST;
2521 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2523 goto fail;
2525 bcopy(bp->b_un.b_addr + UGEN_SETUP_PKT_SIZE,
2526 reqp->ctrl_data->b_wptr, wLength);
2527 reqp->ctrl_data->b_wptr += wLength;
2528 } else if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
2529 USB_DEV_REQ_DEV_TO_HOST) {
2530 if (bp->b_bcount != UGEN_SETUP_PKT_SIZE) {
2531 rval = USB_INVALID_REQUEST;
2532 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
2534 goto fail;
2538 /* submit the request */
2539 mutex_exit(&epp->ep_mutex);
2540 rval = usb_pipe_ctrl_xfer(epp->ep_ph, reqp, USB_FLAGS_NOSLEEP);
2541 mutex_enter(&epp->ep_mutex);
2542 if (rval != USB_SUCCESS) {
2543 epp->ep_lcmd_status =
2544 ugen_cr2lcstat(reqp->ctrl_completion_reason);
2546 goto fail;
2548 done:
2549 *wait = B_TRUE;
2551 return (USB_SUCCESS);
2552 fail:
2553 *wait = B_FALSE;
2555 usb_free_ctrl_req(reqp);
2557 return (rval);
2562 * callback for control requests, normal and exception completion
2564 static void
2565 ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph, usb_ctrl_req_t *reqp)
2567 ugen_state_t *ugenp = (ugen_state_t *)reqp->ctrl_client_private;
2568 ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2570 if (epp == NULL) {
2571 epp = &ugenp->ug_ep[0];
2574 mutex_enter(&epp->ep_mutex);
2576 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2577 "ugen_epx_ctrl_req_cb:\n\t"
2578 "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2579 (void *)epp, epp->ep_state, (void *)ph, (void *)reqp,
2580 reqp->ctrl_completion_reason, reqp->ctrl_cb_flags);
2582 ASSERT((reqp->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2584 /* save any data for the next read */
2585 switch (reqp->ctrl_completion_reason) {
2586 case USB_CR_OK:
2587 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2589 break;
2590 case USB_CR_PIPE_RESET:
2592 break;
2593 default:
2594 epp->ep_lcmd_status =
2595 ugen_cr2lcstat(reqp->ctrl_completion_reason);
2596 if (epp->ep_bp) {
2597 bioerror(epp->ep_bp, EIO);
2600 break;
2603 if (reqp->ctrl_data) {
2604 ASSERT(epp->ep_data == NULL);
2605 epp->ep_data = reqp->ctrl_data;
2606 reqp->ctrl_data = NULL;
2608 epp->ep_done++;
2609 cv_signal(&epp->ep_wait_cv);
2610 mutex_exit(&epp->ep_mutex);
2612 usb_free_ctrl_req(reqp);
2614 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2615 "ugen_epx_ctrl_req_cb: done");
2620 * handle bulk xfers
2622 static int
2623 ugen_epx_bulk_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2624 struct buf *bp, boolean_t *wait)
2626 int rval;
2627 usb_bulk_req_t *reqp = usb_alloc_bulk_req(ugenp->ug_dip,
2628 bp->b_bcount, USB_FLAGS_NOSLEEP);
2630 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2631 "ugen_epx_bulk_req: epp=0x%p state=0x%x bp=0x%p",
2632 (void *)epp, epp->ep_state, (void *)bp);
2634 if (reqp == NULL) {
2635 epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
2637 return (USB_NO_RESOURCES);
2640 ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
2643 * the transfer count is limited in minphys with what the HCD can
2644 * do
2646 reqp->bulk_len = bp->b_bcount;
2647 reqp->bulk_timeout = ugen_bulk_timeout;
2648 reqp->bulk_client_private = (usb_opaque_t)ugenp;
2649 reqp->bulk_attributes = USB_ATTRS_AUTOCLEARING;
2650 reqp->bulk_cb = ugen_epx_bulk_req_cb;
2651 reqp->bulk_exc_cb = ugen_epx_bulk_req_cb;
2653 /* copy data into bp for OUT pipes */
2654 if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
2655 bcopy(epp->ep_bp->b_un.b_addr, reqp->bulk_data->b_rptr,
2656 bp->b_bcount);
2657 reqp->bulk_data->b_wptr += bp->b_bcount;
2658 } else {
2659 reqp->bulk_attributes |= USB_ATTRS_SHORT_XFER_OK;
2662 mutex_exit(&epp->ep_mutex);
2663 if ((rval = usb_pipe_bulk_xfer(epp->ep_ph, reqp,
2664 USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
2665 mutex_enter(&epp->ep_mutex);
2666 epp->ep_lcmd_status =
2667 ugen_cr2lcstat(reqp->bulk_completion_reason);
2668 usb_free_bulk_req(reqp);
2669 bioerror(bp, EIO);
2670 } else {
2671 mutex_enter(&epp->ep_mutex);
2673 *wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
2675 return (rval);
2680 * normal and exception bulk request callback
2682 static void
2683 ugen_epx_bulk_req_cb(usb_pipe_handle_t ph, usb_bulk_req_t *reqp)
2685 ugen_state_t *ugenp = (ugen_state_t *)reqp->bulk_client_private;
2686 ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2688 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2689 "ugen_epx_bulk_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2690 (void *)ph, (void *)reqp, reqp->bulk_completion_reason,
2691 reqp->bulk_cb_flags);
2693 ASSERT((reqp->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2695 /* epp might be NULL if we are closing the pipe */
2696 if (epp) {
2697 mutex_enter(&epp->ep_mutex);
2698 if (epp->ep_bp && reqp->bulk_data) {
2699 int len = min(MBLKL(reqp->bulk_data),
2700 epp->ep_bp->b_bcount);
2701 if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
2702 if (len) {
2703 bcopy(reqp->bulk_data->b_rptr,
2704 epp->ep_bp->b_un.b_addr, len);
2705 epp->ep_bp->b_resid =
2706 epp->ep_bp->b_bcount - len;
2708 } else {
2709 epp->ep_bp->b_resid =
2710 epp->ep_bp->b_bcount - len;
2713 switch (reqp->bulk_completion_reason) {
2714 case USB_CR_OK:
2715 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2717 break;
2718 case USB_CR_PIPE_RESET:
2720 break;
2721 default:
2722 epp->ep_lcmd_status =
2723 ugen_cr2lcstat(reqp->bulk_completion_reason);
2724 if (epp->ep_bp) {
2725 bioerror(epp->ep_bp, EIO);
2728 epp->ep_done++;
2729 cv_signal(&epp->ep_wait_cv);
2730 mutex_exit(&epp->ep_mutex);
2733 usb_free_bulk_req(reqp);
2738 * handle intr IN xfers
2740 static int
2741 ugen_epx_intr_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
2742 struct buf *bp, boolean_t *wait)
2744 int len = 0;
2745 int rval = USB_SUCCESS;
2747 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2748 "ugen_epx_intr_IN_req: epp=0x%p state=0x%x bp=0x%p",
2749 (void *)epp, epp->ep_state, (void *)bp);
2751 *wait = B_FALSE;
2753 /* can we satisfy this read? */
2754 if (epp->ep_data) {
2755 len = min(MBLKL(epp->ep_data),
2756 bp->b_bcount);
2760 * if polling not active, restart, and return failure
2761 * immediately unless one xfer mode has been requested
2762 * if there is some data, return a short read
2764 if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
2765 if (len == 0) {
2766 if (!epp->ep_one_xfer) {
2767 rval = USB_FAILURE;
2768 if (epp->ep_lcmd_status ==
2769 USB_LC_STAT_NOERROR) {
2770 epp->ep_lcmd_status =
2771 USB_LC_STAT_INTR_BUF_FULL;
2774 if (ugen_epx_intr_IN_start_polling(ugenp,
2775 epp) != USB_SUCCESS) {
2776 epp->ep_lcmd_status =
2777 USB_LC_STAT_INTR_POLLING_FAILED;
2779 if (epp->ep_one_xfer) {
2780 *wait = B_TRUE;
2782 goto done;
2783 } else if (epp->ep_data && (len < bp->b_bcount)) {
2784 bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
2785 bp->b_resid = bp->b_bcount - len;
2786 epp->ep_data->b_rptr += len;
2788 goto done;
2793 * if there is data or FNDELAY, return available data
2795 if ((len >= bp->b_bcount) ||
2796 (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK))) {
2797 if (epp->ep_data) {
2798 bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
2799 epp->ep_data->b_rptr += len;
2800 bp->b_resid = bp->b_bcount - len;
2801 } else {
2802 bp->b_resid = bp->b_bcount;
2804 } else {
2805 /* otherwise just wait for data */
2806 *wait = B_TRUE;
2809 done:
2810 if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
2811 freemsg(epp->ep_data);
2812 epp->ep_data = NULL;
2815 if (*wait) {
2816 ASSERT(epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON);
2819 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2820 "ugen_epx_intr_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
2821 rval, bp->b_bcount, len, (void *)epp->ep_data);
2823 return (rval);
2828 * Start polling on interrupt endpoint, synchronously
2830 static int
2831 ugen_epx_intr_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
2833 int rval = USB_FAILURE;
2834 usb_intr_req_t *reqp;
2835 usb_flags_t uflag;
2838 * if polling is being stopped, we restart polling in the
2839 * interrrupt callback again
2841 if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) {
2843 return (rval);
2845 if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
2846 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2847 "ugen_epx_intr_IN_start_polling: epp=0x%p state=0x%x",
2848 (void *)epp, epp->ep_state);
2850 epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_ON;
2851 mutex_exit(&epp->ep_mutex);
2853 reqp = usb_alloc_intr_req(ugenp->ug_dip, 0,
2854 USB_FLAGS_SLEEP);
2855 reqp->intr_client_private = (usb_opaque_t)ugenp;
2857 reqp->intr_attributes = USB_ATTRS_AUTOCLEARING |
2858 USB_ATTRS_SHORT_XFER_OK;
2859 mutex_enter(&epp->ep_mutex);
2860 if (epp->ep_one_xfer) {
2861 reqp->intr_attributes |= USB_ATTRS_ONE_XFER;
2862 uflag = USB_FLAGS_NOSLEEP;
2863 } else {
2864 uflag = USB_FLAGS_SLEEP;
2866 mutex_exit(&epp->ep_mutex);
2868 reqp->intr_len = epp->ep_descr.wMaxPacketSize;
2869 reqp->intr_cb = ugen_epx_intr_IN_req_cb;
2870 reqp->intr_exc_cb = ugen_epx_intr_IN_req_cb;
2873 if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
2874 uflag)) != USB_SUCCESS) {
2875 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2876 "ugen_epx_intr_IN_start_polling: failed %d", rval);
2877 usb_free_intr_req(reqp);
2879 mutex_enter(&epp->ep_mutex);
2880 if (rval != USB_SUCCESS) {
2881 epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLLING_ON;
2883 } else {
2884 rval = USB_SUCCESS;
2887 return (rval);
2892 * stop polling on an interrupt endpoint, asynchronously
2894 static void
2895 ugen_epx_intr_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
2897 if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) &&
2898 ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) == 0)) {
2900 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2901 "ugen_epx_intr_IN_stop_polling: epp=0x%p state=0x%x",
2902 (void *)epp, epp->ep_state);
2904 epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED;
2905 mutex_exit(&epp->ep_mutex);
2906 usb_pipe_stop_intr_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
2907 mutex_enter(&epp->ep_mutex);
2913 * poll management
2915 static void
2916 ugen_epx_intr_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
2918 if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLL_PENDING) {
2919 struct pollhead *phpp = &epp->ep_pollhead;
2921 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2922 "ugen_epx_intr_IN_poll_wakeup: state=0x%x", epp->ep_state);
2924 epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLL_PENDING;
2925 mutex_exit(&epp->ep_mutex);
2926 pollwakeup(phpp, POLLIN);
2927 mutex_enter(&epp->ep_mutex);
2933 * callback functions for interrupt IN pipe
2935 static void
2936 ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
2938 ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
2939 ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
2941 if (epp == NULL) {
2942 /* pipe is closing */
2944 goto done;
2947 mutex_enter(&epp->ep_mutex);
2949 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2950 "ugen_epx_intr_IN_req_cb:\n\t"
2951 "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%ld",
2952 (void *)epp, epp->ep_state, (void *)ph, (void *)reqp,
2953 reqp->intr_completion_reason, reqp->intr_cb_flags,
2954 (reqp->intr_data == NULL) ? 0 :
2955 MBLKL(reqp->intr_data));
2957 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
2959 if (epp->ep_data && reqp->intr_data) {
2960 mblk_t *mp;
2962 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2963 "intr ep%x coalesce data", epp->ep_descr.bEndpointAddress);
2965 /* coalesce the data into one mblk */
2966 epp->ep_data->b_cont = reqp->intr_data;
2967 if ((mp = msgpullup(epp->ep_data, -1)) != NULL) {
2968 reqp->intr_data = NULL;
2969 freemsg(epp->ep_data);
2970 epp->ep_data = mp;
2971 } else {
2972 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2973 "msgpullup failed, discard data");
2974 epp->ep_data->b_cont = NULL;
2976 } else if (reqp->intr_data) {
2977 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2978 "setting ep_data");
2980 epp->ep_data = reqp->intr_data;
2981 reqp->intr_data = NULL;
2984 switch (reqp->intr_completion_reason) {
2985 case USB_CR_OK:
2986 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
2988 break;
2989 case USB_CR_PIPE_RESET:
2990 case USB_CR_STOPPED_POLLING:
2992 break;
2993 default:
2994 epp->ep_lcmd_status =
2995 ugen_cr2lcstat(reqp->intr_completion_reason);
2996 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2997 "ugen_exp_intr_cb_req: lcmd_status=0x%x",
2998 epp->ep_lcmd_status);
3000 break;
3003 /* any non-zero completion reason stops polling */
3004 if ((reqp->intr_completion_reason) ||
3005 (epp->ep_one_xfer)) {
3006 epp->ep_state &= ~(UGEN_EP_STATE_INTR_IN_POLLING_ON |
3007 UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED);
3010 /* is there a poll pending? should we stop polling? */
3011 if (epp->ep_data) {
3012 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3013 "ugen_epx_intr_IN_req_cb: data len=0x%lx",
3014 MBLKL(epp->ep_data));
3016 ugen_epx_intr_IN_poll_wakeup(ugenp, epp);
3018 /* if there is no space left, stop polling */
3019 if (epp->ep_data &&
3020 (MBLKL(epp->ep_data) >=
3021 epp->ep_buf_limit)) {
3022 ugen_epx_intr_IN_stop_polling(ugenp, epp);
3026 if (reqp->intr_completion_reason && epp->ep_bp) {
3027 bioerror(epp->ep_bp, EIO);
3028 epp->ep_done++;
3029 cv_signal(&epp->ep_wait_cv);
3031 /* can we satisfy the read now */
3032 } else if (epp->ep_data && epp->ep_bp &&
3033 (!epp->ep_done || epp->ep_one_xfer)) {
3034 boolean_t wait;
3036 if ((ugen_epx_intr_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
3037 USB_SUCCESS) && (wait == B_FALSE)) {
3038 epp->ep_done++;
3039 cv_signal(&epp->ep_wait_cv);
3042 mutex_exit(&epp->ep_mutex);
3044 done:
3045 usb_free_intr_req(reqp);
3050 * handle intr OUT xfers
3052 static int
3053 ugen_epx_intr_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
3054 struct buf *bp, boolean_t *wait)
3056 int rval = USB_SUCCESS;
3057 usb_intr_req_t *reqp;
3059 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3060 "ugen_epx_intr_OUT_req: epp=0x%p state=0x%x bp=0x%p",
3061 (void *)epp, epp->ep_state, (void *)bp);
3063 reqp = usb_alloc_intr_req(ugenp->ug_dip, bp->b_bcount,
3064 USB_FLAGS_NOSLEEP);
3065 if (reqp == NULL) {
3066 epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
3068 return (USB_NO_RESOURCES);
3071 ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
3073 reqp->intr_timeout = ugen_intr_timeout;
3074 reqp->intr_client_private = (usb_opaque_t)ugenp;
3075 reqp->intr_len = bp->b_bcount;
3076 reqp->intr_attributes = USB_ATTRS_AUTOCLEARING;
3077 reqp->intr_cb = ugen_epx_intr_OUT_req_cb;
3078 reqp->intr_exc_cb = ugen_epx_intr_OUT_req_cb;
3080 /* copy data from bp */
3081 bcopy(epp->ep_bp->b_un.b_addr, reqp->intr_data->b_rptr,
3082 bp->b_bcount);
3083 reqp->intr_data->b_wptr += bp->b_bcount;
3085 mutex_exit(&epp->ep_mutex);
3086 if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
3087 USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
3088 mutex_enter(&epp->ep_mutex);
3089 epp->ep_lcmd_status =
3090 ugen_cr2lcstat(reqp->intr_completion_reason);
3091 usb_free_intr_req(reqp);
3092 bioerror(bp, EIO);
3093 } else {
3094 mutex_enter(&epp->ep_mutex);
3096 *wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
3098 return (rval);
3103 * callback functions for interrupt OUT pipe
3105 static void
3106 ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
3108 ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
3109 ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
3111 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3112 "ugen_epx_intr_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
3113 (void *)ph, (void *)reqp, reqp->intr_completion_reason,
3114 reqp->intr_cb_flags);
3116 ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3118 /* epp might be NULL if we are closing the pipe */
3119 if (epp) {
3120 int len;
3122 mutex_enter(&epp->ep_mutex);
3123 if (epp->ep_bp) {
3124 len = min(MBLKL(reqp->intr_data), epp->ep_bp->b_bcount);
3126 epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
3128 switch (reqp->intr_completion_reason) {
3129 case USB_CR_OK:
3130 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3132 break;
3133 case USB_CR_PIPE_RESET:
3135 break;
3136 default:
3137 epp->ep_lcmd_status =
3138 ugen_cr2lcstat(
3139 reqp->intr_completion_reason);
3140 bioerror(epp->ep_bp, EIO);
3143 epp->ep_done++;
3144 cv_signal(&epp->ep_wait_cv);
3145 mutex_exit(&epp->ep_mutex);
3148 usb_free_intr_req(reqp);
3153 * handle isoc IN xfers
3155 static int
3156 ugen_epx_isoc_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
3157 struct buf *bp, boolean_t *wait)
3159 int rval = USB_SUCCESS;
3160 ugen_isoc_pkt_descr_t *pkt_descr;
3161 ushort_t n_pkt;
3162 uint_t pkts_len, len = 0;
3164 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3165 "ugen_epx_isoc_IN_req: epp=0x%p state=0x%x bp=0x%p",
3166 (void *)epp, epp->ep_state, (void *)bp);
3168 *wait = B_FALSE;
3170 /* check if the isoc in pkt info has been initialized */
3171 pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
3172 n_pkt = epp->ep_isoc_info.isoc_pkts_count;
3173 if ((n_pkt == 0) || (pkt_descr == NULL)) {
3174 rval = USB_FAILURE;
3175 epp->ep_lcmd_status = USB_LC_STAT_ISOC_UNINITIALIZED;
3177 goto done;
3181 /* For OUT endpoint, return pkts transfer status of last request */
3182 if (UGEN_XFER_DIR(epp) != USB_EP_DIR_IN) {
3183 if (bp->b_bcount < sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
3184 rval = USB_INVALID_REQUEST;
3185 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3187 return (rval);
3189 bcopy(epp->ep_isoc_info.isoc_pkt_descr, bp->b_un.b_addr,
3190 n_pkt * sizeof (ugen_isoc_pkt_descr_t));
3191 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3193 return (USB_SUCCESS);
3196 /* read length should be the sum of pkt descrs and data length */
3197 pkts_len = epp->ep_isoc_info.isoc_pkts_length;
3198 if (bp->b_bcount != pkts_len + sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
3199 rval = USB_INVALID_REQUEST;
3200 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3202 goto done;
3205 /* can we satisfy this read? */
3206 if (epp->ep_data) {
3207 len = min(MBLKL(epp->ep_data),
3208 bp->b_bcount);
3210 * every msg block in ep_data must be the size of
3211 * pkts_len(payload length) + pkt descrs len
3213 ASSERT((len == 0) || (len == bp->b_bcount));
3217 * if polling not active, restart
3218 * if there is some data, return the data
3220 if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
3221 if (len == 0) {
3222 rval = USB_FAILURE;
3223 if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
3224 epp)) != USB_SUCCESS) {
3225 epp->ep_lcmd_status =
3226 USB_LC_STAT_ISOC_POLLING_FAILED;
3229 goto done;
3231 } else if (epp->ep_data && (len >= bp->b_bcount)) {
3232 bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr,
3233 bp->b_bcount);
3234 bp->b_resid = 0;
3235 epp->ep_data->b_rptr += bp->b_bcount;
3237 goto done;
3242 * if there is data or FNDELAY, return available data
3244 if (epp->ep_data && (len >= bp->b_bcount)) {
3245 /* can fulfill this read request */
3246 bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, bp->b_bcount);
3247 epp->ep_data->b_rptr += bp->b_bcount;
3248 bp->b_resid = 0;
3249 } else if (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK)) {
3250 bp->b_resid = bp->b_bcount;
3251 } else {
3252 /* otherwise just wait for data */
3253 *wait = B_TRUE;
3256 done:
3257 /* data have been read */
3258 if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
3259 mblk_t *mp = NULL;
3261 /* remove the just read msg block */
3262 mp = unlinkb(epp->ep_data);
3263 freemsg(epp->ep_data);
3265 if (mp) {
3266 epp->ep_data = mp;
3267 } else {
3268 epp->ep_data = NULL;
3272 if (*wait) {
3273 ASSERT(epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON);
3276 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3277 "ugen_epx_isoc_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
3278 rval, bp->b_bcount, len, (void *)epp->ep_data);
3280 return (rval);
3285 * Start polling on isoc endpoint, asynchronously
3287 static int
3288 ugen_epx_isoc_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
3290 int rval = USB_FAILURE;
3291 usb_isoc_req_t *reqp;
3292 ugen_isoc_pkt_descr_t *pkt_descr;
3293 ushort_t n_pkt, pkt;
3294 uint_t pkts_len;
3297 * if polling is being stopped, we restart polling in the
3298 * isoc callback again
3300 if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) {
3302 return (rval);
3305 if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
3306 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3307 "ugen_epx_isoc_IN_start_polling: epp=0x%p state=0x%x",
3308 (void *)epp, epp->ep_state);
3310 pkts_len = epp->ep_isoc_info.isoc_pkts_length;
3311 n_pkt = epp->ep_isoc_info.isoc_pkts_count;
3312 pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
3314 epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_ON;
3315 mutex_exit(&epp->ep_mutex);
3317 if ((reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
3318 USB_FLAGS_NOSLEEP)) == NULL) {
3319 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3320 "ugen_epx_isoc_IN_start_polling: alloc isoc "
3321 "req failed");
3322 mutex_enter(&epp->ep_mutex);
3323 epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
3325 return (USB_NO_RESOURCES);
3327 reqp->isoc_client_private = (usb_opaque_t)ugenp;
3329 reqp->isoc_attributes = USB_ATTRS_AUTOCLEARING |
3330 USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_ISOC_XFER_ASAP;
3333 * isoc_pkts_length was defined to be ushort_t. This
3334 * has been obsoleted by usb high speed isoc support.
3335 * It is set here just for compatibility reason
3337 reqp->isoc_pkts_length = 0;
3339 for (pkt = 0; pkt < n_pkt; pkt++) {
3340 reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
3341 pkt_descr[pkt].dsc_isoc_pkt_len;
3343 reqp->isoc_pkts_count = n_pkt;
3344 reqp->isoc_cb = ugen_epx_isoc_IN_req_cb;
3345 reqp->isoc_exc_cb = ugen_epx_isoc_IN_req_cb;
3347 if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
3348 USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
3349 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3350 "ugen_epx_isoc_IN_start_polling: failed %d", rval);
3351 usb_free_isoc_req(reqp);
3354 mutex_enter(&epp->ep_mutex);
3355 if (rval != USB_SUCCESS) {
3356 epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
3358 } else {
3359 rval = USB_SUCCESS;
3362 return (rval);
3367 * stop polling on an isoc endpoint, asynchronously
3369 static void
3370 ugen_epx_isoc_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
3372 if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) &&
3373 ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) == 0)) {
3374 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3375 "ugen_epx_isoc_IN_stop_polling: epp=0x%p state=0x%x",
3376 (void *)epp, epp->ep_state);
3378 epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED;
3379 mutex_exit(&epp->ep_mutex);
3380 usb_pipe_stop_isoc_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
3381 mutex_enter(&epp->ep_mutex);
3387 * poll management
3389 static void
3390 ugen_epx_isoc_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
3392 if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLL_PENDING) {
3393 struct pollhead *phpp = &epp->ep_pollhead;
3395 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3396 "ugen_epx_isoc_IN_poll_wakeup: state=0x%x", epp->ep_state);
3398 epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
3399 mutex_exit(&epp->ep_mutex);
3400 pollwakeup(phpp, POLLIN);
3401 mutex_enter(&epp->ep_mutex);
3407 * callback functions for isoc IN pipe
3409 static void
3410 ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
3412 ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
3413 ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
3415 if (epp == NULL) {
3416 /* pipe is closing */
3418 goto done;
3421 ASSERT(!mutex_owned(&epp->ep_mutex)); /* not owned */
3423 mutex_enter(&epp->ep_mutex);
3425 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3426 "ugen_epx_isoc_IN_req_cb: "
3427 "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%ld "
3428 "isoc error count=%d, pkt cnt=%d", (void *)epp, epp->ep_state,
3429 (void *)ph, (void *)reqp, reqp->isoc_completion_reason,
3430 reqp->isoc_cb_flags, (reqp->isoc_data == NULL) ? 0 :
3431 MBLKL(reqp->isoc_data),
3432 reqp->isoc_error_count, reqp->isoc_pkts_count);
3434 /* Too many packet errors during isoc transfer of this request */
3435 if (reqp->isoc_error_count == reqp->isoc_pkts_count) {
3436 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3437 "too many errors(%d) in this req, stop polling",
3438 reqp->isoc_error_count);
3439 epp->ep_lcmd_status = USB_LC_STAT_ISOC_PKT_ERROR;
3440 ugen_epx_isoc_IN_stop_polling(ugenp, epp);
3443 /* Data OK */
3444 if (reqp->isoc_data && !reqp->isoc_completion_reason) {
3445 mblk_t *mp1 = NULL, *mp2 = NULL;
3446 usb_isoc_pkt_descr_t *pkt_descr =
3447 reqp->isoc_pkt_descr;
3448 ushort_t i, n_pkt = reqp->isoc_pkts_count;
3450 for (i = 0; i < n_pkt; i++) {
3451 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3452 "pkt %d: len=%d status=%d actual_len=%d", i,
3453 pkt_descr[i].isoc_pkt_length,
3454 pkt_descr[i].isoc_pkt_status,
3455 pkt_descr[i].isoc_pkt_actual_length);
3457 /* translate cr to ugen lcstat */
3458 pkt_descr[i].isoc_pkt_status =
3459 ugen_cr2lcstat(pkt_descr[i].isoc_pkt_status);
3462 /* construct data buffer: pkt descriptors + payload */
3463 mp2 = allocb(sizeof (ugen_isoc_pkt_descr_t) * n_pkt, BPRI_HI);
3464 if (mp2 == NULL) {
3465 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3466 "alloc msgblk failed, discard data");
3467 } else {
3468 /* pkt descrs first */
3469 bcopy(pkt_descr, mp2->b_wptr,
3470 sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3472 mp2->b_wptr += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
3474 /* payload follows */
3475 linkb(mp2, reqp->isoc_data);
3477 /* concatenate data bytes in mp2 */
3478 if ((mp1 = msgpullup(mp2, -1)) != NULL) {
3480 * now we get the required data:
3481 * pkt descrs + payload
3483 reqp->isoc_data = NULL;
3484 } else {
3485 USB_DPRINTF_L2(UGEN_PRINT_XFER,
3486 ugenp->ug_log_hdl,
3487 "msgpullup status blk failed, "
3488 "discard data");
3489 mp2->b_cont = NULL;
3492 freemsg(mp2);
3493 mp2 = NULL;
3496 if (epp->ep_data && (mp1 != NULL)) {
3497 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3498 "ISOC ep%x coalesce ep_data",
3499 epp->ep_descr.bEndpointAddress);
3501 /* add mp1 to the tail of ep_data */
3502 linkb(epp->ep_data, mp1);
3504 } else if (mp1 != NULL) {
3505 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3506 "setting ep_data");
3507 epp->ep_data = mp1;
3511 switch (reqp->isoc_completion_reason) {
3512 case USB_CR_OK:
3513 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3515 break;
3516 case USB_CR_PIPE_RESET:
3517 case USB_CR_STOPPED_POLLING:
3518 case USB_CR_PIPE_CLOSING:
3520 break;
3521 default:
3522 epp->ep_lcmd_status =
3523 ugen_cr2lcstat(reqp->isoc_completion_reason);
3524 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3525 "ugen_exp_isoc_cb_req: error lcmd_status=0x%x ",
3526 epp->ep_lcmd_status);
3528 break;
3531 /* any non-zero completion reason signifies polling has stopped */
3532 if (reqp->isoc_completion_reason) {
3533 epp->ep_state &= ~(UGEN_EP_STATE_ISOC_IN_POLLING_ON |
3534 UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED);
3538 /* is there a poll pending? should we stop polling? */
3539 if (epp->ep_data) {
3540 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3541 "ugen_epx_isoc_IN_req_cb: data len=0x%lx, limit=0x%lx",
3542 msgdsize(epp->ep_data),
3543 epp->ep_buf_limit);
3545 ugen_epx_isoc_IN_poll_wakeup(ugenp, epp);
3549 * Since isoc is unreliable xfer, if buffered data size exceeds
3550 * the limit, we just discard and free data in the oldest mblk
3552 if (epp->ep_data &&
3553 (msgdsize(epp->ep_data) >= epp->ep_buf_limit)) {
3554 mblk_t *mp = NULL;
3556 /* exceed buf lenth limit, remove the oldest one */
3557 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3558 "ugen_epx_isoc_IN_req_cb: overflow!");
3559 mp = unlinkb(epp->ep_data);
3560 if (epp->ep_data) {
3561 freeb(epp->ep_data);
3563 epp->ep_data = mp;
3568 if (reqp->isoc_completion_reason && epp->ep_bp) {
3569 bioerror(epp->ep_bp, EIO);
3570 epp->ep_done++;
3571 cv_signal(&epp->ep_wait_cv);
3573 } else if (epp->ep_data && epp->ep_bp && !epp->ep_done) {
3574 boolean_t wait;
3576 /* can we satisfy the read now */
3577 if ((ugen_epx_isoc_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
3578 USB_SUCCESS) && (wait == B_FALSE)) {
3579 epp->ep_done++;
3580 cv_signal(&epp->ep_wait_cv);
3583 mutex_exit(&epp->ep_mutex);
3585 done:
3587 usb_free_isoc_req(reqp);
3591 * handle isoc OUT xfers or init isoc IN polling
3593 static int
3594 ugen_epx_isoc_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
3595 struct buf *bp, boolean_t *wait)
3597 int rval = USB_SUCCESS;
3598 usb_isoc_req_t *reqp;
3599 ugen_isoc_pkt_descr_t *pkt_descr;
3600 ushort_t pkt, n_pkt = 0;
3601 uint_t pkts_len = 0;
3602 uint_t head_len;
3603 char *p;
3604 ugen_isoc_req_head_t *pkth;
3606 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3607 "ugen_epx_isoc_OUT_req: epp=0x%p state=0x%x bp=0x%p",
3608 (void *)epp, epp->ep_state, (void *)bp);
3610 *wait = B_FALSE;
3612 if (bp->b_bcount < sizeof (int)) {
3613 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3614 rval = USB_INVALID_REQUEST;
3616 goto done;
3619 /* LINTED E_BAD_PTR_CAST_ALIGN */
3620 pkth = (ugen_isoc_req_head_t *)bp->b_un.b_addr;
3621 n_pkt = pkth->req_isoc_pkts_count;
3622 head_len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt +
3623 sizeof (int);
3625 if ((n_pkt == 0) ||
3626 (n_pkt > usb_get_max_pkts_per_isoc_request(ugenp->ug_dip)) ||
3627 (bp->b_bcount < head_len)) {
3628 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3629 "Invalid params: bcount=%lu, head_len=%d, pktcnt=%d",
3630 bp->b_bcount, head_len, n_pkt);
3632 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3633 rval = USB_INVALID_REQUEST;
3635 goto done;
3638 p = bp->b_un.b_addr;
3639 p += sizeof (int); /* points to pkt_descrs */
3641 pkt_descr = kmem_zalloc(sizeof (ugen_isoc_pkt_descr_t) * n_pkt,
3642 KM_NOSLEEP);
3643 if (pkt_descr == NULL) {
3644 epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
3645 rval = USB_NO_RESOURCES;
3647 goto done;
3649 bcopy(p, pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3650 p += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
3652 /* total packet payload length */
3653 for (pkt = 0; pkt < n_pkt; pkt++) {
3654 pkts_len += pkt_descr[pkt].dsc_isoc_pkt_len;
3658 * write length may either be header length for isoc IN endpoint or
3659 * the sum of header and data pkts length for isoc OUT endpoint
3661 if (((bp->b_bcount != head_len) &&
3662 (bp->b_bcount != head_len + pkts_len))) {
3663 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3664 "invalid length: bcount=%lu, head_len=%d, pkts_len = %d,"
3665 "pktcnt=%d", bp->b_bcount, head_len, pkts_len, n_pkt);
3667 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3668 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3669 rval = USB_INVALID_REQUEST;
3671 goto done;
3675 ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
3677 /* Set parameters for READ */
3678 if (bp->b_bcount == head_len) {
3679 /* must be isoc IN endpoint */
3680 if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
3681 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3682 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
3683 n_pkt);
3684 rval = USB_INVALID_REQUEST;
3685 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3686 "write length invalid for OUT ep%x",
3687 epp->ep_descr.bEndpointAddress);
3689 goto done;
3692 if (epp->ep_isoc_in_inited) {
3693 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3694 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
3695 n_pkt);
3696 rval = USB_INVALID_REQUEST;
3697 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3698 "isoc IN polling fail: already inited, need to"
3699 "close the ep before initing again");
3701 goto done;
3704 /* save pkts info for the READ */
3705 epp->ep_isoc_info.isoc_pkts_count = n_pkt;
3706 epp->ep_isoc_info.isoc_pkts_length = pkts_len;
3707 epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
3709 if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
3710 epp)) != USB_SUCCESS) {
3711 epp->ep_lcmd_status =
3712 USB_LC_STAT_ISOC_POLLING_FAILED;
3713 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
3714 n_pkt);
3715 epp->ep_isoc_info.isoc_pkts_count = 0;
3716 epp->ep_isoc_info.isoc_pkts_length = 0;
3717 epp->ep_isoc_info.isoc_pkt_descr = NULL;
3719 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3720 "isoc IN start polling failed");
3722 goto done;
3725 epp->ep_bp->b_resid = epp->ep_bp->b_bcount - head_len;
3727 epp->ep_isoc_in_inited++;
3728 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3729 "isoc IN ep inited");
3731 goto done;
3734 /* must be isoc OUT endpoint */
3735 if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
3736 epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
3737 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3738 rval = USB_INVALID_REQUEST;
3739 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3740 "write length invalid for an IN ep%x",
3741 epp->ep_descr.bEndpointAddress);
3743 goto done;
3746 /* OUT endpoint, free previous info if there's any */
3747 if (epp->ep_isoc_info.isoc_pkt_descr) {
3748 kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
3749 sizeof (ugen_isoc_pkt_descr_t) *
3750 epp->ep_isoc_info.isoc_pkts_count);
3753 /* save pkts info for the WRITE */
3754 epp->ep_isoc_info.isoc_pkts_count = n_pkt;
3755 epp->ep_isoc_info.isoc_pkts_length = pkts_len;
3756 epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
3758 reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
3759 USB_FLAGS_NOSLEEP);
3760 if (reqp == NULL) {
3761 epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
3762 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3763 rval = USB_NO_RESOURCES;
3764 epp->ep_isoc_info.isoc_pkts_count = 0;
3765 epp->ep_isoc_info.isoc_pkts_length = 0;
3766 epp->ep_isoc_info.isoc_pkt_descr = NULL;
3768 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3769 "alloc isoc out req failed");
3770 goto done;
3773 for (pkt = 0; pkt < n_pkt; pkt++) {
3774 reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
3775 pkt_descr[pkt].dsc_isoc_pkt_len;
3777 reqp->isoc_pkts_count = n_pkt;
3778 reqp->isoc_client_private = (usb_opaque_t)ugenp;
3779 reqp->isoc_attributes = USB_ATTRS_AUTOCLEARING |
3780 USB_ATTRS_ISOC_XFER_ASAP;
3782 reqp->isoc_cb = ugen_epx_isoc_OUT_req_cb;
3783 reqp->isoc_exc_cb = ugen_epx_isoc_OUT_req_cb;
3785 /* copy data from bp */
3786 bcopy(p, reqp->isoc_data->b_wptr, pkts_len);
3787 reqp->isoc_data->b_wptr += pkts_len;
3789 mutex_exit(&epp->ep_mutex);
3790 if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
3791 USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
3792 mutex_enter(&epp->ep_mutex);
3793 epp->ep_lcmd_status =
3794 ugen_cr2lcstat(reqp->isoc_completion_reason);
3795 usb_free_isoc_req(reqp);
3796 kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
3798 epp->ep_isoc_info.isoc_pkt_descr = NULL;
3799 epp->ep_isoc_info.isoc_pkts_count = 0;
3800 epp->ep_isoc_info.isoc_pkts_length = 0;
3802 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3803 "isoc out xfer failed");
3805 bioerror(bp, EIO);
3806 } else {
3807 mutex_enter(&epp->ep_mutex);
3809 *wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
3811 done:
3812 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3813 "ugen_epx_isoc_OUT_req end: rval=%d bcount=%lu xfer_len=%d",
3814 rval, bp->b_bcount, pkts_len);
3816 return (rval);
3821 * callback functions for isoc OUT pipe
3823 static void
3824 ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
3826 ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
3827 ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
3829 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3830 "ugen_epx_isoc_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
3831 (void *)ph, (void *)reqp, reqp->isoc_completion_reason,
3832 reqp->isoc_cb_flags);
3834 /* epp might be NULL if we are closing the pipe */
3835 if (epp) {
3836 ugen_isoc_pkt_info_t info;
3838 mutex_enter(&epp->ep_mutex);
3840 info = epp->ep_isoc_info;
3841 if (epp->ep_bp) {
3842 int len, i;
3843 int headlen;
3844 usb_isoc_pkt_descr_t *pktdesc;
3846 pktdesc = reqp->isoc_pkt_descr;
3847 headlen = info.isoc_pkts_count *
3848 sizeof (ugen_isoc_pkt_descr_t);
3850 len = min(headlen + MBLKL(reqp->isoc_data),
3851 epp->ep_bp->b_bcount);
3853 epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
3856 switch (reqp->isoc_completion_reason) {
3857 case USB_CR_OK:
3859 epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
3861 for (i = 0; i < reqp->isoc_pkts_count; i++) {
3862 pktdesc[i].isoc_pkt_status =
3863 ugen_cr2lcstat(pktdesc[i].
3864 isoc_pkt_status);
3867 /* save the status info */
3868 bcopy(reqp->isoc_pkt_descr,
3869 info.isoc_pkt_descr,
3870 (sizeof (ugen_isoc_pkt_descr_t) *
3871 info.isoc_pkts_count));
3873 break;
3874 case USB_CR_PIPE_RESET:
3876 break;
3877 default:
3878 epp->ep_lcmd_status =
3879 ugen_cr2lcstat(
3880 reqp->isoc_completion_reason);
3881 bioerror(epp->ep_bp, EIO);
3884 epp->ep_done++;
3885 cv_signal(&epp->ep_wait_cv);
3886 mutex_exit(&epp->ep_mutex);
3889 usb_free_isoc_req(reqp);
3894 * Endpoint status node management
3896 * open/close an endpoint status node.
3898 * Return values: errno
3900 static int
3901 ugen_eps_open(ugen_state_t *ugenp, dev_t dev, int flag)
3903 ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
3904 int rval = EBUSY;
3906 mutex_enter(&epp->ep_mutex);
3907 USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
3908 "ugen_eps_open: dev=0x%lx flag=0x%x state=0x%x",
3909 dev, flag, epp->ep_state);
3911 ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
3913 /* only one open at the time */
3914 if ((epp->ep_state & UGEN_EP_STATE_STAT_OPEN) == 0) {
3915 epp->ep_state |= UGEN_EP_STATE_STAT_OPEN;
3916 epp->ep_stat_oflag = flag;
3917 rval = 0;
3919 mutex_exit(&epp->ep_mutex);
3921 return (rval);
3926 * close endpoint status
3928 static void
3929 ugen_eps_close(ugen_state_t *ugenp, dev_t dev, int flag)
3931 ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
3933 mutex_enter(&epp->ep_mutex);
3934 USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
3935 "ugen_eps_close: dev=0x%lx flag=0x%x state=0x%x",
3936 dev, flag, epp->ep_state);
3938 epp->ep_state &= ~(UGEN_EP_STATE_STAT_OPEN |
3939 UGEN_EP_STATE_INTR_IN_POLL_PENDING |
3940 UGEN_EP_STATE_ISOC_IN_POLL_PENDING);
3941 epp->ep_one_xfer = B_FALSE;
3943 USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
3944 "ugen_eps_close: state=0x%x", epp->ep_state);
3946 ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
3947 mutex_exit(&epp->ep_mutex);
3952 * return status info
3954 * Return values: errno
3956 static int
3957 ugen_eps_req(ugen_state_t *ugenp, struct buf *bp)
3959 ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, bp->b_edev)];
3961 mutex_enter(&epp->ep_mutex);
3962 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3963 "ugen_eps_req: bp=0x%p lcmd_status=0x%x bcount=%lu",
3964 (void *)bp, epp->ep_lcmd_status, bp->b_bcount);
3966 if (bp->b_flags & B_READ) {
3967 int len = min(sizeof (epp->ep_lcmd_status), bp->b_bcount);
3968 if (len) {
3969 bcopy(&epp->ep_lcmd_status, bp->b_un.b_addr, len);
3971 bp->b_resid = bp->b_bcount - len;
3972 } else {
3973 USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3974 "ugen_eps_req: control=0x%x",
3975 *((char *)(bp->b_un.b_addr)));
3977 if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
3978 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3979 "ugen_eps_req: cannot change one xfer mode if "
3980 "endpoint is open");
3982 mutex_exit(&epp->ep_mutex);
3984 return (EINVAL);
3987 if ((epp->ep_descr.bmAttributes & USB_EP_ATTR_INTR) &&
3988 (epp->ep_descr.bEndpointAddress & USB_EP_DIR_IN)) {
3989 epp->ep_one_xfer = (*((char *)(bp->b_un.b_addr)) &
3990 USB_EP_INTR_ONE_XFER) ? B_TRUE : B_FALSE;
3991 } else {
3992 USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3993 "ugen_eps_req: not an interrupt endpoint");
3995 mutex_exit(&epp->ep_mutex);
3997 return (EINVAL);
4000 bp->b_resid = bp->b_bcount - 1;
4002 mutex_exit(&epp->ep_mutex);
4004 return (0);
4009 * device status node management
4011 static int
4012 ugen_ds_init(ugen_state_t *ugenp)
4014 cv_init(&ugenp->ug_ds.dev_wait_cv, NULL, CV_DRIVER, NULL);
4016 /* Create devstat minor node for this instance */
4017 if (ugen_ds_minor_nodes_create(ugenp) != USB_SUCCESS) {
4018 USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4019 "ugen_create_dev_stat_minor_nodes failed");
4021 return (USB_FAILURE);
4025 return (USB_SUCCESS);
4029 static void
4030 ugen_ds_destroy(ugen_state_t *ugenp)
4032 cv_destroy(&ugenp->ug_ds.dev_wait_cv);
4037 * open devstat minor node
4039 * Return values: errno
4041 static int
4042 ugen_ds_open(ugen_state_t *ugenp, dev_t dev, int flag)
4044 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4045 "ugen_ds_open: dev=0x%lx flag=0x%x", dev, flag);
4047 mutex_enter(&ugenp->ug_mutex);
4048 if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_ACTIVE) == 0) {
4050 * first read on device node should return status
4052 ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED |
4053 UGEN_DEV_STATUS_ACTIVE;
4054 ugenp->ug_ds.dev_oflag = flag;
4055 mutex_exit(&ugenp->ug_mutex);
4057 return (0);
4058 } else {
4059 mutex_exit(&ugenp->ug_mutex);
4061 return (EBUSY);
4066 static void
4067 ugen_ds_close(ugen_state_t *ugenp, dev_t dev, int flag)
4069 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4070 "ugen_ds_close: dev=0x%lx flag=0x%x", dev, flag);
4072 mutex_enter(&ugenp->ug_mutex);
4073 ugenp->ug_ds.dev_stat = UGEN_DEV_STATUS_INACTIVE;
4074 mutex_exit(&ugenp->ug_mutex);
4079 * request for devstat
4081 * Return values: errno
4083 static int
4084 ugen_ds_req(ugen_state_t *ugenp, struct buf *bp)
4086 int len = min(sizeof (ugenp->ug_ds.dev_state), bp->b_bcount);
4088 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4089 "ugen_ds_req: bp=0x%p", (void *)bp);
4091 mutex_enter(&ugenp->ug_mutex);
4092 if ((ugenp->ug_ds.dev_oflag & (FNDELAY | FNONBLOCK)) == 0) {
4093 while ((ugenp->ug_ds.dev_stat &
4094 UGEN_DEV_STATUS_CHANGED) == 0) {
4095 if (cv_wait_sig(&ugenp->ug_ds.dev_wait_cv,
4096 &ugenp->ug_mutex) <= 0) {
4097 mutex_exit(&ugenp->ug_mutex);
4099 return (EINTR);
4102 } else if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) ==
4103 0) {
4104 bp->b_resid = bp->b_bcount;
4105 mutex_exit(&ugenp->ug_mutex);
4107 return (0);
4110 ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_CHANGED;
4111 switch (ugenp->ug_dev_state) {
4112 case USB_DEV_ONLINE:
4113 ugenp->ug_ds.dev_state = USB_DEV_STAT_ONLINE;
4115 break;
4116 case USB_DEV_DISCONNECTED:
4117 ugenp->ug_ds.dev_state = USB_DEV_STAT_DISCONNECTED;
4119 break;
4120 case USB_DEV_SUSPENDED:
4121 case USB_UGEN_DEV_UNAVAILABLE_RESUME:
4122 ugenp->ug_ds.dev_state = USB_DEV_STAT_RESUMED;
4124 break;
4125 case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
4126 default:
4127 ugenp->ug_ds.dev_state = USB_DEV_STAT_UNAVAILABLE;
4129 break;
4132 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4133 "ugen_ds_req: dev_state=0x%x dev_stat=0x%x",
4134 ugenp->ug_dev_state, ugenp->ug_ds.dev_stat);
4136 bcopy(&ugenp->ug_ds.dev_state, bp->b_un.b_addr, len);
4137 bp->b_resid = bp->b_bcount - len;
4139 mutex_exit(&ugenp->ug_mutex);
4141 return (0);
4145 static void
4146 ugen_ds_change(ugen_state_t *ugenp)
4148 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4149 "ugen_ds_change:");
4151 ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED;
4152 cv_signal(&ugenp->ug_ds.dev_wait_cv);
4157 * poll management
4159 static void
4160 ugen_ds_poll_wakeup(ugen_state_t *ugenp)
4162 USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4163 "ugen_ds_poll_wakeup:");
4165 if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_POLL_PENDING) {
4166 struct pollhead *phpp = &ugenp->ug_ds.dev_pollhead;
4167 ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_POLL_PENDING;
4168 mutex_exit(&ugenp->ug_mutex);
4169 pollwakeup(phpp, POLLIN);
4170 mutex_enter(&ugenp->ug_mutex);
4176 * minor node management:
4178 static int
4179 ugen_ds_minor_nodes_create(ugen_state_t *ugenp)
4181 char node_name[32];
4182 int vid = ugenp->ug_dev_data->dev_descr->idVendor;
4183 int pid = ugenp->ug_dev_data->dev_descr->idProduct;
4184 minor_t minor;
4185 int minor_index;
4186 int owns_device = (usb_owns_device(ugenp->ug_dip) ?
4187 UGEN_OWNS_DEVICE : 0);
4189 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4190 "ugen_ds_minor_nodes_create: idx shift=%d inst shift=%d",
4191 UGEN_MINOR_IDX_SHIFT(ugenp),
4192 UGEN_MINOR_INSTANCE_SHIFT(ugenp));
4194 if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
4195 USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4196 "instance number too high (%d)", ugenp->ug_instance);
4198 return (USB_FAILURE);
4201 /* create devstat minor node */
4202 if (owns_device) {
4203 (void) sprintf(node_name, "%x.%x.devstat", vid, pid);
4204 } else {
4205 (void) sprintf(node_name, "%x.%x.if%ddevstat", vid, pid,
4206 ugenp->ug_dev_data->dev_curr_if);
4209 minor_index = ugen_minor_index_create(ugenp,
4210 (UGEN_MINOR_DEV_STAT_NODE | owns_device) <<
4211 UGEN_MINOR_IDX_SHIFT(ugenp));
4213 if (minor_index < 0) {
4214 USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4215 "too many minor nodes");
4217 return (USB_FAILURE);
4219 minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
4220 ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
4222 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4223 "minor=0x%x minor_index=%d name=%s",
4224 minor, minor_index, node_name);
4226 ASSERT(minor < L_MAXMIN);
4228 if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
4229 S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
4231 return (USB_FAILURE);
4234 ugen_store_devt(ugenp, minor);
4236 return (USB_SUCCESS);
4241 * utility functions:
4243 * conversion from completion reason to USB_LC_STAT_*
4245 static struct ugen_cr2lcstat_entry {
4246 int cr;
4247 int lcstat;
4248 } ugen_cr2lcstat_table[] = {
4249 { USB_CR_OK, USB_LC_STAT_NOERROR },
4250 { USB_CR_CRC, USB_LC_STAT_CRC },
4251 { USB_CR_BITSTUFFING, USB_LC_STAT_BITSTUFFING },
4252 { USB_CR_DATA_TOGGLE_MM, USB_LC_STAT_DATA_TOGGLE_MM },
4253 { USB_CR_STALL, USB_LC_STAT_STALL },
4254 { USB_CR_DEV_NOT_RESP, USB_LC_STAT_DEV_NOT_RESP },
4255 { USB_CR_PID_CHECKFAILURE, USB_LC_STAT_PID_CHECKFAILURE },
4256 { USB_CR_UNEXP_PID, USB_LC_STAT_UNEXP_PID },
4257 { USB_CR_DATA_OVERRUN, USB_LC_STAT_DATA_OVERRUN },
4258 { USB_CR_DATA_UNDERRUN, USB_LC_STAT_DATA_UNDERRUN },
4259 { USB_CR_BUFFER_OVERRUN, USB_LC_STAT_BUFFER_OVERRUN },
4260 { USB_CR_BUFFER_UNDERRUN, USB_LC_STAT_BUFFER_UNDERRUN },
4261 { USB_CR_TIMEOUT, USB_LC_STAT_TIMEOUT },
4262 { USB_CR_NOT_ACCESSED, USB_LC_STAT_NOT_ACCESSED },
4263 { USB_CR_NO_RESOURCES, USB_LC_STAT_NO_BANDWIDTH },
4264 { USB_CR_UNSPECIFIED_ERR, USB_LC_STAT_UNSPECIFIED_ERR },
4265 { USB_CR_STOPPED_POLLING, USB_LC_STAT_HW_ERR },
4266 { USB_CR_PIPE_CLOSING, USB_LC_STAT_UNSPECIFIED_ERR },
4267 { USB_CR_PIPE_RESET, USB_LC_STAT_UNSPECIFIED_ERR },
4268 { USB_CR_NOT_SUPPORTED, USB_LC_STAT_UNSPECIFIED_ERR },
4269 { USB_CR_FLUSHED, USB_LC_STAT_UNSPECIFIED_ERR }
4272 #define UGEN_CR2LCSTAT_TABLE_SIZE (sizeof (ugen_cr2lcstat_table) / \
4273 sizeof (struct ugen_cr2lcstat_entry))
4274 static int
4275 ugen_cr2lcstat(int cr)
4277 int i;
4279 for (i = 0; i < UGEN_CR2LCSTAT_TABLE_SIZE; i++) {
4280 if (ugen_cr2lcstat_table[i].cr == cr) {
4282 return (ugen_cr2lcstat_table[i].lcstat);
4286 return (USB_LC_STAT_UNSPECIFIED_ERR);
4291 * create and lookup minor index
4293 static int
4294 ugen_minor_index_create(ugen_state_t *ugenp, ugen_minor_t minor)
4296 int i;
4298 /* check if already in the table */
4299 for (i = 1; i < ugenp->ug_minor_node_table_index; i++) {
4300 if (ugenp->ug_minor_node_table[i] == minor) {
4302 return (-1);
4305 if (ugenp->ug_minor_node_table_index <
4306 (ugenp->ug_minor_node_table_size/sizeof (ugen_minor_t))) {
4307 ugenp->ug_minor_node_table[ugenp->
4308 ug_minor_node_table_index] = minor;
4310 USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4311 "ugen_minor_index_create: %d: 0x%lx",
4312 ugenp->ug_minor_node_table_index,
4313 (unsigned long)minor);
4315 return (ugenp->ug_minor_node_table_index++);
4316 } else {
4318 return (-1);
4323 static ugen_minor_t
4324 ugen_devt2minor(ugen_state_t *ugenp, dev_t dev)
4326 USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4327 "ugen_devt2minor: minorindex=%lu, minor=0x%" PRIx64,
4328 UGEN_MINOR_GET_IDX(ugenp, dev),
4329 ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
4331 ASSERT(UGEN_MINOR_GET_IDX(ugenp, dev) <
4332 ugenp->ug_minor_node_table_index);
4334 return (ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
4338 static int
4339 ugen_is_valid_minor_node(ugen_state_t *ugenp, dev_t dev)
4341 int idx = UGEN_MINOR_GET_IDX(ugenp, dev);
4343 if ((idx < ugenp->ug_minor_node_table_index) &&
4344 (idx > 0)) {
4346 return (USB_SUCCESS);
4348 USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4349 "ugen_is_valid_minor_node: invalid minorindex=%d", idx);
4351 return (USB_FAILURE);
4355 static void
4356 ugen_minor_node_table_create(ugen_state_t *ugenp)
4358 size_t size = sizeof (ugen_minor_t) * UGEN_MINOR_IDX_LIMIT(ugenp);
4360 /* allocate the max table size needed, we reduce later */
4361 ugenp->ug_minor_node_table = kmem_zalloc(size, KM_SLEEP);
4362 ugenp->ug_minor_node_table_size = size;
4363 ugenp->ug_minor_node_table_index = 1;
4367 static void
4368 ugen_minor_node_table_shrink(ugen_state_t *ugenp)
4370 /* reduce the table size to save some memory */
4371 if (ugenp->ug_minor_node_table_index < UGEN_MINOR_IDX_LIMIT(ugenp)) {
4372 size_t newsize = sizeof (ugen_minor_t) *
4373 ugenp->ug_minor_node_table_index;
4374 ugen_minor_t *buf = kmem_zalloc(newsize, KM_SLEEP);
4376 bcopy(ugenp->ug_minor_node_table, buf, newsize);
4377 kmem_free(ugenp->ug_minor_node_table,
4378 ugenp->ug_minor_node_table_size);
4379 ugenp->ug_minor_node_table = buf;
4380 ugenp->ug_minor_node_table_size = newsize;
4385 static void
4386 ugen_minor_node_table_destroy(ugen_state_t *ugenp)
4388 if (ugenp->ug_minor_node_table) {
4389 kmem_free(ugenp->ug_minor_node_table,
4390 ugenp->ug_minor_node_table_size);
4395 static void
4396 ugen_check_mask(uint_t mask, uint_t *shift, uint_t *limit)
4398 uint_t i, j;
4400 for (i = 0; i < UGEN_MINOR_NODE_SIZE; i++) {
4401 if ((1 << i) & mask) {
4403 break;
4407 for (j = i; j < UGEN_MINOR_NODE_SIZE; j++) {
4408 if (((1 << j) & mask) == 0) {
4410 break;
4414 *limit = (i == j) ? 0 : 1 << (j - i);
4415 *shift = i;
4421 * power management:
4423 * ugen_pm_init:
4424 * Initialize power management and remote wakeup functionality.
4425 * No mutex is necessary in this function as it's called only by attach.
4427 static void
4428 ugen_pm_init(ugen_state_t *ugenp)
4430 dev_info_t *dip = ugenp->ug_dip;
4431 ugen_power_t *ugenpm;
4433 USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4434 "ugen_pm_init:");
4436 /* Allocate the state structure */
4437 ugenpm = kmem_zalloc(sizeof (ugen_power_t), KM_SLEEP);
4439 mutex_enter(&ugenp->ug_mutex);
4440 ugenp->ug_pm = ugenpm;
4441 ugenpm->pwr_wakeup_enabled = B_FALSE;
4442 ugenpm->pwr_current = USB_DEV_OS_FULL_PWR;
4443 mutex_exit(&ugenp->ug_mutex);
4446 * If remote wakeup is not available you may not want to do
4447 * power management.
4449 if (ugen_enable_pm || usb_handle_remote_wakeup(dip,
4450 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
4451 if (usb_create_pm_components(dip,
4452 &ugenpm->pwr_states) == USB_SUCCESS) {
4453 USB_DPRINTF_L4(UGEN_PRINT_PM,
4454 ugenp->ug_log_hdl,
4455 "ugen_pm_init: "
4456 "created PM components");
4458 mutex_enter(&ugenp->ug_mutex);
4459 ugenpm->pwr_wakeup_enabled = B_TRUE;
4460 mutex_exit(&ugenp->ug_mutex);
4462 if (pm_raise_power(dip, 0,
4463 USB_DEV_OS_FULL_PWR) != DDI_SUCCESS) {
4464 USB_DPRINTF_L2(UGEN_PRINT_PM,
4465 ugenp->ug_log_hdl,
4466 "ugen_pm_init: "
4467 "raising power failed");
4469 } else {
4470 USB_DPRINTF_L2(UGEN_PRINT_PM,
4471 ugenp->ug_log_hdl,
4472 "ugen_pm_init: "
4473 "create_pm_comps failed");
4475 } else {
4476 USB_DPRINTF_L2(UGEN_PRINT_PM,
4477 ugenp->ug_log_hdl, "ugen_pm_init: "
4478 "failure enabling remote wakeup");
4481 USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4482 "ugen_pm_init: end");
4487 * ugen_pm_destroy:
4488 * Shut down and destroy power management and remote wakeup functionality.
4490 static void
4491 ugen_pm_destroy(ugen_state_t *ugenp)
4493 dev_info_t *dip = ugenp->ug_dip;
4495 USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4496 "ugen_pm_destroy:");
4498 if (ugenp->ug_pm) {
4499 mutex_exit(&ugenp->ug_mutex);
4500 ugen_pm_busy_component(ugenp);
4501 mutex_enter(&ugenp->ug_mutex);
4503 if ((ugenp->ug_pm->pwr_wakeup_enabled) &&
4504 (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
4505 int rval;
4507 mutex_exit(&ugenp->ug_mutex);
4508 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
4510 if ((rval = usb_handle_remote_wakeup(dip,
4511 USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
4512 USB_DPRINTF_L4(UGEN_PRINT_PM,
4513 ugenp->ug_log_hdl, "ugen_pm_destroy: "
4514 "disabling rmt wakeup: rval=%d", rval);
4517 * Since remote wakeup is disabled now,
4518 * no one can raise power
4519 * and get to device once power is lowered here.
4521 } else {
4522 mutex_exit(&ugenp->ug_mutex);
4524 (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
4525 ugen_pm_idle_component(ugenp);
4527 mutex_enter(&ugenp->ug_mutex);
4528 kmem_free(ugenp->ug_pm, sizeof (ugen_power_t));
4529 ugenp->ug_pm = NULL;
4535 * ugen_power :
4536 * Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
4537 * usb_req_raise_power and usb_req_lower_power.
4539 /*ARGSUSED*/
4541 usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl, int comp, int level)
4543 ugen_power_t *pm;
4544 int rval = USB_FAILURE;
4545 usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
4546 (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
4547 ugen_state_t *ugenp;
4548 dev_info_t *dip;
4550 if (usb_ugen_hdl == NULL) {
4552 return (USB_FAILURE);
4555 ugenp = usb_ugen_hdl_impl->hdl_ugenp;
4556 dip = ugenp->ug_dip;
4558 if (ugenp->ug_pm == NULL) {
4560 return (USB_SUCCESS);
4563 USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4564 "usb_ugen_power: level=%d", level);
4566 (void) usb_serialize_access(ugenp->ug_ser_cookie,
4567 USB_WAIT, 0);
4569 * If we are disconnected/suspended, return success. Note that if we
4570 * return failure, bringing down the system will hang when
4571 * PM tries to power up all devices
4573 mutex_enter(&ugenp->ug_mutex);
4574 switch (ugenp->ug_dev_state) {
4575 case USB_DEV_ONLINE:
4577 break;
4578 case USB_DEV_DISCONNECTED:
4579 case USB_DEV_SUSPENDED:
4580 case USB_UGEN_DEV_UNAVAILABLE_RESUME:
4581 case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
4582 default:
4583 USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4584 "ugen_power: disconnected/suspended "
4585 "dev_state=%d", ugenp->ug_dev_state);
4586 rval = USB_SUCCESS;
4588 goto done;
4591 pm = ugenp->ug_pm;
4593 /* Check if we are transitioning to a legal power level */
4594 if (USB_DEV_PWRSTATE_OK(pm->pwr_states, level)) {
4595 USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4596 "ugen_power: illegal power level=%d "
4597 "pwr_states: 0x%x", level, pm->pwr_states);
4599 goto done;
4602 switch (level) {
4603 case USB_DEV_OS_PWR_OFF :
4604 switch (ugenp->ug_dev_state) {
4605 case USB_DEV_ONLINE:
4606 /* Deny the powerdown request if the device is busy */
4607 if (ugenp->ug_pm->pwr_busy != 0) {
4609 break;
4611 ASSERT(ugenp->ug_open_count == 0);
4612 ASSERT(ugenp->ug_pending_cmds == 0);
4613 ugenp->ug_pm->pwr_current = USB_DEV_OS_PWR_OFF;
4614 mutex_exit(&ugenp->ug_mutex);
4616 /* Issue USB D3 command to the device here */
4617 rval = usb_set_device_pwrlvl3(dip);
4618 mutex_enter(&ugenp->ug_mutex);
4620 break;
4621 default:
4622 rval = USB_SUCCESS;
4624 break;
4626 break;
4627 case USB_DEV_OS_FULL_PWR :
4629 * PM framework tries to put us in full power during system
4630 * shutdown.
4632 switch (ugenp->ug_dev_state) {
4633 case USB_UGEN_DEV_UNAVAILABLE_RESUME:
4634 case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
4636 break;
4637 default:
4638 ugenp->ug_dev_state = USB_DEV_ONLINE;
4640 /* wakeup devstat reads and polls */
4641 ugen_ds_change(ugenp);
4642 ugen_ds_poll_wakeup(ugenp);
4644 break;
4646 ugenp->ug_pm->pwr_current = USB_DEV_OS_FULL_PWR;
4647 mutex_exit(&ugenp->ug_mutex);
4648 rval = usb_set_device_pwrlvl0(dip);
4649 mutex_enter(&ugenp->ug_mutex);
4651 break;
4652 default:
4653 /* Levels 1 and 2 are not supported to keep it simple. */
4654 USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
4655 "ugen_power: power level %d not supported", level);
4657 break;
4659 done:
4660 mutex_exit(&ugenp->ug_mutex);
4661 usb_release_access(ugenp->ug_ser_cookie);
4663 return (rval);
4667 static void
4668 ugen_pm_busy_component(ugen_state_t *ugen_statep)
4670 ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
4672 if (ugen_statep->ug_pm != NULL) {
4673 mutex_enter(&ugen_statep->ug_mutex);
4674 ugen_statep->ug_pm->pwr_busy++;
4676 USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
4677 "ugen_pm_busy_component: %d", ugen_statep->ug_pm->pwr_busy);
4679 mutex_exit(&ugen_statep->ug_mutex);
4680 if (pm_busy_component(ugen_statep->ug_dip, 0) != DDI_SUCCESS) {
4681 mutex_enter(&ugen_statep->ug_mutex);
4682 ugen_statep->ug_pm->pwr_busy--;
4684 USB_DPRINTF_L2(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
4685 "ugen_pm_busy_component failed: %d",
4686 ugen_statep->ug_pm->pwr_busy);
4688 mutex_exit(&ugen_statep->ug_mutex);
4694 static void
4695 ugen_pm_idle_component(ugen_state_t *ugen_statep)
4697 ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
4699 if (ugen_statep->ug_pm != NULL) {
4700 if (pm_idle_component(ugen_statep->ug_dip, 0) == DDI_SUCCESS) {
4701 mutex_enter(&ugen_statep->ug_mutex);
4702 ASSERT(ugen_statep->ug_pm->pwr_busy > 0);
4703 ugen_statep->ug_pm->pwr_busy--;
4705 USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
4706 "ugen_pm_idle_component: %d",
4707 ugen_statep->ug_pm->pwr_busy);
4709 mutex_exit(&ugen_statep->ug_mutex);
4716 * devt lookup support
4717 * In ugen_strategy and ugen_minphys, we only have the devt and need
4718 * the ugen_state pointer. Since we don't know instance mask, we can't
4719 * easily derive a softstate pointer. Therefore, we use a list
4721 static void
4722 ugen_store_devt(ugen_state_t *ugenp, minor_t minor)
4724 ugen_devt_list_entry_t *e = kmem_zalloc(
4725 sizeof (ugen_devt_list_entry_t), KM_SLEEP);
4726 ugen_devt_list_entry_t *t;
4728 mutex_enter(&ugen_devt_list_mutex);
4729 e->list_dev = makedevice(ddi_driver_major(ugenp->ug_dip), minor);
4730 e->list_state = ugenp;
4732 t = ugen_devt_list.list_next;
4734 /* check if the entry is already in the list */
4735 while (t) {
4736 ASSERT(t->list_dev != e->list_dev);
4737 t = t->list_next;
4740 /* add to the head of the list */
4741 e->list_next = ugen_devt_list.list_next;
4742 if (ugen_devt_list.list_next) {
4743 ugen_devt_list.list_next->list_prev = e;
4745 ugen_devt_list.list_next = e;
4746 mutex_exit(&ugen_devt_list_mutex);
4750 static ugen_state_t *
4751 ugen_devt2state(dev_t dev)
4753 ugen_devt_list_entry_t *t;
4754 ugen_state_t *ugenp = NULL;
4755 int index, count;
4757 mutex_enter(&ugen_devt_list_mutex);
4759 for (index = ugen_devt_cache_index, count = 0;
4760 count < UGEN_DEVT_CACHE_SIZE; count++) {
4761 if (ugen_devt_cache[index].cache_dev == dev) {
4762 ugen_devt_cache[index].cache_hit++;
4763 ugenp = ugen_devt_cache[index].cache_state;
4765 mutex_exit(&ugen_devt_list_mutex);
4767 return (ugenp);
4769 index++;
4770 index %= UGEN_DEVT_CACHE_SIZE;
4773 t = ugen_devt_list.list_next;
4775 while (t) {
4776 if (t->list_dev == dev) {
4777 ugenp = t->list_state;
4778 ugen_devt_cache_index++;
4779 ugen_devt_cache_index %= UGEN_DEVT_CACHE_SIZE;
4780 ugen_devt_cache[ugen_devt_cache_index].cache_dev = dev;
4781 ugen_devt_cache[ugen_devt_cache_index].cache_state =
4782 ugenp;
4783 mutex_exit(&ugen_devt_list_mutex);
4785 return (ugenp);
4787 t = t->list_next;
4789 mutex_exit(&ugen_devt_list_mutex);
4791 return (ugenp);
4795 static void
4796 ugen_free_devt(ugen_state_t *ugenp)
4798 ugen_devt_list_entry_t *e, *next, *prev;
4799 major_t major = ddi_driver_major(ugenp->ug_dip);
4800 int instance = ddi_get_instance(ugenp->ug_dip);
4802 mutex_enter(&ugen_devt_list_mutex);
4803 prev = &ugen_devt_list;
4804 for (e = prev->list_next; e != 0; e = next) {
4805 int i = (getminor(e->list_dev) &
4806 ugenp->ug_hdl->hdl_minor_node_instance_mask) >>
4807 ugenp->ug_hdl->hdl_minor_node_instance_shift;
4808 int m = getmajor(e->list_dev);
4810 next = e->list_next;
4812 if ((i == instance) && (m == major)) {
4813 prev->list_next = e->list_next;
4814 if (e->list_next) {
4815 e->list_next->list_prev = prev;
4817 kmem_free(e, sizeof (ugen_devt_list_entry_t));
4818 } else {
4819 prev = e;
4823 bzero(ugen_devt_cache, sizeof (ugen_devt_cache));
4824 ugen_devt_cache_index = 0;
4825 mutex_exit(&ugen_devt_list_mutex);