10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
53255Slg150142 * Common Development and Distribution License (the "License").
63255Slg150142 * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*11066Srafael.vanoni@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate * EHCI Host Controller Driver (EHCI)
290Sstevel@tonic-gate *
300Sstevel@tonic-gate * The EHCI driver is a software driver which interfaces to the Universal
310Sstevel@tonic-gate * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
320Sstevel@tonic-gate * the Host Controller is defined by the EHCI Host Controller Interface.
330Sstevel@tonic-gate *
340Sstevel@tonic-gate * This module contains the EHCI driver isochronous code, which handles all
350Sstevel@tonic-gate * Checking of status of USB transfers, error recovery and callbacks.
360Sstevel@tonic-gate */
370Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
380Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_xfer.h>
390Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_util.h>
400Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_isoch.h>
410Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_isoch_util.h>
427492SZhigang.Lu@Sun.COM #include <sys/strsun.h>
430Sstevel@tonic-gate
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate * Isochronous initialization functions
460Sstevel@tonic-gate */
470Sstevel@tonic-gate int ehci_isoc_init(
480Sstevel@tonic-gate ehci_state_t *ehcip);
490Sstevel@tonic-gate void ehci_isoc_cleanup(
500Sstevel@tonic-gate ehci_state_t *ehcip);
510Sstevel@tonic-gate void ehci_isoc_pipe_cleanup(
520Sstevel@tonic-gate ehci_state_t *ehcip,
530Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
540Sstevel@tonic-gate static void ehci_wait_for_isoc_completion(
550Sstevel@tonic-gate ehci_state_t *ehcip,
560Sstevel@tonic-gate ehci_pipe_private_t *pp);
570Sstevel@tonic-gate
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate * Isochronous request functions
600Sstevel@tonic-gate */
610Sstevel@tonic-gate ehci_isoc_xwrapper_t *ehci_allocate_isoc_resources(
620Sstevel@tonic-gate ehci_state_t *ehcip,
630Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
640Sstevel@tonic-gate usb_isoc_req_t *isoc_reqp,
650Sstevel@tonic-gate usb_flags_t usb_flags);
660Sstevel@tonic-gate int ehci_insert_isoc_req(
670Sstevel@tonic-gate ehci_state_t *ehcip,
680Sstevel@tonic-gate ehci_pipe_private_t *pp,
690Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
700Sstevel@tonic-gate usb_flags_t usb_flags);
713255Slg150142 static int ehci_insert_itd_req(
723255Slg150142 ehci_state_t *ehcip,
733255Slg150142 ehci_pipe_private_t *pp,
743255Slg150142 ehci_isoc_xwrapper_t *itw,
753255Slg150142 usb_flags_t usb_flags);
760Sstevel@tonic-gate static int ehci_insert_sitd_req(
770Sstevel@tonic-gate ehci_state_t *ehcip,
780Sstevel@tonic-gate ehci_pipe_private_t *pp,
790Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
800Sstevel@tonic-gate usb_flags_t usb_flags);
810Sstevel@tonic-gate static void ehci_remove_isoc_itds(
820Sstevel@tonic-gate ehci_state_t *ehcip,
830Sstevel@tonic-gate ehci_pipe_private_t *pp);
840Sstevel@tonic-gate static void ehci_mark_reclaim_isoc(
850Sstevel@tonic-gate ehci_state_t *ehcip,
860Sstevel@tonic-gate ehci_pipe_private_t *pp);
870Sstevel@tonic-gate static void ehci_reclaim_isoc(
880Sstevel@tonic-gate ehci_state_t *ehcip,
890Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
900Sstevel@tonic-gate ehci_itd_t *itd,
910Sstevel@tonic-gate ehci_pipe_private_t *pp);
920Sstevel@tonic-gate int ehci_start_isoc_polling(
930Sstevel@tonic-gate ehci_state_t *ehcip,
940Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
950Sstevel@tonic-gate usb_flags_t flags);
960Sstevel@tonic-gate
970Sstevel@tonic-gate /*
980Sstevel@tonic-gate * Isochronronous handling functions.
990Sstevel@tonic-gate */
1000Sstevel@tonic-gate void ehci_traverse_active_isoc_list(
1010Sstevel@tonic-gate ehci_state_t *ehcip);
1020Sstevel@tonic-gate static void ehci_handle_isoc(
1030Sstevel@tonic-gate ehci_state_t *ehcip,
1040Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
1050Sstevel@tonic-gate ehci_itd_t *itd);
1060Sstevel@tonic-gate static void ehci_handle_itd(
1070Sstevel@tonic-gate ehci_state_t *ehcip,
1080Sstevel@tonic-gate ehci_pipe_private_t *pp,
1090Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
1100Sstevel@tonic-gate ehci_itd_t *itd,
1110Sstevel@tonic-gate void *tw_handle_callback_value);
1120Sstevel@tonic-gate static void ehci_sendup_itd_message(
1130Sstevel@tonic-gate ehci_state_t *ehcip,
1140Sstevel@tonic-gate ehci_pipe_private_t *pp,
1150Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
1160Sstevel@tonic-gate ehci_itd_t *td,
1170Sstevel@tonic-gate usb_cr_t error);
1180Sstevel@tonic-gate void ehci_hcdi_isoc_callback(
1190Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1200Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
1210Sstevel@tonic-gate usb_cr_t completion_reason);
1220Sstevel@tonic-gate
1230Sstevel@tonic-gate
1240Sstevel@tonic-gate /*
1250Sstevel@tonic-gate * Isochronous initialization functions
1260Sstevel@tonic-gate */
1270Sstevel@tonic-gate /*
1280Sstevel@tonic-gate * Initialize all the needed resources needed by isochronous pipes.
1290Sstevel@tonic-gate */
1300Sstevel@tonic-gate int
ehci_isoc_init(ehci_state_t * ehcip)1310Sstevel@tonic-gate ehci_isoc_init(
1320Sstevel@tonic-gate ehci_state_t *ehcip)
1330Sstevel@tonic-gate {
1340Sstevel@tonic-gate return (ehci_allocate_isoc_pools(ehcip));
1350Sstevel@tonic-gate }
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate
1380Sstevel@tonic-gate /*
1390Sstevel@tonic-gate * Cleanup isochronous resources.
1400Sstevel@tonic-gate */
1410Sstevel@tonic-gate void
ehci_isoc_cleanup(ehci_state_t * ehcip)1420Sstevel@tonic-gate ehci_isoc_cleanup(
1430Sstevel@tonic-gate ehci_state_t *ehcip)
1440Sstevel@tonic-gate {
1450Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw;
1460Sstevel@tonic-gate ehci_pipe_private_t *pp;
1470Sstevel@tonic-gate ehci_itd_t *itd;
1480Sstevel@tonic-gate int i, ctrl, rval;
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate /* Free all the buffers */
1510Sstevel@tonic-gate if (ehcip->ehci_itd_pool_addr && ehcip->ehci_itd_pool_mem_handle) {
1520Sstevel@tonic-gate for (i = 0; i < ehci_get_itd_pool_size(); i ++) {
1530Sstevel@tonic-gate itd = &ehcip->ehci_itd_pool_addr[i];
1540Sstevel@tonic-gate ctrl = Get_ITD(ehcip->
1550Sstevel@tonic-gate ehci_itd_pool_addr[i].itd_state);
1560Sstevel@tonic-gate
1570Sstevel@tonic-gate if ((ctrl != EHCI_ITD_FREE) &&
1580Sstevel@tonic-gate (ctrl != EHCI_ITD_DUMMY) &&
1590Sstevel@tonic-gate (itd->itd_trans_wrapper)) {
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate itw = (ehci_isoc_xwrapper_t *)
1645653Slc152243 EHCI_LOOKUP_ID((uint32_t)
1655653Slc152243 Get_ITD(itd->itd_trans_wrapper));
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate /* Obtain the pipe private structure */
1680Sstevel@tonic-gate pp = itw->itw_pipe_private;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate ehci_deallocate_itd(ehcip, itw, itd);
1710Sstevel@tonic-gate ehci_deallocate_itw(ehcip, pp, itw);
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate /*
1780Sstevel@tonic-gate * If EHCI_ITD_POOL_BOUND flag is set, then unbind
1790Sstevel@tonic-gate * the handle for ITD pools.
1800Sstevel@tonic-gate */
1810Sstevel@tonic-gate if ((ehcip->ehci_dma_addr_bind_flag &
1820Sstevel@tonic-gate EHCI_ITD_POOL_BOUND) == EHCI_ITD_POOL_BOUND) {
1830Sstevel@tonic-gate
1840Sstevel@tonic-gate rval = ddi_dma_unbind_handle(
1850Sstevel@tonic-gate ehcip->ehci_itd_pool_dma_handle);
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate ASSERT(rval == DDI_SUCCESS);
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate ddi_dma_mem_free(&ehcip->ehci_itd_pool_mem_handle);
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /* Free the ITD pool */
1930Sstevel@tonic-gate if (ehcip->ehci_itd_pool_dma_handle) {
1940Sstevel@tonic-gate ddi_dma_free_handle(&ehcip->ehci_itd_pool_dma_handle);
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate /*
2000Sstevel@tonic-gate * ehci_isoc_pipe_cleanup
2010Sstevel@tonic-gate *
2020Sstevel@tonic-gate * Cleanup ehci isoc pipes.
2030Sstevel@tonic-gate */
ehci_isoc_pipe_cleanup(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)2040Sstevel@tonic-gate void ehci_isoc_pipe_cleanup(
2050Sstevel@tonic-gate ehci_state_t *ehcip,
2060Sstevel@tonic-gate usba_pipe_handle_data_t *ph) {
2070Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
2080Sstevel@tonic-gate uint_t pipe_state = pp->pp_state;
2090Sstevel@tonic-gate usb_cr_t completion_reason;
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
2120Sstevel@tonic-gate "ehci_isoc_pipe_cleanup: ph = 0x%p", (void *)ph);
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate /* Stop all further processing */
2170Sstevel@tonic-gate ehci_mark_reclaim_isoc(ehcip, pp);
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate * Wait for processing all completed transfers
2210Sstevel@tonic-gate * and send result upstream/
2220Sstevel@tonic-gate */
2230Sstevel@tonic-gate ehci_wait_for_isoc_completion(ehcip, pp);
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate /* Go ahead and remove all remaining itds if there are any */
2260Sstevel@tonic-gate ehci_remove_isoc_itds(ehcip, pp);
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate switch (pipe_state) {
2290Sstevel@tonic-gate case EHCI_PIPE_STATE_CLOSE:
2300Sstevel@tonic-gate completion_reason = USB_CR_PIPE_CLOSING;
2310Sstevel@tonic-gate break;
2320Sstevel@tonic-gate case EHCI_PIPE_STATE_RESET:
2330Sstevel@tonic-gate case EHCI_PIPE_STATE_STOP_POLLING:
2340Sstevel@tonic-gate /* Set completion reason */
2350Sstevel@tonic-gate completion_reason = (pipe_state ==
2360Sstevel@tonic-gate EHCI_PIPE_STATE_RESET) ?
2370Sstevel@tonic-gate USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate /* Set pipe state to idle */
2400Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_IDLE;
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate break;
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate /*
2460Sstevel@tonic-gate * Do the callback for the original client
2470Sstevel@tonic-gate * periodic IN request.
2480Sstevel@tonic-gate */
2490Sstevel@tonic-gate if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
2500Sstevel@tonic-gate USB_EP_DIR_IN) {
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate ehci_do_client_periodic_in_req_callback(
2530Sstevel@tonic-gate ehcip, pp, completion_reason);
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate
2580Sstevel@tonic-gate /*
2590Sstevel@tonic-gate * ehci_wait_for_transfers_completion:
2600Sstevel@tonic-gate *
2610Sstevel@tonic-gate * Wait for processing all completed transfers and to send results
2620Sstevel@tonic-gate * to upstream.
2630Sstevel@tonic-gate */
2640Sstevel@tonic-gate static void
ehci_wait_for_isoc_completion(ehci_state_t * ehcip,ehci_pipe_private_t * pp)2650Sstevel@tonic-gate ehci_wait_for_isoc_completion(
2660Sstevel@tonic-gate ehci_state_t *ehcip,
2670Sstevel@tonic-gate ehci_pipe_private_t *pp)
2680Sstevel@tonic-gate {
2690Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate if (pp->pp_itw_head == NULL) {
2720Sstevel@tonic-gate
2730Sstevel@tonic-gate return;
2740Sstevel@tonic-gate }
2750Sstevel@tonic-gate
276*11066Srafael.vanoni@sun.com (void) cv_reltimedwait(&pp->pp_xfer_cmpl_cv, &ehcip->ehci_int_mutex,
277*11066Srafael.vanoni@sun.com drv_usectohz(EHCI_XFER_CMPL_TIMEWAIT * 1000000), TR_CLOCK_TICK);
2780Sstevel@tonic-gate
2790Sstevel@tonic-gate if (pp->pp_itw_head) {
2800Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
2810Sstevel@tonic-gate "ehci_wait_for_isoc_completion: "
2820Sstevel@tonic-gate "No transfers completion confirmation received");
2830Sstevel@tonic-gate }
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate
2860Sstevel@tonic-gate
2870Sstevel@tonic-gate /*
2880Sstevel@tonic-gate * Isochronous request functions
2890Sstevel@tonic-gate */
2900Sstevel@tonic-gate /*
2910Sstevel@tonic-gate * ehci_allocate_isoc_resources:
2920Sstevel@tonic-gate *
2930Sstevel@tonic-gate * Calculates the number of tds necessary for a isoch transfer, and
2940Sstevel@tonic-gate * allocates all the necessary resources.
2950Sstevel@tonic-gate *
2960Sstevel@tonic-gate * Returns NULL if there is insufficient resources otherwise ITW.
2970Sstevel@tonic-gate */
2980Sstevel@tonic-gate ehci_isoc_xwrapper_t *
ehci_allocate_isoc_resources(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_isoc_req_t * isoc_reqp,usb_flags_t usb_flags)2990Sstevel@tonic-gate ehci_allocate_isoc_resources(
3000Sstevel@tonic-gate ehci_state_t *ehcip,
3010Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
3020Sstevel@tonic-gate usb_isoc_req_t *isoc_reqp,
3030Sstevel@tonic-gate usb_flags_t usb_flags)
3040Sstevel@tonic-gate {
3050Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
3063255Slg150142 int pipe_dir, i;
3070Sstevel@tonic-gate uint_t max_ep_pkt_size, max_isoc_xfer_size;
3080Sstevel@tonic-gate usb_isoc_pkt_descr_t *isoc_pkt_descr;
3093255Slg150142 size_t isoc_pkt_count, isoc_pkts_length;
3105653Slc152243 size_t itw_xfer_size = 0;
3110Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw;
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3140Sstevel@tonic-gate "ehci_allocate_isoc_resources: flags = 0x%x", usb_flags);
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3170Sstevel@tonic-gate
3180Sstevel@tonic-gate /*
3190Sstevel@tonic-gate * Check whether pipe is in halted state.
3200Sstevel@tonic-gate */
3210Sstevel@tonic-gate if (pp->pp_state == EHCI_PIPE_STATE_ERROR) {
3220Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3230Sstevel@tonic-gate "ehci_allocate_isoc_resources:"
3240Sstevel@tonic-gate "Pipe is in error state, need pipe reset to continue");
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate return (NULL);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate /* Calculate the maximum isochronous transfer size we allow */
3305653Slc152243 max_ep_pkt_size = (ph->p_ep.wMaxPacketSize &
3315653Slc152243 EHCI_ITD_CTRL_MAX_PACKET_MASK) *
3325653Slc152243 CalculateITDMultiField(ph->p_ep.wMaxPacketSize);
3335653Slc152243
3340Sstevel@tonic-gate max_isoc_xfer_size = EHCI_MAX_ISOC_PKTS_PER_XFER * max_ep_pkt_size;
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate /* Get the packet descriptor and number of packets to send */
3370Sstevel@tonic-gate if (isoc_reqp) {
3380Sstevel@tonic-gate isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
3390Sstevel@tonic-gate isoc_pkt_count = isoc_reqp->isoc_pkts_count;
3403255Slg150142 isoc_pkts_length = isoc_reqp->isoc_pkts_length;
3410Sstevel@tonic-gate } else {
3420Sstevel@tonic-gate isoc_pkt_descr = ((usb_isoc_req_t *)
3430Sstevel@tonic-gate pp->pp_client_periodic_in_reqp)->isoc_pkt_descr;
3440Sstevel@tonic-gate
3450Sstevel@tonic-gate isoc_pkt_count = ((usb_isoc_req_t *)
3460Sstevel@tonic-gate pp->pp_client_periodic_in_reqp)->isoc_pkts_count;
3473255Slg150142
3483255Slg150142 isoc_pkts_length = ((usb_isoc_req_t *)
3490Sstevel@tonic-gate pp->pp_client_periodic_in_reqp)->isoc_pkts_length;
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate
3520Sstevel@tonic-gate /* Calculate the size of the transfer. */
3530Sstevel@tonic-gate pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
3540Sstevel@tonic-gate if (pipe_dir == USB_EP_DIR_IN) {
3553255Slg150142 for (i = 0; i < isoc_pkt_count; i++) {
3565653Slc152243 /*
3575653Slc152243 * isoc_pkt_length is used as Transaction Length and
3585653Slc152243 * according to EHCI spec Table 3-3, the maximum value
3595653Slc152243 * allowed is 3072
3605653Slc152243 */
3615653Slc152243 if (isoc_pkt_descr->isoc_pkt_length > 3072) {
3625653Slc152243
3635653Slc152243 return (NULL);
3645653Slc152243 }
3655653Slc152243
3663255Slg150142 itw_xfer_size += isoc_pkt_descr->isoc_pkt_length;
3675653Slc152243
3683255Slg150142 isoc_pkt_descr++;
3693255Slg150142 }
3703255Slg150142
3713255Slg150142 if ((isoc_pkts_length) &&
3725653Slc152243 (isoc_pkts_length != itw_xfer_size)) {
3733255Slg150142
3743255Slg150142 USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3753255Slg150142 "ehci_allocate_isoc_resources: "
3766898Sfb209375 "isoc_pkts_length 0x%lx is not equal to the sum of "
3776898Sfb209375 "all pkt lengths 0x%lx in an isoc request",
3783255Slg150142 isoc_pkts_length, itw_xfer_size);
3793255Slg150142
3803255Slg150142 return (NULL);
3813255Slg150142 }
3823255Slg150142
3830Sstevel@tonic-gate } else {
3840Sstevel@tonic-gate ASSERT(isoc_reqp != NULL);
3857492SZhigang.Lu@Sun.COM itw_xfer_size = MBLKL(isoc_reqp->isoc_data);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate
3880Sstevel@tonic-gate /* Check the size of isochronous request */
3890Sstevel@tonic-gate if (itw_xfer_size > max_isoc_xfer_size) {
3900Sstevel@tonic-gate
3910Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3923255Slg150142 "ehci_allocate_isoc_resources: Maximum isoc request "
3936898Sfb209375 "size 0x%x Given isoc request size 0x%lx",
3940Sstevel@tonic-gate max_isoc_xfer_size, itw_xfer_size);
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate return (NULL);
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
4006898Sfb209375 "ehci_allocate_isoc_resources: length = 0x%lx", itw_xfer_size);
4010Sstevel@tonic-gate
4020Sstevel@tonic-gate /* Allocate the itw for this request */
4030Sstevel@tonic-gate if ((itw = ehci_allocate_itw_resources(ehcip, pp, itw_xfer_size,
4040Sstevel@tonic-gate usb_flags, isoc_pkt_count)) == NULL) {
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate return (NULL);
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate itw->itw_handle_callback_value = NULL;
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate if (pipe_dir == USB_EP_DIR_IN) {
4120Sstevel@tonic-gate if (ehci_allocate_isoc_in_resource(ehcip, pp, itw, usb_flags) !=
4130Sstevel@tonic-gate USB_SUCCESS) {
4140Sstevel@tonic-gate
4150Sstevel@tonic-gate ehci_deallocate_itw(ehcip, pp, itw);
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate return (NULL);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate } else {
4200Sstevel@tonic-gate if (itw->itw_length) {
4210Sstevel@tonic-gate ASSERT(isoc_reqp->isoc_data != NULL);
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate /* Copy the data into the buffer */
4240Sstevel@tonic-gate bcopy(isoc_reqp->isoc_data->b_rptr,
4250Sstevel@tonic-gate itw->itw_buf, itw->itw_length);
4260Sstevel@tonic-gate
4270Sstevel@tonic-gate Sync_IO_Buffer_for_device(itw->itw_dmahandle,
4280Sstevel@tonic-gate itw->itw_length);
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate itw->itw_curr_xfer_reqp = isoc_reqp;
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate
4330Sstevel@tonic-gate return (itw);
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate
4370Sstevel@tonic-gate /*
4380Sstevel@tonic-gate * ehci_insert_isoc_req:
4390Sstevel@tonic-gate *
4400Sstevel@tonic-gate * Insert an isochronous request into the Host Controller's
4410Sstevel@tonic-gate * isochronous list.
4420Sstevel@tonic-gate */
4430Sstevel@tonic-gate int
ehci_insert_isoc_req(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_isoc_xwrapper_t * itw,usb_flags_t usb_flags)4440Sstevel@tonic-gate ehci_insert_isoc_req(
4450Sstevel@tonic-gate ehci_state_t *ehcip,
4460Sstevel@tonic-gate ehci_pipe_private_t *pp,
4470Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
4480Sstevel@tonic-gate usb_flags_t usb_flags)
4490Sstevel@tonic-gate {
4500Sstevel@tonic-gate int error;
4513255Slg150142 ehci_itd_t *new_itd, *temp_itd;
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
4540Sstevel@tonic-gate "ehci_insert_isoc_req: flags = 0x%x port status = 0x%x",
4550Sstevel@tonic-gate usb_flags, itw->itw_port_status);
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
4580Sstevel@tonic-gate
4590Sstevel@tonic-gate ASSERT(itw->itw_curr_xfer_reqp != NULL);
4600Sstevel@tonic-gate ASSERT(itw->itw_curr_xfer_reqp->isoc_pkt_descr != NULL);
4610Sstevel@tonic-gate
4620Sstevel@tonic-gate /*
4630Sstevel@tonic-gate * Save address of first usb isochronous packet descriptor.
4640Sstevel@tonic-gate */
4650Sstevel@tonic-gate itw->itw_curr_isoc_pktp = itw->itw_curr_xfer_reqp->isoc_pkt_descr;
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate if (itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
4683255Slg150142 error = ehci_insert_itd_req(ehcip, pp, itw, usb_flags);
4690Sstevel@tonic-gate } else {
4700Sstevel@tonic-gate error = ehci_insert_sitd_req(ehcip, pp, itw, usb_flags);
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate
4733255Slg150142 /* Either all the isocs will be added or none of them will */
4743255Slg150142 error = ehci_insert_isoc_to_pfl(ehcip, pp, itw);
4753255Slg150142
4763255Slg150142 if (error != USB_SUCCESS) {
4773255Slg150142 /*
4783255Slg150142 * Deallocate all the ITDs, otherwise they will be
4793255Slg150142 * lost forever.
4803255Slg150142 */
4813255Slg150142 new_itd = itw->itw_itd_head;
4823255Slg150142 while (new_itd) {
4833255Slg150142 temp_itd = ehci_itd_iommu_to_cpu(ehcip,
4843255Slg150142 Get_ITD(new_itd->itd_itw_next_itd));
4853255Slg150142 ehci_deallocate_itd(ehcip, itw, new_itd);
4863255Slg150142 new_itd = temp_itd;
4873255Slg150142 }
4883255Slg150142 if ((itw->itw_direction == USB_EP_DIR_IN)) {
4893255Slg150142 ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
4903255Slg150142
4913255Slg150142 if (pp->pp_cur_periodic_req_cnt) {
4923255Slg150142 /*
4933255Slg150142 * Set pipe state to stop polling and
4943255Slg150142 * error to no resource. Don't insert
4953255Slg150142 * any more isoch polling requests.
4963255Slg150142 */
4973255Slg150142 pp->pp_state =
4983255Slg150142 EHCI_PIPE_STATE_STOP_POLLING;
4993255Slg150142 pp->pp_error = error;
5003255Slg150142 } else {
5013255Slg150142 /* Set periodic in pipe state to idle */
5023255Slg150142 pp->pp_state = EHCI_PIPE_STATE_IDLE;
5033255Slg150142 }
5043255Slg150142
5053255Slg150142 return (error);
5063255Slg150142 }
5073255Slg150142
5083255Slg150142 /* Save how many packets and data actually went */
5093255Slg150142 itw->itw_num_itds = 0;
5103255Slg150142 itw->itw_length = 0;
5113255Slg150142 }
5123255Slg150142
5133255Slg150142 /*
5143255Slg150142 * Reset back to the address of first usb isochronous
5153255Slg150142 * packet descriptor.
5163255Slg150142 */
5173255Slg150142 itw->itw_curr_isoc_pktp = itw->itw_curr_xfer_reqp->isoc_pkt_descr;
5183255Slg150142
5193255Slg150142 /* Reset the CONTINUE flag */
5203255Slg150142 pp->pp_flag &= ~EHCI_ISOC_XFER_CONTINUE;
5213255Slg150142
5220Sstevel@tonic-gate return (error);
5230Sstevel@tonic-gate }
5240Sstevel@tonic-gate
5250Sstevel@tonic-gate
5260Sstevel@tonic-gate /*
5273255Slg150142 * ehci_insert_itd_req:
5283255Slg150142 *
5293255Slg150142 * Insert an ITD request into the Host Controller's isochronous list.
5303255Slg150142 */
5313255Slg150142 /* ARGSUSED */
5323255Slg150142 static int
ehci_insert_itd_req(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_isoc_xwrapper_t * itw,usb_flags_t usb_flags)5333255Slg150142 ehci_insert_itd_req(
5343255Slg150142 ehci_state_t *ehcip,
5353255Slg150142 ehci_pipe_private_t *pp,
5363255Slg150142 ehci_isoc_xwrapper_t *itw,
5373255Slg150142 usb_flags_t usb_flags)
5383255Slg150142 {
5393255Slg150142 usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
5403255Slg150142 usb_isoc_req_t *curr_isoc_reqp;
5413255Slg150142 usb_isoc_pkt_descr_t *curr_isoc_pkt_descr;
5423255Slg150142 size_t curr_isoc_xfer_offset;
5433255Slg150142 size_t isoc_pkt_length;
5443255Slg150142 uint_t count, xactcount;
5453255Slg150142 uint32_t xact_status;
5463255Slg150142 uint32_t page, pageselected;
5473255Slg150142 uint32_t buf[EHCI_ITD_BUFFER_LIST_SIZE];
5483255Slg150142 uint16_t index = 0;
5493255Slg150142 uint16_t multi = 0;
5503255Slg150142 ehci_itd_t *new_itd;
5513255Slg150142
5523255Slg150142 /*
5533255Slg150142 * Get the current isochronous request and packet
5543255Slg150142 * descriptor pointers.
5553255Slg150142 */
5563255Slg150142 curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
5573255Slg150142
5583255Slg150142 page = itw->itw_cookie.dmac_address;
5593255Slg150142 ASSERT((page % EHCI_4K_ALIGN) == 0);
5603255Slg150142
5613255Slg150142 USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
5626898Sfb209375 "ehci_insert_itd_req: itw_curr_xfer_reqp = 0x%p page = 0x%x,"
5636898Sfb209375 " pagesize = 0x%lx", (void *)itw->itw_curr_xfer_reqp, page,
5645653Slc152243 itw->itw_cookie.dmac_size);
5653255Slg150142
5663255Slg150142 /* Insert all the isochronous TDs */
5673255Slg150142 count = 0;
5683255Slg150142 curr_isoc_xfer_offset = 0;
5693255Slg150142
5703255Slg150142 while (count < curr_isoc_reqp->isoc_pkts_count) {
5713255Slg150142
5723255Slg150142 /* Grab a new itd */
5733255Slg150142 new_itd = itw->itw_itd_free_list;
5743255Slg150142
5753255Slg150142 ASSERT(new_itd != NULL);
5763255Slg150142
5773255Slg150142 itw->itw_itd_free_list = ehci_itd_iommu_to_cpu(ehcip,
5783255Slg150142 Get_ITD(new_itd->itd_link_ptr));
5793255Slg150142 Set_ITD(new_itd->itd_link_ptr, NULL);
5803255Slg150142
5813255Slg150142 bzero(buf, EHCI_ITD_BUFFER_LIST_SIZE * sizeof (uint32_t));
5823255Slg150142
5833255Slg150142 multi = CalculateITDMultiField(ph->p_ep.wMaxPacketSize);
5843255Slg150142
5853255Slg150142 if (multi > EHCI_ITD_CTRL_MULTI_MASK) {
5863255Slg150142 USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
5873255Slg150142 "ehci_insert_itd_req: Wrong multi value.");
5883255Slg150142
5893255Slg150142 return (USB_FAILURE);
5903255Slg150142 }
5913255Slg150142
5923255Slg150142 /* Fill 8 transaction for every iTD */
5933255Slg150142 for (xactcount = 0, pageselected = 0;
5943255Slg150142 xactcount < EHCI_ITD_CTRL_LIST_SIZE; xactcount++) {
5953255Slg150142
5963255Slg150142 curr_isoc_pkt_descr = itw->itw_curr_isoc_pktp;
5973255Slg150142
5985653Slc152243 isoc_pkt_length =
5995653Slc152243 curr_isoc_pkt_descr->isoc_pkt_length;
6003255Slg150142
6013255Slg150142 curr_isoc_pkt_descr->isoc_pkt_actual_length
6027492SZhigang.Lu@Sun.COM = (ushort_t)isoc_pkt_length;
6033255Slg150142
6043255Slg150142 xact_status = 0;
6053255Slg150142
6063255Slg150142 if (pageselected < EHCI_ITD_BUFFER_LIST_SIZE) {
6073255Slg150142
6083255Slg150142 buf[pageselected] |= page;
6093255Slg150142 } else {
6103255Slg150142 USB_DPRINTF_L2(PRINT_MASK_INTR,
6113255Slg150142 ehcip->ehci_log_hdl,
6123255Slg150142 "ehci_insert_itd_req: "
6133255Slg150142 "Error in buffer pointer.");
6143255Slg150142
6153255Slg150142 return (USB_FAILURE);
6163255Slg150142 }
6173255Slg150142
6187492SZhigang.Lu@Sun.COM xact_status = (uint32_t)curr_isoc_xfer_offset;
6193255Slg150142 xact_status |= (pageselected << 12);
6205653Slc152243 xact_status |= isoc_pkt_length << 16;
6213255Slg150142 xact_status |= EHCI_ITD_XFER_ACTIVE;
6223255Slg150142
6233255Slg150142 /* Set IOC on the last TD. */
6243255Slg150142 if (count == (curr_isoc_reqp->isoc_pkts_count - 1)) {
6253255Slg150142 xact_status |= EHCI_ITD_XFER_IOC_ON;
6263255Slg150142 }
6273255Slg150142
6283255Slg150142 USB_DPRINTF_L3(PRINT_MASK_INTR,
6293255Slg150142 ehcip->ehci_log_hdl,
6303255Slg150142 "ehci_insert_itd_req: count = 0x%x multi = %d"
6313255Slg150142 "status = 0x%x page = 0x%x index = %d "
6326898Sfb209375 "pageselected = %d isoc_pkt_length = 0x%lx",
6333255Slg150142 xactcount, multi, xact_status, page,
6343255Slg150142 index, pageselected, isoc_pkt_length);
6353255Slg150142
6363255Slg150142 /* Fill in the new itd */
6373255Slg150142 Set_ITD_BODY(new_itd, xactcount, xact_status);
6383255Slg150142
6393255Slg150142 itw->itw_curr_isoc_pktp++;
6403255Slg150142 Set_ITD_INDEX(new_itd, xactcount, index++);
6413255Slg150142
6425653Slc152243 curr_isoc_xfer_offset += isoc_pkt_length;
6433255Slg150142
6443255Slg150142 if (curr_isoc_xfer_offset >= EHCI_4K_ALIGN) {
6453255Slg150142 pageselected ++;
6463255Slg150142 page += EHCI_4K_ALIGN;
6473255Slg150142 curr_isoc_xfer_offset -= EHCI_4K_ALIGN;
6483255Slg150142 }
6493255Slg150142
6503255Slg150142 count ++;
6513255Slg150142 if (count >= curr_isoc_reqp->isoc_pkts_count) {
6523255Slg150142
6535653Slc152243 break;
6543255Slg150142 }
6553255Slg150142 }
6563255Slg150142
6573255Slg150142 buf[0] |= (itw->itw_endpoint_num << 8);
6583255Slg150142 buf[0] |= itw->itw_device_addr;
6595653Slc152243 buf[1] |= ph->p_ep.wMaxPacketSize &
6605653Slc152243 EHCI_ITD_CTRL_MAX_PACKET_MASK;
6615653Slc152243
6623255Slg150142 if (itw->itw_direction == USB_EP_DIR_IN) {
6633255Slg150142 buf[1] |= EHCI_ITD_CTRL_DIR_IN;
6643255Slg150142 }
6653255Slg150142 buf[2] |= multi;
6663255Slg150142
6673255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER0, buf[0]);
6683255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER1, buf[1]);
6693255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER2, buf[2]);
6703255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER3, buf[3]);
6713255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER4, buf[4]);
6723255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER5, buf[5]);
6733255Slg150142 Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER6, buf[6]);
6743255Slg150142
6753255Slg150142 Set_ITD(new_itd->itd_state, EHCI_ITD_ACTIVE);
6763255Slg150142 ehci_print_itd(ehcip, new_itd);
6773255Slg150142
6783255Slg150142 /*
6793255Slg150142 * Add this itd to the itw before we add it in the PFL
6803255Slg150142 * If adding it to the PFL fails, we will have to cleanup.
6813255Slg150142 */
6823255Slg150142 ehci_insert_itd_on_itw(ehcip, itw, new_itd);
6833255Slg150142
6843255Slg150142 }
6853255Slg150142
6863255Slg150142 return (USB_SUCCESS);
6873255Slg150142 }
6883255Slg150142
6893255Slg150142
6903255Slg150142 /*
6910Sstevel@tonic-gate * ehci_insert_sitd_req:
6920Sstevel@tonic-gate *
6930Sstevel@tonic-gate * Insert an SITD request into the Host Controller's isochronous list.
6940Sstevel@tonic-gate */
6950Sstevel@tonic-gate /* ARGSUSED */
6960Sstevel@tonic-gate static int
ehci_insert_sitd_req(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_isoc_xwrapper_t * itw,usb_flags_t usb_flags)6970Sstevel@tonic-gate ehci_insert_sitd_req(
6980Sstevel@tonic-gate ehci_state_t *ehcip,
6990Sstevel@tonic-gate ehci_pipe_private_t *pp,
7000Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
7010Sstevel@tonic-gate usb_flags_t usb_flags)
7020Sstevel@tonic-gate {
7030Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
7040Sstevel@tonic-gate usb_isoc_req_t *curr_isoc_reqp;
7050Sstevel@tonic-gate usb_isoc_pkt_descr_t *curr_isoc_pkt_descr;
7060Sstevel@tonic-gate size_t curr_isoc_xfer_offset;
7070Sstevel@tonic-gate size_t isoc_pkt_length;
7083255Slg150142 uint_t count;
7090Sstevel@tonic-gate uint32_t ctrl, uframe_sched, xfer_state;
7100Sstevel@tonic-gate uint32_t page0, page1, prev_sitd;
7110Sstevel@tonic-gate uint32_t ssplit_count;
7123255Slg150142 ehci_itd_t *new_sitd;
7130Sstevel@tonic-gate
7140Sstevel@tonic-gate /*
7150Sstevel@tonic-gate * Get the current isochronous request and packet
7160Sstevel@tonic-gate * descriptor pointers.
7170Sstevel@tonic-gate */
7180Sstevel@tonic-gate curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
7190Sstevel@tonic-gate
7200Sstevel@tonic-gate /* Set the ctrl field */
7210Sstevel@tonic-gate ctrl = 0;
7220Sstevel@tonic-gate if (itw->itw_direction == USB_EP_DIR_IN) {
7230Sstevel@tonic-gate ctrl |= EHCI_SITD_CTRL_DIR_IN;
7240Sstevel@tonic-gate } else {
7250Sstevel@tonic-gate ctrl |= EHCI_SITD_CTRL_DIR_OUT;
7260Sstevel@tonic-gate }
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate ctrl |= (itw->itw_hub_port << EHCI_SITD_CTRL_PORT_SHIFT) &
7290Sstevel@tonic-gate EHCI_SITD_CTRL_PORT_MASK;
7300Sstevel@tonic-gate ctrl |= (itw->itw_hub_addr << EHCI_SITD_CTRL_HUB_SHIFT) &
7310Sstevel@tonic-gate EHCI_SITD_CTRL_HUB_MASK;
7320Sstevel@tonic-gate ctrl |= (itw->itw_endpoint_num << EHCI_SITD_CTRL_END_PT_SHIFT) &
7330Sstevel@tonic-gate EHCI_SITD_CTRL_END_PT_MASK;
7340Sstevel@tonic-gate ctrl |= (itw->itw_device_addr << EHCI_SITD_CTRL_DEVICE_SHIFT) &
7350Sstevel@tonic-gate EHCI_SITD_CTRL_DEVICE_MASK;
7360Sstevel@tonic-gate
7370Sstevel@tonic-gate /* Set the micro frame schedule */
7380Sstevel@tonic-gate uframe_sched = 0;
7390Sstevel@tonic-gate uframe_sched |= (pp->pp_smask << EHCI_SITD_UFRAME_SMASK_SHIFT) &
7400Sstevel@tonic-gate EHCI_SITD_UFRAME_SMASK_MASK;
7410Sstevel@tonic-gate uframe_sched |= (pp->pp_cmask << EHCI_SITD_UFRAME_CMASK_SHIFT) &
7420Sstevel@tonic-gate EHCI_SITD_UFRAME_CMASK_MASK;
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate /* Set the default page information */
7450Sstevel@tonic-gate page0 = itw->itw_cookie.dmac_address;
7460Sstevel@tonic-gate page1 = 0;
7470Sstevel@tonic-gate
7480Sstevel@tonic-gate prev_sitd = EHCI_ITD_LINK_PTR_INVALID;
7490Sstevel@tonic-gate
7500Sstevel@tonic-gate /*
7510Sstevel@tonic-gate * Save the number of isochronous TDs needs
7520Sstevel@tonic-gate * to be insert to complete current isochronous request.
7530Sstevel@tonic-gate */
7540Sstevel@tonic-gate itw->itw_num_itds = curr_isoc_reqp->isoc_pkts_count;
7550Sstevel@tonic-gate
7560Sstevel@tonic-gate /* Insert all the isochronous TDs */
7570Sstevel@tonic-gate for (count = 0, curr_isoc_xfer_offset = 0;
7585653Slc152243 count < itw->itw_num_itds; count++) {
7590Sstevel@tonic-gate
7600Sstevel@tonic-gate curr_isoc_pkt_descr = itw->itw_curr_isoc_pktp;
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate isoc_pkt_length = curr_isoc_pkt_descr->isoc_pkt_length;
7637492SZhigang.Lu@Sun.COM curr_isoc_pkt_descr->isoc_pkt_actual_length =
7647492SZhigang.Lu@Sun.COM (ushort_t)isoc_pkt_length;
7650Sstevel@tonic-gate
7660Sstevel@tonic-gate /* Set the transfer state information */
7670Sstevel@tonic-gate xfer_state = 0;
7680Sstevel@tonic-gate
7690Sstevel@tonic-gate if (itw->itw_direction == USB_EP_DIR_IN) {
7700Sstevel@tonic-gate /* Set the size to the max packet size */
7710Sstevel@tonic-gate xfer_state |= (ph->p_ep.wMaxPacketSize <<
7720Sstevel@tonic-gate EHCI_SITD_XFER_TOTAL_SHIFT) &
7730Sstevel@tonic-gate EHCI_SITD_XFER_TOTAL_MASK;
7740Sstevel@tonic-gate } else {
7750Sstevel@tonic-gate /* Set the size to the packet length */
7760Sstevel@tonic-gate xfer_state |= (isoc_pkt_length <<
7770Sstevel@tonic-gate EHCI_SITD_XFER_TOTAL_SHIFT) &
7780Sstevel@tonic-gate EHCI_SITD_XFER_TOTAL_MASK;
7790Sstevel@tonic-gate }
7800Sstevel@tonic-gate xfer_state |= EHCI_SITD_XFER_ACTIVE;
7810Sstevel@tonic-gate
7820Sstevel@tonic-gate /* Set IOC on the last TD. */
7830Sstevel@tonic-gate if (count == (itw->itw_num_itds - 1)) {
7840Sstevel@tonic-gate xfer_state |= EHCI_SITD_XFER_IOC_ON;
7850Sstevel@tonic-gate }
7860Sstevel@tonic-gate
7870Sstevel@tonic-gate ssplit_count = isoc_pkt_length / MAX_UFRAME_SITD_XFER;
7880Sstevel@tonic-gate if (isoc_pkt_length % MAX_UFRAME_SITD_XFER) {
7890Sstevel@tonic-gate ssplit_count++;
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate
7920Sstevel@tonic-gate page1 = (ssplit_count & EHCI_SITD_XFER_TCOUNT_MASK) <<
7930Sstevel@tonic-gate EHCI_SITD_XFER_TCOUNT_SHIFT;
7940Sstevel@tonic-gate if (ssplit_count > 1) {
7950Sstevel@tonic-gate page1 |= EHCI_SITD_XFER_TP_BEGIN;
7960Sstevel@tonic-gate } else {
7970Sstevel@tonic-gate page1 |= EHCI_SITD_XFER_TP_ALL;
7980Sstevel@tonic-gate }
7990Sstevel@tonic-gate
8000Sstevel@tonic-gate /* Grab a new sitd */
8010Sstevel@tonic-gate new_sitd = itw->itw_itd_free_list;
8020Sstevel@tonic-gate
8030Sstevel@tonic-gate ASSERT(new_sitd != NULL);
8040Sstevel@tonic-gate
8050Sstevel@tonic-gate itw->itw_itd_free_list = ehci_itd_iommu_to_cpu(ehcip,
8060Sstevel@tonic-gate Get_ITD(new_sitd->itd_link_ptr));
8070Sstevel@tonic-gate Set_ITD(new_sitd->itd_link_ptr, NULL);
8080Sstevel@tonic-gate
8090Sstevel@tonic-gate /* Fill in the new sitd */
8100Sstevel@tonic-gate Set_ITD_BODY(new_sitd, EHCI_SITD_CTRL, ctrl);
8110Sstevel@tonic-gate Set_ITD_BODY(new_sitd, EHCI_SITD_UFRAME_SCHED, uframe_sched);
8120Sstevel@tonic-gate Set_ITD_BODY(new_sitd, EHCI_SITD_XFER_STATE, xfer_state);
8130Sstevel@tonic-gate Set_ITD_BODY(new_sitd, EHCI_SITD_BUFFER0,
8140Sstevel@tonic-gate page0 + curr_isoc_xfer_offset);
8150Sstevel@tonic-gate Set_ITD_BODY(new_sitd, EHCI_SITD_BUFFER1, page1);
8160Sstevel@tonic-gate Set_ITD_BODY(new_sitd, EHCI_SITD_PREV_SITD, prev_sitd);
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate Set_ITD(new_sitd->itd_state, EHCI_ITD_ACTIVE);
8190Sstevel@tonic-gate
8200Sstevel@tonic-gate /*
8210Sstevel@tonic-gate * Add this itd to the itw before we add it in the PFL
8220Sstevel@tonic-gate * If adding it to the PFL fails, we will have to cleanup.
8230Sstevel@tonic-gate */
8240Sstevel@tonic-gate ehci_insert_itd_on_itw(ehcip, itw, new_sitd);
8250Sstevel@tonic-gate
8260Sstevel@tonic-gate itw->itw_curr_isoc_pktp++;
8270Sstevel@tonic-gate curr_isoc_xfer_offset += isoc_pkt_length;
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate
8300Sstevel@tonic-gate return (USB_SUCCESS);
8310Sstevel@tonic-gate }
8320Sstevel@tonic-gate
8330Sstevel@tonic-gate
8340Sstevel@tonic-gate /*
8350Sstevel@tonic-gate * ehci_remove_isoc_itds:
8360Sstevel@tonic-gate *
8370Sstevel@tonic-gate * Remove all itds from the PFL.
8380Sstevel@tonic-gate */
8390Sstevel@tonic-gate static void
ehci_remove_isoc_itds(ehci_state_t * ehcip,ehci_pipe_private_t * pp)8400Sstevel@tonic-gate ehci_remove_isoc_itds(
8410Sstevel@tonic-gate ehci_state_t *ehcip,
8420Sstevel@tonic-gate ehci_pipe_private_t *pp)
8430Sstevel@tonic-gate {
8440Sstevel@tonic-gate ehci_isoc_xwrapper_t *curr_itw, *next_itw;
8450Sstevel@tonic-gate ehci_itd_t *curr_itd, *next_itd;
8460Sstevel@tonic-gate
8470Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
8486898Sfb209375 "ehci_remove_isoc_itds: pp = 0x%p", (void *)pp);
8490Sstevel@tonic-gate
8500Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
8510Sstevel@tonic-gate
8520Sstevel@tonic-gate curr_itw = pp->pp_itw_head;
8530Sstevel@tonic-gate while (curr_itw) {
8540Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
8550Sstevel@tonic-gate "ehci_remove_isoc_itds: itw = 0x%p num itds = %d",
8566898Sfb209375 (void *)curr_itw, curr_itw->itw_num_itds);
8570Sstevel@tonic-gate
8580Sstevel@tonic-gate next_itw = curr_itw->itw_next;
8590Sstevel@tonic-gate
8600Sstevel@tonic-gate curr_itd = curr_itw->itw_itd_head;
8610Sstevel@tonic-gate while (curr_itd) {
8620Sstevel@tonic-gate next_itd = ehci_itd_iommu_to_cpu(ehcip,
8630Sstevel@tonic-gate Get_ITD(curr_itd->itd_itw_next_itd));
8640Sstevel@tonic-gate
8650Sstevel@tonic-gate ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp);
8660Sstevel@tonic-gate
8670Sstevel@tonic-gate curr_itd = next_itd;
8680Sstevel@tonic-gate }
8690Sstevel@tonic-gate
8700Sstevel@tonic-gate ehci_deallocate_itw(ehcip, pp, curr_itw);
8710Sstevel@tonic-gate
8720Sstevel@tonic-gate curr_itw = next_itw;
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate }
8750Sstevel@tonic-gate
8760Sstevel@tonic-gate
8770Sstevel@tonic-gate /*
8780Sstevel@tonic-gate * ehci_mark_reclaim_isoc:
8790Sstevel@tonic-gate *
8800Sstevel@tonic-gate * Set active ITDs to RECLAIM.
8810Sstevel@tonic-gate * Return number of ITD that need to be processed.
8820Sstevel@tonic-gate */
8830Sstevel@tonic-gate static void
ehci_mark_reclaim_isoc(ehci_state_t * ehcip,ehci_pipe_private_t * pp)8840Sstevel@tonic-gate ehci_mark_reclaim_isoc(
8850Sstevel@tonic-gate ehci_state_t *ehcip,
8860Sstevel@tonic-gate ehci_pipe_private_t *pp)
8870Sstevel@tonic-gate {
8880Sstevel@tonic-gate usb_frame_number_t current_frame_number;
8890Sstevel@tonic-gate ehci_isoc_xwrapper_t *curr_itw, *next_itw;
8900Sstevel@tonic-gate ehci_itd_t *curr_itd, *next_itd;
8910Sstevel@tonic-gate uint_t ctrl;
8920Sstevel@tonic-gate uint_t isActive;
8933255Slg150142 int i;
8940Sstevel@tonic-gate
8950Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
8966898Sfb209375 "ehci_mark_reclaim_isoc: pp = 0x%p", (void *)pp);
8970Sstevel@tonic-gate
8980Sstevel@tonic-gate if (pp->pp_itw_head == NULL) {
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate return;
9010Sstevel@tonic-gate }
9020Sstevel@tonic-gate
9030Sstevel@tonic-gate /* Get the current frame number. */
9040Sstevel@tonic-gate current_frame_number = ehci_get_current_frame_number(ehcip);
9050Sstevel@tonic-gate
9060Sstevel@tonic-gate /* Traverse the list of transfer descriptors */
9070Sstevel@tonic-gate curr_itw = pp->pp_itw_head;
9080Sstevel@tonic-gate while (curr_itw) {
9090Sstevel@tonic-gate next_itw = curr_itw->itw_next;
9100Sstevel@tonic-gate
9110Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
9120Sstevel@tonic-gate "ehci_mark_reclaim_isoc: itw = 0x%p num itds = %d",
9136898Sfb209375 (void *)curr_itw, curr_itw->itw_num_itds);
9140Sstevel@tonic-gate
9150Sstevel@tonic-gate curr_itd = curr_itw->itw_itd_head;
9160Sstevel@tonic-gate while (curr_itd) {
9170Sstevel@tonic-gate next_itd = ehci_itd_iommu_to_cpu(ehcip,
9180Sstevel@tonic-gate Get_ITD(curr_itd->itd_itw_next_itd));
9190Sstevel@tonic-gate
9200Sstevel@tonic-gate if (curr_itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
9210Sstevel@tonic-gate
9223255Slg150142 for (i = 0; i < EHCI_ITD_CTRL_LIST_SIZE; i++) {
9233255Slg150142 ctrl = Get_ITD_BODY(curr_itd,
9243255Slg150142 EHCI_ITD_CTRL0 + i);
9253255Slg150142 isActive = ctrl & EHCI_ITD_XFER_ACTIVE;
9263255Slg150142 /* If still active, deactivate it */
9273255Slg150142 if (isActive) {
9283255Slg150142 ctrl &= ~EHCI_ITD_XFER_ACTIVE;
9293255Slg150142 Set_ITD_BODY(curr_itd,
9303255Slg150142 EHCI_ITD_CTRL0 + i,
9313255Slg150142 ctrl);
9323255Slg150142 break;
9333255Slg150142 }
9343255Slg150142 }
9350Sstevel@tonic-gate } else {
9360Sstevel@tonic-gate ctrl = Get_ITD_BODY(curr_itd,
9370Sstevel@tonic-gate EHCI_SITD_XFER_STATE);
9380Sstevel@tonic-gate isActive = ctrl & EHCI_SITD_XFER_ACTIVE;
9393255Slg150142 /* If it is still active deactivate it */
9400Sstevel@tonic-gate if (isActive) {
9410Sstevel@tonic-gate ctrl &= ~EHCI_SITD_XFER_ACTIVE;
9420Sstevel@tonic-gate Set_ITD_BODY(curr_itd,
9430Sstevel@tonic-gate EHCI_SITD_XFER_STATE,
9440Sstevel@tonic-gate ctrl);
9450Sstevel@tonic-gate }
9460Sstevel@tonic-gate }
9470Sstevel@tonic-gate
9480Sstevel@tonic-gate /*
9490Sstevel@tonic-gate * If the itd was active put it on the reclaim status,
9500Sstevel@tonic-gate * so the interrupt handler will know not to process it.
9510Sstevel@tonic-gate * Otherwise leave it alone and let the interrupt
9520Sstevel@tonic-gate * handler process it normally.
9530Sstevel@tonic-gate */
9540Sstevel@tonic-gate if (isActive) {
9550Sstevel@tonic-gate Set_ITD(curr_itd->itd_state, EHCI_ITD_RECLAIM);
9560Sstevel@tonic-gate Set_ITD_FRAME(curr_itd->itd_reclaim_number,
9570Sstevel@tonic-gate current_frame_number);
9580Sstevel@tonic-gate ehci_remove_isoc_from_pfl(ehcip, curr_itd);
9590Sstevel@tonic-gate }
9600Sstevel@tonic-gate curr_itd = next_itd;
9610Sstevel@tonic-gate }
9620Sstevel@tonic-gate curr_itw = next_itw;
9630Sstevel@tonic-gate }
9640Sstevel@tonic-gate }
9650Sstevel@tonic-gate
9660Sstevel@tonic-gate
9670Sstevel@tonic-gate /*
9680Sstevel@tonic-gate * ehci_reclaim_isoc:
9690Sstevel@tonic-gate *
9700Sstevel@tonic-gate * "Reclaim" itds that were marked as RECLAIM.
9710Sstevel@tonic-gate */
9720Sstevel@tonic-gate static void
ehci_reclaim_isoc(ehci_state_t * ehcip,ehci_isoc_xwrapper_t * itw,ehci_itd_t * itd,ehci_pipe_private_t * pp)9730Sstevel@tonic-gate ehci_reclaim_isoc(
9740Sstevel@tonic-gate ehci_state_t *ehcip,
9750Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
9760Sstevel@tonic-gate ehci_itd_t *itd,
9770Sstevel@tonic-gate ehci_pipe_private_t *pp)
9780Sstevel@tonic-gate {
9790Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
9806898Sfb209375 "ehci_reclaim_isoc: itd = 0x%p", (void *)itd);
9810Sstevel@tonic-gate
9820Sstevel@tonic-gate /*
9830Sstevel@tonic-gate * These are itds that were marked "RECLAIM"
9840Sstevel@tonic-gate * by the pipe cleanup.
9850Sstevel@tonic-gate *
9860Sstevel@tonic-gate * Decrement the num_itds and the periodic in
9870Sstevel@tonic-gate * request count if necessary.
9880Sstevel@tonic-gate */
9890Sstevel@tonic-gate if ((--itw->itw_num_itds == 0) && (itw->itw_curr_xfer_reqp)) {
9900Sstevel@tonic-gate if (itw->itw_direction == USB_EP_DIR_IN) {
9910Sstevel@tonic-gate
9920Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt--;
9930Sstevel@tonic-gate
9940Sstevel@tonic-gate ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
9950Sstevel@tonic-gate } else {
9960Sstevel@tonic-gate ehci_hcdi_isoc_callback(pp->pp_pipe_handle, itw,
9970Sstevel@tonic-gate USB_CR_FLUSHED);
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate }
10000Sstevel@tonic-gate
10010Sstevel@tonic-gate /* Deallocate this transfer descriptor */
10020Sstevel@tonic-gate ehci_deallocate_itd(ehcip, itw, itd);
10030Sstevel@tonic-gate }
10040Sstevel@tonic-gate
10050Sstevel@tonic-gate
10060Sstevel@tonic-gate /*
10070Sstevel@tonic-gate * ehci_start_isoc_polling:
10080Sstevel@tonic-gate *
10090Sstevel@tonic-gate * Insert the number of periodic requests corresponding to polling
10100Sstevel@tonic-gate * interval as calculated during pipe open.
10110Sstevel@tonic-gate */
10120Sstevel@tonic-gate int
ehci_start_isoc_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)10130Sstevel@tonic-gate ehci_start_isoc_polling(
10140Sstevel@tonic-gate ehci_state_t *ehcip,
10150Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
10160Sstevel@tonic-gate usb_flags_t flags)
10170Sstevel@tonic-gate {
10180Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
10190Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw_list, *itw;
10200Sstevel@tonic-gate int i, total_itws;
10210Sstevel@tonic-gate int error = USB_SUCCESS;
10220Sstevel@tonic-gate
10233255Slg150142 USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
10245653Slc152243 "ehci_start_isoc_polling:");
10253255Slg150142
10260Sstevel@tonic-gate /* Allocate all the necessary resources for the IN transfer */
10270Sstevel@tonic-gate itw_list = NULL;
10280Sstevel@tonic-gate total_itws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
10290Sstevel@tonic-gate for (i = 0; i < total_itws; i += 1) {
10300Sstevel@tonic-gate itw = ehci_allocate_isoc_resources(ehcip, ph, NULL, flags);
10310Sstevel@tonic-gate if (itw == NULL) {
10320Sstevel@tonic-gate error = USB_NO_RESOURCES;
10330Sstevel@tonic-gate /* There are not enough resources deallocate the ITWs */
10340Sstevel@tonic-gate itw = itw_list;
10350Sstevel@tonic-gate while (itw != NULL) {
10360Sstevel@tonic-gate itw_list = itw->itw_next;
10370Sstevel@tonic-gate ehci_deallocate_isoc_in_resource(
10385653Slc152243 ehcip, pp, itw);
10390Sstevel@tonic-gate ehci_deallocate_itw(ehcip, pp, itw);
10400Sstevel@tonic-gate itw = itw_list;
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate
10430Sstevel@tonic-gate return (error);
10440Sstevel@tonic-gate } else {
10450Sstevel@tonic-gate if (itw_list == NULL) {
10460Sstevel@tonic-gate itw_list = itw;
10470Sstevel@tonic-gate }
10480Sstevel@tonic-gate }
10490Sstevel@tonic-gate }
10500Sstevel@tonic-gate
10510Sstevel@tonic-gate i = 0;
10520Sstevel@tonic-gate while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
10530Sstevel@tonic-gate
10540Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10550Sstevel@tonic-gate "ehci_start_isoc_polling: max = %d curr = %d itw = %p:",
10560Sstevel@tonic-gate pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
10576898Sfb209375 (void *)itw_list);
10580Sstevel@tonic-gate
10590Sstevel@tonic-gate itw = itw_list;
10600Sstevel@tonic-gate itw_list = itw->itw_next;
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate error = ehci_insert_isoc_req(ehcip, pp, itw, flags);
10630Sstevel@tonic-gate
10640Sstevel@tonic-gate if (error == USB_SUCCESS) {
10650Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt++;
10660Sstevel@tonic-gate } else {
10670Sstevel@tonic-gate /*
10680Sstevel@tonic-gate * Deallocate the remaining tw
10690Sstevel@tonic-gate * The current tw should have already been deallocated
10700Sstevel@tonic-gate */
10710Sstevel@tonic-gate itw = itw_list;
10720Sstevel@tonic-gate while (itw != NULL) {
10730Sstevel@tonic-gate itw_list = itw->itw_next;
10740Sstevel@tonic-gate ehci_deallocate_isoc_in_resource(
10755653Slc152243 ehcip, pp, itw);
10760Sstevel@tonic-gate ehci_deallocate_itw(ehcip, pp, itw);
10770Sstevel@tonic-gate itw = itw_list;
10780Sstevel@tonic-gate }
10790Sstevel@tonic-gate /*
10800Sstevel@tonic-gate * If this is the first req return an error.
10810Sstevel@tonic-gate * Otherwise return success.
10820Sstevel@tonic-gate */
10830Sstevel@tonic-gate if (i != 0) {
10840Sstevel@tonic-gate error = USB_SUCCESS;
10850Sstevel@tonic-gate }
10860Sstevel@tonic-gate
10870Sstevel@tonic-gate break;
10880Sstevel@tonic-gate }
10890Sstevel@tonic-gate i++;
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate
10920Sstevel@tonic-gate return (error);
10930Sstevel@tonic-gate }
10940Sstevel@tonic-gate
10950Sstevel@tonic-gate
10960Sstevel@tonic-gate /*
10970Sstevel@tonic-gate * Isochronronous handling functions.
10980Sstevel@tonic-gate */
10990Sstevel@tonic-gate /*
11000Sstevel@tonic-gate * ehci_traverse_active_isoc_list:
11010Sstevel@tonic-gate */
11020Sstevel@tonic-gate void
ehci_traverse_active_isoc_list(ehci_state_t * ehcip)11030Sstevel@tonic-gate ehci_traverse_active_isoc_list(
11040Sstevel@tonic-gate ehci_state_t *ehcip)
11050Sstevel@tonic-gate {
11060Sstevel@tonic-gate ehci_isoc_xwrapper_t *curr_itw;
11070Sstevel@tonic-gate ehci_itd_t *curr_itd, *next_itd;
11080Sstevel@tonic-gate uint_t state;
11090Sstevel@tonic-gate ehci_pipe_private_t *pp;
11100Sstevel@tonic-gate
11110Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
11120Sstevel@tonic-gate "ehci_traverse_active_isoc_list:");
11130Sstevel@tonic-gate
11140Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11150Sstevel@tonic-gate
11160Sstevel@tonic-gate /* Sync ITD pool */
11170Sstevel@tonic-gate Sync_ITD_Pool(ehcip);
11180Sstevel@tonic-gate
11190Sstevel@tonic-gate /* Traverse the list of done itds */
11200Sstevel@tonic-gate curr_itd = ehci_create_done_itd_list(ehcip);
11213255Slg150142 USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
11226898Sfb209375 "ehci_traverse_active_isoc_list: current itd = 0x%p",
11236898Sfb209375 (void *)curr_itd);
11240Sstevel@tonic-gate
11250Sstevel@tonic-gate while (curr_itd) {
11260Sstevel@tonic-gate /* Save the next_itd */
11270Sstevel@tonic-gate next_itd = ehci_itd_iommu_to_cpu(ehcip,
11280Sstevel@tonic-gate Get_ITD(curr_itd->itd_next_active_itd));
11290Sstevel@tonic-gate
11300Sstevel@tonic-gate /* Get the transfer wrapper and the pp */
11310Sstevel@tonic-gate curr_itw = (ehci_isoc_xwrapper_t *)EHCI_LOOKUP_ID(
11325653Slc152243 (uint32_t)Get_ITD(curr_itd->itd_trans_wrapper));
11330Sstevel@tonic-gate pp = curr_itw->itw_pipe_private;
11340Sstevel@tonic-gate
11353255Slg150142 if (curr_itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
11363255Slg150142 ehci_print_itd(ehcip, curr_itd);
11373255Slg150142 } else {
11383255Slg150142 ehci_print_sitd(ehcip, curr_itd);
11393255Slg150142 }
11400Sstevel@tonic-gate
11410Sstevel@tonic-gate /* Get the ITD state */
11420Sstevel@tonic-gate state = Get_ITD(curr_itd->itd_state);
11430Sstevel@tonic-gate
11440Sstevel@tonic-gate /* Only process the ITDs marked as active. */
11450Sstevel@tonic-gate if (state == EHCI_ITD_ACTIVE) {
11460Sstevel@tonic-gate ehci_parse_isoc_error(ehcip, curr_itw, curr_itd);
11470Sstevel@tonic-gate ehci_handle_isoc(ehcip, curr_itw, curr_itd);
11480Sstevel@tonic-gate } else {
11490Sstevel@tonic-gate ASSERT(state == EHCI_ITD_RECLAIM);
11500Sstevel@tonic-gate ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp);
11510Sstevel@tonic-gate }
11520Sstevel@tonic-gate
11530Sstevel@tonic-gate /*
11540Sstevel@tonic-gate * Deallocate the transfer wrapper if there are no more
11550Sstevel@tonic-gate * ITD's for the transfer wrapper. ehci_deallocate_itw()
11560Sstevel@tonic-gate * will not deallocate the tw for a periodic in endpoint
11570Sstevel@tonic-gate * since it will always have a ITD attached to it.
11580Sstevel@tonic-gate */
11590Sstevel@tonic-gate ehci_deallocate_itw(ehcip, pp, curr_itw);
11600Sstevel@tonic-gate
11610Sstevel@tonic-gate /* Check any ISOC is waiting for transfers completion event */
11620Sstevel@tonic-gate if (pp->pp_itw_head == NULL) {
11630Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11640Sstevel@tonic-gate "ehci_traverse_active_isoc_list: "
11656898Sfb209375 "Sent transfers completion event pp = 0x%p",
11666898Sfb209375 (void *)pp);
11670Sstevel@tonic-gate cv_signal(&pp->pp_xfer_cmpl_cv);
11680Sstevel@tonic-gate }
11690Sstevel@tonic-gate
11700Sstevel@tonic-gate curr_itd = next_itd;
11713255Slg150142
11723255Slg150142 USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
11733255Slg150142 "ehci_traverse_active_isoc_list: state = 0x%x "
11743255Slg150142 "pp = 0x%p itw = 0x%p itd = 0x%p next_itd = 0x%p",
11756898Sfb209375 state, (void *)pp, (void *)curr_itw, (void *)curr_itd,
11766898Sfb209375 (void *)next_itd);
11770Sstevel@tonic-gate }
11780Sstevel@tonic-gate }
11790Sstevel@tonic-gate
11800Sstevel@tonic-gate
11810Sstevel@tonic-gate static void
ehci_handle_isoc(ehci_state_t * ehcip,ehci_isoc_xwrapper_t * itw,ehci_itd_t * itd)11820Sstevel@tonic-gate ehci_handle_isoc(
11830Sstevel@tonic-gate ehci_state_t *ehcip,
11840Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
11850Sstevel@tonic-gate ehci_itd_t *itd)
11860Sstevel@tonic-gate {
11870Sstevel@tonic-gate ehci_pipe_private_t *pp; /* Pipe private field */
11880Sstevel@tonic-gate
11890Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11900Sstevel@tonic-gate
11910Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
11920Sstevel@tonic-gate "ehci_handle_isoc:");
11930Sstevel@tonic-gate
11940Sstevel@tonic-gate /* Obtain the pipe private structure */
11950Sstevel@tonic-gate pp = itw->itw_pipe_private;
11960Sstevel@tonic-gate
11973255Slg150142 ehci_handle_itd(ehcip, pp, itw, itd, itw->itw_handle_callback_value);
11980Sstevel@tonic-gate }
11990Sstevel@tonic-gate
12000Sstevel@tonic-gate
12010Sstevel@tonic-gate /*
12020Sstevel@tonic-gate * ehci_handle_itd:
12030Sstevel@tonic-gate *
12043255Slg150142 * Handle an (split) isochronous transfer descriptor.
12053255Slg150142 * This function will deallocate the itd from the list as well.
12060Sstevel@tonic-gate */
12070Sstevel@tonic-gate /* ARGSUSED */
12080Sstevel@tonic-gate static void
ehci_handle_itd(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_isoc_xwrapper_t * itw,ehci_itd_t * itd,void * tw_handle_callback_value)12090Sstevel@tonic-gate ehci_handle_itd(
12100Sstevel@tonic-gate ehci_state_t *ehcip,
12110Sstevel@tonic-gate ehci_pipe_private_t *pp,
12120Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
12130Sstevel@tonic-gate ehci_itd_t *itd,
12140Sstevel@tonic-gate void *tw_handle_callback_value)
12150Sstevel@tonic-gate {
12160Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
12170Sstevel@tonic-gate usb_isoc_req_t *curr_isoc_reqp =
12185653Slc152243 (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
12190Sstevel@tonic-gate int error = USB_SUCCESS;
12203255Slg150142 int i, index;
12210Sstevel@tonic-gate
12220Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
12233255Slg150142 "ehci_handle_itd: pp=0x%p itw=0x%p itd=0x%p "
12246898Sfb209375 "isoc_reqp=0%p data=0x%p", (void *)pp, (void *)itw, (void *)itd,
12256898Sfb209375 (void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
12260Sstevel@tonic-gate
12273255Slg150142 if (itw->itw_port_status == USBA_HIGH_SPEED_DEV &&
12283255Slg150142 curr_isoc_reqp != NULL) {
12293255Slg150142
12305653Slc152243 for (i = 0; i < EHCI_ITD_CTRL_LIST_SIZE; i++) {
12313255Slg150142
12325653Slc152243 index = Get_ITD_INDEX(itd, i);
12335653Slc152243 if (index == EHCI_ITD_UNUSED_INDEX) {
12343255Slg150142
12355653Slc152243 continue;
12365653Slc152243 }
12375653Slc152243 curr_isoc_reqp->
12385653Slc152243 isoc_pkt_descr[index].isoc_pkt_actual_length =
12395653Slc152243 (Get_ITD_BODY(itd, i) & EHCI_ITD_XFER_LENGTH) >> 16;
12403255Slg150142 }
12413255Slg150142 }
12423255Slg150142
12430Sstevel@tonic-gate /*
12440Sstevel@tonic-gate * Decrement the ITDs counter and check whether all the isoc
12450Sstevel@tonic-gate * data has been send or received. If ITDs counter reaches
12460Sstevel@tonic-gate * zero then inform client driver about completion current
12470Sstevel@tonic-gate * isoc request. Otherwise wait for completion of other isoc
12480Sstevel@tonic-gate * ITDs or transactions on this pipe.
12490Sstevel@tonic-gate */
12500Sstevel@tonic-gate if (--itw->itw_num_itds != 0) {
12510Sstevel@tonic-gate /* Deallocate this transfer descriptor */
12520Sstevel@tonic-gate ehci_deallocate_itd(ehcip, itw, itd);
12530Sstevel@tonic-gate
12540Sstevel@tonic-gate return;
12550Sstevel@tonic-gate }
12560Sstevel@tonic-gate
12570Sstevel@tonic-gate /*
12580Sstevel@tonic-gate * If this is a isoc in pipe, return the data to the client.
12590Sstevel@tonic-gate * For a isoc out pipe, there is no need to do anything.
12600Sstevel@tonic-gate */
12610Sstevel@tonic-gate if (itw->itw_direction == USB_EP_DIR_OUT) {
12620Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
12636898Sfb209375 "ehci_handle_itd: Isoc out pipe, isoc_reqp=0x%p, data=0x%p",
12646898Sfb209375 (void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
12650Sstevel@tonic-gate
12660Sstevel@tonic-gate /* Do the callback */
12670Sstevel@tonic-gate ehci_hcdi_isoc_callback(ph, itw, USB_CR_OK);
12680Sstevel@tonic-gate
12690Sstevel@tonic-gate /* Deallocate this transfer descriptor */
12700Sstevel@tonic-gate ehci_deallocate_itd(ehcip, itw, itd);
12710Sstevel@tonic-gate
12720Sstevel@tonic-gate return;
12730Sstevel@tonic-gate }
12740Sstevel@tonic-gate
12750Sstevel@tonic-gate /* Decrement number of IN isochronous request count */
12760Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt--;
12770Sstevel@tonic-gate
12783255Slg150142 USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
12793255Slg150142 "ehci_handle_itd: pp_cur_periodic_req_cnt = 0x%x ",
12803255Slg150142 pp->pp_cur_periodic_req_cnt);
12813255Slg150142
12820Sstevel@tonic-gate /* Call ehci_sendup_itd_message to send message to upstream */
12830Sstevel@tonic-gate ehci_sendup_itd_message(ehcip, pp, itw, itd, USB_CR_OK);
12840Sstevel@tonic-gate
12850Sstevel@tonic-gate /* Deallocate this transfer descriptor */
12860Sstevel@tonic-gate ehci_deallocate_itd(ehcip, itw, itd);
12870Sstevel@tonic-gate
12880Sstevel@tonic-gate /*
12890Sstevel@tonic-gate * If isochronous pipe state is still active, insert next isochronous
12900Sstevel@tonic-gate * request into the Host Controller's isochronous list.
12910Sstevel@tonic-gate */
12920Sstevel@tonic-gate if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) {
12930Sstevel@tonic-gate
12940Sstevel@tonic-gate return;
12950Sstevel@tonic-gate }
12960Sstevel@tonic-gate
12970Sstevel@tonic-gate if ((error = ehci_allocate_isoc_in_resource(ehcip, pp, itw, 0)) ==
12980Sstevel@tonic-gate USB_SUCCESS) {
12990Sstevel@tonic-gate curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
13000Sstevel@tonic-gate
13010Sstevel@tonic-gate ASSERT(curr_isoc_reqp != NULL);
13020Sstevel@tonic-gate
13030Sstevel@tonic-gate itw->itw_num_itds = ehci_calc_num_itds(itw,
13040Sstevel@tonic-gate curr_isoc_reqp->isoc_pkts_count);
13050Sstevel@tonic-gate
13060Sstevel@tonic-gate if (ehci_allocate_itds_for_itw(ehcip, itw, itw->itw_num_itds) !=
13070Sstevel@tonic-gate USB_SUCCESS) {
13080Sstevel@tonic-gate ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
13090Sstevel@tonic-gate itw->itw_num_itds = 0;
13100Sstevel@tonic-gate error = USB_FAILURE;
13110Sstevel@tonic-gate }
13120Sstevel@tonic-gate }
13130Sstevel@tonic-gate
13140Sstevel@tonic-gate if ((error != USB_SUCCESS) ||
13150Sstevel@tonic-gate (ehci_insert_isoc_req(ehcip, pp, itw, 0) != USB_SUCCESS)) {
13160Sstevel@tonic-gate /*
13170Sstevel@tonic-gate * Set pipe state to stop polling and error to no
13180Sstevel@tonic-gate * resource. Don't insert any more isoch polling
13190Sstevel@tonic-gate * requests.
13200Sstevel@tonic-gate */
13210Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
13220Sstevel@tonic-gate pp->pp_error = USB_CR_NO_RESOURCES;
13230Sstevel@tonic-gate
13240Sstevel@tonic-gate } else {
13250Sstevel@tonic-gate /* Increment number of IN isochronous request count */
13260Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt++;
13270Sstevel@tonic-gate
13280Sstevel@tonic-gate ASSERT(pp->pp_cur_periodic_req_cnt ==
13290Sstevel@tonic-gate pp->pp_max_periodic_req_cnt);
13300Sstevel@tonic-gate }
13310Sstevel@tonic-gate }
13320Sstevel@tonic-gate
13330Sstevel@tonic-gate
13340Sstevel@tonic-gate /*
13350Sstevel@tonic-gate * ehci_sendup_qtd_message:
13360Sstevel@tonic-gate * copy data, if necessary and do callback
13370Sstevel@tonic-gate */
13380Sstevel@tonic-gate /* ARGSUSED */
13390Sstevel@tonic-gate static void
ehci_sendup_itd_message(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_isoc_xwrapper_t * itw,ehci_itd_t * td,usb_cr_t error)13400Sstevel@tonic-gate ehci_sendup_itd_message(
13410Sstevel@tonic-gate ehci_state_t *ehcip,
13420Sstevel@tonic-gate ehci_pipe_private_t *pp,
13430Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
13440Sstevel@tonic-gate ehci_itd_t *td,
13450Sstevel@tonic-gate usb_cr_t error)
13460Sstevel@tonic-gate {
13470Sstevel@tonic-gate usb_isoc_req_t *isoc_reqp = itw->itw_curr_xfer_reqp;
13480Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
13490Sstevel@tonic-gate size_t length;
13500Sstevel@tonic-gate uchar_t *buf;
13510Sstevel@tonic-gate mblk_t *mp;
13520Sstevel@tonic-gate
13530Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
13540Sstevel@tonic-gate
13550Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
13560Sstevel@tonic-gate "ehci_sendup_itd_message:");
13570Sstevel@tonic-gate
13580Sstevel@tonic-gate ASSERT(itw != NULL);
13590Sstevel@tonic-gate
13600Sstevel@tonic-gate length = itw->itw_length;
13610Sstevel@tonic-gate
13620Sstevel@tonic-gate /* Copy the data into the mblk_t */
13630Sstevel@tonic-gate buf = (uchar_t *)itw->itw_buf;
13640Sstevel@tonic-gate
13653255Slg150142 USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
13666898Sfb209375 "ehci_sendup_itd_message: length %ld error %d", length, error);
13670Sstevel@tonic-gate
13680Sstevel@tonic-gate /* Get the message block */
13690Sstevel@tonic-gate mp = isoc_reqp->isoc_data;
13700Sstevel@tonic-gate
13710Sstevel@tonic-gate ASSERT(mp != NULL);
13720Sstevel@tonic-gate
13730Sstevel@tonic-gate if (length) {
13740Sstevel@tonic-gate /* Sync IO buffer */
13750Sstevel@tonic-gate Sync_IO_Buffer(itw->itw_dmahandle, length);
13760Sstevel@tonic-gate
13770Sstevel@tonic-gate /* Copy the data into the message */
13783255Slg150142 ddi_rep_get8(itw->itw_accesshandle,
13793255Slg150142 mp->b_rptr, buf, length, DDI_DEV_AUTOINCR);
13800Sstevel@tonic-gate
13810Sstevel@tonic-gate /* Increment the write pointer */
13820Sstevel@tonic-gate mp->b_wptr = mp->b_wptr + length;
13830Sstevel@tonic-gate } else {
13840Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
13850Sstevel@tonic-gate "ehci_sendup_itd_message: Zero length packet");
13860Sstevel@tonic-gate }
13870Sstevel@tonic-gate
13880Sstevel@tonic-gate ehci_hcdi_isoc_callback(ph, itw, error);
13890Sstevel@tonic-gate }
13900Sstevel@tonic-gate
13910Sstevel@tonic-gate
13920Sstevel@tonic-gate /*
13930Sstevel@tonic-gate * ehci_hcdi_isoc_callback:
13940Sstevel@tonic-gate *
13950Sstevel@tonic-gate * Convenience wrapper around usba_hcdi_cb() other than root hub.
13960Sstevel@tonic-gate */
13970Sstevel@tonic-gate void
ehci_hcdi_isoc_callback(usba_pipe_handle_data_t * ph,ehci_isoc_xwrapper_t * itw,usb_cr_t completion_reason)13980Sstevel@tonic-gate ehci_hcdi_isoc_callback(
13990Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
14000Sstevel@tonic-gate ehci_isoc_xwrapper_t *itw,
14010Sstevel@tonic-gate usb_cr_t completion_reason)
14020Sstevel@tonic-gate {
14030Sstevel@tonic-gate ehci_state_t *ehcip = ehci_obtain_state(
14045653Slc152243 ph->p_usba_device->usb_root_hub_dip);
14050Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
14060Sstevel@tonic-gate usb_opaque_t curr_xfer_reqp;
14070Sstevel@tonic-gate uint_t pipe_state = 0;
14080Sstevel@tonic-gate
14090Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
14100Sstevel@tonic-gate "ehci_hcdi_isoc_callback: ph = 0x%p, itw = 0x%p, cr = 0x%x",
14116898Sfb209375 (void *)ph, (void *)itw, completion_reason);
14120Sstevel@tonic-gate
14130Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
14140Sstevel@tonic-gate
14150Sstevel@tonic-gate /* Set the pipe state as per completion reason */
14160Sstevel@tonic-gate switch (completion_reason) {
14170Sstevel@tonic-gate case USB_CR_OK:
14180Sstevel@tonic-gate pipe_state = pp->pp_state;
14190Sstevel@tonic-gate break;
14200Sstevel@tonic-gate case USB_CR_NO_RESOURCES:
14210Sstevel@tonic-gate case USB_CR_NOT_SUPPORTED:
14220Sstevel@tonic-gate case USB_CR_PIPE_RESET:
14230Sstevel@tonic-gate case USB_CR_STOPPED_POLLING:
14240Sstevel@tonic-gate pipe_state = EHCI_PIPE_STATE_IDLE;
14250Sstevel@tonic-gate break;
14260Sstevel@tonic-gate case USB_CR_PIPE_CLOSING:
14270Sstevel@tonic-gate break;
14280Sstevel@tonic-gate }
14290Sstevel@tonic-gate
14300Sstevel@tonic-gate pp->pp_state = pipe_state;
14310Sstevel@tonic-gate
14320Sstevel@tonic-gate if (itw && itw->itw_curr_xfer_reqp) {
14330Sstevel@tonic-gate curr_xfer_reqp = (usb_opaque_t)itw->itw_curr_xfer_reqp;
14340Sstevel@tonic-gate itw->itw_curr_xfer_reqp = NULL;
14350Sstevel@tonic-gate } else {
14360Sstevel@tonic-gate ASSERT(pp->pp_client_periodic_in_reqp != NULL);
14370Sstevel@tonic-gate
14380Sstevel@tonic-gate curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
14390Sstevel@tonic-gate pp->pp_client_periodic_in_reqp = NULL;
14400Sstevel@tonic-gate }
14410Sstevel@tonic-gate
14420Sstevel@tonic-gate ASSERT(curr_xfer_reqp != NULL);
14430Sstevel@tonic-gate
14440Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
14450Sstevel@tonic-gate
14460Sstevel@tonic-gate usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
14470Sstevel@tonic-gate
14480Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
14490Sstevel@tonic-gate }
1450