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
51500Ssl147100 * Common Development and Distribution License (the "License").
61500Ssl147100 * 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 */
2112733SRaymond.Chen@Sun.COM
220Sstevel@tonic-gate /*
2312733SRaymond.Chen@Sun.COM * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * EHCI Host Controller Driver (EHCI)
280Sstevel@tonic-gate *
290Sstevel@tonic-gate * The EHCI driver is a software driver which interfaces to the Universal
300Sstevel@tonic-gate * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
310Sstevel@tonic-gate * the Host Controller is defined by the EHCI Host Controller Interface.
320Sstevel@tonic-gate *
330Sstevel@tonic-gate * This module contains the main EHCI driver code which handles all USB
340Sstevel@tonic-gate * transfers, bandwidth allocations and other general functionalities.
350Sstevel@tonic-gate */
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
380Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_intr.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
420Sstevel@tonic-gate /* Adjustable variables for the size of the pools */
430Sstevel@tonic-gate extern int ehci_qh_pool_size;
440Sstevel@tonic-gate extern int ehci_qtd_pool_size;
450Sstevel@tonic-gate
460Sstevel@tonic-gate
470Sstevel@tonic-gate /* Endpoint Descriptor (QH) related functions */
480Sstevel@tonic-gate ehci_qh_t *ehci_alloc_qh(
490Sstevel@tonic-gate ehci_state_t *ehcip,
500Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
510Sstevel@tonic-gate uint_t flag);
520Sstevel@tonic-gate static void ehci_unpack_endpoint(
530Sstevel@tonic-gate ehci_state_t *ehcip,
540Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
550Sstevel@tonic-gate ehci_qh_t *qh);
560Sstevel@tonic-gate void ehci_insert_qh(
570Sstevel@tonic-gate ehci_state_t *ehcip,
580Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
590Sstevel@tonic-gate static void ehci_insert_async_qh(
600Sstevel@tonic-gate ehci_state_t *ehcip,
610Sstevel@tonic-gate ehci_pipe_private_t *pp);
620Sstevel@tonic-gate static void ehci_insert_intr_qh(
630Sstevel@tonic-gate ehci_state_t *ehcip,
640Sstevel@tonic-gate ehci_pipe_private_t *pp);
650Sstevel@tonic-gate static void ehci_modify_qh_status_bit(
660Sstevel@tonic-gate ehci_state_t *ehcip,
670Sstevel@tonic-gate ehci_pipe_private_t *pp,
680Sstevel@tonic-gate halt_bit_t action);
690Sstevel@tonic-gate static void ehci_halt_hs_qh(
700Sstevel@tonic-gate ehci_state_t *ehcip,
710Sstevel@tonic-gate ehci_pipe_private_t *pp,
720Sstevel@tonic-gate ehci_qh_t *qh);
730Sstevel@tonic-gate static void ehci_halt_fls_ctrl_and_bulk_qh(
740Sstevel@tonic-gate ehci_state_t *ehcip,
750Sstevel@tonic-gate ehci_pipe_private_t *pp,
760Sstevel@tonic-gate ehci_qh_t *qh);
770Sstevel@tonic-gate static void ehci_clear_tt_buffer(
780Sstevel@tonic-gate ehci_state_t *ehcip,
790Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
800Sstevel@tonic-gate ehci_qh_t *qh);
810Sstevel@tonic-gate static void ehci_halt_fls_intr_qh(
820Sstevel@tonic-gate ehci_state_t *ehcip,
830Sstevel@tonic-gate ehci_qh_t *qh);
840Sstevel@tonic-gate void ehci_remove_qh(
850Sstevel@tonic-gate ehci_state_t *ehcip,
860Sstevel@tonic-gate ehci_pipe_private_t *pp,
870Sstevel@tonic-gate boolean_t reclaim);
880Sstevel@tonic-gate static void ehci_remove_async_qh(
890Sstevel@tonic-gate ehci_state_t *ehcip,
900Sstevel@tonic-gate ehci_pipe_private_t *pp,
910Sstevel@tonic-gate boolean_t reclaim);
920Sstevel@tonic-gate static void ehci_remove_intr_qh(
930Sstevel@tonic-gate ehci_state_t *ehcip,
940Sstevel@tonic-gate ehci_pipe_private_t *pp,
950Sstevel@tonic-gate boolean_t reclaim);
960Sstevel@tonic-gate static void ehci_insert_qh_on_reclaim_list(
970Sstevel@tonic-gate ehci_state_t *ehcip,
980Sstevel@tonic-gate ehci_pipe_private_t *pp);
990Sstevel@tonic-gate void ehci_deallocate_qh(
1000Sstevel@tonic-gate ehci_state_t *ehcip,
1010Sstevel@tonic-gate ehci_qh_t *old_qh);
1020Sstevel@tonic-gate uint32_t ehci_qh_cpu_to_iommu(
1030Sstevel@tonic-gate ehci_state_t *ehcip,
1040Sstevel@tonic-gate ehci_qh_t *addr);
1050Sstevel@tonic-gate ehci_qh_t *ehci_qh_iommu_to_cpu(
1060Sstevel@tonic-gate ehci_state_t *ehcip,
1070Sstevel@tonic-gate uintptr_t addr);
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate /* Transfer Descriptor (QTD) related functions */
1100Sstevel@tonic-gate static int ehci_initialize_dummy(
1110Sstevel@tonic-gate ehci_state_t *ehcip,
1120Sstevel@tonic-gate ehci_qh_t *qh);
1130Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_ctrl_resources(
1140Sstevel@tonic-gate ehci_state_t *ehcip,
1150Sstevel@tonic-gate ehci_pipe_private_t *pp,
1160Sstevel@tonic-gate usb_ctrl_req_t *ctrl_reqp,
1170Sstevel@tonic-gate usb_flags_t usb_flags);
1180Sstevel@tonic-gate void ehci_insert_ctrl_req(
1190Sstevel@tonic-gate ehci_state_t *ehcip,
1200Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1210Sstevel@tonic-gate usb_ctrl_req_t *ctrl_reqp,
1220Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
1230Sstevel@tonic-gate usb_flags_t usb_flags);
1240Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_bulk_resources(
1250Sstevel@tonic-gate ehci_state_t *ehcip,
1260Sstevel@tonic-gate ehci_pipe_private_t *pp,
1270Sstevel@tonic-gate usb_bulk_req_t *bulk_reqp,
1280Sstevel@tonic-gate usb_flags_t usb_flags);
1290Sstevel@tonic-gate void ehci_insert_bulk_req(
1300Sstevel@tonic-gate ehci_state_t *ehcip,
1310Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1320Sstevel@tonic-gate usb_bulk_req_t *bulk_reqp,
1330Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
1340Sstevel@tonic-gate usb_flags_t flags);
1350Sstevel@tonic-gate int ehci_start_periodic_pipe_polling(
1360Sstevel@tonic-gate ehci_state_t *ehcip,
1370Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1380Sstevel@tonic-gate usb_opaque_t periodic_in_reqp,
1390Sstevel@tonic-gate usb_flags_t flags);
1400Sstevel@tonic-gate static int ehci_start_pipe_polling(
1410Sstevel@tonic-gate ehci_state_t *ehcip,
1420Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1430Sstevel@tonic-gate usb_flags_t flags);
1440Sstevel@tonic-gate static int ehci_start_intr_polling(
1450Sstevel@tonic-gate ehci_state_t *ehcip,
1460Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1470Sstevel@tonic-gate usb_flags_t flags);
1480Sstevel@tonic-gate static void ehci_set_periodic_pipe_polling(
1490Sstevel@tonic-gate ehci_state_t *ehcip,
1500Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
1510Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_intr_resources(
1520Sstevel@tonic-gate ehci_state_t *ehcip,
1530Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1540Sstevel@tonic-gate usb_intr_req_t *intr_reqp,
1550Sstevel@tonic-gate usb_flags_t usb_flags);
1560Sstevel@tonic-gate void ehci_insert_intr_req(
1570Sstevel@tonic-gate ehci_state_t *ehcip,
1580Sstevel@tonic-gate ehci_pipe_private_t *pp,
1590Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
1600Sstevel@tonic-gate usb_flags_t flags);
1610Sstevel@tonic-gate int ehci_stop_periodic_pipe_polling(
1620Sstevel@tonic-gate ehci_state_t *ehcip,
1630Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
1640Sstevel@tonic-gate usb_flags_t flags);
1650Sstevel@tonic-gate int ehci_insert_qtd(
1660Sstevel@tonic-gate ehci_state_t *ehcip,
1670Sstevel@tonic-gate uint32_t qtd_ctrl,
1681500Ssl147100 size_t qtd_dma_offs,
1690Sstevel@tonic-gate size_t qtd_length,
1700Sstevel@tonic-gate uint32_t qtd_ctrl_phase,
1710Sstevel@tonic-gate ehci_pipe_private_t *pp,
1720Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
1730Sstevel@tonic-gate static ehci_qtd_t *ehci_allocate_qtd_from_pool(
1740Sstevel@tonic-gate ehci_state_t *ehcip);
1750Sstevel@tonic-gate static void ehci_fill_in_qtd(
1760Sstevel@tonic-gate ehci_state_t *ehcip,
1770Sstevel@tonic-gate ehci_qtd_t *qtd,
1780Sstevel@tonic-gate uint32_t qtd_ctrl,
1791500Ssl147100 size_t qtd_dma_offs,
1800Sstevel@tonic-gate size_t qtd_length,
1810Sstevel@tonic-gate uint32_t qtd_ctrl_phase,
1820Sstevel@tonic-gate ehci_pipe_private_t *pp,
1830Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
1840Sstevel@tonic-gate static void ehci_insert_qtd_on_tw(
1850Sstevel@tonic-gate ehci_state_t *ehcip,
1860Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
1870Sstevel@tonic-gate ehci_qtd_t *qtd);
1880Sstevel@tonic-gate static void ehci_insert_qtd_into_active_qtd_list(
1890Sstevel@tonic-gate ehci_state_t *ehcip,
1900Sstevel@tonic-gate ehci_qtd_t *curr_qtd);
1910Sstevel@tonic-gate void ehci_remove_qtd_from_active_qtd_list(
1920Sstevel@tonic-gate ehci_state_t *ehcip,
1930Sstevel@tonic-gate ehci_qtd_t *curr_qtd);
1940Sstevel@tonic-gate static void ehci_traverse_qtds(
1950Sstevel@tonic-gate ehci_state_t *ehcip,
1960Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
1970Sstevel@tonic-gate void ehci_deallocate_qtd(
1980Sstevel@tonic-gate ehci_state_t *ehcip,
1990Sstevel@tonic-gate ehci_qtd_t *old_qtd);
2000Sstevel@tonic-gate uint32_t ehci_qtd_cpu_to_iommu(
2010Sstevel@tonic-gate ehci_state_t *ehcip,
2020Sstevel@tonic-gate ehci_qtd_t *addr);
2030Sstevel@tonic-gate ehci_qtd_t *ehci_qtd_iommu_to_cpu(
2040Sstevel@tonic-gate ehci_state_t *ehcip,
2050Sstevel@tonic-gate uintptr_t addr);
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate /* Transfer Wrapper (TW) functions */
2080Sstevel@tonic-gate static ehci_trans_wrapper_t *ehci_create_transfer_wrapper(
2090Sstevel@tonic-gate ehci_state_t *ehcip,
2100Sstevel@tonic-gate ehci_pipe_private_t *pp,
2110Sstevel@tonic-gate size_t length,
2120Sstevel@tonic-gate uint_t usb_flags);
2130Sstevel@tonic-gate int ehci_allocate_tds_for_tw(
2140Sstevel@tonic-gate ehci_state_t *ehcip,
2150Sstevel@tonic-gate ehci_pipe_private_t *pp,
2160Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
2170Sstevel@tonic-gate size_t qtd_count);
2180Sstevel@tonic-gate static ehci_trans_wrapper_t *ehci_allocate_tw_resources(
2190Sstevel@tonic-gate ehci_state_t *ehcip,
2200Sstevel@tonic-gate ehci_pipe_private_t *pp,
2210Sstevel@tonic-gate size_t length,
2220Sstevel@tonic-gate usb_flags_t usb_flags,
2230Sstevel@tonic-gate size_t td_count);
2240Sstevel@tonic-gate static void ehci_free_tw_td_resources(
2250Sstevel@tonic-gate ehci_state_t *ehcip,
2260Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
2270Sstevel@tonic-gate static void ehci_start_xfer_timer(
2280Sstevel@tonic-gate ehci_state_t *ehcip,
2290Sstevel@tonic-gate ehci_pipe_private_t *pp,
2300Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
2310Sstevel@tonic-gate void ehci_stop_xfer_timer(
2320Sstevel@tonic-gate ehci_state_t *ehcip,
2330Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
2340Sstevel@tonic-gate uint_t flag);
2350Sstevel@tonic-gate static void ehci_xfer_timeout_handler(void *arg);
2360Sstevel@tonic-gate static void ehci_remove_tw_from_timeout_list(
2370Sstevel@tonic-gate ehci_state_t *ehcip,
2380Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
2390Sstevel@tonic-gate static void ehci_start_timer(ehci_state_t *ehcip,
2400Sstevel@tonic-gate ehci_pipe_private_t *pp);
2410Sstevel@tonic-gate void ehci_deallocate_tw(
2420Sstevel@tonic-gate ehci_state_t *ehcip,
2430Sstevel@tonic-gate ehci_pipe_private_t *pp,
2440Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
2450Sstevel@tonic-gate void ehci_free_dma_resources(
2460Sstevel@tonic-gate ehci_state_t *ehcip,
2470Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
2480Sstevel@tonic-gate static void ehci_free_tw(
2490Sstevel@tonic-gate ehci_state_t *ehcip,
2500Sstevel@tonic-gate ehci_pipe_private_t *pp,
2510Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate /* Miscellaneous functions */
2540Sstevel@tonic-gate int ehci_allocate_intr_in_resource(
2550Sstevel@tonic-gate ehci_state_t *ehcip,
2560Sstevel@tonic-gate ehci_pipe_private_t *pp,
2570Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
2580Sstevel@tonic-gate usb_flags_t flags);
2590Sstevel@tonic-gate void ehci_pipe_cleanup(
2600Sstevel@tonic-gate ehci_state_t *ehcip,
2610Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
2620Sstevel@tonic-gate static void ehci_wait_for_transfers_completion(
2630Sstevel@tonic-gate ehci_state_t *ehcip,
2640Sstevel@tonic-gate ehci_pipe_private_t *pp);
2650Sstevel@tonic-gate void ehci_check_for_transfers_completion(
2660Sstevel@tonic-gate ehci_state_t *ehcip,
2670Sstevel@tonic-gate ehci_pipe_private_t *pp);
2680Sstevel@tonic-gate static void ehci_save_data_toggle(
2690Sstevel@tonic-gate ehci_state_t *ehcip,
2700Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
2710Sstevel@tonic-gate void ehci_restore_data_toggle(
2720Sstevel@tonic-gate ehci_state_t *ehcip,
2730Sstevel@tonic-gate usba_pipe_handle_data_t *ph);
2740Sstevel@tonic-gate void ehci_handle_outstanding_requests(
2750Sstevel@tonic-gate ehci_state_t *ehcip,
2760Sstevel@tonic-gate ehci_pipe_private_t *pp);
2770Sstevel@tonic-gate void ehci_deallocate_intr_in_resource(
2780Sstevel@tonic-gate ehci_state_t *ehcip,
2790Sstevel@tonic-gate ehci_pipe_private_t *pp,
2800Sstevel@tonic-gate ehci_trans_wrapper_t *tw);
2810Sstevel@tonic-gate void ehci_do_client_periodic_in_req_callback(
2820Sstevel@tonic-gate ehci_state_t *ehcip,
2830Sstevel@tonic-gate ehci_pipe_private_t *pp,
2840Sstevel@tonic-gate usb_cr_t completion_reason);
2850Sstevel@tonic-gate void ehci_hcdi_callback(
2860Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
2870Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
2880Sstevel@tonic-gate usb_cr_t completion_reason);
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate * Endpoint Descriptor (QH) manipulations functions
2930Sstevel@tonic-gate */
2940Sstevel@tonic-gate
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate * ehci_alloc_qh:
2970Sstevel@tonic-gate *
2980Sstevel@tonic-gate * Allocate an endpoint descriptor (QH)
2990Sstevel@tonic-gate *
3000Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
3010Sstevel@tonic-gate */
3020Sstevel@tonic-gate ehci_qh_t *
ehci_alloc_qh(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,uint_t flag)3030Sstevel@tonic-gate ehci_alloc_qh(
3040Sstevel@tonic-gate ehci_state_t *ehcip,
3050Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
3060Sstevel@tonic-gate uint_t flag)
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate int i, state;
3090Sstevel@tonic-gate ehci_qh_t *qh;
3100Sstevel@tonic-gate
3110Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3120Sstevel@tonic-gate "ehci_alloc_qh: ph = 0x%p flag = 0x%x", (void *)ph, flag);
3130Sstevel@tonic-gate
3140Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate /*
3170Sstevel@tonic-gate * If this is for a ISOC endpoint return null.
3180Sstevel@tonic-gate * Isochronous uses ITD put directly onto the PFL.
3190Sstevel@tonic-gate */
3200Sstevel@tonic-gate if (ph) {
3210Sstevel@tonic-gate if (EHCI_ISOC_ENDPOINT((&ph->p_ep))) {
3220Sstevel@tonic-gate
3230Sstevel@tonic-gate return (NULL);
3240Sstevel@tonic-gate }
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate
3270Sstevel@tonic-gate /*
3280Sstevel@tonic-gate * The first 63 endpoints in the Endpoint Descriptor (QH)
3290Sstevel@tonic-gate * buffer pool are reserved for building interrupt lattice
3300Sstevel@tonic-gate * tree. Search for a blank endpoint descriptor in the QH
3310Sstevel@tonic-gate * buffer pool.
3320Sstevel@tonic-gate */
3330Sstevel@tonic-gate for (i = EHCI_NUM_STATIC_NODES; i < ehci_qh_pool_size; i ++) {
3340Sstevel@tonic-gate state = Get_QH(ehcip->ehci_qh_pool_addr[i].qh_state);
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate if (state == EHCI_QH_FREE) {
3370Sstevel@tonic-gate break;
3380Sstevel@tonic-gate }
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3420Sstevel@tonic-gate "ehci_alloc_qh: Allocated %d", i);
3430Sstevel@tonic-gate
3440Sstevel@tonic-gate if (i == ehci_qh_pool_size) {
3450Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3460Sstevel@tonic-gate "ehci_alloc_qh: QH exhausted");
3470Sstevel@tonic-gate
3480Sstevel@tonic-gate return (NULL);
3490Sstevel@tonic-gate } else {
3500Sstevel@tonic-gate qh = &ehcip->ehci_qh_pool_addr[i];
3515441Ssl147100 bzero((void *)qh, sizeof (ehci_qh_t));
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3540Sstevel@tonic-gate "ehci_alloc_qh: Allocated address 0x%p", (void *)qh);
3550Sstevel@tonic-gate
3560Sstevel@tonic-gate /* Check polled mode flag */
3570Sstevel@tonic-gate if (flag == EHCI_POLLED_MODE_FLAG) {
3580Sstevel@tonic-gate Set_QH(qh->qh_link_ptr, EHCI_QH_LINK_PTR_VALID);
3590Sstevel@tonic-gate Set_QH(qh->qh_ctrl, EHCI_QH_CTRL_ED_INACTIVATE);
3600Sstevel@tonic-gate }
3610Sstevel@tonic-gate
3620Sstevel@tonic-gate /* Unpack the endpoint descriptor into a control field */
3630Sstevel@tonic-gate if (ph) {
3640Sstevel@tonic-gate if ((ehci_initialize_dummy(ehcip,
3650Sstevel@tonic-gate qh)) == USB_NO_RESOURCES) {
3660Sstevel@tonic-gate
3670Sstevel@tonic-gate Set_QH(qh->qh_state, EHCI_QH_FREE);
3680Sstevel@tonic-gate
3690Sstevel@tonic-gate return (NULL);
3700Sstevel@tonic-gate }
3710Sstevel@tonic-gate
3720Sstevel@tonic-gate ehci_unpack_endpoint(ehcip, ph, qh);
3730Sstevel@tonic-gate
3740Sstevel@tonic-gate Set_QH(qh->qh_curr_qtd, NULL);
3750Sstevel@tonic-gate Set_QH(qh->qh_alt_next_qtd,
3760Sstevel@tonic-gate EHCI_QH_ALT_NEXT_QTD_PTR_VALID);
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate /* Change QH's state Active */
3790Sstevel@tonic-gate Set_QH(qh->qh_state, EHCI_QH_ACTIVE);
3800Sstevel@tonic-gate } else {
3810Sstevel@tonic-gate Set_QH(qh->qh_status, EHCI_QH_STS_HALTED);
3820Sstevel@tonic-gate
3830Sstevel@tonic-gate /* Change QH's state Static */
3840Sstevel@tonic-gate Set_QH(qh->qh_state, EHCI_QH_STATIC);
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate
3870Sstevel@tonic-gate ehci_print_qh(ehcip, qh);
3880Sstevel@tonic-gate
3890Sstevel@tonic-gate return (qh);
3900Sstevel@tonic-gate }
3910Sstevel@tonic-gate }
3920Sstevel@tonic-gate
3930Sstevel@tonic-gate
3940Sstevel@tonic-gate /*
3950Sstevel@tonic-gate * ehci_unpack_endpoint:
3960Sstevel@tonic-gate *
3970Sstevel@tonic-gate * Unpack the information in the pipe handle and create the first byte
3980Sstevel@tonic-gate * of the Host Controller's (HC) Endpoint Descriptor (QH).
3990Sstevel@tonic-gate */
4000Sstevel@tonic-gate static void
ehci_unpack_endpoint(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,ehci_qh_t * qh)4010Sstevel@tonic-gate ehci_unpack_endpoint(
4020Sstevel@tonic-gate ehci_state_t *ehcip,
4030Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
4040Sstevel@tonic-gate ehci_qh_t *qh)
4050Sstevel@tonic-gate {
4060Sstevel@tonic-gate usb_ep_descr_t *endpoint = &ph->p_ep;
4070Sstevel@tonic-gate uint_t maxpacketsize, addr, xactions;
4080Sstevel@tonic-gate uint_t ctrl = 0, status = 0, split_ctrl = 0;
4090Sstevel@tonic-gate usb_port_status_t usb_port_status;
4100Sstevel@tonic-gate usba_device_t *usba_device = ph->p_usba_device;
4110Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
4120Sstevel@tonic-gate
4130Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
4140Sstevel@tonic-gate "ehci_unpack_endpoint:");
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
4170Sstevel@tonic-gate ctrl = usba_device->usb_addr;
4180Sstevel@tonic-gate usb_port_status = usba_device->usb_port_status;
4190Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
4200Sstevel@tonic-gate
4210Sstevel@tonic-gate addr = endpoint->bEndpointAddress;
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate /* Assign the endpoint's address */
4240Sstevel@tonic-gate ctrl |= ((addr & USB_EP_NUM_MASK) << EHCI_QH_CTRL_ED_NUMBER_SHIFT);
4250Sstevel@tonic-gate
4260Sstevel@tonic-gate /* Assign the speed */
4270Sstevel@tonic-gate switch (usb_port_status) {
4280Sstevel@tonic-gate case USBA_LOW_SPEED_DEV:
4290Sstevel@tonic-gate ctrl |= EHCI_QH_CTRL_ED_LOW_SPEED;
4300Sstevel@tonic-gate break;
4310Sstevel@tonic-gate case USBA_FULL_SPEED_DEV:
4320Sstevel@tonic-gate ctrl |= EHCI_QH_CTRL_ED_FULL_SPEED;
4330Sstevel@tonic-gate break;
4340Sstevel@tonic-gate case USBA_HIGH_SPEED_DEV:
4350Sstevel@tonic-gate ctrl |= EHCI_QH_CTRL_ED_HIGH_SPEED;
4360Sstevel@tonic-gate break;
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate switch (endpoint->bmAttributes & USB_EP_ATTR_MASK) {
4400Sstevel@tonic-gate case USB_EP_ATTR_CONTROL:
4410Sstevel@tonic-gate /* Assign data toggle information */
4420Sstevel@tonic-gate ctrl |= EHCI_QH_CTRL_DATA_TOGGLE;
4430Sstevel@tonic-gate
4440Sstevel@tonic-gate if (usb_port_status != USBA_HIGH_SPEED_DEV) {
4450Sstevel@tonic-gate ctrl |= EHCI_QH_CTRL_CONTROL_ED_FLAG;
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate /* FALLTHRU */
4480Sstevel@tonic-gate case USB_EP_ATTR_BULK:
4490Sstevel@tonic-gate /* Maximum nak counter */
4500Sstevel@tonic-gate ctrl |= EHCI_QH_CTRL_MAX_NC;
4510Sstevel@tonic-gate
4520Sstevel@tonic-gate if (usb_port_status == USBA_HIGH_SPEED_DEV) {
4530Sstevel@tonic-gate /*
4540Sstevel@tonic-gate * Perform ping before executing control
4550Sstevel@tonic-gate * and bulk transactions.
4560Sstevel@tonic-gate */
4570Sstevel@tonic-gate status = EHCI_QH_STS_DO_PING;
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate break;
4600Sstevel@tonic-gate case USB_EP_ATTR_INTR:
4610Sstevel@tonic-gate /* Set start split mask */
4620Sstevel@tonic-gate split_ctrl = (pp->pp_smask & EHCI_QH_SPLIT_CTRL_INTR_MASK);
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate /*
4650Sstevel@tonic-gate * Set complete split mask for low/full speed
4660Sstevel@tonic-gate * usb devices.
4670Sstevel@tonic-gate */
4680Sstevel@tonic-gate if (usb_port_status != USBA_HIGH_SPEED_DEV) {
4690Sstevel@tonic-gate split_ctrl |= ((pp->pp_cmask <<
4700Sstevel@tonic-gate EHCI_QH_SPLIT_CTRL_COMP_SHIFT) &
4710Sstevel@tonic-gate EHCI_QH_SPLIT_CTRL_COMP_MASK);
4720Sstevel@tonic-gate }
4730Sstevel@tonic-gate break;
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate
4760Sstevel@tonic-gate /* Get the max transactions per microframe */
4770Sstevel@tonic-gate xactions = (endpoint->wMaxPacketSize &
4780Sstevel@tonic-gate USB_EP_MAX_XACTS_MASK) >> USB_EP_MAX_XACTS_SHIFT;
4790Sstevel@tonic-gate
4800Sstevel@tonic-gate switch (xactions) {
4810Sstevel@tonic-gate case 0:
4820Sstevel@tonic-gate split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
4830Sstevel@tonic-gate break;
4840Sstevel@tonic-gate case 1:
4850Sstevel@tonic-gate split_ctrl |= EHCI_QH_SPLIT_CTRL_2_XACTS;
4860Sstevel@tonic-gate break;
4870Sstevel@tonic-gate case 2:
4880Sstevel@tonic-gate split_ctrl |= EHCI_QH_SPLIT_CTRL_3_XACTS;
4890Sstevel@tonic-gate break;
4900Sstevel@tonic-gate default:
4910Sstevel@tonic-gate split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
4920Sstevel@tonic-gate break;
4930Sstevel@tonic-gate }
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate /*
4960Sstevel@tonic-gate * For low/full speed devices, program high speed hub
4970Sstevel@tonic-gate * address and port number.
4980Sstevel@tonic-gate */
4990Sstevel@tonic-gate if (usb_port_status != USBA_HIGH_SPEED_DEV) {
5000Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
5010Sstevel@tonic-gate split_ctrl |= ((usba_device->usb_hs_hub_addr
5020Sstevel@tonic-gate << EHCI_QH_SPLIT_CTRL_HUB_ADDR_SHIFT) &
5030Sstevel@tonic-gate EHCI_QH_SPLIT_CTRL_HUB_ADDR);
5040Sstevel@tonic-gate
5050Sstevel@tonic-gate split_ctrl |= ((usba_device->usb_hs_hub_port
5060Sstevel@tonic-gate << EHCI_QH_SPLIT_CTRL_HUB_PORT_SHIFT) &
5070Sstevel@tonic-gate EHCI_QH_SPLIT_CTRL_HUB_PORT);
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
5100Sstevel@tonic-gate
5110Sstevel@tonic-gate /* Set start split transaction state */
5120Sstevel@tonic-gate status = EHCI_QH_STS_DO_START_SPLIT;
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate
5150Sstevel@tonic-gate /* Assign endpoint's maxpacketsize */
5160Sstevel@tonic-gate maxpacketsize = endpoint->wMaxPacketSize & USB_EP_MAX_PKTSZ_MASK;
5170Sstevel@tonic-gate maxpacketsize = maxpacketsize << EHCI_QH_CTRL_MAXPKTSZ_SHIFT;
5180Sstevel@tonic-gate ctrl |= (maxpacketsize & EHCI_QH_CTRL_MAXPKTSZ);
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate Set_QH(qh->qh_ctrl, ctrl);
5210Sstevel@tonic-gate Set_QH(qh->qh_split_ctrl, split_ctrl);
5220Sstevel@tonic-gate Set_QH(qh->qh_status, status);
5230Sstevel@tonic-gate }
5240Sstevel@tonic-gate
5250Sstevel@tonic-gate
5260Sstevel@tonic-gate /*
5270Sstevel@tonic-gate * ehci_insert_qh:
5280Sstevel@tonic-gate *
5290Sstevel@tonic-gate * Add the Endpoint Descriptor (QH) into the Host Controller's
5300Sstevel@tonic-gate * (HC) appropriate endpoint list.
5310Sstevel@tonic-gate */
5320Sstevel@tonic-gate void
ehci_insert_qh(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)5330Sstevel@tonic-gate ehci_insert_qh(
5340Sstevel@tonic-gate ehci_state_t *ehcip,
5350Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
5360Sstevel@tonic-gate {
5370Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
5406898Sfb209375 "ehci_insert_qh: qh=0x%p", (void *)pp->pp_qh);
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
5430Sstevel@tonic-gate
5440Sstevel@tonic-gate switch (ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) {
5450Sstevel@tonic-gate case USB_EP_ATTR_CONTROL:
5460Sstevel@tonic-gate case USB_EP_ATTR_BULK:
5470Sstevel@tonic-gate ehci_insert_async_qh(ehcip, pp);
5480Sstevel@tonic-gate ehcip->ehci_open_async_count++;
5490Sstevel@tonic-gate break;
5500Sstevel@tonic-gate case USB_EP_ATTR_INTR:
5510Sstevel@tonic-gate ehci_insert_intr_qh(ehcip, pp);
5520Sstevel@tonic-gate ehcip->ehci_open_periodic_count++;
5530Sstevel@tonic-gate break;
5540Sstevel@tonic-gate case USB_EP_ATTR_ISOCH:
5550Sstevel@tonic-gate /* ISOCH does not use QH, don't do anything but update count */
5560Sstevel@tonic-gate ehcip->ehci_open_periodic_count++;
5570Sstevel@tonic-gate break;
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate }
5600Sstevel@tonic-gate
5610Sstevel@tonic-gate
5620Sstevel@tonic-gate /*
5630Sstevel@tonic-gate * ehci_insert_async_qh:
5640Sstevel@tonic-gate *
5650Sstevel@tonic-gate * Insert a control/bulk endpoint into the Host Controller's (HC)
5660Sstevel@tonic-gate * Asynchronous schedule endpoint list.
5670Sstevel@tonic-gate */
5680Sstevel@tonic-gate static void
ehci_insert_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)5690Sstevel@tonic-gate ehci_insert_async_qh(
5700Sstevel@tonic-gate ehci_state_t *ehcip,
5710Sstevel@tonic-gate ehci_pipe_private_t *pp)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh;
5740Sstevel@tonic-gate ehci_qh_t *async_head_qh;
5750Sstevel@tonic-gate ehci_qh_t *next_qh;
5760Sstevel@tonic-gate uintptr_t qh_addr;
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
5790Sstevel@tonic-gate "ehci_insert_async_qh:");
5800Sstevel@tonic-gate
5810Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate /* Make sure this QH is not already in the list */
5840Sstevel@tonic-gate ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == NULL);
5850Sstevel@tonic-gate
5860Sstevel@tonic-gate qh_addr = ehci_qh_cpu_to_iommu(ehcip, qh);
5870Sstevel@tonic-gate
5880Sstevel@tonic-gate /* Obtain a ptr to the head of the Async schedule list */
5890Sstevel@tonic-gate async_head_qh = ehcip->ehci_head_of_async_sched_list;
5900Sstevel@tonic-gate
5910Sstevel@tonic-gate if (async_head_qh == NULL) {
5920Sstevel@tonic-gate /* Set this QH to be the "head" of the circular list */
5930Sstevel@tonic-gate Set_QH(qh->qh_ctrl,
5940Sstevel@tonic-gate (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_RECLAIM_HEAD));
5950Sstevel@tonic-gate
5960Sstevel@tonic-gate /* Set new QH's link and previous pointer to itself */
5970Sstevel@tonic-gate Set_QH(qh->qh_link_ptr, qh_addr | EHCI_QH_LINK_REF_QH);
5980Sstevel@tonic-gate Set_QH(qh->qh_prev, qh_addr);
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate ehcip->ehci_head_of_async_sched_list = qh;
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate /* Set the head ptr to the new endpoint */
6030Sstevel@tonic-gate Set_OpReg(ehci_async_list_addr, qh_addr);
6042225Sgk73471
6052225Sgk73471 /*
6062225Sgk73471 * For some reason this register might get nulled out by
6072225Sgk73471 * the Uli M1575 South Bridge. To workaround the hardware
6082225Sgk73471 * problem, check the value after write and retry if the
6092225Sgk73471 * last write fails.
6102225Sgk73471 *
6112225Sgk73471 * If the ASYNCLISTADDR remains "stuck" after
6122225Sgk73471 * EHCI_MAX_RETRY retries, then the M1575 is broken
6132225Sgk73471 * and is stuck in an inconsistent state and is about
6142225Sgk73471 * to crash the machine with a trn_oor panic when it
6152225Sgk73471 * does a DMA read from 0x0. It is better to panic
6162225Sgk73471 * now rather than wait for the trn_oor crash; this
6172225Sgk73471 * way Customer Service will have a clean signature
6182225Sgk73471 * that indicts the M1575 chip rather than a
6192225Sgk73471 * mysterious and hard-to-diagnose trn_oor panic.
6202225Sgk73471 */
6212225Sgk73471 if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
6222225Sgk73471 (ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
6232225Sgk73471 (qh_addr != Get_OpReg(ehci_async_list_addr))) {
6242225Sgk73471 int retry = 0;
6252225Sgk73471
6262225Sgk73471 Set_OpRegRetry(ehci_async_list_addr, qh_addr, retry);
6272225Sgk73471 if (retry >= EHCI_MAX_RETRY)
6282225Sgk73471 cmn_err(CE_PANIC, "ehci_insert_async_qh:"
6292225Sgk73471 " ASYNCLISTADDR write failed.");
6302225Sgk73471
6312225Sgk73471 USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
6322225Sgk73471 "ehci_insert_async_qh: ASYNCLISTADDR "
6335441Ssl147100 "write failed, retry=%d", retry);
6342225Sgk73471 }
6350Sstevel@tonic-gate } else {
6360Sstevel@tonic-gate ASSERT(Get_QH(async_head_qh->qh_ctrl) &
6370Sstevel@tonic-gate EHCI_QH_CTRL_RECLAIM_HEAD);
6380Sstevel@tonic-gate
6390Sstevel@tonic-gate /* Ensure this QH's "H" bit is not set */
6400Sstevel@tonic-gate Set_QH(qh->qh_ctrl,
6410Sstevel@tonic-gate (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_RECLAIM_HEAD));
6420Sstevel@tonic-gate
6430Sstevel@tonic-gate next_qh = ehci_qh_iommu_to_cpu(ehcip,
6440Sstevel@tonic-gate Get_QH(async_head_qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate /* Set new QH's link and previous pointers */
6470Sstevel@tonic-gate Set_QH(qh->qh_link_ptr,
6480Sstevel@tonic-gate Get_QH(async_head_qh->qh_link_ptr) | EHCI_QH_LINK_REF_QH);
6490Sstevel@tonic-gate Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, async_head_qh));
6500Sstevel@tonic-gate
6510Sstevel@tonic-gate /* Set next QH's prev pointer */
6520Sstevel@tonic-gate Set_QH(next_qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, qh));
6530Sstevel@tonic-gate
6540Sstevel@tonic-gate /* Set QH Head's link pointer points to new QH */
6550Sstevel@tonic-gate Set_QH(async_head_qh->qh_link_ptr,
6560Sstevel@tonic-gate qh_addr | EHCI_QH_LINK_REF_QH);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate }
6590Sstevel@tonic-gate
6600Sstevel@tonic-gate
6610Sstevel@tonic-gate /*
6620Sstevel@tonic-gate * ehci_insert_intr_qh:
6630Sstevel@tonic-gate *
6640Sstevel@tonic-gate * Insert a interrupt endpoint into the Host Controller's (HC) interrupt
6650Sstevel@tonic-gate * lattice tree.
6660Sstevel@tonic-gate */
6670Sstevel@tonic-gate static void
ehci_insert_intr_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)6680Sstevel@tonic-gate ehci_insert_intr_qh(
6690Sstevel@tonic-gate ehci_state_t *ehcip,
6700Sstevel@tonic-gate ehci_pipe_private_t *pp)
6710Sstevel@tonic-gate {
6720Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh;
6730Sstevel@tonic-gate ehci_qh_t *next_lattice_qh, *lattice_qh;
6740Sstevel@tonic-gate uint_t hnode;
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
6770Sstevel@tonic-gate "ehci_insert_intr_qh:");
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
6800Sstevel@tonic-gate
6810Sstevel@tonic-gate /* Make sure this QH is not already in the list */
6820Sstevel@tonic-gate ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == NULL);
6830Sstevel@tonic-gate
6840Sstevel@tonic-gate /*
6850Sstevel@tonic-gate * The appropriate high speed node was found
6860Sstevel@tonic-gate * during the opening of the pipe.
6870Sstevel@tonic-gate */
6880Sstevel@tonic-gate hnode = pp->pp_pnode;
6890Sstevel@tonic-gate
6900Sstevel@tonic-gate /* Find the lattice endpoint */
6910Sstevel@tonic-gate lattice_qh = &ehcip->ehci_qh_pool_addr[hnode];
6920Sstevel@tonic-gate
6930Sstevel@tonic-gate /* Find the next lattice endpoint */
6940Sstevel@tonic-gate next_lattice_qh = ehci_qh_iommu_to_cpu(
6950Sstevel@tonic-gate ehcip, (Get_QH(lattice_qh->qh_link_ptr) & EHCI_QH_LINK_PTR));
6960Sstevel@tonic-gate
6970Sstevel@tonic-gate /* Update the previous pointer */
6980Sstevel@tonic-gate Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, lattice_qh));
6990Sstevel@tonic-gate
7000Sstevel@tonic-gate /* Check next_lattice_qh value */
7010Sstevel@tonic-gate if (next_lattice_qh) {
7020Sstevel@tonic-gate /* Update this qh to point to the next one in the lattice */
7030Sstevel@tonic-gate Set_QH(qh->qh_link_ptr, Get_QH(lattice_qh->qh_link_ptr));
7040Sstevel@tonic-gate
7050Sstevel@tonic-gate /* Update the previous pointer of qh->qh_link_ptr */
7060Sstevel@tonic-gate if (Get_QH(next_lattice_qh->qh_state) != EHCI_QH_STATIC) {
7070Sstevel@tonic-gate Set_QH(next_lattice_qh->qh_prev,
7080Sstevel@tonic-gate ehci_qh_cpu_to_iommu(ehcip, qh));
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate } else {
7110Sstevel@tonic-gate /* Update qh's link pointer to terminate periodic list */
7120Sstevel@tonic-gate Set_QH(qh->qh_link_ptr,
7130Sstevel@tonic-gate (Get_QH(lattice_qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate
7160Sstevel@tonic-gate /* Insert this endpoint into the lattice */
7170Sstevel@tonic-gate Set_QH(lattice_qh->qh_link_ptr,
7180Sstevel@tonic-gate (ehci_qh_cpu_to_iommu(ehcip, qh) | EHCI_QH_LINK_REF_QH));
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate
7210Sstevel@tonic-gate
7220Sstevel@tonic-gate /*
7230Sstevel@tonic-gate * ehci_modify_qh_status_bit:
7240Sstevel@tonic-gate *
7250Sstevel@tonic-gate * Modify the halt bit on the Host Controller (HC) Endpoint Descriptor (QH).
7260Sstevel@tonic-gate *
7270Sstevel@tonic-gate * If several threads try to halt the same pipe, they will need to wait on
7280Sstevel@tonic-gate * a condition variable. Only one thread is allowed to halt or unhalt the
7290Sstevel@tonic-gate * pipe at a time.
7300Sstevel@tonic-gate *
7310Sstevel@tonic-gate * Usually after a halt pipe, an unhalt pipe will follow soon after. There
7320Sstevel@tonic-gate * is an assumption that an Unhalt pipe will never occur without a halt pipe.
7330Sstevel@tonic-gate */
7340Sstevel@tonic-gate static void
ehci_modify_qh_status_bit(ehci_state_t * ehcip,ehci_pipe_private_t * pp,halt_bit_t action)7350Sstevel@tonic-gate ehci_modify_qh_status_bit(
7360Sstevel@tonic-gate ehci_state_t *ehcip,
7370Sstevel@tonic-gate ehci_pipe_private_t *pp,
7380Sstevel@tonic-gate halt_bit_t action)
7390Sstevel@tonic-gate {
7400Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh;
7410Sstevel@tonic-gate uint_t smask, eps, split_intr_qh;
7420Sstevel@tonic-gate uint_t status;
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
7450Sstevel@tonic-gate "ehci_modify_qh_status_bit: action=0x%x qh=0x%p",
7466898Sfb209375 action, (void *)qh);
7470Sstevel@tonic-gate
7480Sstevel@tonic-gate ehci_print_qh(ehcip, qh);
7490Sstevel@tonic-gate
7500Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate /*
7530Sstevel@tonic-gate * If this pipe is in the middle of halting don't allow another
7540Sstevel@tonic-gate * thread to come in and modify the same pipe.
7550Sstevel@tonic-gate */
7560Sstevel@tonic-gate while (pp->pp_halt_state & EHCI_HALT_STATE_HALTING) {
7570Sstevel@tonic-gate
7580Sstevel@tonic-gate cv_wait(&pp->pp_halt_cmpl_cv,
7595441Ssl147100 &ehcip->ehci_int_mutex);
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate /* Sync the QH QTD pool to get up to date information */
7630Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
7640Sstevel@tonic-gate
7650Sstevel@tonic-gate
7660Sstevel@tonic-gate if (action == CLEAR_HALT) {
7670Sstevel@tonic-gate /*
7680Sstevel@tonic-gate * If the halt bit is to be cleared, just clear it.
7690Sstevel@tonic-gate * there shouldn't be any race condition problems.
7700Sstevel@tonic-gate * If the host controller reads the bit before the
7710Sstevel@tonic-gate * driver has a chance to set the bit, the bit will
7720Sstevel@tonic-gate * be reread on the next frame.
7730Sstevel@tonic-gate */
7740Sstevel@tonic-gate Set_QH(qh->qh_ctrl,
7750Sstevel@tonic-gate (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_ED_INACTIVATE));
7760Sstevel@tonic-gate Set_QH(qh->qh_status,
7770Sstevel@tonic-gate Get_QH(qh->qh_status) & ~(EHCI_QH_STS_XACT_STATUS));
7780Sstevel@tonic-gate
7790Sstevel@tonic-gate goto success;
7800Sstevel@tonic-gate }
7810Sstevel@tonic-gate
7820Sstevel@tonic-gate /* Halt the the QH, but first check to see if it is already halted */
7830Sstevel@tonic-gate status = Get_QH(qh->qh_status);
7840Sstevel@tonic-gate if (!(status & EHCI_QH_STS_HALTED)) {
7850Sstevel@tonic-gate /* Indicate that this pipe is in the middle of halting. */
7860Sstevel@tonic-gate pp->pp_halt_state |= EHCI_HALT_STATE_HALTING;
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate /*
7890Sstevel@tonic-gate * Find out if this is an full/low speed interrupt endpoint.
7900Sstevel@tonic-gate * A non-zero Cmask indicates that this QH is an interrupt
7910Sstevel@tonic-gate * endpoint. Check the endpoint speed to see if it is either
7920Sstevel@tonic-gate * FULL or LOW .
7930Sstevel@tonic-gate */
7940Sstevel@tonic-gate smask = Get_QH(qh->qh_split_ctrl) &
7950Sstevel@tonic-gate EHCI_QH_SPLIT_CTRL_INTR_MASK;
7960Sstevel@tonic-gate eps = Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_SPEED;
7970Sstevel@tonic-gate split_intr_qh = ((smask != 0) &&
7980Sstevel@tonic-gate (eps != EHCI_QH_CTRL_ED_HIGH_SPEED));
7990Sstevel@tonic-gate
8000Sstevel@tonic-gate if (eps == EHCI_QH_CTRL_ED_HIGH_SPEED) {
8010Sstevel@tonic-gate ehci_halt_hs_qh(ehcip, pp, qh);
8020Sstevel@tonic-gate } else {
8030Sstevel@tonic-gate if (split_intr_qh) {
8040Sstevel@tonic-gate ehci_halt_fls_intr_qh(ehcip, qh);
8050Sstevel@tonic-gate } else {
8060Sstevel@tonic-gate ehci_halt_fls_ctrl_and_bulk_qh(ehcip, pp, qh);
8070Sstevel@tonic-gate }
8080Sstevel@tonic-gate }
8090Sstevel@tonic-gate
8100Sstevel@tonic-gate /* Indicate that this pipe is not in the middle of halting. */
8110Sstevel@tonic-gate pp->pp_halt_state &= ~EHCI_HALT_STATE_HALTING;
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate
8140Sstevel@tonic-gate /* Sync the QH QTD pool again to get the most up to date information */
8150Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
8160Sstevel@tonic-gate
8170Sstevel@tonic-gate ehci_print_qh(ehcip, qh);
8180Sstevel@tonic-gate
8190Sstevel@tonic-gate status = Get_QH(qh->qh_status);
8200Sstevel@tonic-gate if (!(status & EHCI_QH_STS_HALTED)) {
8210Sstevel@tonic-gate USB_DPRINTF_L1(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8226898Sfb209375 "ehci_modify_qh_status_bit: Failed to halt qh=0x%p",
8236898Sfb209375 (void *)qh);
8240Sstevel@tonic-gate
8250Sstevel@tonic-gate ehci_print_qh(ehcip, qh);
8260Sstevel@tonic-gate
8270Sstevel@tonic-gate /* Set host controller soft state to error */
8280Sstevel@tonic-gate ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
8290Sstevel@tonic-gate
8300Sstevel@tonic-gate ASSERT(status & EHCI_QH_STS_HALTED);
8310Sstevel@tonic-gate }
8320Sstevel@tonic-gate
8330Sstevel@tonic-gate success:
8340Sstevel@tonic-gate /* Wake up threads waiting for this pipe to be halted. */
8350Sstevel@tonic-gate cv_signal(&pp->pp_halt_cmpl_cv);
8360Sstevel@tonic-gate }
8370Sstevel@tonic-gate
8380Sstevel@tonic-gate
8390Sstevel@tonic-gate /*
8400Sstevel@tonic-gate * ehci_halt_hs_qh:
8410Sstevel@tonic-gate *
8420Sstevel@tonic-gate * Halts all types of HIGH SPEED QHs.
8430Sstevel@tonic-gate */
8440Sstevel@tonic-gate static void
ehci_halt_hs_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_qh_t * qh)8450Sstevel@tonic-gate ehci_halt_hs_qh(
8460Sstevel@tonic-gate ehci_state_t *ehcip,
8470Sstevel@tonic-gate ehci_pipe_private_t *pp,
8480Sstevel@tonic-gate ehci_qh_t *qh)
8490Sstevel@tonic-gate {
8500Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
8510Sstevel@tonic-gate
8520Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8530Sstevel@tonic-gate "ehci_halt_hs_qh:");
8540Sstevel@tonic-gate
8550Sstevel@tonic-gate /* Remove this qh from the HCD's view, but do not reclaim it */
8560Sstevel@tonic-gate ehci_remove_qh(ehcip, pp, B_FALSE);
857*12886SRaymond.Chen@Sun.COM ehci_toggle_scheduler_on_pipe(ehcip);
8580Sstevel@tonic-gate
8590Sstevel@tonic-gate /*
8600Sstevel@tonic-gate * Wait for atleast one SOF, just in case the HCD is in the
8610Sstevel@tonic-gate * middle accessing this QH.
8620Sstevel@tonic-gate */
8630Sstevel@tonic-gate (void) ehci_wait_for_sof(ehcip);
8640Sstevel@tonic-gate
8650Sstevel@tonic-gate /* Sync the QH QTD pool to get up to date information */
8660Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
8670Sstevel@tonic-gate
8680Sstevel@tonic-gate /* Modify the status bit and halt this QH. */
8690Sstevel@tonic-gate Set_QH(qh->qh_status,
8700Sstevel@tonic-gate ((Get_QH(qh->qh_status) &
8715441Ssl147100 ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
8720Sstevel@tonic-gate
8730Sstevel@tonic-gate /* Insert this QH back into the HCD's view */
8740Sstevel@tonic-gate ehci_insert_qh(ehcip, ph);
875*12886SRaymond.Chen@Sun.COM ehci_toggle_scheduler_on_pipe(ehcip);
8760Sstevel@tonic-gate }
8770Sstevel@tonic-gate
8780Sstevel@tonic-gate
8790Sstevel@tonic-gate /*
8800Sstevel@tonic-gate * ehci_halt_fls_ctrl_and_bulk_qh:
8810Sstevel@tonic-gate *
8820Sstevel@tonic-gate * Halts FULL/LOW Ctrl and Bulk QHs only.
8830Sstevel@tonic-gate */
8840Sstevel@tonic-gate static void
ehci_halt_fls_ctrl_and_bulk_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_qh_t * qh)8850Sstevel@tonic-gate ehci_halt_fls_ctrl_and_bulk_qh(
8860Sstevel@tonic-gate ehci_state_t *ehcip,
8870Sstevel@tonic-gate ehci_pipe_private_t *pp,
8880Sstevel@tonic-gate ehci_qh_t *qh)
8890Sstevel@tonic-gate {
8900Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
8910Sstevel@tonic-gate uint_t status, split_status, bytes_left;
8920Sstevel@tonic-gate
8930Sstevel@tonic-gate
8940Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8950Sstevel@tonic-gate "ehci_halt_fls_ctrl_and_bulk_qh:");
8960Sstevel@tonic-gate
8970Sstevel@tonic-gate /* Remove this qh from the HCD's view, but do not reclaim it */
8980Sstevel@tonic-gate ehci_remove_qh(ehcip, pp, B_FALSE);
899*12886SRaymond.Chen@Sun.COM ehci_toggle_scheduler_on_pipe(ehcip);
9000Sstevel@tonic-gate
9010Sstevel@tonic-gate /*
9020Sstevel@tonic-gate * Wait for atleast one SOF, just in case the HCD is in the
9030Sstevel@tonic-gate * middle accessing this QH.
9040Sstevel@tonic-gate */
9050Sstevel@tonic-gate (void) ehci_wait_for_sof(ehcip);
9060Sstevel@tonic-gate
9070Sstevel@tonic-gate /* Sync the QH QTD pool to get up to date information */
9080Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate /* Modify the status bit and halt this QH. */
9110Sstevel@tonic-gate Set_QH(qh->qh_status,
9120Sstevel@tonic-gate ((Get_QH(qh->qh_status) &
9135441Ssl147100 ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
9140Sstevel@tonic-gate
9150Sstevel@tonic-gate /* Check to see if the QH was in the middle of a transaction */
9160Sstevel@tonic-gate status = Get_QH(qh->qh_status);
9170Sstevel@tonic-gate split_status = status & EHCI_QH_STS_SPLIT_XSTATE;
9180Sstevel@tonic-gate bytes_left = status & EHCI_QH_STS_BYTES_TO_XFER;
9190Sstevel@tonic-gate if ((split_status == EHCI_QH_STS_DO_COMPLETE_SPLIT) &&
9200Sstevel@tonic-gate (bytes_left != 0)) {
9210Sstevel@tonic-gate /* send ClearTTBuffer to this device's parent 2.0 hub */
9220Sstevel@tonic-gate ehci_clear_tt_buffer(ehcip, ph, qh);
9230Sstevel@tonic-gate }
9240Sstevel@tonic-gate
9250Sstevel@tonic-gate /* Insert this QH back into the HCD's view */
9260Sstevel@tonic-gate ehci_insert_qh(ehcip, ph);
927*12886SRaymond.Chen@Sun.COM ehci_toggle_scheduler_on_pipe(ehcip);
9280Sstevel@tonic-gate }
9290Sstevel@tonic-gate
9300Sstevel@tonic-gate
9310Sstevel@tonic-gate /*
9320Sstevel@tonic-gate * ehci_clear_tt_buffer
9330Sstevel@tonic-gate *
9340Sstevel@tonic-gate * This function will sent a Clear_TT_Buffer request to the pipe's
9350Sstevel@tonic-gate * parent 2.0 hub.
9360Sstevel@tonic-gate */
9370Sstevel@tonic-gate static void
ehci_clear_tt_buffer(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,ehci_qh_t * qh)9380Sstevel@tonic-gate ehci_clear_tt_buffer(
9390Sstevel@tonic-gate ehci_state_t *ehcip,
9400Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
9410Sstevel@tonic-gate ehci_qh_t *qh)
9420Sstevel@tonic-gate {
9430Sstevel@tonic-gate usba_device_t *usba_device;
9440Sstevel@tonic-gate usba_device_t *hub_usba_device;
9450Sstevel@tonic-gate usb_pipe_handle_t hub_def_ph;
9460Sstevel@tonic-gate usb_ep_descr_t *eptd;
9470Sstevel@tonic-gate uchar_t attributes;
9480Sstevel@tonic-gate uint16_t wValue;
9490Sstevel@tonic-gate usb_ctrl_setup_t setup;
9500Sstevel@tonic-gate usb_cr_t completion_reason;
9510Sstevel@tonic-gate usb_cb_flags_t cb_flags;
9520Sstevel@tonic-gate int retry;
9530Sstevel@tonic-gate
9540Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
9550Sstevel@tonic-gate "ehci_clear_tt_buffer: ");
9560Sstevel@tonic-gate
9570Sstevel@tonic-gate /* Get some information about the current pipe */
9580Sstevel@tonic-gate usba_device = ph->p_usba_device;
9590Sstevel@tonic-gate eptd = &ph->p_ep;
9600Sstevel@tonic-gate attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
9610Sstevel@tonic-gate
9620Sstevel@tonic-gate /*
9630Sstevel@tonic-gate * Create the wIndex for this request (usb spec 11.24.2.3)
9640Sstevel@tonic-gate * 3..0 Endpoint Number
9650Sstevel@tonic-gate * 10..4 Device Address
9660Sstevel@tonic-gate * 12..11 Endpoint Type
9670Sstevel@tonic-gate * 14..13 Reserved (must be 0)
9680Sstevel@tonic-gate * 15 Direction 1 = IN, 0 = OUT
9690Sstevel@tonic-gate */
9700Sstevel@tonic-gate wValue = 0;
9710Sstevel@tonic-gate if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
9720Sstevel@tonic-gate wValue |= 0x8000;
9730Sstevel@tonic-gate }
9740Sstevel@tonic-gate wValue |= attributes << 11;
9750Sstevel@tonic-gate wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_DEVICE_ADDRESS) << 4;
9760Sstevel@tonic-gate wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_HIGH_SPEED) >>
9770Sstevel@tonic-gate EHCI_QH_CTRL_ED_NUMBER_SHIFT;
9780Sstevel@tonic-gate
9790Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
9800Sstevel@tonic-gate
9810Sstevel@tonic-gate /* Manually fill in the request. */
9820Sstevel@tonic-gate setup.bmRequestType = EHCI_CLEAR_TT_BUFFER_REQTYPE;
9830Sstevel@tonic-gate setup.bRequest = EHCI_CLEAR_TT_BUFFER_BREQ;
9840Sstevel@tonic-gate setup.wValue = wValue;
9850Sstevel@tonic-gate setup.wIndex = 1;
9860Sstevel@tonic-gate setup.wLength = 0;
9870Sstevel@tonic-gate setup.attrs = USB_ATTRS_NONE;
9880Sstevel@tonic-gate
9890Sstevel@tonic-gate /* Get the usba_device of the parent 2.0 hub. */
9900Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
9910Sstevel@tonic-gate hub_usba_device = usba_device->usb_hs_hub_usba_dev;
9920Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
9930Sstevel@tonic-gate
9940Sstevel@tonic-gate /* Get the default ctrl pipe for the parent 2.0 hub */
9950Sstevel@tonic-gate mutex_enter(&hub_usba_device->usb_mutex);
9960Sstevel@tonic-gate hub_def_ph = (usb_pipe_handle_t)&hub_usba_device->usb_ph_list[0];
9970Sstevel@tonic-gate mutex_exit(&hub_usba_device->usb_mutex);
9980Sstevel@tonic-gate
9990Sstevel@tonic-gate for (retry = 0; retry < 3; retry++) {
10000Sstevel@tonic-gate
10010Sstevel@tonic-gate /* sync send the request to the default pipe */
10020Sstevel@tonic-gate if (usb_pipe_ctrl_xfer_wait(
10030Sstevel@tonic-gate hub_def_ph,
10040Sstevel@tonic-gate &setup,
10050Sstevel@tonic-gate NULL,
10060Sstevel@tonic-gate &completion_reason, &cb_flags, 0) == USB_SUCCESS) {
10070Sstevel@tonic-gate
10080Sstevel@tonic-gate break;
10090Sstevel@tonic-gate }
10100Sstevel@tonic-gate
10110Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10120Sstevel@tonic-gate "ehci_clear_tt_buffer: Failed to clear tt buffer,"
10130Sstevel@tonic-gate "retry = %d, cr = %d, cb_flags = 0x%x\n",
10140Sstevel@tonic-gate retry, completion_reason, cb_flags);
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate
10170Sstevel@tonic-gate if (retry >= 3) {
10180Sstevel@tonic-gate char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
10190Sstevel@tonic-gate dev_info_t *dip = hub_usba_device->usb_dip;
10200Sstevel@tonic-gate
10210Sstevel@tonic-gate /*
10220Sstevel@tonic-gate * Ask the user to hotplug the 2.0 hub, to make sure that
10230Sstevel@tonic-gate * all the buffer is in sync since this command has failed.
10240Sstevel@tonic-gate */
10250Sstevel@tonic-gate USB_DPRINTF_L0(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10260Sstevel@tonic-gate "Error recovery failure: Please hotplug the 2.0 hub at"
10270Sstevel@tonic-gate "%s", ddi_pathname(dip, path));
10280Sstevel@tonic-gate
10290Sstevel@tonic-gate kmem_free(path, MAXPATHLEN);
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate
10320Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
10330Sstevel@tonic-gate }
10340Sstevel@tonic-gate
10350Sstevel@tonic-gate /*
10360Sstevel@tonic-gate * ehci_halt_fls_intr_qh:
10370Sstevel@tonic-gate *
10380Sstevel@tonic-gate * Halts FULL/LOW speed Intr QHs.
10390Sstevel@tonic-gate */
10400Sstevel@tonic-gate static void
ehci_halt_fls_intr_qh(ehci_state_t * ehcip,ehci_qh_t * qh)10410Sstevel@tonic-gate ehci_halt_fls_intr_qh(
10420Sstevel@tonic-gate ehci_state_t *ehcip,
10430Sstevel@tonic-gate ehci_qh_t *qh)
10440Sstevel@tonic-gate {
10450Sstevel@tonic-gate usb_frame_number_t starting_frame;
10460Sstevel@tonic-gate usb_frame_number_t frames_past;
10470Sstevel@tonic-gate uint_t status, i;
10480Sstevel@tonic-gate
10490Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10500Sstevel@tonic-gate "ehci_halt_fls_intr_qh:");
10510Sstevel@tonic-gate
10520Sstevel@tonic-gate /*
10530Sstevel@tonic-gate * Ask the HC to deactivate the QH in a
10540Sstevel@tonic-gate * full/low periodic QH.
10550Sstevel@tonic-gate */
10560Sstevel@tonic-gate Set_QH(qh->qh_ctrl,
10570Sstevel@tonic-gate (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_ED_INACTIVATE));
10580Sstevel@tonic-gate
10590Sstevel@tonic-gate starting_frame = ehci_get_current_frame_number(ehcip);
10600Sstevel@tonic-gate
10610Sstevel@tonic-gate /*
10620Sstevel@tonic-gate * Wait at least EHCI_NUM_INTR_QH_LISTS+2 frame or until
10630Sstevel@tonic-gate * the QH has been halted.
10640Sstevel@tonic-gate */
10650Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
10660Sstevel@tonic-gate frames_past = 0;
10670Sstevel@tonic-gate status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
10680Sstevel@tonic-gate
10690Sstevel@tonic-gate while ((frames_past <= (EHCI_NUM_INTR_QH_LISTS + 2)) &&
10700Sstevel@tonic-gate (status != 0)) {
10710Sstevel@tonic-gate
10720Sstevel@tonic-gate (void) ehci_wait_for_sof(ehcip);
10730Sstevel@tonic-gate
10740Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
10750Sstevel@tonic-gate status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
10760Sstevel@tonic-gate frames_past = ehci_get_current_frame_number(ehcip) -
10770Sstevel@tonic-gate starting_frame;
10780Sstevel@tonic-gate }
10790Sstevel@tonic-gate
10800Sstevel@tonic-gate /* Modify the status bit and halt this QH. */
10810Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
10820Sstevel@tonic-gate
10830Sstevel@tonic-gate status = Get_QH(qh->qh_status);
10840Sstevel@tonic-gate
10850Sstevel@tonic-gate for (i = 0; i < EHCI_NUM_INTR_QH_LISTS; i++) {
10860Sstevel@tonic-gate Set_QH(qh->qh_status,
10875441Ssl147100 ((Get_QH(qh->qh_status) &
10885441Ssl147100 ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
10890Sstevel@tonic-gate
10900Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
10910Sstevel@tonic-gate
10920Sstevel@tonic-gate (void) ehci_wait_for_sof(ehcip);
10930Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
10940Sstevel@tonic-gate
10950Sstevel@tonic-gate if (Get_QH(qh->qh_status) & EHCI_QH_STS_HALTED) {
10960Sstevel@tonic-gate
10970Sstevel@tonic-gate break;
10980Sstevel@tonic-gate }
10990Sstevel@tonic-gate }
11000Sstevel@tonic-gate
11010Sstevel@tonic-gate Sync_QH_QTD_Pool(ehcip);
11020Sstevel@tonic-gate
11030Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11046898Sfb209375 "ehci_halt_fls_intr_qh: qh=0x%p frames past=%llu,"
11056898Sfb209375 " status=0x%x, 0x%x", (void *)qh,
11066898Sfb209375 (unsigned long long)(ehci_get_current_frame_number(ehcip) -
11076898Sfb209375 starting_frame), status, Get_QH(qh->qh_status));
11080Sstevel@tonic-gate }
11090Sstevel@tonic-gate
11100Sstevel@tonic-gate
11110Sstevel@tonic-gate /*
11120Sstevel@tonic-gate * ehci_remove_qh:
11130Sstevel@tonic-gate *
11140Sstevel@tonic-gate * Remove the Endpoint Descriptor (QH) from the Host Controller's appropriate
11150Sstevel@tonic-gate * endpoint list.
11160Sstevel@tonic-gate */
11170Sstevel@tonic-gate void
ehci_remove_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)11180Sstevel@tonic-gate ehci_remove_qh(
11190Sstevel@tonic-gate ehci_state_t *ehcip,
11200Sstevel@tonic-gate ehci_pipe_private_t *pp,
11210Sstevel@tonic-gate boolean_t reclaim)
11220Sstevel@tonic-gate {
11230Sstevel@tonic-gate uchar_t attributes;
11240Sstevel@tonic-gate
11250Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11286898Sfb209375 "ehci_remove_qh: qh=0x%p", (void *)pp->pp_qh);
11290Sstevel@tonic-gate
11300Sstevel@tonic-gate attributes = pp->pp_pipe_handle->p_ep.bmAttributes & USB_EP_ATTR_MASK;
11310Sstevel@tonic-gate
11320Sstevel@tonic-gate switch (attributes) {
11330Sstevel@tonic-gate case USB_EP_ATTR_CONTROL:
11340Sstevel@tonic-gate case USB_EP_ATTR_BULK:
11350Sstevel@tonic-gate ehci_remove_async_qh(ehcip, pp, reclaim);
11360Sstevel@tonic-gate ehcip->ehci_open_async_count--;
11370Sstevel@tonic-gate break;
11380Sstevel@tonic-gate case USB_EP_ATTR_INTR:
11390Sstevel@tonic-gate ehci_remove_intr_qh(ehcip, pp, reclaim);
11400Sstevel@tonic-gate ehcip->ehci_open_periodic_count--;
11410Sstevel@tonic-gate break;
11420Sstevel@tonic-gate case USB_EP_ATTR_ISOCH:
11430Sstevel@tonic-gate /* ISOCH does not use QH, don't do anything but update count */
11440Sstevel@tonic-gate ehcip->ehci_open_periodic_count--;
11450Sstevel@tonic-gate break;
11460Sstevel@tonic-gate }
11470Sstevel@tonic-gate }
11480Sstevel@tonic-gate
11490Sstevel@tonic-gate
11500Sstevel@tonic-gate /*
11510Sstevel@tonic-gate * ehci_remove_async_qh:
11520Sstevel@tonic-gate *
11530Sstevel@tonic-gate * Remove a control/bulk endpoint into the Host Controller's (HC)
11540Sstevel@tonic-gate * Asynchronous schedule endpoint list.
11550Sstevel@tonic-gate */
11560Sstevel@tonic-gate static void
ehci_remove_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)11570Sstevel@tonic-gate ehci_remove_async_qh(
11580Sstevel@tonic-gate ehci_state_t *ehcip,
11590Sstevel@tonic-gate ehci_pipe_private_t *pp,
11600Sstevel@tonic-gate boolean_t reclaim)
11610Sstevel@tonic-gate {
11620Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh; /* qh to be removed */
11630Sstevel@tonic-gate ehci_qh_t *prev_qh, *next_qh;
11640Sstevel@tonic-gate
11650Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11660Sstevel@tonic-gate "ehci_remove_async_qh:");
11670Sstevel@tonic-gate
11680Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11690Sstevel@tonic-gate
11700Sstevel@tonic-gate prev_qh = ehci_qh_iommu_to_cpu(ehcip,
11710Sstevel@tonic-gate Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR);
11720Sstevel@tonic-gate next_qh = ehci_qh_iommu_to_cpu(ehcip,
11730Sstevel@tonic-gate Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
11740Sstevel@tonic-gate
11750Sstevel@tonic-gate /* Make sure this QH is in the list */
11760Sstevel@tonic-gate ASSERT(prev_qh != NULL);
11770Sstevel@tonic-gate
11780Sstevel@tonic-gate /*
11790Sstevel@tonic-gate * If next QH and current QH are the same, then this is the last
11800Sstevel@tonic-gate * QH on the Asynchronous Schedule list.
11810Sstevel@tonic-gate */
11820Sstevel@tonic-gate if (qh == next_qh) {
11830Sstevel@tonic-gate ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
11840Sstevel@tonic-gate /*
11850Sstevel@tonic-gate * Null our pointer to the async sched list, but do not
11860Sstevel@tonic-gate * touch the host controller's list_addr.
11870Sstevel@tonic-gate */
11880Sstevel@tonic-gate ehcip->ehci_head_of_async_sched_list = NULL;
11890Sstevel@tonic-gate ASSERT(ehcip->ehci_open_async_count == 1);
11900Sstevel@tonic-gate } else {
11910Sstevel@tonic-gate /* If this QH is the HEAD then find another one to replace it */
11920Sstevel@tonic-gate if (ehcip->ehci_head_of_async_sched_list == qh) {
11930Sstevel@tonic-gate
11940Sstevel@tonic-gate ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
11950Sstevel@tonic-gate ehcip->ehci_head_of_async_sched_list = next_qh;
11960Sstevel@tonic-gate Set_QH(next_qh->qh_ctrl,
11970Sstevel@tonic-gate Get_QH(next_qh->qh_ctrl) |
11980Sstevel@tonic-gate EHCI_QH_CTRL_RECLAIM_HEAD);
11990Sstevel@tonic-gate }
12000Sstevel@tonic-gate Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
12010Sstevel@tonic-gate Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
12020Sstevel@tonic-gate }
12030Sstevel@tonic-gate
12040Sstevel@tonic-gate /* qh_prev to indicate it is no longer in the circular list */
12050Sstevel@tonic-gate Set_QH(qh->qh_prev, NULL);
12060Sstevel@tonic-gate
12070Sstevel@tonic-gate if (reclaim) {
12080Sstevel@tonic-gate ehci_insert_qh_on_reclaim_list(ehcip, pp);
12090Sstevel@tonic-gate }
12100Sstevel@tonic-gate }
12110Sstevel@tonic-gate
12120Sstevel@tonic-gate
12130Sstevel@tonic-gate /*
12140Sstevel@tonic-gate * ehci_remove_intr_qh:
12150Sstevel@tonic-gate *
12160Sstevel@tonic-gate * Set up an interrupt endpoint to be removed from the Host Controller's (HC)
12170Sstevel@tonic-gate * interrupt lattice tree. The Endpoint Descriptor (QH) will be freed in the
12180Sstevel@tonic-gate * interrupt handler.
12190Sstevel@tonic-gate */
12200Sstevel@tonic-gate static void
ehci_remove_intr_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)12210Sstevel@tonic-gate ehci_remove_intr_qh(
12220Sstevel@tonic-gate ehci_state_t *ehcip,
12230Sstevel@tonic-gate ehci_pipe_private_t *pp,
12240Sstevel@tonic-gate boolean_t reclaim)
12250Sstevel@tonic-gate {
12260Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh; /* qh to be removed */
12270Sstevel@tonic-gate ehci_qh_t *prev_qh, *next_qh;
12280Sstevel@tonic-gate
12290Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
12300Sstevel@tonic-gate "ehci_remove_intr_qh:");
12310Sstevel@tonic-gate
12320Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
12330Sstevel@tonic-gate
12340Sstevel@tonic-gate prev_qh = ehci_qh_iommu_to_cpu(ehcip, Get_QH(qh->qh_prev));
12350Sstevel@tonic-gate next_qh = ehci_qh_iommu_to_cpu(ehcip,
12360Sstevel@tonic-gate Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
12370Sstevel@tonic-gate
12380Sstevel@tonic-gate /* Make sure this QH is in the list */
12390Sstevel@tonic-gate ASSERT(prev_qh != NULL);
12400Sstevel@tonic-gate
12410Sstevel@tonic-gate if (next_qh) {
12420Sstevel@tonic-gate /* Update previous qh's link pointer */
12430Sstevel@tonic-gate Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
12440Sstevel@tonic-gate
12450Sstevel@tonic-gate if (Get_QH(next_qh->qh_state) != EHCI_QH_STATIC) {
12460Sstevel@tonic-gate /* Set the previous pointer of the next one */
12470Sstevel@tonic-gate Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
12480Sstevel@tonic-gate }
12490Sstevel@tonic-gate } else {
12500Sstevel@tonic-gate /* Update previous qh's link pointer */
12510Sstevel@tonic-gate Set_QH(prev_qh->qh_link_ptr,
12520Sstevel@tonic-gate (Get_QH(qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
12530Sstevel@tonic-gate }
12540Sstevel@tonic-gate
12550Sstevel@tonic-gate /* qh_prev to indicate it is no longer in the circular list */
12560Sstevel@tonic-gate Set_QH(qh->qh_prev, NULL);
12570Sstevel@tonic-gate
12580Sstevel@tonic-gate if (reclaim) {
12590Sstevel@tonic-gate ehci_insert_qh_on_reclaim_list(ehcip, pp);
12600Sstevel@tonic-gate }
12610Sstevel@tonic-gate }
12620Sstevel@tonic-gate
12630Sstevel@tonic-gate
12640Sstevel@tonic-gate /*
12650Sstevel@tonic-gate * ehci_insert_qh_on_reclaim_list:
12660Sstevel@tonic-gate *
12670Sstevel@tonic-gate * Insert Endpoint onto the reclaim list
12680Sstevel@tonic-gate */
12690Sstevel@tonic-gate static void
ehci_insert_qh_on_reclaim_list(ehci_state_t * ehcip,ehci_pipe_private_t * pp)12700Sstevel@tonic-gate ehci_insert_qh_on_reclaim_list(
12710Sstevel@tonic-gate ehci_state_t *ehcip,
12720Sstevel@tonic-gate ehci_pipe_private_t *pp)
12730Sstevel@tonic-gate {
12740Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh; /* qh to be removed */
12750Sstevel@tonic-gate ehci_qh_t *next_qh, *prev_qh;
12760Sstevel@tonic-gate usb_frame_number_t frame_number;
12770Sstevel@tonic-gate
12780Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
12790Sstevel@tonic-gate
12800Sstevel@tonic-gate /*
12810Sstevel@tonic-gate * Read current usb frame number and add appropriate number of
12820Sstevel@tonic-gate * usb frames needs to wait before reclaiming current endpoint.
12830Sstevel@tonic-gate */
12840Sstevel@tonic-gate frame_number =
12850Sstevel@tonic-gate ehci_get_current_frame_number(ehcip) + MAX_SOF_WAIT_COUNT;
12860Sstevel@tonic-gate
12870Sstevel@tonic-gate /* Store 32-bit ID */
12880Sstevel@tonic-gate Set_QH(qh->qh_reclaim_frame,
12890Sstevel@tonic-gate ((uint32_t)(EHCI_GET_ID((void *)(uintptr_t)frame_number))));
12900Sstevel@tonic-gate
12910Sstevel@tonic-gate /* Insert the endpoint onto the reclamation list */
12920Sstevel@tonic-gate if (ehcip->ehci_reclaim_list) {
12930Sstevel@tonic-gate next_qh = ehcip->ehci_reclaim_list;
12940Sstevel@tonic-gate
12950Sstevel@tonic-gate while (next_qh) {
12960Sstevel@tonic-gate prev_qh = next_qh;
12970Sstevel@tonic-gate next_qh = ehci_qh_iommu_to_cpu(ehcip,
12980Sstevel@tonic-gate Get_QH(next_qh->qh_reclaim_next));
12990Sstevel@tonic-gate }
13000Sstevel@tonic-gate
13010Sstevel@tonic-gate Set_QH(prev_qh->qh_reclaim_next,
13020Sstevel@tonic-gate ehci_qh_cpu_to_iommu(ehcip, qh));
13030Sstevel@tonic-gate } else {
13040Sstevel@tonic-gate ehcip->ehci_reclaim_list = qh;
13050Sstevel@tonic-gate }
13060Sstevel@tonic-gate
13070Sstevel@tonic-gate ASSERT(Get_QH(qh->qh_reclaim_next) == NULL);
13080Sstevel@tonic-gate }
13090Sstevel@tonic-gate
13100Sstevel@tonic-gate
13110Sstevel@tonic-gate /*
13120Sstevel@tonic-gate * ehci_deallocate_qh:
13130Sstevel@tonic-gate *
13140Sstevel@tonic-gate * Deallocate a Host Controller's (HC) Endpoint Descriptor (QH).
13150Sstevel@tonic-gate *
13160Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
13170Sstevel@tonic-gate */
13180Sstevel@tonic-gate void
ehci_deallocate_qh(ehci_state_t * ehcip,ehci_qh_t * old_qh)13190Sstevel@tonic-gate ehci_deallocate_qh(
13200Sstevel@tonic-gate ehci_state_t *ehcip,
13210Sstevel@tonic-gate ehci_qh_t *old_qh)
13220Sstevel@tonic-gate {
13230Sstevel@tonic-gate ehci_qtd_t *first_dummy_qtd, *second_dummy_qtd;
13240Sstevel@tonic-gate
13250Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
13260Sstevel@tonic-gate "ehci_deallocate_qh:");
13270Sstevel@tonic-gate
13280Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
13290Sstevel@tonic-gate
13300Sstevel@tonic-gate first_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
13310Sstevel@tonic-gate (Get_QH(old_qh->qh_next_qtd) & EHCI_QH_NEXT_QTD_PTR));
13320Sstevel@tonic-gate
13330Sstevel@tonic-gate if (first_dummy_qtd) {
13340Sstevel@tonic-gate ASSERT(Get_QTD(first_dummy_qtd->qtd_state) == EHCI_QTD_DUMMY);
13350Sstevel@tonic-gate
13360Sstevel@tonic-gate second_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
13370Sstevel@tonic-gate Get_QTD(first_dummy_qtd->qtd_next_qtd));
13380Sstevel@tonic-gate
13390Sstevel@tonic-gate if (second_dummy_qtd) {
13400Sstevel@tonic-gate ASSERT(Get_QTD(second_dummy_qtd->qtd_state) ==
13410Sstevel@tonic-gate EHCI_QTD_DUMMY);
13420Sstevel@tonic-gate
13430Sstevel@tonic-gate ehci_deallocate_qtd(ehcip, second_dummy_qtd);
13440Sstevel@tonic-gate }
13450Sstevel@tonic-gate
13460Sstevel@tonic-gate ehci_deallocate_qtd(ehcip, first_dummy_qtd);
13470Sstevel@tonic-gate }
13480Sstevel@tonic-gate
13490Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
13500Sstevel@tonic-gate "ehci_deallocate_qh: Deallocated 0x%p", (void *)old_qh);
13510Sstevel@tonic-gate
13520Sstevel@tonic-gate Set_QH(old_qh->qh_state, EHCI_QH_FREE);
13530Sstevel@tonic-gate }
13540Sstevel@tonic-gate
13550Sstevel@tonic-gate
13560Sstevel@tonic-gate /*
13570Sstevel@tonic-gate * ehci_qh_cpu_to_iommu:
13580Sstevel@tonic-gate *
13590Sstevel@tonic-gate * This function converts for the given Endpoint Descriptor (QH) CPU address
13600Sstevel@tonic-gate * to IO address.
13610Sstevel@tonic-gate *
13620Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
13630Sstevel@tonic-gate */
13640Sstevel@tonic-gate uint32_t
ehci_qh_cpu_to_iommu(ehci_state_t * ehcip,ehci_qh_t * addr)13650Sstevel@tonic-gate ehci_qh_cpu_to_iommu(
13660Sstevel@tonic-gate ehci_state_t *ehcip,
13670Sstevel@tonic-gate ehci_qh_t *addr)
13680Sstevel@tonic-gate {
13690Sstevel@tonic-gate uint32_t qh;
13700Sstevel@tonic-gate
13710Sstevel@tonic-gate qh = (uint32_t)ehcip->ehci_qh_pool_cookie.dmac_address +
13720Sstevel@tonic-gate (uint32_t)((uintptr_t)addr - (uintptr_t)(ehcip->ehci_qh_pool_addr));
13730Sstevel@tonic-gate
13740Sstevel@tonic-gate ASSERT(qh >= ehcip->ehci_qh_pool_cookie.dmac_address);
13750Sstevel@tonic-gate ASSERT(qh <= ehcip->ehci_qh_pool_cookie.dmac_address +
13760Sstevel@tonic-gate sizeof (ehci_qh_t) * ehci_qh_pool_size);
13770Sstevel@tonic-gate
13780Sstevel@tonic-gate return (qh);
13790Sstevel@tonic-gate }
13800Sstevel@tonic-gate
13810Sstevel@tonic-gate
13820Sstevel@tonic-gate /*
13830Sstevel@tonic-gate * ehci_qh_iommu_to_cpu:
13840Sstevel@tonic-gate *
13850Sstevel@tonic-gate * This function converts for the given Endpoint Descriptor (QH) IO address
13860Sstevel@tonic-gate * to CPU address.
13870Sstevel@tonic-gate */
13880Sstevel@tonic-gate ehci_qh_t *
ehci_qh_iommu_to_cpu(ehci_state_t * ehcip,uintptr_t addr)13890Sstevel@tonic-gate ehci_qh_iommu_to_cpu(
13900Sstevel@tonic-gate ehci_state_t *ehcip,
13910Sstevel@tonic-gate uintptr_t addr)
13920Sstevel@tonic-gate {
13930Sstevel@tonic-gate ehci_qh_t *qh;
13940Sstevel@tonic-gate
13950Sstevel@tonic-gate if (addr == NULL) {
13960Sstevel@tonic-gate
13970Sstevel@tonic-gate return (NULL);
13980Sstevel@tonic-gate }
13990Sstevel@tonic-gate
14000Sstevel@tonic-gate qh = (ehci_qh_t *)((uintptr_t)
14010Sstevel@tonic-gate (addr - ehcip->ehci_qh_pool_cookie.dmac_address) +
14020Sstevel@tonic-gate (uintptr_t)ehcip->ehci_qh_pool_addr);
14030Sstevel@tonic-gate
14040Sstevel@tonic-gate ASSERT(qh >= ehcip->ehci_qh_pool_addr);
14050Sstevel@tonic-gate ASSERT((uintptr_t)qh <= (uintptr_t)ehcip->ehci_qh_pool_addr +
14060Sstevel@tonic-gate (uintptr_t)(sizeof (ehci_qh_t) * ehci_qh_pool_size));
14070Sstevel@tonic-gate
14080Sstevel@tonic-gate return (qh);
14090Sstevel@tonic-gate }
14100Sstevel@tonic-gate
14110Sstevel@tonic-gate
14120Sstevel@tonic-gate /*
14130Sstevel@tonic-gate * Transfer Descriptor manipulations functions
14140Sstevel@tonic-gate */
14150Sstevel@tonic-gate
14160Sstevel@tonic-gate /*
14170Sstevel@tonic-gate * ehci_initialize_dummy:
14180Sstevel@tonic-gate *
14190Sstevel@tonic-gate * An Endpoint Descriptor (QH) has a dummy Transfer Descriptor (QTD) on the
14200Sstevel@tonic-gate * end of its QTD list. Initially, both the head and tail pointers of the QH
14210Sstevel@tonic-gate * point to the dummy QTD.
14220Sstevel@tonic-gate */
14230Sstevel@tonic-gate static int
ehci_initialize_dummy(ehci_state_t * ehcip,ehci_qh_t * qh)14240Sstevel@tonic-gate ehci_initialize_dummy(
14250Sstevel@tonic-gate ehci_state_t *ehcip,
14260Sstevel@tonic-gate ehci_qh_t *qh)
14270Sstevel@tonic-gate {
14280Sstevel@tonic-gate ehci_qtd_t *first_dummy_qtd, *second_dummy_qtd;
14290Sstevel@tonic-gate
14300Sstevel@tonic-gate /* Allocate first dummy QTD */
14310Sstevel@tonic-gate first_dummy_qtd = ehci_allocate_qtd_from_pool(ehcip);
14320Sstevel@tonic-gate
14330Sstevel@tonic-gate if (first_dummy_qtd == NULL) {
14340Sstevel@tonic-gate return (USB_NO_RESOURCES);
14350Sstevel@tonic-gate }
14360Sstevel@tonic-gate
14370Sstevel@tonic-gate /* Allocate second dummy QTD */
14380Sstevel@tonic-gate second_dummy_qtd = ehci_allocate_qtd_from_pool(ehcip);
14390Sstevel@tonic-gate
14400Sstevel@tonic-gate if (second_dummy_qtd == NULL) {
14410Sstevel@tonic-gate /* Deallocate first dummy QTD */
14420Sstevel@tonic-gate ehci_deallocate_qtd(ehcip, first_dummy_qtd);
14430Sstevel@tonic-gate
14440Sstevel@tonic-gate return (USB_NO_RESOURCES);
14450Sstevel@tonic-gate }
14460Sstevel@tonic-gate
14470Sstevel@tonic-gate /* Next QTD pointer of an QH point to this new dummy QTD */
14480Sstevel@tonic-gate Set_QH(qh->qh_next_qtd, ehci_qtd_cpu_to_iommu(ehcip,
14490Sstevel@tonic-gate first_dummy_qtd) & EHCI_QH_NEXT_QTD_PTR);
14500Sstevel@tonic-gate
14510Sstevel@tonic-gate /* Set qh's dummy qtd field */
14520Sstevel@tonic-gate Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, first_dummy_qtd));
14530Sstevel@tonic-gate
14540Sstevel@tonic-gate /* Set first_dummy's next qtd pointer */
14550Sstevel@tonic-gate Set_QTD(first_dummy_qtd->qtd_next_qtd,
14560Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, second_dummy_qtd));
14570Sstevel@tonic-gate
14580Sstevel@tonic-gate return (USB_SUCCESS);
14590Sstevel@tonic-gate }
14600Sstevel@tonic-gate
14610Sstevel@tonic-gate /*
14620Sstevel@tonic-gate * ehci_allocate_ctrl_resources:
14630Sstevel@tonic-gate *
14640Sstevel@tonic-gate * Calculates the number of tds necessary for a ctrl transfer, and allocates
14650Sstevel@tonic-gate * all the resources necessary.
14660Sstevel@tonic-gate *
14670Sstevel@tonic-gate * Returns NULL if there is insufficient resources otherwise TW.
14680Sstevel@tonic-gate */
14690Sstevel@tonic-gate ehci_trans_wrapper_t *
ehci_allocate_ctrl_resources(ehci_state_t * ehcip,ehci_pipe_private_t * pp,usb_ctrl_req_t * ctrl_reqp,usb_flags_t usb_flags)14700Sstevel@tonic-gate ehci_allocate_ctrl_resources(
14710Sstevel@tonic-gate ehci_state_t *ehcip,
14720Sstevel@tonic-gate ehci_pipe_private_t *pp,
14730Sstevel@tonic-gate usb_ctrl_req_t *ctrl_reqp,
14740Sstevel@tonic-gate usb_flags_t usb_flags)
14750Sstevel@tonic-gate {
14760Sstevel@tonic-gate size_t qtd_count = 2;
14771500Ssl147100 size_t ctrl_buf_size;
14780Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
14790Sstevel@tonic-gate
14800Sstevel@tonic-gate /* Add one more td for data phase */
14810Sstevel@tonic-gate if (ctrl_reqp->ctrl_wLength) {
14820Sstevel@tonic-gate qtd_count += 1;
14830Sstevel@tonic-gate }
14840Sstevel@tonic-gate
14851500Ssl147100 /*
14861500Ssl147100 * If we have a control data phase, the data buffer starts
14871500Ssl147100 * on the next 4K page boundary. So the TW buffer is allocated
14881500Ssl147100 * to be larger than required. The buffer in the range of
14891500Ssl147100 * [SETUP_SIZE, EHCI_MAX_QTD_BUF_SIZE) is just for padding
14901500Ssl147100 * and not to be transferred.
14911500Ssl147100 */
14921500Ssl147100 if (ctrl_reqp->ctrl_wLength) {
14931500Ssl147100 ctrl_buf_size = EHCI_MAX_QTD_BUF_SIZE +
14941500Ssl147100 ctrl_reqp->ctrl_wLength;
14951500Ssl147100 } else {
14961500Ssl147100 ctrl_buf_size = SETUP_SIZE;
14971500Ssl147100 }
14981500Ssl147100
14991500Ssl147100 tw = ehci_allocate_tw_resources(ehcip, pp, ctrl_buf_size,
15000Sstevel@tonic-gate usb_flags, qtd_count);
15010Sstevel@tonic-gate
15020Sstevel@tonic-gate return (tw);
15030Sstevel@tonic-gate }
15040Sstevel@tonic-gate
15050Sstevel@tonic-gate /*
15060Sstevel@tonic-gate * ehci_insert_ctrl_req:
15070Sstevel@tonic-gate *
15080Sstevel@tonic-gate * Create a Transfer Descriptor (QTD) and a data buffer for a control endpoint.
15090Sstevel@tonic-gate */
15100Sstevel@tonic-gate /* ARGSUSED */
15110Sstevel@tonic-gate void
ehci_insert_ctrl_req(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_ctrl_req_t * ctrl_reqp,ehci_trans_wrapper_t * tw,usb_flags_t usb_flags)15120Sstevel@tonic-gate ehci_insert_ctrl_req(
15130Sstevel@tonic-gate ehci_state_t *ehcip,
15140Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
15150Sstevel@tonic-gate usb_ctrl_req_t *ctrl_reqp,
15160Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
15170Sstevel@tonic-gate usb_flags_t usb_flags)
15180Sstevel@tonic-gate {
15190Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
15200Sstevel@tonic-gate uchar_t bmRequestType = ctrl_reqp->ctrl_bmRequestType;
15210Sstevel@tonic-gate uchar_t bRequest = ctrl_reqp->ctrl_bRequest;
15220Sstevel@tonic-gate uint16_t wValue = ctrl_reqp->ctrl_wValue;
15230Sstevel@tonic-gate uint16_t wIndex = ctrl_reqp->ctrl_wIndex;
15240Sstevel@tonic-gate uint16_t wLength = ctrl_reqp->ctrl_wLength;
15250Sstevel@tonic-gate mblk_t *data = ctrl_reqp->ctrl_data;
15260Sstevel@tonic-gate uint32_t ctrl = 0;
15270Sstevel@tonic-gate uint8_t setup_packet[8];
15280Sstevel@tonic-gate
15290Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
15300Sstevel@tonic-gate "ehci_insert_ctrl_req:");
15310Sstevel@tonic-gate
15320Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
15330Sstevel@tonic-gate
15340Sstevel@tonic-gate /*
15350Sstevel@tonic-gate * Save current control request pointer and timeout values
15360Sstevel@tonic-gate * in transfer wrapper.
15370Sstevel@tonic-gate */
15380Sstevel@tonic-gate tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
15390Sstevel@tonic-gate tw->tw_timeout = ctrl_reqp->ctrl_timeout ?
15400Sstevel@tonic-gate ctrl_reqp->ctrl_timeout : EHCI_DEFAULT_XFER_TIMEOUT;
15410Sstevel@tonic-gate
15420Sstevel@tonic-gate /*
15430Sstevel@tonic-gate * Initialize the callback and any callback data for when
15440Sstevel@tonic-gate * the qtd completes.
15450Sstevel@tonic-gate */
15460Sstevel@tonic-gate tw->tw_handle_qtd = ehci_handle_ctrl_qtd;
15470Sstevel@tonic-gate tw->tw_handle_callback_value = NULL;
15480Sstevel@tonic-gate
15490Sstevel@tonic-gate /*
15500Sstevel@tonic-gate * swap the setup bytes where necessary since we specified
15510Sstevel@tonic-gate * NEVERSWAP
15520Sstevel@tonic-gate */
15530Sstevel@tonic-gate setup_packet[0] = bmRequestType;
15540Sstevel@tonic-gate setup_packet[1] = bRequest;
15557492SZhigang.Lu@Sun.COM setup_packet[2] = (uint8_t)wValue;
15560Sstevel@tonic-gate setup_packet[3] = wValue >> 8;
15577492SZhigang.Lu@Sun.COM setup_packet[4] = (uint8_t)wIndex;
15580Sstevel@tonic-gate setup_packet[5] = wIndex >> 8;
15597492SZhigang.Lu@Sun.COM setup_packet[6] = (uint8_t)wLength;
15600Sstevel@tonic-gate setup_packet[7] = wLength >> 8;
15610Sstevel@tonic-gate
15620Sstevel@tonic-gate bcopy(setup_packet, tw->tw_buf, SETUP_SIZE);
15630Sstevel@tonic-gate
15640Sstevel@tonic-gate Sync_IO_Buffer_for_device(tw->tw_dmahandle, SETUP_SIZE);
15650Sstevel@tonic-gate
15660Sstevel@tonic-gate ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_0 | EHCI_QTD_CTRL_SETUP_PID);
15670Sstevel@tonic-gate
15680Sstevel@tonic-gate /*
15690Sstevel@tonic-gate * The QTD's are placed on the QH one at a time.
15700Sstevel@tonic-gate * Once this QTD is placed on the done list, the
15710Sstevel@tonic-gate * data or status phase QTD will be enqueued.
15720Sstevel@tonic-gate */
15731500Ssl147100 (void) ehci_insert_qtd(ehcip, ctrl, 0, SETUP_SIZE,
15740Sstevel@tonic-gate EHCI_CTRL_SETUP_PHASE, pp, tw);
15750Sstevel@tonic-gate
15760Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
15770Sstevel@tonic-gate "ehci_insert_ctrl_req: pp 0x%p", (void *)pp);
15780Sstevel@tonic-gate
15790Sstevel@tonic-gate /*
15800Sstevel@tonic-gate * If this control transfer has a data phase, record the
15810Sstevel@tonic-gate * direction. If the data phase is an OUT transaction,
15820Sstevel@tonic-gate * copy the data into the buffer of the transfer wrapper.
15830Sstevel@tonic-gate */
15840Sstevel@tonic-gate if (wLength != 0) {
15850Sstevel@tonic-gate /* There is a data stage. Find the direction */
15860Sstevel@tonic-gate if (bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
15870Sstevel@tonic-gate tw->tw_direction = EHCI_QTD_CTRL_IN_PID;
15880Sstevel@tonic-gate } else {
15890Sstevel@tonic-gate tw->tw_direction = EHCI_QTD_CTRL_OUT_PID;
15900Sstevel@tonic-gate
15910Sstevel@tonic-gate /* Copy the data into the message */
15921500Ssl147100 bcopy(data->b_rptr, tw->tw_buf + EHCI_MAX_QTD_BUF_SIZE,
15935441Ssl147100 wLength);
15940Sstevel@tonic-gate
15950Sstevel@tonic-gate Sync_IO_Buffer_for_device(tw->tw_dmahandle,
15965441Ssl147100 wLength + EHCI_MAX_QTD_BUF_SIZE);
15970Sstevel@tonic-gate }
15980Sstevel@tonic-gate
15990Sstevel@tonic-gate ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1 | tw->tw_direction);
16000Sstevel@tonic-gate
16010Sstevel@tonic-gate /*
16020Sstevel@tonic-gate * Create the QTD. If this is an OUT transaction,
16030Sstevel@tonic-gate * the data is already in the buffer of the TW.
16041500Ssl147100 * The transfer should start from EHCI_MAX_QTD_BUF_SIZE
16051500Ssl147100 * which is 4K aligned, though the ctrl phase only
16061500Ssl147100 * transfers a length of SETUP_SIZE. The padding data
16071500Ssl147100 * in the TW buffer are discarded.
16080Sstevel@tonic-gate */
16091500Ssl147100 (void) ehci_insert_qtd(ehcip, ctrl, EHCI_MAX_QTD_BUF_SIZE,
16101500Ssl147100 tw->tw_length - EHCI_MAX_QTD_BUF_SIZE,
16111500Ssl147100 EHCI_CTRL_DATA_PHASE, pp, tw);
16120Sstevel@tonic-gate
16130Sstevel@tonic-gate /*
16140Sstevel@tonic-gate * The direction of the STATUS QTD depends on
16150Sstevel@tonic-gate * the direction of the transfer.
16160Sstevel@tonic-gate */
16170Sstevel@tonic-gate if (tw->tw_direction == EHCI_QTD_CTRL_IN_PID) {
16180Sstevel@tonic-gate ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1|
16190Sstevel@tonic-gate EHCI_QTD_CTRL_OUT_PID |
16200Sstevel@tonic-gate EHCI_QTD_CTRL_INTR_ON_COMPLETE);
16210Sstevel@tonic-gate } else {
16220Sstevel@tonic-gate ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1|
16230Sstevel@tonic-gate EHCI_QTD_CTRL_IN_PID |
16240Sstevel@tonic-gate EHCI_QTD_CTRL_INTR_ON_COMPLETE);
16250Sstevel@tonic-gate }
16260Sstevel@tonic-gate } else {
16270Sstevel@tonic-gate /*
16280Sstevel@tonic-gate * There is no data stage, then initiate
16290Sstevel@tonic-gate * status phase from the host.
16300Sstevel@tonic-gate */
16310Sstevel@tonic-gate ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1 |
16320Sstevel@tonic-gate EHCI_QTD_CTRL_IN_PID |
16330Sstevel@tonic-gate EHCI_QTD_CTRL_INTR_ON_COMPLETE);
16340Sstevel@tonic-gate }
16350Sstevel@tonic-gate
16360Sstevel@tonic-gate
16371500Ssl147100 (void) ehci_insert_qtd(ehcip, ctrl, 0, 0,
16380Sstevel@tonic-gate EHCI_CTRL_STATUS_PHASE, pp, tw);
16390Sstevel@tonic-gate
16400Sstevel@tonic-gate /* Start the timer for this control transfer */
16410Sstevel@tonic-gate ehci_start_xfer_timer(ehcip, pp, tw);
16420Sstevel@tonic-gate }
16430Sstevel@tonic-gate
16440Sstevel@tonic-gate
16450Sstevel@tonic-gate /*
16460Sstevel@tonic-gate * ehci_allocate_bulk_resources:
16470Sstevel@tonic-gate *
16480Sstevel@tonic-gate * Calculates the number of tds necessary for a ctrl transfer, and allocates
16490Sstevel@tonic-gate * all the resources necessary.
16500Sstevel@tonic-gate *
16510Sstevel@tonic-gate * Returns NULL if there is insufficient resources otherwise TW.
16520Sstevel@tonic-gate */
16530Sstevel@tonic-gate ehci_trans_wrapper_t *
ehci_allocate_bulk_resources(ehci_state_t * ehcip,ehci_pipe_private_t * pp,usb_bulk_req_t * bulk_reqp,usb_flags_t usb_flags)16540Sstevel@tonic-gate ehci_allocate_bulk_resources(
16550Sstevel@tonic-gate ehci_state_t *ehcip,
16560Sstevel@tonic-gate ehci_pipe_private_t *pp,
16570Sstevel@tonic-gate usb_bulk_req_t *bulk_reqp,
16580Sstevel@tonic-gate usb_flags_t usb_flags)
16590Sstevel@tonic-gate {
16600Sstevel@tonic-gate size_t qtd_count = 0;
16610Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
16620Sstevel@tonic-gate
16630Sstevel@tonic-gate /* Check the size of bulk request */
16640Sstevel@tonic-gate if (bulk_reqp->bulk_len > EHCI_MAX_BULK_XFER_SIZE) {
16650Sstevel@tonic-gate
16660Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
16670Sstevel@tonic-gate "ehci_allocate_bulk_resources: Bulk request size 0x%x is "
16680Sstevel@tonic-gate "more than 0x%x", bulk_reqp->bulk_len,
16690Sstevel@tonic-gate EHCI_MAX_BULK_XFER_SIZE);
16700Sstevel@tonic-gate
16710Sstevel@tonic-gate return (NULL);
16720Sstevel@tonic-gate }
16730Sstevel@tonic-gate
16740Sstevel@tonic-gate /* Get the required bulk packet size */
16750Sstevel@tonic-gate qtd_count = bulk_reqp->bulk_len / EHCI_MAX_QTD_XFER_SIZE;
16762495Sgc161489 if (bulk_reqp->bulk_len % EHCI_MAX_QTD_XFER_SIZE ||
16775441Ssl147100 bulk_reqp->bulk_len == 0) {
16780Sstevel@tonic-gate qtd_count += 1;
16790Sstevel@tonic-gate }
16800Sstevel@tonic-gate
16810Sstevel@tonic-gate tw = ehci_allocate_tw_resources(ehcip, pp, bulk_reqp->bulk_len,
16820Sstevel@tonic-gate usb_flags, qtd_count);
16830Sstevel@tonic-gate
16840Sstevel@tonic-gate return (tw);
16850Sstevel@tonic-gate }
16860Sstevel@tonic-gate
16870Sstevel@tonic-gate /*
16880Sstevel@tonic-gate * ehci_insert_bulk_req:
16890Sstevel@tonic-gate *
16900Sstevel@tonic-gate * Create a Transfer Descriptor (QTD) and a data buffer for a bulk
16910Sstevel@tonic-gate * endpoint.
16920Sstevel@tonic-gate */
16930Sstevel@tonic-gate /* ARGSUSED */
16940Sstevel@tonic-gate void
ehci_insert_bulk_req(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_bulk_req_t * bulk_reqp,ehci_trans_wrapper_t * tw,usb_flags_t flags)16950Sstevel@tonic-gate ehci_insert_bulk_req(
16960Sstevel@tonic-gate ehci_state_t *ehcip,
16970Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
16980Sstevel@tonic-gate usb_bulk_req_t *bulk_reqp,
16990Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
17000Sstevel@tonic-gate usb_flags_t flags)
17010Sstevel@tonic-gate {
17020Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
17030Sstevel@tonic-gate uint_t bulk_pkt_size, count;
17040Sstevel@tonic-gate size_t residue = 0, len = 0;
17050Sstevel@tonic-gate uint32_t ctrl = 0;
17060Sstevel@tonic-gate int pipe_dir;
17070Sstevel@tonic-gate
17080Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
17090Sstevel@tonic-gate "ehci_insert_bulk_req: bulk_reqp = 0x%p flags = 0x%x",
17106898Sfb209375 (void *)bulk_reqp, flags);
17110Sstevel@tonic-gate
17120Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
17130Sstevel@tonic-gate
17140Sstevel@tonic-gate /* Get the bulk pipe direction */
17150Sstevel@tonic-gate pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
17160Sstevel@tonic-gate
17170Sstevel@tonic-gate /* Get the required bulk packet size */
17180Sstevel@tonic-gate bulk_pkt_size = min(bulk_reqp->bulk_len, EHCI_MAX_QTD_XFER_SIZE);
17190Sstevel@tonic-gate
17202495Sgc161489 if (bulk_pkt_size) {
17212495Sgc161489 residue = tw->tw_length % bulk_pkt_size;
17222495Sgc161489 }
17230Sstevel@tonic-gate
17240Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
17250Sstevel@tonic-gate "ehci_insert_bulk_req: bulk_pkt_size = %d", bulk_pkt_size);
17260Sstevel@tonic-gate
17270Sstevel@tonic-gate /*
17280Sstevel@tonic-gate * Save current bulk request pointer and timeout values
17290Sstevel@tonic-gate * in transfer wrapper.
17300Sstevel@tonic-gate */
17310Sstevel@tonic-gate tw->tw_curr_xfer_reqp = (usb_opaque_t)bulk_reqp;
17320Sstevel@tonic-gate tw->tw_timeout = bulk_reqp->bulk_timeout;
17330Sstevel@tonic-gate
17340Sstevel@tonic-gate /*
17350Sstevel@tonic-gate * Initialize the callback and any callback
17360Sstevel@tonic-gate * data required when the qtd completes.
17370Sstevel@tonic-gate */
17380Sstevel@tonic-gate tw->tw_handle_qtd = ehci_handle_bulk_qtd;
17390Sstevel@tonic-gate tw->tw_handle_callback_value = NULL;
17400Sstevel@tonic-gate
17410Sstevel@tonic-gate tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT) ?
17420Sstevel@tonic-gate EHCI_QTD_CTRL_OUT_PID : EHCI_QTD_CTRL_IN_PID;
17430Sstevel@tonic-gate
17440Sstevel@tonic-gate if (tw->tw_direction == EHCI_QTD_CTRL_OUT_PID) {
17450Sstevel@tonic-gate
17462495Sgc161489 if (bulk_reqp->bulk_len) {
17472495Sgc161489 ASSERT(bulk_reqp->bulk_data != NULL);
17482495Sgc161489
17492495Sgc161489 bcopy(bulk_reqp->bulk_data->b_rptr, tw->tw_buf,
17505441Ssl147100 bulk_reqp->bulk_len);
17512495Sgc161489
17522495Sgc161489 Sync_IO_Buffer_for_device(tw->tw_dmahandle,
17535441Ssl147100 bulk_reqp->bulk_len);
17542495Sgc161489 }
17550Sstevel@tonic-gate }
17560Sstevel@tonic-gate
17570Sstevel@tonic-gate ctrl = tw->tw_direction;
17580Sstevel@tonic-gate
17590Sstevel@tonic-gate /* Insert all the bulk QTDs */
17600Sstevel@tonic-gate for (count = 0; count < tw->tw_num_qtds; count++) {
17610Sstevel@tonic-gate
17620Sstevel@tonic-gate /* Check for last qtd */
17630Sstevel@tonic-gate if (count == (tw->tw_num_qtds - 1)) {
17640Sstevel@tonic-gate
17650Sstevel@tonic-gate ctrl |= EHCI_QTD_CTRL_INTR_ON_COMPLETE;
17660Sstevel@tonic-gate
17670Sstevel@tonic-gate /* Check for inserting residue data */
17680Sstevel@tonic-gate if (residue) {
17697492SZhigang.Lu@Sun.COM bulk_pkt_size = (uint_t)residue;
17700Sstevel@tonic-gate }
17710Sstevel@tonic-gate }
17720Sstevel@tonic-gate
17730Sstevel@tonic-gate /* Insert the QTD onto the endpoint */
17741500Ssl147100 (void) ehci_insert_qtd(ehcip, ctrl, len, bulk_pkt_size,
17751500Ssl147100 0, pp, tw);
17760Sstevel@tonic-gate
17770Sstevel@tonic-gate len = len + bulk_pkt_size;
17780Sstevel@tonic-gate }
17790Sstevel@tonic-gate
17800Sstevel@tonic-gate /* Start the timer for this bulk transfer */
17810Sstevel@tonic-gate ehci_start_xfer_timer(ehcip, pp, tw);
17820Sstevel@tonic-gate }
17830Sstevel@tonic-gate
17840Sstevel@tonic-gate
17850Sstevel@tonic-gate /*
17860Sstevel@tonic-gate * ehci_start_periodic_pipe_polling:
17870Sstevel@tonic-gate *
17880Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
17890Sstevel@tonic-gate */
17900Sstevel@tonic-gate int
ehci_start_periodic_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_opaque_t periodic_in_reqp,usb_flags_t flags)17910Sstevel@tonic-gate ehci_start_periodic_pipe_polling(
17920Sstevel@tonic-gate ehci_state_t *ehcip,
17930Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
17940Sstevel@tonic-gate usb_opaque_t periodic_in_reqp,
17950Sstevel@tonic-gate usb_flags_t flags)
17960Sstevel@tonic-gate {
17970Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
17980Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
17990Sstevel@tonic-gate int error = USB_SUCCESS;
18000Sstevel@tonic-gate
18010Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
18020Sstevel@tonic-gate "ehci_start_periodic_pipe_polling: ep%d",
18030Sstevel@tonic-gate ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK);
18040Sstevel@tonic-gate
18050Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
18060Sstevel@tonic-gate
18070Sstevel@tonic-gate /*
18080Sstevel@tonic-gate * Check and handle start polling on root hub interrupt pipe.
18090Sstevel@tonic-gate */
18100Sstevel@tonic-gate if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
18110Sstevel@tonic-gate ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
18120Sstevel@tonic-gate USB_EP_ATTR_INTR)) {
18130Sstevel@tonic-gate
18140Sstevel@tonic-gate error = ehci_handle_root_hub_pipe_start_intr_polling(ph,
18150Sstevel@tonic-gate (usb_intr_req_t *)periodic_in_reqp, flags);
18160Sstevel@tonic-gate
18170Sstevel@tonic-gate return (error);
18180Sstevel@tonic-gate }
18190Sstevel@tonic-gate
18200Sstevel@tonic-gate switch (pp->pp_state) {
18210Sstevel@tonic-gate case EHCI_PIPE_STATE_IDLE:
18220Sstevel@tonic-gate /* Save the Original client's Periodic IN request */
18230Sstevel@tonic-gate pp->pp_client_periodic_in_reqp = periodic_in_reqp;
18240Sstevel@tonic-gate
18250Sstevel@tonic-gate /*
18260Sstevel@tonic-gate * This pipe is uninitialized or if a valid QTD is
18270Sstevel@tonic-gate * not found then insert a QTD on the interrupt IN
18280Sstevel@tonic-gate * endpoint.
18290Sstevel@tonic-gate */
18300Sstevel@tonic-gate error = ehci_start_pipe_polling(ehcip, ph, flags);
18310Sstevel@tonic-gate
18320Sstevel@tonic-gate if (error != USB_SUCCESS) {
18330Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_INTR,
18340Sstevel@tonic-gate ehcip->ehci_log_hdl,
18350Sstevel@tonic-gate "ehci_start_periodic_pipe_polling: "
18360Sstevel@tonic-gate "Start polling failed");
18370Sstevel@tonic-gate
18380Sstevel@tonic-gate pp->pp_client_periodic_in_reqp = NULL;
18390Sstevel@tonic-gate
18400Sstevel@tonic-gate return (error);
18410Sstevel@tonic-gate }
18420Sstevel@tonic-gate
18430Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
18446898Sfb209375 "ehci_start_periodic_pipe_polling: PP = 0x%p", (void *)pp);
18450Sstevel@tonic-gate
18460Sstevel@tonic-gate #ifdef DEBUG
18470Sstevel@tonic-gate switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
18480Sstevel@tonic-gate case USB_EP_ATTR_INTR:
18490Sstevel@tonic-gate ASSERT((pp->pp_tw_head != NULL) &&
18500Sstevel@tonic-gate (pp->pp_tw_tail != NULL));
18510Sstevel@tonic-gate break;
18520Sstevel@tonic-gate case USB_EP_ATTR_ISOCH:
18530Sstevel@tonic-gate ASSERT((pp->pp_itw_head != NULL) &&
18540Sstevel@tonic-gate (pp->pp_itw_tail != NULL));
18550Sstevel@tonic-gate break;
18560Sstevel@tonic-gate }
18570Sstevel@tonic-gate #endif
18580Sstevel@tonic-gate
18590Sstevel@tonic-gate break;
18600Sstevel@tonic-gate case EHCI_PIPE_STATE_ACTIVE:
18610Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_INTR,
18620Sstevel@tonic-gate ehcip->ehci_log_hdl,
18630Sstevel@tonic-gate "ehci_start_periodic_pipe_polling: "
18640Sstevel@tonic-gate "Polling is already in progress");
18650Sstevel@tonic-gate
18660Sstevel@tonic-gate error = USB_FAILURE;
18670Sstevel@tonic-gate break;
18680Sstevel@tonic-gate case EHCI_PIPE_STATE_ERROR:
18690Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_INTR,
18700Sstevel@tonic-gate ehcip->ehci_log_hdl,
18710Sstevel@tonic-gate "ehci_start_periodic_pipe_polling: "
18720Sstevel@tonic-gate "Pipe is halted and perform reset"
18730Sstevel@tonic-gate "before restart polling");
18740Sstevel@tonic-gate
18750Sstevel@tonic-gate error = USB_FAILURE;
18760Sstevel@tonic-gate break;
18770Sstevel@tonic-gate default:
18780Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_INTR,
18790Sstevel@tonic-gate ehcip->ehci_log_hdl,
18800Sstevel@tonic-gate "ehci_start_periodic_pipe_polling: "
18810Sstevel@tonic-gate "Undefined state");
18820Sstevel@tonic-gate
18830Sstevel@tonic-gate error = USB_FAILURE;
18840Sstevel@tonic-gate break;
18850Sstevel@tonic-gate }
18860Sstevel@tonic-gate
18870Sstevel@tonic-gate return (error);
18880Sstevel@tonic-gate }
18890Sstevel@tonic-gate
18900Sstevel@tonic-gate
18910Sstevel@tonic-gate /*
18920Sstevel@tonic-gate * ehci_start_pipe_polling:
18930Sstevel@tonic-gate *
18940Sstevel@tonic-gate * Insert the number of periodic requests corresponding to polling
18950Sstevel@tonic-gate * interval as calculated during pipe open.
18960Sstevel@tonic-gate */
18970Sstevel@tonic-gate static int
ehci_start_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)18980Sstevel@tonic-gate ehci_start_pipe_polling(
18990Sstevel@tonic-gate ehci_state_t *ehcip,
19000Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
19010Sstevel@tonic-gate usb_flags_t flags)
19020Sstevel@tonic-gate {
19030Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
19040Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
19050Sstevel@tonic-gate int error = USB_FAILURE;
19060Sstevel@tonic-gate
19070Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
19080Sstevel@tonic-gate "ehci_start_pipe_polling:");
19090Sstevel@tonic-gate
19100Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
19110Sstevel@tonic-gate
19120Sstevel@tonic-gate /*
19130Sstevel@tonic-gate * For the start polling, pp_max_periodic_req_cnt will be zero
19140Sstevel@tonic-gate * and for the restart polling request, it will be non zero.
19150Sstevel@tonic-gate *
19160Sstevel@tonic-gate * In case of start polling request, find out number of requests
19170Sstevel@tonic-gate * required for the Interrupt IN endpoints corresponding to the
19180Sstevel@tonic-gate * endpoint polling interval. For Isochronous IN endpoints, it is
19190Sstevel@tonic-gate * always fixed since its polling interval will be one ms.
19200Sstevel@tonic-gate */
19210Sstevel@tonic-gate if (pp->pp_max_periodic_req_cnt == 0) {
19220Sstevel@tonic-gate
19230Sstevel@tonic-gate ehci_set_periodic_pipe_polling(ehcip, ph);
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate
19260Sstevel@tonic-gate ASSERT(pp->pp_max_periodic_req_cnt != 0);
19270Sstevel@tonic-gate
19280Sstevel@tonic-gate switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
19290Sstevel@tonic-gate case USB_EP_ATTR_INTR:
19300Sstevel@tonic-gate error = ehci_start_intr_polling(ehcip, ph, flags);
19310Sstevel@tonic-gate break;
19320Sstevel@tonic-gate case USB_EP_ATTR_ISOCH:
19330Sstevel@tonic-gate error = ehci_start_isoc_polling(ehcip, ph, flags);
19340Sstevel@tonic-gate break;
19350Sstevel@tonic-gate }
19360Sstevel@tonic-gate
19370Sstevel@tonic-gate return (error);
19380Sstevel@tonic-gate }
19390Sstevel@tonic-gate
19400Sstevel@tonic-gate static int
ehci_start_intr_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)19410Sstevel@tonic-gate ehci_start_intr_polling(
19420Sstevel@tonic-gate ehci_state_t *ehcip,
19430Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
19440Sstevel@tonic-gate usb_flags_t flags)
19450Sstevel@tonic-gate {
19460Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
19470Sstevel@tonic-gate ehci_trans_wrapper_t *tw_list, *tw;
19480Sstevel@tonic-gate int i, total_tws;
19490Sstevel@tonic-gate int error = USB_SUCCESS;
19500Sstevel@tonic-gate
19510Sstevel@tonic-gate /* Allocate all the necessary resources for the IN transfer */
19520Sstevel@tonic-gate tw_list = NULL;
19530Sstevel@tonic-gate total_tws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
19540Sstevel@tonic-gate for (i = 0; i < total_tws; i += 1) {
19550Sstevel@tonic-gate tw = ehci_allocate_intr_resources(ehcip, ph, NULL, flags);
19560Sstevel@tonic-gate if (tw == NULL) {
19570Sstevel@tonic-gate error = USB_NO_RESOURCES;
19580Sstevel@tonic-gate /* There are not enough resources, deallocate the TWs */
19590Sstevel@tonic-gate tw = tw_list;
19600Sstevel@tonic-gate while (tw != NULL) {
19610Sstevel@tonic-gate tw_list = tw->tw_next;
19620Sstevel@tonic-gate ehci_deallocate_intr_in_resource(
19635441Ssl147100 ehcip, pp, tw);
19640Sstevel@tonic-gate ehci_deallocate_tw(ehcip, pp, tw);
19650Sstevel@tonic-gate tw = tw_list;
19660Sstevel@tonic-gate }
19670Sstevel@tonic-gate
19680Sstevel@tonic-gate return (error);
19690Sstevel@tonic-gate } else {
19700Sstevel@tonic-gate if (tw_list == NULL) {
19710Sstevel@tonic-gate tw_list = tw;
19720Sstevel@tonic-gate }
19730Sstevel@tonic-gate }
19740Sstevel@tonic-gate }
19750Sstevel@tonic-gate
19760Sstevel@tonic-gate while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
19770Sstevel@tonic-gate
19780Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
19790Sstevel@tonic-gate "ehci_start_pipe_polling: max = %d curr = %d tw = %p:",
19800Sstevel@tonic-gate pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
19816898Sfb209375 (void *)tw_list);
19820Sstevel@tonic-gate
19830Sstevel@tonic-gate tw = tw_list;
19840Sstevel@tonic-gate tw_list = tw->tw_next;
19850Sstevel@tonic-gate
19860Sstevel@tonic-gate ehci_insert_intr_req(ehcip, pp, tw, flags);
19870Sstevel@tonic-gate
19880Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt++;
19890Sstevel@tonic-gate }
19900Sstevel@tonic-gate
19910Sstevel@tonic-gate return (error);
19920Sstevel@tonic-gate }
19930Sstevel@tonic-gate
19940Sstevel@tonic-gate
19950Sstevel@tonic-gate /*
19960Sstevel@tonic-gate * ehci_set_periodic_pipe_polling:
19970Sstevel@tonic-gate *
19980Sstevel@tonic-gate * Calculate the number of periodic requests needed corresponding to the
19990Sstevel@tonic-gate * interrupt IN endpoints polling interval. Table below gives the number
20000Sstevel@tonic-gate * of periodic requests needed for the interrupt IN endpoints according
20010Sstevel@tonic-gate * to endpoint polling interval.
20020Sstevel@tonic-gate *
20030Sstevel@tonic-gate * Polling interval Number of periodic requests
20040Sstevel@tonic-gate *
20050Sstevel@tonic-gate * 1ms 4
20060Sstevel@tonic-gate * 2ms 2
20070Sstevel@tonic-gate * 4ms to 32ms 1
20080Sstevel@tonic-gate */
20090Sstevel@tonic-gate static void
ehci_set_periodic_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)20100Sstevel@tonic-gate ehci_set_periodic_pipe_polling(
20110Sstevel@tonic-gate ehci_state_t *ehcip,
20120Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
20130Sstevel@tonic-gate {
20140Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
20150Sstevel@tonic-gate usb_ep_descr_t *endpoint = &ph->p_ep;
20160Sstevel@tonic-gate uchar_t ep_attr = endpoint->bmAttributes;
20170Sstevel@tonic-gate uint_t interval;
20180Sstevel@tonic-gate
20190Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
20200Sstevel@tonic-gate "ehci_set_periodic_pipe_polling:");
20210Sstevel@tonic-gate
20220Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
20230Sstevel@tonic-gate
20240Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt = 0;
20250Sstevel@tonic-gate
20260Sstevel@tonic-gate /*
20270Sstevel@tonic-gate * Check usb flag whether USB_FLAGS_ONE_TIME_POLL flag is
20280Sstevel@tonic-gate * set and if so, set pp->pp_max_periodic_req_cnt to one.
20290Sstevel@tonic-gate */
20300Sstevel@tonic-gate if (((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) &&
20310Sstevel@tonic-gate (pp->pp_client_periodic_in_reqp)) {
20320Sstevel@tonic-gate usb_intr_req_t *intr_reqp = (usb_intr_req_t *)
20335441Ssl147100 pp->pp_client_periodic_in_reqp;
20340Sstevel@tonic-gate
20350Sstevel@tonic-gate if (intr_reqp->intr_attributes &
20360Sstevel@tonic-gate USB_ATTRS_ONE_XFER) {
20370Sstevel@tonic-gate
20380Sstevel@tonic-gate pp->pp_max_periodic_req_cnt = EHCI_INTR_XMS_REQS;
20390Sstevel@tonic-gate
20400Sstevel@tonic-gate return;
20410Sstevel@tonic-gate }
20420Sstevel@tonic-gate }
20430Sstevel@tonic-gate
20440Sstevel@tonic-gate mutex_enter(&ph->p_usba_device->usb_mutex);
20450Sstevel@tonic-gate
20460Sstevel@tonic-gate /*
20470Sstevel@tonic-gate * The ehci_adjust_polling_interval function will not fail
20480Sstevel@tonic-gate * at this instance since bandwidth allocation is already
20490Sstevel@tonic-gate * done. Here we are getting only the periodic interval.
20500Sstevel@tonic-gate */
20510Sstevel@tonic-gate interval = ehci_adjust_polling_interval(ehcip, endpoint,
20525441Ssl147100 ph->p_usba_device->usb_port_status);
20530Sstevel@tonic-gate
20540Sstevel@tonic-gate mutex_exit(&ph->p_usba_device->usb_mutex);
20550Sstevel@tonic-gate
20560Sstevel@tonic-gate switch (interval) {
20570Sstevel@tonic-gate case EHCI_INTR_1MS_POLL:
20580Sstevel@tonic-gate pp->pp_max_periodic_req_cnt = EHCI_INTR_1MS_REQS;
20590Sstevel@tonic-gate break;
20600Sstevel@tonic-gate case EHCI_INTR_2MS_POLL:
20610Sstevel@tonic-gate pp->pp_max_periodic_req_cnt = EHCI_INTR_2MS_REQS;
20620Sstevel@tonic-gate break;
20630Sstevel@tonic-gate default:
20640Sstevel@tonic-gate pp->pp_max_periodic_req_cnt = EHCI_INTR_XMS_REQS;
20650Sstevel@tonic-gate break;
20660Sstevel@tonic-gate }
20670Sstevel@tonic-gate
20680Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
20690Sstevel@tonic-gate "ehci_set_periodic_pipe_polling: Max periodic requests = %d",
20700Sstevel@tonic-gate pp->pp_max_periodic_req_cnt);
20710Sstevel@tonic-gate }
20720Sstevel@tonic-gate
20730Sstevel@tonic-gate /*
20740Sstevel@tonic-gate * ehci_allocate_intr_resources:
20750Sstevel@tonic-gate *
20760Sstevel@tonic-gate * Calculates the number of tds necessary for a intr transfer, and allocates
20770Sstevel@tonic-gate * all the necessary resources.
20780Sstevel@tonic-gate *
20790Sstevel@tonic-gate * Returns NULL if there is insufficient resources otherwise TW.
20800Sstevel@tonic-gate */
20810Sstevel@tonic-gate ehci_trans_wrapper_t *
ehci_allocate_intr_resources(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_intr_req_t * intr_reqp,usb_flags_t flags)20820Sstevel@tonic-gate ehci_allocate_intr_resources(
20830Sstevel@tonic-gate ehci_state_t *ehcip,
20840Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
20850Sstevel@tonic-gate usb_intr_req_t *intr_reqp,
20860Sstevel@tonic-gate usb_flags_t flags)
20870Sstevel@tonic-gate {
20880Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
20890Sstevel@tonic-gate int pipe_dir;
20900Sstevel@tonic-gate size_t qtd_count = 1;
20910Sstevel@tonic-gate size_t tw_length;
20920Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
20930Sstevel@tonic-gate
20940Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
20950Sstevel@tonic-gate "ehci_allocate_intr_resources:");
20960Sstevel@tonic-gate
20970Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
20980Sstevel@tonic-gate
20990Sstevel@tonic-gate pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
21000Sstevel@tonic-gate
21010Sstevel@tonic-gate /* Get the length of interrupt transfer & alloc data */
21020Sstevel@tonic-gate if (intr_reqp) {
21030Sstevel@tonic-gate tw_length = intr_reqp->intr_len;
21040Sstevel@tonic-gate } else {
21050Sstevel@tonic-gate ASSERT(pipe_dir == USB_EP_DIR_IN);
21060Sstevel@tonic-gate tw_length = (pp->pp_client_periodic_in_reqp) ?
21070Sstevel@tonic-gate (((usb_intr_req_t *)pp->
21080Sstevel@tonic-gate pp_client_periodic_in_reqp)->intr_len) :
21090Sstevel@tonic-gate ph->p_ep.wMaxPacketSize;
21100Sstevel@tonic-gate }
21110Sstevel@tonic-gate
21120Sstevel@tonic-gate /* Check the size of interrupt request */
21130Sstevel@tonic-gate if (tw_length > EHCI_MAX_QTD_XFER_SIZE) {
21140Sstevel@tonic-gate
21150Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
21160Sstevel@tonic-gate "ehci_allocate_intr_resources: Intr request size 0x%lx is "
21170Sstevel@tonic-gate "more than 0x%x", tw_length, EHCI_MAX_QTD_XFER_SIZE);
21180Sstevel@tonic-gate
21190Sstevel@tonic-gate return (NULL);
21200Sstevel@tonic-gate }
21210Sstevel@tonic-gate
21220Sstevel@tonic-gate if ((tw = ehci_allocate_tw_resources(ehcip, pp, tw_length, flags,
21230Sstevel@tonic-gate qtd_count)) == NULL) {
21240Sstevel@tonic-gate
21250Sstevel@tonic-gate return (NULL);
21260Sstevel@tonic-gate }
21270Sstevel@tonic-gate
21280Sstevel@tonic-gate if (pipe_dir == USB_EP_DIR_IN) {
21290Sstevel@tonic-gate if (ehci_allocate_intr_in_resource(ehcip, pp, tw, flags) !=
21300Sstevel@tonic-gate USB_SUCCESS) {
21310Sstevel@tonic-gate ehci_deallocate_tw(ehcip, pp, tw);
21320Sstevel@tonic-gate }
21330Sstevel@tonic-gate tw->tw_direction = EHCI_QTD_CTRL_IN_PID;
21340Sstevel@tonic-gate } else {
21352495Sgc161489 if (tw_length) {
21362495Sgc161489 ASSERT(intr_reqp->intr_data != NULL);
21372495Sgc161489
21382495Sgc161489 /* Copy the data into the buffer */
21392495Sgc161489 bcopy(intr_reqp->intr_data->b_rptr, tw->tw_buf,
21402495Sgc161489 intr_reqp->intr_len);
21412495Sgc161489
21422495Sgc161489 Sync_IO_Buffer_for_device(tw->tw_dmahandle,
21432495Sgc161489 intr_reqp->intr_len);
21442495Sgc161489 }
21450Sstevel@tonic-gate
21460Sstevel@tonic-gate tw->tw_curr_xfer_reqp = (usb_opaque_t)intr_reqp;
21470Sstevel@tonic-gate tw->tw_direction = EHCI_QTD_CTRL_OUT_PID;
21480Sstevel@tonic-gate }
21490Sstevel@tonic-gate
21500Sstevel@tonic-gate if (intr_reqp) {
21510Sstevel@tonic-gate tw->tw_timeout = intr_reqp->intr_timeout;
21520Sstevel@tonic-gate }
21530Sstevel@tonic-gate
21540Sstevel@tonic-gate /*
21550Sstevel@tonic-gate * Initialize the callback and any callback
21560Sstevel@tonic-gate * data required when the qtd completes.
21570Sstevel@tonic-gate */
21580Sstevel@tonic-gate tw->tw_handle_qtd = ehci_handle_intr_qtd;
21590Sstevel@tonic-gate tw->tw_handle_callback_value = NULL;
21600Sstevel@tonic-gate
21610Sstevel@tonic-gate return (tw);
21620Sstevel@tonic-gate }
21630Sstevel@tonic-gate
21640Sstevel@tonic-gate
21650Sstevel@tonic-gate /*
21660Sstevel@tonic-gate * ehci_insert_intr_req:
21670Sstevel@tonic-gate *
21680Sstevel@tonic-gate * Insert an Interrupt request into the Host Controller's periodic list.
21690Sstevel@tonic-gate */
21700Sstevel@tonic-gate /* ARGSUSED */
21710Sstevel@tonic-gate void
ehci_insert_intr_req(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw,usb_flags_t flags)21720Sstevel@tonic-gate ehci_insert_intr_req(
21730Sstevel@tonic-gate ehci_state_t *ehcip,
21740Sstevel@tonic-gate ehci_pipe_private_t *pp,
21750Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
21760Sstevel@tonic-gate usb_flags_t flags)
21770Sstevel@tonic-gate {
21780Sstevel@tonic-gate uint_t ctrl = 0;
21790Sstevel@tonic-gate
21800Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
21810Sstevel@tonic-gate
21820Sstevel@tonic-gate ASSERT(tw->tw_curr_xfer_reqp != NULL);
21830Sstevel@tonic-gate
21840Sstevel@tonic-gate ctrl = (tw->tw_direction | EHCI_QTD_CTRL_INTR_ON_COMPLETE);
21850Sstevel@tonic-gate
21860Sstevel@tonic-gate /* Insert another interrupt QTD */
21871500Ssl147100 (void) ehci_insert_qtd(ehcip, ctrl, 0, tw->tw_length, 0, pp, tw);
21880Sstevel@tonic-gate
21890Sstevel@tonic-gate /* Start the timer for this Interrupt transfer */
21900Sstevel@tonic-gate ehci_start_xfer_timer(ehcip, pp, tw);
21910Sstevel@tonic-gate }
21920Sstevel@tonic-gate
21930Sstevel@tonic-gate
21940Sstevel@tonic-gate /*
21950Sstevel@tonic-gate * ehci_stop_periodic_pipe_polling:
21960Sstevel@tonic-gate */
21970Sstevel@tonic-gate /* ARGSUSED */
21980Sstevel@tonic-gate int
ehci_stop_periodic_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)21990Sstevel@tonic-gate ehci_stop_periodic_pipe_polling(
22000Sstevel@tonic-gate ehci_state_t *ehcip,
22010Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
22020Sstevel@tonic-gate usb_flags_t flags)
22030Sstevel@tonic-gate {
22040Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
22050Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
22060Sstevel@tonic-gate
22070Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
22080Sstevel@tonic-gate "ehci_stop_periodic_pipe_polling: Flags = 0x%x", flags);
22090Sstevel@tonic-gate
22100Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
22110Sstevel@tonic-gate
22120Sstevel@tonic-gate /*
22130Sstevel@tonic-gate * Check and handle stop polling on root hub interrupt pipe.
22140Sstevel@tonic-gate */
22150Sstevel@tonic-gate if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
22160Sstevel@tonic-gate ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
22170Sstevel@tonic-gate USB_EP_ATTR_INTR)) {
22180Sstevel@tonic-gate
22190Sstevel@tonic-gate ehci_handle_root_hub_pipe_stop_intr_polling(ph, flags);
22200Sstevel@tonic-gate
22210Sstevel@tonic-gate return (USB_SUCCESS);
22220Sstevel@tonic-gate }
22230Sstevel@tonic-gate
22240Sstevel@tonic-gate if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) {
22250Sstevel@tonic-gate
22260Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
22270Sstevel@tonic-gate "ehci_stop_periodic_pipe_polling: "
22280Sstevel@tonic-gate "Polling already stopped");
22290Sstevel@tonic-gate
22300Sstevel@tonic-gate return (USB_SUCCESS);
22310Sstevel@tonic-gate }
22320Sstevel@tonic-gate
22330Sstevel@tonic-gate /* Set pipe state to pipe stop polling */
22340Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
22350Sstevel@tonic-gate
22360Sstevel@tonic-gate ehci_pipe_cleanup(ehcip, ph);
22370Sstevel@tonic-gate
22380Sstevel@tonic-gate return (USB_SUCCESS);
22390Sstevel@tonic-gate }
22400Sstevel@tonic-gate
22410Sstevel@tonic-gate
22420Sstevel@tonic-gate /*
22430Sstevel@tonic-gate * ehci_insert_qtd:
22440Sstevel@tonic-gate *
22450Sstevel@tonic-gate * Insert a Transfer Descriptor (QTD) on an Endpoint Descriptor (QH).
22460Sstevel@tonic-gate * Always returns USB_SUCCESS for now. Once Isoch has been implemented,
22470Sstevel@tonic-gate * it may return USB_FAILURE.
22480Sstevel@tonic-gate */
22490Sstevel@tonic-gate int
ehci_insert_qtd(ehci_state_t * ehcip,uint32_t qtd_ctrl,size_t qtd_dma_offs,size_t qtd_length,uint32_t qtd_ctrl_phase,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)22500Sstevel@tonic-gate ehci_insert_qtd(
22510Sstevel@tonic-gate ehci_state_t *ehcip,
22520Sstevel@tonic-gate uint32_t qtd_ctrl,
22531500Ssl147100 size_t qtd_dma_offs,
22540Sstevel@tonic-gate size_t qtd_length,
22550Sstevel@tonic-gate uint32_t qtd_ctrl_phase,
22560Sstevel@tonic-gate ehci_pipe_private_t *pp,
22570Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
22580Sstevel@tonic-gate {
22590Sstevel@tonic-gate ehci_qtd_t *curr_dummy_qtd, *next_dummy_qtd;
22600Sstevel@tonic-gate ehci_qtd_t *new_dummy_qtd;
22610Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh;
22620Sstevel@tonic-gate int error = USB_SUCCESS;
22630Sstevel@tonic-gate
22640Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
22650Sstevel@tonic-gate
22660Sstevel@tonic-gate /* Allocate new dummy QTD */
22670Sstevel@tonic-gate new_dummy_qtd = tw->tw_qtd_free_list;
22680Sstevel@tonic-gate
22690Sstevel@tonic-gate ASSERT(new_dummy_qtd != NULL);
22700Sstevel@tonic-gate tw->tw_qtd_free_list = ehci_qtd_iommu_to_cpu(ehcip,
22710Sstevel@tonic-gate Get_QTD(new_dummy_qtd->qtd_tw_next_qtd));
22720Sstevel@tonic-gate Set_QTD(new_dummy_qtd->qtd_tw_next_qtd, NULL);
22730Sstevel@tonic-gate
22740Sstevel@tonic-gate /* Get the current and next dummy QTDs */
22750Sstevel@tonic-gate curr_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
22760Sstevel@tonic-gate Get_QH(qh->qh_dummy_qtd));
22770Sstevel@tonic-gate next_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
22780Sstevel@tonic-gate Get_QTD(curr_dummy_qtd->qtd_next_qtd));
22790Sstevel@tonic-gate
22800Sstevel@tonic-gate /* Update QH's dummy qtd field */
22810Sstevel@tonic-gate Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, next_dummy_qtd));
22820Sstevel@tonic-gate
22830Sstevel@tonic-gate /* Update next dummy's next qtd pointer */
22840Sstevel@tonic-gate Set_QTD(next_dummy_qtd->qtd_next_qtd,
22850Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, new_dummy_qtd));
22860Sstevel@tonic-gate
22870Sstevel@tonic-gate /*
22880Sstevel@tonic-gate * Fill in the current dummy qtd and
22890Sstevel@tonic-gate * add the new dummy to the end.
22900Sstevel@tonic-gate */
22910Sstevel@tonic-gate ehci_fill_in_qtd(ehcip, curr_dummy_qtd, qtd_ctrl,
22921500Ssl147100 qtd_dma_offs, qtd_length, qtd_ctrl_phase, pp, tw);
22930Sstevel@tonic-gate
22940Sstevel@tonic-gate /* Insert this qtd onto the tw */
22950Sstevel@tonic-gate ehci_insert_qtd_on_tw(ehcip, tw, curr_dummy_qtd);
22960Sstevel@tonic-gate
22970Sstevel@tonic-gate /*
22980Sstevel@tonic-gate * Insert this qtd onto active qtd list.
22990Sstevel@tonic-gate * Don't insert polled mode qtd here.
23000Sstevel@tonic-gate */
23010Sstevel@tonic-gate if (pp->pp_flag != EHCI_POLLED_MODE_FLAG) {
23020Sstevel@tonic-gate /* Insert this qtd onto active qtd list */
23030Sstevel@tonic-gate ehci_insert_qtd_into_active_qtd_list(ehcip, curr_dummy_qtd);
23040Sstevel@tonic-gate }
23050Sstevel@tonic-gate
23060Sstevel@tonic-gate /* Print qh and qtd */
23070Sstevel@tonic-gate ehci_print_qh(ehcip, qh);
23080Sstevel@tonic-gate ehci_print_qtd(ehcip, curr_dummy_qtd);
23090Sstevel@tonic-gate
23100Sstevel@tonic-gate return (error);
23110Sstevel@tonic-gate }
23120Sstevel@tonic-gate
23130Sstevel@tonic-gate
23140Sstevel@tonic-gate /*
23150Sstevel@tonic-gate * ehci_allocate_qtd_from_pool:
23160Sstevel@tonic-gate *
23170Sstevel@tonic-gate * Allocate a Transfer Descriptor (QTD) from the QTD buffer pool.
23180Sstevel@tonic-gate */
23190Sstevel@tonic-gate static ehci_qtd_t *
ehci_allocate_qtd_from_pool(ehci_state_t * ehcip)23200Sstevel@tonic-gate ehci_allocate_qtd_from_pool(ehci_state_t *ehcip)
23210Sstevel@tonic-gate {
23220Sstevel@tonic-gate int i, ctrl;
23230Sstevel@tonic-gate ehci_qtd_t *qtd;
23240Sstevel@tonic-gate
23250Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
23260Sstevel@tonic-gate
23270Sstevel@tonic-gate /*
23280Sstevel@tonic-gate * Search for a blank Transfer Descriptor (QTD)
23290Sstevel@tonic-gate * in the QTD buffer pool.
23300Sstevel@tonic-gate */
23310Sstevel@tonic-gate for (i = 0; i < ehci_qtd_pool_size; i ++) {
23320Sstevel@tonic-gate ctrl = Get_QTD(ehcip->ehci_qtd_pool_addr[i].qtd_state);
23330Sstevel@tonic-gate if (ctrl == EHCI_QTD_FREE) {
23340Sstevel@tonic-gate break;
23350Sstevel@tonic-gate }
23360Sstevel@tonic-gate }
23370Sstevel@tonic-gate
23380Sstevel@tonic-gate if (i >= ehci_qtd_pool_size) {
23390Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
23400Sstevel@tonic-gate "ehci_allocate_qtd_from_pool: QTD exhausted");
23410Sstevel@tonic-gate
23420Sstevel@tonic-gate return (NULL);
23430Sstevel@tonic-gate }
23440Sstevel@tonic-gate
23450Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
23460Sstevel@tonic-gate "ehci_allocate_qtd_from_pool: Allocated %d", i);
23470Sstevel@tonic-gate
23480Sstevel@tonic-gate /* Create a new dummy for the end of the QTD list */
23490Sstevel@tonic-gate qtd = &ehcip->ehci_qtd_pool_addr[i];
23500Sstevel@tonic-gate
23510Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
23520Sstevel@tonic-gate "ehci_allocate_qtd_from_pool: qtd 0x%p", (void *)qtd);
23530Sstevel@tonic-gate
23540Sstevel@tonic-gate /* Mark the newly allocated QTD as a dummy */
23550Sstevel@tonic-gate Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
23560Sstevel@tonic-gate
23570Sstevel@tonic-gate /* Mark the status of this new QTD to halted state */
23580Sstevel@tonic-gate Set_QTD(qtd->qtd_ctrl, EHCI_QTD_CTRL_HALTED_XACT);
23590Sstevel@tonic-gate
23600Sstevel@tonic-gate /* Disable dummy QTD's next and alternate next pointers */
23610Sstevel@tonic-gate Set_QTD(qtd->qtd_next_qtd, EHCI_QTD_NEXT_QTD_PTR_VALID);
23620Sstevel@tonic-gate Set_QTD(qtd->qtd_alt_next_qtd, EHCI_QTD_ALT_NEXT_QTD_PTR_VALID);
23630Sstevel@tonic-gate
23640Sstevel@tonic-gate return (qtd);
23650Sstevel@tonic-gate }
23660Sstevel@tonic-gate
23670Sstevel@tonic-gate
23680Sstevel@tonic-gate /*
23690Sstevel@tonic-gate * ehci_fill_in_qtd:
23700Sstevel@tonic-gate *
23710Sstevel@tonic-gate * Fill in the fields of a Transfer Descriptor (QTD).
23721500Ssl147100 * The "Buffer Pointer" fields of a QTD are retrieved from the TW
23731500Ssl147100 * it is associated with.
23741500Ssl147100 *
23751500Ssl147100 * Note:
23761500Ssl147100 * qtd_dma_offs - the starting offset into the TW buffer, where the QTD
23776898Sfb209375 * should transfer from. It should be 4K aligned. And when
23786898Sfb209375 * a TW has more than one QTDs, the QTDs must be filled in
23796898Sfb209375 * increasing order.
23801500Ssl147100 * qtd_length - the total bytes to transfer.
23810Sstevel@tonic-gate */
23820Sstevel@tonic-gate /*ARGSUSED*/
23830Sstevel@tonic-gate static void
ehci_fill_in_qtd(ehci_state_t * ehcip,ehci_qtd_t * qtd,uint32_t qtd_ctrl,size_t qtd_dma_offs,size_t qtd_length,uint32_t qtd_ctrl_phase,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)23840Sstevel@tonic-gate ehci_fill_in_qtd(
23850Sstevel@tonic-gate ehci_state_t *ehcip,
23860Sstevel@tonic-gate ehci_qtd_t *qtd,
23870Sstevel@tonic-gate uint32_t qtd_ctrl,
23881500Ssl147100 size_t qtd_dma_offs,
23890Sstevel@tonic-gate size_t qtd_length,
23900Sstevel@tonic-gate uint32_t qtd_ctrl_phase,
23910Sstevel@tonic-gate ehci_pipe_private_t *pp,
23920Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
23930Sstevel@tonic-gate {
23941500Ssl147100 uint32_t buf_addr;
23950Sstevel@tonic-gate size_t buf_len = qtd_length;
23960Sstevel@tonic-gate uint32_t ctrl = qtd_ctrl;
23970Sstevel@tonic-gate uint_t i = 0;
23981500Ssl147100 int rem_len;
23990Sstevel@tonic-gate
24000Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
24011500Ssl147100 "ehci_fill_in_qtd: qtd 0x%p ctrl 0x%x bufoffs 0x%lx "
24026898Sfb209375 "len 0x%lx", (void *)qtd, qtd_ctrl, qtd_dma_offs, qtd_length);
24030Sstevel@tonic-gate
24040Sstevel@tonic-gate /* Assert that the qtd to be filled in is a dummy */
24050Sstevel@tonic-gate ASSERT(Get_QTD(qtd->qtd_state) == EHCI_QTD_DUMMY);
24060Sstevel@tonic-gate
24070Sstevel@tonic-gate /* Change QTD's state Active */
24080Sstevel@tonic-gate Set_QTD(qtd->qtd_state, EHCI_QTD_ACTIVE);
24090Sstevel@tonic-gate
24100Sstevel@tonic-gate /* Set the total length data transfer */
24110Sstevel@tonic-gate ctrl |= (((qtd_length << EHCI_QTD_CTRL_BYTES_TO_XFER_SHIFT)
24120Sstevel@tonic-gate & EHCI_QTD_CTRL_BYTES_TO_XFER) | EHCI_QTD_CTRL_MAX_ERR_COUNTS);
24130Sstevel@tonic-gate
24140Sstevel@tonic-gate /*
24151500Ssl147100 * QTDs must be filled in increasing DMA offset order.
24161500Ssl147100 * tw_dma_offs is initialized to be 0 at TW creation and
24171500Ssl147100 * is only increased in this function.
24181500Ssl147100 */
24191500Ssl147100 ASSERT(buf_len == 0 || qtd_dma_offs >= tw->tw_dma_offs);
24201500Ssl147100
24211500Ssl147100 /*
24221500Ssl147100 * Save the starting dma buffer offset used and
24230Sstevel@tonic-gate * length of data that will be transfered in
24240Sstevel@tonic-gate * the current QTD.
24250Sstevel@tonic-gate */
24261500Ssl147100 Set_QTD(qtd->qtd_xfer_offs, qtd_dma_offs);
24270Sstevel@tonic-gate Set_QTD(qtd->qtd_xfer_len, buf_len);
24280Sstevel@tonic-gate
24290Sstevel@tonic-gate while (buf_len) {
24301500Ssl147100 /*
24311500Ssl147100 * Advance to the next DMA cookie until finding the cookie
24321500Ssl147100 * that qtd_dma_offs falls in.
24331500Ssl147100 * It is very likely this loop will never repeat more than
24341500Ssl147100 * once. It is here just to accommodate the case qtd_dma_offs
24351500Ssl147100 * is increased by multiple cookies during two consecutive
24361500Ssl147100 * calls into this function. In that case, the interim DMA
24371500Ssl147100 * buffer is allowed to be skipped.
24381500Ssl147100 */
24391500Ssl147100 while ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
24401500Ssl147100 qtd_dma_offs) {
24411500Ssl147100 /*
24421500Ssl147100 * tw_dma_offs always points to the starting offset
24431500Ssl147100 * of a cookie
24441500Ssl147100 */
24451500Ssl147100 tw->tw_dma_offs += tw->tw_cookie.dmac_size;
24461500Ssl147100 ddi_dma_nextcookie(tw->tw_dmahandle, &tw->tw_cookie);
24471500Ssl147100 tw->tw_cookie_idx++;
24481500Ssl147100 ASSERT(tw->tw_cookie_idx < tw->tw_ncookies);
24491500Ssl147100 }
24501500Ssl147100
24511500Ssl147100 /*
24521500Ssl147100 * Counting the remained buffer length to be filled in
24531500Ssl147100 * the QTD for current DMA cookie
24541500Ssl147100 */
24551500Ssl147100 rem_len = (tw->tw_dma_offs + tw->tw_cookie.dmac_size) -
24561500Ssl147100 qtd_dma_offs;
24571500Ssl147100
24580Sstevel@tonic-gate /* Update the beginning of the buffer */
24591500Ssl147100 buf_addr = (qtd_dma_offs - tw->tw_dma_offs) +
24601500Ssl147100 tw->tw_cookie.dmac_address;
24611500Ssl147100 ASSERT((buf_addr % EHCI_4K_ALIGN) == 0);
24620Sstevel@tonic-gate Set_QTD(qtd->qtd_buf[i], buf_addr);
24630Sstevel@tonic-gate
24641500Ssl147100 USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
24656898Sfb209375 "ehci_fill_in_qtd: dmac_addr 0x%x dmac_size "
24661500Ssl147100 "0x%lx idx %d", buf_addr, tw->tw_cookie.dmac_size,
24671500Ssl147100 tw->tw_cookie_idx);
24681500Ssl147100
24690Sstevel@tonic-gate if (buf_len <= EHCI_MAX_QTD_BUF_SIZE) {
24701500Ssl147100 ASSERT(buf_len <= rem_len);
24710Sstevel@tonic-gate break;
24720Sstevel@tonic-gate } else {
24731500Ssl147100 ASSERT(rem_len >= EHCI_MAX_QTD_BUF_SIZE);
24740Sstevel@tonic-gate buf_len -= EHCI_MAX_QTD_BUF_SIZE;
24751500Ssl147100 qtd_dma_offs += EHCI_MAX_QTD_BUF_SIZE;
24760Sstevel@tonic-gate }
24770Sstevel@tonic-gate
24780Sstevel@tonic-gate i++;
24790Sstevel@tonic-gate }
24800Sstevel@tonic-gate
24810Sstevel@tonic-gate /*
24820Sstevel@tonic-gate * Setup the alternate next qTD pointer if appropriate. The alternate
24830Sstevel@tonic-gate * qtd is currently pointing to a QTD that is not yet linked, but will
24840Sstevel@tonic-gate * be in the very near future. If a short_xfer occurs in this
24850Sstevel@tonic-gate * situation , the HC will automatically skip this QH. Eventually
24860Sstevel@tonic-gate * everything will be placed and the alternate_qtd will be valid QTD.
24870Sstevel@tonic-gate * For more information on alternate qtds look at section 3.5.2 in the
24880Sstevel@tonic-gate * EHCI spec.
24890Sstevel@tonic-gate */
24900Sstevel@tonic-gate if (tw->tw_alt_qtd != NULL) {
24910Sstevel@tonic-gate Set_QTD(qtd->qtd_alt_next_qtd,
24920Sstevel@tonic-gate (ehci_qtd_cpu_to_iommu(ehcip, tw->tw_alt_qtd) &
24930Sstevel@tonic-gate EHCI_QTD_ALT_NEXT_QTD_PTR));
24940Sstevel@tonic-gate }
24950Sstevel@tonic-gate
24960Sstevel@tonic-gate /*
24970Sstevel@tonic-gate * For control, bulk and interrupt QTD, now
24980Sstevel@tonic-gate * enable current QTD by setting active bit.
24990Sstevel@tonic-gate */
25000Sstevel@tonic-gate Set_QTD(qtd->qtd_ctrl, (ctrl | EHCI_QTD_CTRL_ACTIVE_XACT));
25010Sstevel@tonic-gate
25020Sstevel@tonic-gate /*
25030Sstevel@tonic-gate * For Control Xfer, qtd_ctrl_phase is a valid filed.
25040Sstevel@tonic-gate */
25050Sstevel@tonic-gate if (qtd_ctrl_phase) {
25060Sstevel@tonic-gate Set_QTD(qtd->qtd_ctrl_phase, qtd_ctrl_phase);
25070Sstevel@tonic-gate }
25080Sstevel@tonic-gate
25090Sstevel@tonic-gate /* Set the transfer wrapper */
25100Sstevel@tonic-gate ASSERT(tw != NULL);
25110Sstevel@tonic-gate ASSERT(tw->tw_id != NULL);
25120Sstevel@tonic-gate
25130Sstevel@tonic-gate Set_QTD(qtd->qtd_trans_wrapper, (uint32_t)tw->tw_id);
25140Sstevel@tonic-gate }
25150Sstevel@tonic-gate
25160Sstevel@tonic-gate
25170Sstevel@tonic-gate /*
25180Sstevel@tonic-gate * ehci_insert_qtd_on_tw:
25190Sstevel@tonic-gate *
25200Sstevel@tonic-gate * The transfer wrapper keeps a list of all Transfer Descriptors (QTD) that
25210Sstevel@tonic-gate * are allocated for this transfer. Insert a QTD onto this list. The list
25220Sstevel@tonic-gate * of QTD's does not include the dummy QTD that is at the end of the list of
25230Sstevel@tonic-gate * QTD's for the endpoint.
25240Sstevel@tonic-gate */
25250Sstevel@tonic-gate static void
ehci_insert_qtd_on_tw(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw,ehci_qtd_t * qtd)25260Sstevel@tonic-gate ehci_insert_qtd_on_tw(
25270Sstevel@tonic-gate ehci_state_t *ehcip,
25280Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
25290Sstevel@tonic-gate ehci_qtd_t *qtd)
25300Sstevel@tonic-gate {
25310Sstevel@tonic-gate /*
25320Sstevel@tonic-gate * Set the next pointer to NULL because
25330Sstevel@tonic-gate * this is the last QTD on list.
25340Sstevel@tonic-gate */
25350Sstevel@tonic-gate Set_QTD(qtd->qtd_tw_next_qtd, NULL);
25360Sstevel@tonic-gate
25370Sstevel@tonic-gate if (tw->tw_qtd_head == NULL) {
25380Sstevel@tonic-gate ASSERT(tw->tw_qtd_tail == NULL);
25390Sstevel@tonic-gate tw->tw_qtd_head = qtd;
25400Sstevel@tonic-gate tw->tw_qtd_tail = qtd;
25410Sstevel@tonic-gate } else {
25420Sstevel@tonic-gate ehci_qtd_t *dummy = (ehci_qtd_t *)tw->tw_qtd_tail;
25430Sstevel@tonic-gate
25440Sstevel@tonic-gate ASSERT(dummy != NULL);
25450Sstevel@tonic-gate ASSERT(dummy != qtd);
25460Sstevel@tonic-gate ASSERT(Get_QTD(qtd->qtd_state) != EHCI_QTD_DUMMY);
25470Sstevel@tonic-gate
25480Sstevel@tonic-gate /* Add the qtd to the end of the list */
25490Sstevel@tonic-gate Set_QTD(dummy->qtd_tw_next_qtd,
25500Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, qtd));
25510Sstevel@tonic-gate
25520Sstevel@tonic-gate tw->tw_qtd_tail = qtd;
25530Sstevel@tonic-gate
25540Sstevel@tonic-gate ASSERT(Get_QTD(qtd->qtd_tw_next_qtd) == NULL);
25550Sstevel@tonic-gate }
25560Sstevel@tonic-gate }
25570Sstevel@tonic-gate
25580Sstevel@tonic-gate
25590Sstevel@tonic-gate /*
25600Sstevel@tonic-gate * ehci_insert_qtd_into_active_qtd_list:
25610Sstevel@tonic-gate *
25620Sstevel@tonic-gate * Insert current QTD into active QTD list.
25630Sstevel@tonic-gate */
25640Sstevel@tonic-gate static void
ehci_insert_qtd_into_active_qtd_list(ehci_state_t * ehcip,ehci_qtd_t * qtd)25650Sstevel@tonic-gate ehci_insert_qtd_into_active_qtd_list(
25660Sstevel@tonic-gate ehci_state_t *ehcip,
25670Sstevel@tonic-gate ehci_qtd_t *qtd)
25680Sstevel@tonic-gate {
25690Sstevel@tonic-gate ehci_qtd_t *curr_qtd, *next_qtd;
25700Sstevel@tonic-gate
25710Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
25720Sstevel@tonic-gate
25730Sstevel@tonic-gate curr_qtd = ehcip->ehci_active_qtd_list;
25740Sstevel@tonic-gate
25750Sstevel@tonic-gate /* Insert this QTD into QTD Active List */
25760Sstevel@tonic-gate if (curr_qtd) {
25770Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
25780Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
25790Sstevel@tonic-gate
25800Sstevel@tonic-gate while (next_qtd) {
25810Sstevel@tonic-gate curr_qtd = next_qtd;
25820Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
25830Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
25840Sstevel@tonic-gate }
25850Sstevel@tonic-gate
25860Sstevel@tonic-gate Set_QTD(qtd->qtd_active_qtd_prev,
25870Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, curr_qtd));
25880Sstevel@tonic-gate
25890Sstevel@tonic-gate Set_QTD(curr_qtd->qtd_active_qtd_next,
25900Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(ehcip, qtd));
25910Sstevel@tonic-gate } else {
25920Sstevel@tonic-gate ehcip->ehci_active_qtd_list = qtd;
25930Sstevel@tonic-gate Set_QTD(qtd->qtd_active_qtd_next, NULL);
25940Sstevel@tonic-gate Set_QTD(qtd->qtd_active_qtd_prev, NULL);
25950Sstevel@tonic-gate }
25960Sstevel@tonic-gate }
25970Sstevel@tonic-gate
25980Sstevel@tonic-gate
25990Sstevel@tonic-gate /*
26000Sstevel@tonic-gate * ehci_remove_qtd_from_active_qtd_list:
26010Sstevel@tonic-gate *
26020Sstevel@tonic-gate * Remove current QTD from the active QTD list.
26030Sstevel@tonic-gate *
26040Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
26050Sstevel@tonic-gate */
26060Sstevel@tonic-gate void
ehci_remove_qtd_from_active_qtd_list(ehci_state_t * ehcip,ehci_qtd_t * qtd)26070Sstevel@tonic-gate ehci_remove_qtd_from_active_qtd_list(
26080Sstevel@tonic-gate ehci_state_t *ehcip,
26090Sstevel@tonic-gate ehci_qtd_t *qtd)
26100Sstevel@tonic-gate {
26110Sstevel@tonic-gate ehci_qtd_t *curr_qtd, *prev_qtd, *next_qtd;
26120Sstevel@tonic-gate
26130Sstevel@tonic-gate ASSERT(qtd != NULL);
26140Sstevel@tonic-gate
26150Sstevel@tonic-gate curr_qtd = ehcip->ehci_active_qtd_list;
26160Sstevel@tonic-gate
26170Sstevel@tonic-gate while ((curr_qtd) && (curr_qtd != qtd)) {
26180Sstevel@tonic-gate curr_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26190Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
26200Sstevel@tonic-gate }
26210Sstevel@tonic-gate
26220Sstevel@tonic-gate if ((curr_qtd) && (curr_qtd == qtd)) {
26230Sstevel@tonic-gate prev_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26240Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_prev));
26250Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26260Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
26270Sstevel@tonic-gate
26280Sstevel@tonic-gate if (prev_qtd) {
26290Sstevel@tonic-gate Set_QTD(prev_qtd->qtd_active_qtd_next,
26300Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_next));
26310Sstevel@tonic-gate } else {
26320Sstevel@tonic-gate ehcip->ehci_active_qtd_list = next_qtd;
26330Sstevel@tonic-gate }
26340Sstevel@tonic-gate
26350Sstevel@tonic-gate if (next_qtd) {
26360Sstevel@tonic-gate Set_QTD(next_qtd->qtd_active_qtd_prev,
26370Sstevel@tonic-gate Get_QTD(curr_qtd->qtd_active_qtd_prev));
26380Sstevel@tonic-gate }
26390Sstevel@tonic-gate } else {
26400Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
26410Sstevel@tonic-gate "ehci_remove_qtd_from_active_qtd_list: "
26425441Ssl147100 "Unable to find QTD in active_qtd_list");
26430Sstevel@tonic-gate }
26440Sstevel@tonic-gate }
26450Sstevel@tonic-gate
26460Sstevel@tonic-gate
26470Sstevel@tonic-gate /*
26480Sstevel@tonic-gate * ehci_traverse_qtds:
26490Sstevel@tonic-gate *
26500Sstevel@tonic-gate * Traverse the list of QTDs for given pipe using transfer wrapper. Since
26510Sstevel@tonic-gate * the endpoint is marked as Halted, the Host Controller (HC) is no longer
26520Sstevel@tonic-gate * accessing these QTDs. Remove all the QTDs that are attached to endpoint.
26530Sstevel@tonic-gate */
26540Sstevel@tonic-gate static void
ehci_traverse_qtds(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)26550Sstevel@tonic-gate ehci_traverse_qtds(
26560Sstevel@tonic-gate ehci_state_t *ehcip,
26570Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
26580Sstevel@tonic-gate {
26590Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
26600Sstevel@tonic-gate ehci_trans_wrapper_t *next_tw;
26610Sstevel@tonic-gate ehci_qtd_t *qtd;
26620Sstevel@tonic-gate ehci_qtd_t *next_qtd;
26630Sstevel@tonic-gate
26640Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
26650Sstevel@tonic-gate
26660Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
26670Sstevel@tonic-gate "ehci_traverse_qtds:");
26680Sstevel@tonic-gate
26690Sstevel@tonic-gate /* Process the transfer wrappers for this pipe */
26700Sstevel@tonic-gate next_tw = pp->pp_tw_head;
26710Sstevel@tonic-gate
26720Sstevel@tonic-gate while (next_tw) {
26730Sstevel@tonic-gate /* Stop the the transfer timer */
26740Sstevel@tonic-gate ehci_stop_xfer_timer(ehcip, next_tw, EHCI_REMOVE_XFER_ALWAYS);
26750Sstevel@tonic-gate
26760Sstevel@tonic-gate qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
26770Sstevel@tonic-gate
26780Sstevel@tonic-gate /* Walk through each QTD for this transfer wrapper */
26790Sstevel@tonic-gate while (qtd) {
26800Sstevel@tonic-gate /* Remove this QTD from active QTD list */
26810Sstevel@tonic-gate ehci_remove_qtd_from_active_qtd_list(ehcip, qtd);
26820Sstevel@tonic-gate
26830Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26840Sstevel@tonic-gate Get_QTD(qtd->qtd_tw_next_qtd));
26850Sstevel@tonic-gate
26860Sstevel@tonic-gate /* Deallocate this QTD */
26870Sstevel@tonic-gate ehci_deallocate_qtd(ehcip, qtd);
26880Sstevel@tonic-gate
26890Sstevel@tonic-gate qtd = next_qtd;
26900Sstevel@tonic-gate }
26910Sstevel@tonic-gate
26920Sstevel@tonic-gate next_tw = next_tw->tw_next;
26930Sstevel@tonic-gate }
26940Sstevel@tonic-gate
26950Sstevel@tonic-gate /* Clear current qtd pointer */
26960Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_curr_qtd, (uint32_t)0x00000000);
26970Sstevel@tonic-gate
26980Sstevel@tonic-gate /* Update the next qtd pointer in the QH */
26990Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_next_qtd, Get_QH(pp->pp_qh->qh_dummy_qtd));
27000Sstevel@tonic-gate }
27010Sstevel@tonic-gate
27020Sstevel@tonic-gate
27030Sstevel@tonic-gate /*
27040Sstevel@tonic-gate * ehci_deallocate_qtd:
27050Sstevel@tonic-gate *
27060Sstevel@tonic-gate * Deallocate a Host Controller's (HC) Transfer Descriptor (QTD).
27070Sstevel@tonic-gate *
27080Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
27090Sstevel@tonic-gate */
27100Sstevel@tonic-gate void
ehci_deallocate_qtd(ehci_state_t * ehcip,ehci_qtd_t * old_qtd)27110Sstevel@tonic-gate ehci_deallocate_qtd(
27120Sstevel@tonic-gate ehci_state_t *ehcip,
27130Sstevel@tonic-gate ehci_qtd_t *old_qtd)
27140Sstevel@tonic-gate {
27150Sstevel@tonic-gate ehci_trans_wrapper_t *tw = NULL;
27160Sstevel@tonic-gate
27170Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
27180Sstevel@tonic-gate "ehci_deallocate_qtd: old_qtd = 0x%p", (void *)old_qtd);
27190Sstevel@tonic-gate
27200Sstevel@tonic-gate /*
27210Sstevel@tonic-gate * Obtain the transaction wrapper and tw will be
27220Sstevel@tonic-gate * NULL for the dummy QTDs.
27230Sstevel@tonic-gate */
27240Sstevel@tonic-gate if (Get_QTD(old_qtd->qtd_state) != EHCI_QTD_DUMMY) {
27250Sstevel@tonic-gate tw = (ehci_trans_wrapper_t *)
27265441Ssl147100 EHCI_LOOKUP_ID((uint32_t)
27275441Ssl147100 Get_QTD(old_qtd->qtd_trans_wrapper));
27280Sstevel@tonic-gate
27290Sstevel@tonic-gate ASSERT(tw != NULL);
27300Sstevel@tonic-gate }
27310Sstevel@tonic-gate
27320Sstevel@tonic-gate /*
27330Sstevel@tonic-gate * If QTD's transfer wrapper is NULL, don't access its TW.
27340Sstevel@tonic-gate * Just free the QTD.
27350Sstevel@tonic-gate */
27360Sstevel@tonic-gate if (tw) {
27370Sstevel@tonic-gate ehci_qtd_t *qtd, *next_qtd;
27380Sstevel@tonic-gate
27390Sstevel@tonic-gate qtd = tw->tw_qtd_head;
27400Sstevel@tonic-gate
27410Sstevel@tonic-gate if (old_qtd != qtd) {
27420Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(
27435441Ssl147100 ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
27440Sstevel@tonic-gate
27450Sstevel@tonic-gate while (next_qtd != old_qtd) {
27460Sstevel@tonic-gate qtd = next_qtd;
27470Sstevel@tonic-gate next_qtd = ehci_qtd_iommu_to_cpu(
27480Sstevel@tonic-gate ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
27490Sstevel@tonic-gate }
27500Sstevel@tonic-gate
27510Sstevel@tonic-gate Set_QTD(qtd->qtd_tw_next_qtd, old_qtd->qtd_tw_next_qtd);
27520Sstevel@tonic-gate
27530Sstevel@tonic-gate if (qtd->qtd_tw_next_qtd == NULL) {
27540Sstevel@tonic-gate tw->tw_qtd_tail = qtd;
27550Sstevel@tonic-gate }
27560Sstevel@tonic-gate } else {
27570Sstevel@tonic-gate tw->tw_qtd_head = ehci_qtd_iommu_to_cpu(
27580Sstevel@tonic-gate ehcip, Get_QTD(old_qtd->qtd_tw_next_qtd));
27590Sstevel@tonic-gate
27600Sstevel@tonic-gate if (tw->tw_qtd_head == NULL) {
27610Sstevel@tonic-gate tw->tw_qtd_tail = NULL;
27620Sstevel@tonic-gate }
27630Sstevel@tonic-gate }
27640Sstevel@tonic-gate }
27650Sstevel@tonic-gate
27660Sstevel@tonic-gate bzero((void *)old_qtd, sizeof (ehci_qtd_t));
27670Sstevel@tonic-gate Set_QTD(old_qtd->qtd_state, EHCI_QTD_FREE);
27680Sstevel@tonic-gate
27690Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
27700Sstevel@tonic-gate "Dealloc_qtd: qtd 0x%p", (void *)old_qtd);
27710Sstevel@tonic-gate }
27720Sstevel@tonic-gate
27730Sstevel@tonic-gate
27740Sstevel@tonic-gate /*
27750Sstevel@tonic-gate * ehci_qtd_cpu_to_iommu:
27760Sstevel@tonic-gate *
27770Sstevel@tonic-gate * This function converts for the given Transfer Descriptor (QTD) CPU address
27780Sstevel@tonic-gate * to IO address.
27790Sstevel@tonic-gate *
27800Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
27810Sstevel@tonic-gate */
27820Sstevel@tonic-gate uint32_t
ehci_qtd_cpu_to_iommu(ehci_state_t * ehcip,ehci_qtd_t * addr)27830Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(
27840Sstevel@tonic-gate ehci_state_t *ehcip,
27850Sstevel@tonic-gate ehci_qtd_t *addr)
27860Sstevel@tonic-gate {
27870Sstevel@tonic-gate uint32_t td;
27880Sstevel@tonic-gate
27890Sstevel@tonic-gate td = (uint32_t)ehcip->ehci_qtd_pool_cookie.dmac_address +
27900Sstevel@tonic-gate (uint32_t)((uintptr_t)addr -
27910Sstevel@tonic-gate (uintptr_t)(ehcip->ehci_qtd_pool_addr));
27920Sstevel@tonic-gate
27930Sstevel@tonic-gate ASSERT((ehcip->ehci_qtd_pool_cookie.dmac_address +
27940Sstevel@tonic-gate (uint32_t) (sizeof (ehci_qtd_t) *
27950Sstevel@tonic-gate (addr - ehcip->ehci_qtd_pool_addr))) ==
27960Sstevel@tonic-gate (ehcip->ehci_qtd_pool_cookie.dmac_address +
27970Sstevel@tonic-gate (uint32_t)((uintptr_t)addr - (uintptr_t)
27980Sstevel@tonic-gate (ehcip->ehci_qtd_pool_addr))));
27990Sstevel@tonic-gate
28000Sstevel@tonic-gate ASSERT(td >= ehcip->ehci_qtd_pool_cookie.dmac_address);
28010Sstevel@tonic-gate ASSERT(td <= ehcip->ehci_qtd_pool_cookie.dmac_address +
28020Sstevel@tonic-gate sizeof (ehci_qtd_t) * ehci_qtd_pool_size);
28030Sstevel@tonic-gate
28040Sstevel@tonic-gate return (td);
28050Sstevel@tonic-gate }
28060Sstevel@tonic-gate
28070Sstevel@tonic-gate
28080Sstevel@tonic-gate /*
28090Sstevel@tonic-gate * ehci_qtd_iommu_to_cpu:
28100Sstevel@tonic-gate *
28110Sstevel@tonic-gate * This function converts for the given Transfer Descriptor (QTD) IO address
28120Sstevel@tonic-gate * to CPU address.
28130Sstevel@tonic-gate *
28140Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
28150Sstevel@tonic-gate */
28160Sstevel@tonic-gate ehci_qtd_t *
ehci_qtd_iommu_to_cpu(ehci_state_t * ehcip,uintptr_t addr)28170Sstevel@tonic-gate ehci_qtd_iommu_to_cpu(
28180Sstevel@tonic-gate ehci_state_t *ehcip,
28190Sstevel@tonic-gate uintptr_t addr)
28200Sstevel@tonic-gate {
28210Sstevel@tonic-gate ehci_qtd_t *qtd;
28220Sstevel@tonic-gate
28230Sstevel@tonic-gate if (addr == NULL) {
28240Sstevel@tonic-gate
28250Sstevel@tonic-gate return (NULL);
28260Sstevel@tonic-gate }
28270Sstevel@tonic-gate
28280Sstevel@tonic-gate qtd = (ehci_qtd_t *)((uintptr_t)
28290Sstevel@tonic-gate (addr - ehcip->ehci_qtd_pool_cookie.dmac_address) +
28300Sstevel@tonic-gate (uintptr_t)ehcip->ehci_qtd_pool_addr);
28310Sstevel@tonic-gate
28320Sstevel@tonic-gate ASSERT(qtd >= ehcip->ehci_qtd_pool_addr);
28330Sstevel@tonic-gate ASSERT((uintptr_t)qtd <= (uintptr_t)ehcip->ehci_qtd_pool_addr +
28340Sstevel@tonic-gate (uintptr_t)(sizeof (ehci_qtd_t) * ehci_qtd_pool_size));
28350Sstevel@tonic-gate
28360Sstevel@tonic-gate return (qtd);
28370Sstevel@tonic-gate }
28380Sstevel@tonic-gate
28390Sstevel@tonic-gate /*
28400Sstevel@tonic-gate * ehci_allocate_tds_for_tw_resources:
28410Sstevel@tonic-gate *
28420Sstevel@tonic-gate * Allocate n Transfer Descriptors (TD) from the TD buffer pool and places it
28430Sstevel@tonic-gate * into the TW. Also chooses the correct alternate qtd when required. It is
28440Sstevel@tonic-gate * used for hardware short transfer support. For more information on
28450Sstevel@tonic-gate * alternate qtds look at section 3.5.2 in the EHCI spec.
28460Sstevel@tonic-gate * Here is how each alternate qtd's are used:
28470Sstevel@tonic-gate *
28480Sstevel@tonic-gate * Bulk: used fully.
28490Sstevel@tonic-gate * Intr: xfers only require 1 QTD, so alternate qtds are never used.
28500Sstevel@tonic-gate * Ctrl: Should not use alternate QTD
28510Sstevel@tonic-gate * Isoch: Doesn't support short_xfer nor does it use QTD
28520Sstevel@tonic-gate *
28530Sstevel@tonic-gate * Returns USB_NO_RESOURCES if it was not able to allocate all the requested TD
28540Sstevel@tonic-gate * otherwise USB_SUCCESS.
28550Sstevel@tonic-gate */
28560Sstevel@tonic-gate int
ehci_allocate_tds_for_tw(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw,size_t qtd_count)28570Sstevel@tonic-gate ehci_allocate_tds_for_tw(
28580Sstevel@tonic-gate ehci_state_t *ehcip,
28590Sstevel@tonic-gate ehci_pipe_private_t *pp,
28600Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
28610Sstevel@tonic-gate size_t qtd_count)
28620Sstevel@tonic-gate {
28630Sstevel@tonic-gate usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
28640Sstevel@tonic-gate uchar_t attributes;
28650Sstevel@tonic-gate ehci_qtd_t *qtd;
28660Sstevel@tonic-gate uint32_t qtd_addr;
28670Sstevel@tonic-gate int i;
28680Sstevel@tonic-gate int error = USB_SUCCESS;
28690Sstevel@tonic-gate
28700Sstevel@tonic-gate attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
28710Sstevel@tonic-gate
28720Sstevel@tonic-gate for (i = 0; i < qtd_count; i += 1) {
28730Sstevel@tonic-gate qtd = ehci_allocate_qtd_from_pool(ehcip);
28740Sstevel@tonic-gate if (qtd == NULL) {
28750Sstevel@tonic-gate error = USB_NO_RESOURCES;
28760Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
28770Sstevel@tonic-gate "ehci_allocate_qtds_for_tw: "
28780Sstevel@tonic-gate "Unable to allocate %lu QTDs",
28790Sstevel@tonic-gate qtd_count);
28800Sstevel@tonic-gate break;
28810Sstevel@tonic-gate }
28820Sstevel@tonic-gate if (i > 0) {
28830Sstevel@tonic-gate qtd_addr = ehci_qtd_cpu_to_iommu(ehcip,
28840Sstevel@tonic-gate tw->tw_qtd_free_list);
28850Sstevel@tonic-gate Set_QTD(qtd->qtd_tw_next_qtd, qtd_addr);
28860Sstevel@tonic-gate }
28870Sstevel@tonic-gate tw->tw_qtd_free_list = qtd;
28880Sstevel@tonic-gate
28890Sstevel@tonic-gate /*
28900Sstevel@tonic-gate * Save the second one as a pointer to the new dummy 1.
28910Sstevel@tonic-gate * It is used later for the alt_qtd_ptr. Xfers with only
28920Sstevel@tonic-gate * one qtd do not need alt_qtd_ptr.
28930Sstevel@tonic-gate * The tds's are allocated and put into a stack, that is
28940Sstevel@tonic-gate * why the second qtd allocated will turn out to be the
28950Sstevel@tonic-gate * new dummy 1.
28960Sstevel@tonic-gate */
28970Sstevel@tonic-gate if ((i == 1) && (attributes == USB_EP_ATTR_BULK)) {
28980Sstevel@tonic-gate tw->tw_alt_qtd = qtd;
28990Sstevel@tonic-gate }
29000Sstevel@tonic-gate }
29010Sstevel@tonic-gate
29020Sstevel@tonic-gate return (error);
29030Sstevel@tonic-gate }
29040Sstevel@tonic-gate
29050Sstevel@tonic-gate /*
29060Sstevel@tonic-gate * ehci_allocate_tw_resources:
29070Sstevel@tonic-gate *
29080Sstevel@tonic-gate * Allocate a Transaction Wrapper (TW) and n Transfer Descriptors (QTD)
29090Sstevel@tonic-gate * from the QTD buffer pool and places it into the TW. It does an all
29100Sstevel@tonic-gate * or nothing transaction.
29110Sstevel@tonic-gate *
29120Sstevel@tonic-gate * Returns NULL if there is insufficient resources otherwise TW.
29130Sstevel@tonic-gate */
29140Sstevel@tonic-gate static ehci_trans_wrapper_t *
ehci_allocate_tw_resources(ehci_state_t * ehcip,ehci_pipe_private_t * pp,size_t tw_length,usb_flags_t usb_flags,size_t qtd_count)29150Sstevel@tonic-gate ehci_allocate_tw_resources(
29160Sstevel@tonic-gate ehci_state_t *ehcip,
29170Sstevel@tonic-gate ehci_pipe_private_t *pp,
29180Sstevel@tonic-gate size_t tw_length,
29190Sstevel@tonic-gate usb_flags_t usb_flags,
29200Sstevel@tonic-gate size_t qtd_count)
29210Sstevel@tonic-gate {
29220Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
29230Sstevel@tonic-gate
29240Sstevel@tonic-gate tw = ehci_create_transfer_wrapper(ehcip, pp, tw_length, usb_flags);
29250Sstevel@tonic-gate
29260Sstevel@tonic-gate if (tw == NULL) {
29270Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
29280Sstevel@tonic-gate "ehci_allocate_tw_resources: Unable to allocate TW");
29290Sstevel@tonic-gate } else {
29300Sstevel@tonic-gate if (ehci_allocate_tds_for_tw(ehcip, pp, tw, qtd_count) ==
29310Sstevel@tonic-gate USB_SUCCESS) {
29327492SZhigang.Lu@Sun.COM tw->tw_num_qtds = (uint_t)qtd_count;
29330Sstevel@tonic-gate } else {
29340Sstevel@tonic-gate ehci_deallocate_tw(ehcip, pp, tw);
29350Sstevel@tonic-gate tw = NULL;
29360Sstevel@tonic-gate }
29370Sstevel@tonic-gate }
29380Sstevel@tonic-gate
29390Sstevel@tonic-gate return (tw);
29400Sstevel@tonic-gate }
29410Sstevel@tonic-gate
29420Sstevel@tonic-gate
29430Sstevel@tonic-gate /*
29440Sstevel@tonic-gate * ehci_free_tw_td_resources:
29450Sstevel@tonic-gate *
29460Sstevel@tonic-gate * Free all allocated resources for Transaction Wrapper (TW).
29470Sstevel@tonic-gate * Does not free the TW itself.
29480Sstevel@tonic-gate *
29490Sstevel@tonic-gate * Returns NULL if there is insufficient resources otherwise TW.
29500Sstevel@tonic-gate */
29510Sstevel@tonic-gate static void
ehci_free_tw_td_resources(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw)29520Sstevel@tonic-gate ehci_free_tw_td_resources(
29530Sstevel@tonic-gate ehci_state_t *ehcip,
29540Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
29550Sstevel@tonic-gate {
29560Sstevel@tonic-gate ehci_qtd_t *qtd = NULL;
29570Sstevel@tonic-gate ehci_qtd_t *temp_qtd = NULL;
29580Sstevel@tonic-gate
29590Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
29606898Sfb209375 "ehci_free_tw_td_resources: tw = 0x%p", (void *)tw);
29610Sstevel@tonic-gate
29620Sstevel@tonic-gate qtd = tw->tw_qtd_free_list;
29630Sstevel@tonic-gate while (qtd != NULL) {
29640Sstevel@tonic-gate /* Save the pointer to the next qtd before destroying it */
29650Sstevel@tonic-gate temp_qtd = ehci_qtd_iommu_to_cpu(ehcip,
29660Sstevel@tonic-gate Get_QTD(qtd->qtd_tw_next_qtd));
29670Sstevel@tonic-gate ehci_deallocate_qtd(ehcip, qtd);
29680Sstevel@tonic-gate qtd = temp_qtd;
29690Sstevel@tonic-gate }
29700Sstevel@tonic-gate tw->tw_qtd_free_list = NULL;
29710Sstevel@tonic-gate }
29720Sstevel@tonic-gate
29730Sstevel@tonic-gate /*
29740Sstevel@tonic-gate * Transfer Wrapper functions
29750Sstevel@tonic-gate *
29760Sstevel@tonic-gate * ehci_create_transfer_wrapper:
29770Sstevel@tonic-gate *
29780Sstevel@tonic-gate * Create a Transaction Wrapper (TW) and this involves the allocating of DMA
29790Sstevel@tonic-gate * resources.
29800Sstevel@tonic-gate */
29810Sstevel@tonic-gate static ehci_trans_wrapper_t *
ehci_create_transfer_wrapper(ehci_state_t * ehcip,ehci_pipe_private_t * pp,size_t length,uint_t usb_flags)29820Sstevel@tonic-gate ehci_create_transfer_wrapper(
29830Sstevel@tonic-gate ehci_state_t *ehcip,
29840Sstevel@tonic-gate ehci_pipe_private_t *pp,
29850Sstevel@tonic-gate size_t length,
29860Sstevel@tonic-gate uint_t usb_flags)
29870Sstevel@tonic-gate {
29880Sstevel@tonic-gate ddi_device_acc_attr_t dev_attr;
29891500Ssl147100 ddi_dma_attr_t dma_attr;
29900Sstevel@tonic-gate int result;
29910Sstevel@tonic-gate size_t real_length;
29920Sstevel@tonic-gate ehci_trans_wrapper_t *tw;
29931500Ssl147100 int kmem_flag;
29941500Ssl147100 int (*dmamem_wait)(caddr_t);
299512733SRaymond.Chen@Sun.COM usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
29960Sstevel@tonic-gate
29970Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
29980Sstevel@tonic-gate "ehci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
29990Sstevel@tonic-gate length, usb_flags);
30000Sstevel@tonic-gate
30010Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
30020Sstevel@tonic-gate
30039072SZhigang.Lu@Sun.COM /* SLEEP flag should not be used while holding mutex */
30049072SZhigang.Lu@Sun.COM kmem_flag = KM_NOSLEEP;
30059072SZhigang.Lu@Sun.COM dmamem_wait = DDI_DMA_DONTWAIT;
30061500Ssl147100
30070Sstevel@tonic-gate /* Allocate space for the transfer wrapper */
30081500Ssl147100 tw = kmem_zalloc(sizeof (ehci_trans_wrapper_t), kmem_flag);
30090Sstevel@tonic-gate
30100Sstevel@tonic-gate if (tw == NULL) {
30110Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
30120Sstevel@tonic-gate "ehci_create_transfer_wrapper: kmem_zalloc failed");
30130Sstevel@tonic-gate
30140Sstevel@tonic-gate return (NULL);
30150Sstevel@tonic-gate }
30160Sstevel@tonic-gate
30172495Sgc161489 /* zero-length packet doesn't need to allocate dma memory */
30182495Sgc161489 if (length == 0) {
30192495Sgc161489
30202495Sgc161489 goto dmadone;
30212495Sgc161489 }
30222495Sgc161489
30231500Ssl147100 /* allow sg lists for transfer wrapper dma memory */
30241500Ssl147100 bcopy(&ehcip->ehci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
30251500Ssl147100 dma_attr.dma_attr_sgllen = EHCI_DMA_ATTR_TW_SGLLEN;
30261500Ssl147100 dma_attr.dma_attr_align = EHCI_DMA_ATTR_ALIGNMENT;
30270Sstevel@tonic-gate
30280Sstevel@tonic-gate /* Allocate the DMA handle */
30290Sstevel@tonic-gate result = ddi_dma_alloc_handle(ehcip->ehci_dip,
30301500Ssl147100 &dma_attr, dmamem_wait, 0, &tw->tw_dmahandle);
30310Sstevel@tonic-gate
30320Sstevel@tonic-gate if (result != DDI_SUCCESS) {
30330Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
30340Sstevel@tonic-gate "ehci_create_transfer_wrapper: Alloc handle failed");
30350Sstevel@tonic-gate
30360Sstevel@tonic-gate kmem_free(tw, sizeof (ehci_trans_wrapper_t));
30370Sstevel@tonic-gate
30380Sstevel@tonic-gate return (NULL);
30390Sstevel@tonic-gate }
30400Sstevel@tonic-gate
30410Sstevel@tonic-gate dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
30420Sstevel@tonic-gate
30430Sstevel@tonic-gate /* no need for swapping the raw data */
30440Sstevel@tonic-gate dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
30450Sstevel@tonic-gate dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
30460Sstevel@tonic-gate
30470Sstevel@tonic-gate /* Allocate the memory */
30480Sstevel@tonic-gate result = ddi_dma_mem_alloc(tw->tw_dmahandle, length,
30491500Ssl147100 &dev_attr, DDI_DMA_CONSISTENT, dmamem_wait, NULL,
30500Sstevel@tonic-gate (caddr_t *)&tw->tw_buf, &real_length, &tw->tw_accesshandle);
30510Sstevel@tonic-gate
30520Sstevel@tonic-gate if (result != DDI_SUCCESS) {
30530Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
30540Sstevel@tonic-gate "ehci_create_transfer_wrapper: dma_mem_alloc fail");
30550Sstevel@tonic-gate
30560Sstevel@tonic-gate ddi_dma_free_handle(&tw->tw_dmahandle);
30570Sstevel@tonic-gate kmem_free(tw, sizeof (ehci_trans_wrapper_t));
30580Sstevel@tonic-gate
30590Sstevel@tonic-gate return (NULL);
30600Sstevel@tonic-gate }
30610Sstevel@tonic-gate
30620Sstevel@tonic-gate ASSERT(real_length >= length);
30630Sstevel@tonic-gate
30640Sstevel@tonic-gate /* Bind the handle */
30650Sstevel@tonic-gate result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
30660Sstevel@tonic-gate (caddr_t)tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
30671500Ssl147100 dmamem_wait, NULL, &tw->tw_cookie, &tw->tw_ncookies);
30681500Ssl147100
30691500Ssl147100 if (result != DDI_DMA_MAPPED) {
30700Sstevel@tonic-gate ehci_decode_ddi_dma_addr_bind_handle_result(ehcip, result);
30710Sstevel@tonic-gate
30720Sstevel@tonic-gate ddi_dma_mem_free(&tw->tw_accesshandle);
30730Sstevel@tonic-gate ddi_dma_free_handle(&tw->tw_dmahandle);
30740Sstevel@tonic-gate kmem_free(tw, sizeof (ehci_trans_wrapper_t));
30750Sstevel@tonic-gate
30760Sstevel@tonic-gate return (NULL);
30770Sstevel@tonic-gate }
30780Sstevel@tonic-gate
30791500Ssl147100 tw->tw_cookie_idx = 0;
30801500Ssl147100 tw->tw_dma_offs = 0;
30811500Ssl147100
30822495Sgc161489 dmadone:
30830Sstevel@tonic-gate /*
30840Sstevel@tonic-gate * Only allow one wrapper to be added at a time. Insert the
30850Sstevel@tonic-gate * new transaction wrapper into the list for this pipe.
30860Sstevel@tonic-gate */
30870Sstevel@tonic-gate if (pp->pp_tw_head == NULL) {
30880Sstevel@tonic-gate pp->pp_tw_head = tw;
30890Sstevel@tonic-gate pp->pp_tw_tail = tw;
30900Sstevel@tonic-gate } else {
30910Sstevel@tonic-gate pp->pp_tw_tail->tw_next = tw;
30920Sstevel@tonic-gate pp->pp_tw_tail = tw;
30930Sstevel@tonic-gate }
30940Sstevel@tonic-gate
30950Sstevel@tonic-gate /* Store the transfer length */
30960Sstevel@tonic-gate tw->tw_length = length;
30970Sstevel@tonic-gate
30980Sstevel@tonic-gate /* Store a back pointer to the pipe private structure */
30990Sstevel@tonic-gate tw->tw_pipe_private = pp;
31000Sstevel@tonic-gate
31010Sstevel@tonic-gate /* Store the transfer type - synchronous or asynchronous */
31020Sstevel@tonic-gate tw->tw_flags = usb_flags;
31030Sstevel@tonic-gate
31040Sstevel@tonic-gate /* Get and Store 32bit ID */
31050Sstevel@tonic-gate tw->tw_id = EHCI_GET_ID((void *)tw);
31060Sstevel@tonic-gate
31070Sstevel@tonic-gate ASSERT(tw->tw_id != NULL);
31080Sstevel@tonic-gate
310912733SRaymond.Chen@Sun.COM /* isoc ep will not come here */
311012733SRaymond.Chen@Sun.COM if (EHCI_INTR_ENDPOINT(eptd)) {
311112733SRaymond.Chen@Sun.COM ehcip->ehci_periodic_req_count++;
311212733SRaymond.Chen@Sun.COM } else {
311312733SRaymond.Chen@Sun.COM ehcip->ehci_async_req_count++;
311412733SRaymond.Chen@Sun.COM }
311512733SRaymond.Chen@Sun.COM ehci_toggle_scheduler(ehcip);
311612733SRaymond.Chen@Sun.COM
31170Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
31181500Ssl147100 "ehci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
31196898Sfb209375 (void *)tw, tw->tw_ncookies);
31200Sstevel@tonic-gate
31210Sstevel@tonic-gate return (tw);
31220Sstevel@tonic-gate }
31230Sstevel@tonic-gate
31240Sstevel@tonic-gate
31250Sstevel@tonic-gate /*
31260Sstevel@tonic-gate * ehci_start_xfer_timer:
31270Sstevel@tonic-gate *
31280Sstevel@tonic-gate * Start the timer for the control, bulk and for one time interrupt
31290Sstevel@tonic-gate * transfers.
31300Sstevel@tonic-gate */
31310Sstevel@tonic-gate /* ARGSUSED */
31320Sstevel@tonic-gate static void
ehci_start_xfer_timer(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)31330Sstevel@tonic-gate ehci_start_xfer_timer(
31340Sstevel@tonic-gate ehci_state_t *ehcip,
31350Sstevel@tonic-gate ehci_pipe_private_t *pp,
31360Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
31370Sstevel@tonic-gate {
31380Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
31396898Sfb209375 "ehci_start_xfer_timer: tw = 0x%p", (void *)tw);
31400Sstevel@tonic-gate
31410Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
31420Sstevel@tonic-gate
31430Sstevel@tonic-gate /*
31440Sstevel@tonic-gate * The timeout handling is done only for control, bulk and for
31450Sstevel@tonic-gate * one time Interrupt transfers.
31460Sstevel@tonic-gate *
31470Sstevel@tonic-gate * NOTE: If timeout is zero; Assume infinite timeout and don't
31480Sstevel@tonic-gate * insert this transfer on the timeout list.
31490Sstevel@tonic-gate */
31500Sstevel@tonic-gate if (tw->tw_timeout) {
31510Sstevel@tonic-gate /*
31520Sstevel@tonic-gate * Add this transfer wrapper to the head of the pipe's
31530Sstevel@tonic-gate * tw timeout list.
31540Sstevel@tonic-gate */
31550Sstevel@tonic-gate if (pp->pp_timeout_list) {
31560Sstevel@tonic-gate tw->tw_timeout_next = pp->pp_timeout_list;
31570Sstevel@tonic-gate }
31580Sstevel@tonic-gate
31590Sstevel@tonic-gate pp->pp_timeout_list = tw;
31600Sstevel@tonic-gate ehci_start_timer(ehcip, pp);
31610Sstevel@tonic-gate }
31620Sstevel@tonic-gate }
31630Sstevel@tonic-gate
31640Sstevel@tonic-gate
31650Sstevel@tonic-gate /*
31660Sstevel@tonic-gate * ehci_stop_xfer_timer:
31670Sstevel@tonic-gate *
31680Sstevel@tonic-gate * Start the timer for the control, bulk and for one time interrupt
31690Sstevel@tonic-gate * transfers.
31700Sstevel@tonic-gate */
31710Sstevel@tonic-gate void
ehci_stop_xfer_timer(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw,uint_t flag)31720Sstevel@tonic-gate ehci_stop_xfer_timer(
31730Sstevel@tonic-gate ehci_state_t *ehcip,
31740Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
31750Sstevel@tonic-gate uint_t flag)
31760Sstevel@tonic-gate {
31770Sstevel@tonic-gate ehci_pipe_private_t *pp;
31780Sstevel@tonic-gate timeout_id_t timer_id;
31790Sstevel@tonic-gate
31800Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
31816898Sfb209375 "ehci_stop_xfer_timer: tw = 0x%p", (void *)tw);
31820Sstevel@tonic-gate
31830Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
31840Sstevel@tonic-gate
31850Sstevel@tonic-gate /* Obtain the pipe private structure */
31860Sstevel@tonic-gate pp = tw->tw_pipe_private;
31870Sstevel@tonic-gate
31880Sstevel@tonic-gate /* check if the timeout tw list is empty */
31890Sstevel@tonic-gate if (pp->pp_timeout_list == NULL) {
31900Sstevel@tonic-gate
31910Sstevel@tonic-gate return;
31920Sstevel@tonic-gate }
31930Sstevel@tonic-gate
31940Sstevel@tonic-gate switch (flag) {
31950Sstevel@tonic-gate case EHCI_REMOVE_XFER_IFLAST:
31960Sstevel@tonic-gate if (tw->tw_qtd_head != tw->tw_qtd_tail) {
31970Sstevel@tonic-gate break;
31980Sstevel@tonic-gate }
31990Sstevel@tonic-gate
32000Sstevel@tonic-gate /* FALLTHRU */
32010Sstevel@tonic-gate case EHCI_REMOVE_XFER_ALWAYS:
32020Sstevel@tonic-gate ehci_remove_tw_from_timeout_list(ehcip, tw);
32030Sstevel@tonic-gate
32040Sstevel@tonic-gate if ((pp->pp_timeout_list == NULL) &&
32050Sstevel@tonic-gate (pp->pp_timer_id)) {
32060Sstevel@tonic-gate
32070Sstevel@tonic-gate timer_id = pp->pp_timer_id;
32080Sstevel@tonic-gate
32090Sstevel@tonic-gate /* Reset the timer id to zero */
32100Sstevel@tonic-gate pp->pp_timer_id = 0;
32110Sstevel@tonic-gate
32120Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
32130Sstevel@tonic-gate
32140Sstevel@tonic-gate (void) untimeout(timer_id);
32150Sstevel@tonic-gate
32160Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
32170Sstevel@tonic-gate }
32180Sstevel@tonic-gate break;
32190Sstevel@tonic-gate default:
32200Sstevel@tonic-gate break;
32210Sstevel@tonic-gate }
32220Sstevel@tonic-gate }
32230Sstevel@tonic-gate
32240Sstevel@tonic-gate
32250Sstevel@tonic-gate /*
32260Sstevel@tonic-gate * ehci_xfer_timeout_handler:
32270Sstevel@tonic-gate *
32280Sstevel@tonic-gate * Control or bulk transfer timeout handler.
32290Sstevel@tonic-gate */
32300Sstevel@tonic-gate static void
ehci_xfer_timeout_handler(void * arg)32310Sstevel@tonic-gate ehci_xfer_timeout_handler(void *arg)
32320Sstevel@tonic-gate {
32330Sstevel@tonic-gate usba_pipe_handle_data_t *ph = (usba_pipe_handle_data_t *)arg;
32340Sstevel@tonic-gate ehci_state_t *ehcip = ehci_obtain_state(
32355441Ssl147100 ph->p_usba_device->usb_root_hub_dip);
32360Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
32370Sstevel@tonic-gate ehci_trans_wrapper_t *tw, *next;
32380Sstevel@tonic-gate ehci_trans_wrapper_t *expire_xfer_list = NULL;
32390Sstevel@tonic-gate ehci_qtd_t *qtd;
32400Sstevel@tonic-gate
32410Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
32426898Sfb209375 "ehci_xfer_timeout_handler: ehcip = 0x%p, ph = 0x%p",
32436898Sfb209375 (void *)ehcip, (void *)ph);
32440Sstevel@tonic-gate
32450Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
32460Sstevel@tonic-gate
32470Sstevel@tonic-gate /*
32480Sstevel@tonic-gate * Check whether still timeout handler is valid.
32490Sstevel@tonic-gate */
32500Sstevel@tonic-gate if (pp->pp_timer_id != 0) {
32510Sstevel@tonic-gate
32520Sstevel@tonic-gate /* Reset the timer id to zero */
32530Sstevel@tonic-gate pp->pp_timer_id = 0;
32540Sstevel@tonic-gate } else {
32550Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
32560Sstevel@tonic-gate
32570Sstevel@tonic-gate return;
32580Sstevel@tonic-gate }
32590Sstevel@tonic-gate
32600Sstevel@tonic-gate /* Get the transfer timeout list head */
32610Sstevel@tonic-gate tw = pp->pp_timeout_list;
32620Sstevel@tonic-gate
32630Sstevel@tonic-gate while (tw) {
32640Sstevel@tonic-gate
32650Sstevel@tonic-gate /* Get the transfer on the timeout list */
32660Sstevel@tonic-gate next = tw->tw_timeout_next;
32670Sstevel@tonic-gate
32680Sstevel@tonic-gate tw->tw_timeout--;
32690Sstevel@tonic-gate
32700Sstevel@tonic-gate if (tw->tw_timeout <= 0) {
32710Sstevel@tonic-gate
32720Sstevel@tonic-gate /* remove the tw from the timeout list */
32730Sstevel@tonic-gate ehci_remove_tw_from_timeout_list(ehcip, tw);
32740Sstevel@tonic-gate
32750Sstevel@tonic-gate /* remove QTDs from active QTD list */
32760Sstevel@tonic-gate qtd = tw->tw_qtd_head;
32770Sstevel@tonic-gate while (qtd) {
32780Sstevel@tonic-gate ehci_remove_qtd_from_active_qtd_list(
32795441Ssl147100 ehcip, qtd);
32800Sstevel@tonic-gate
32810Sstevel@tonic-gate /* Get the next QTD from the wrapper */
32820Sstevel@tonic-gate qtd = ehci_qtd_iommu_to_cpu(ehcip,
32830Sstevel@tonic-gate Get_QTD(qtd->qtd_tw_next_qtd));
32840Sstevel@tonic-gate }
32850Sstevel@tonic-gate
32860Sstevel@tonic-gate /*
32870Sstevel@tonic-gate * Preserve the order to the requests
32880Sstevel@tonic-gate * started time sequence.
32890Sstevel@tonic-gate */
32900Sstevel@tonic-gate tw->tw_timeout_next = expire_xfer_list;
32910Sstevel@tonic-gate expire_xfer_list = tw;
32920Sstevel@tonic-gate }
32930Sstevel@tonic-gate
32940Sstevel@tonic-gate tw = next;
32950Sstevel@tonic-gate }
32960Sstevel@tonic-gate
32970Sstevel@tonic-gate /*
32980Sstevel@tonic-gate * The timer should be started before the callbacks.
32990Sstevel@tonic-gate * There is always a chance that ehci interrupts come
33000Sstevel@tonic-gate * in when we release the mutex while calling the tw back.
33010Sstevel@tonic-gate * To keep an accurate timeout it should be restarted
33020Sstevel@tonic-gate * as soon as possible.
33030Sstevel@tonic-gate */
33040Sstevel@tonic-gate ehci_start_timer(ehcip, pp);
33050Sstevel@tonic-gate
33060Sstevel@tonic-gate /* Get the expired transfer timeout list head */
33070Sstevel@tonic-gate tw = expire_xfer_list;
33080Sstevel@tonic-gate
33090Sstevel@tonic-gate while (tw) {
33100Sstevel@tonic-gate
33110Sstevel@tonic-gate /* Get the next tw on the expired transfer timeout list */
33120Sstevel@tonic-gate next = tw->tw_timeout_next;
33130Sstevel@tonic-gate
33140Sstevel@tonic-gate /*
33150Sstevel@tonic-gate * The error handle routine will release the mutex when
33160Sstevel@tonic-gate * calling back to USBA. But this will not cause any race.
33170Sstevel@tonic-gate * We do the callback and are relying on ehci_pipe_cleanup()
33180Sstevel@tonic-gate * to halt the queue head and clean up since we should not
33190Sstevel@tonic-gate * block in timeout context.
33200Sstevel@tonic-gate */
33210Sstevel@tonic-gate ehci_handle_error(ehcip, tw->tw_qtd_head, USB_CR_TIMEOUT);
33220Sstevel@tonic-gate
33230Sstevel@tonic-gate tw = next;
33240Sstevel@tonic-gate }
33250Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
33260Sstevel@tonic-gate }
33270Sstevel@tonic-gate
33280Sstevel@tonic-gate
33290Sstevel@tonic-gate /*
33300Sstevel@tonic-gate * ehci_remove_tw_from_timeout_list:
33310Sstevel@tonic-gate *
33320Sstevel@tonic-gate * Remove Control or bulk transfer from the timeout list.
33330Sstevel@tonic-gate */
33340Sstevel@tonic-gate static void
ehci_remove_tw_from_timeout_list(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw)33350Sstevel@tonic-gate ehci_remove_tw_from_timeout_list(
33360Sstevel@tonic-gate ehci_state_t *ehcip,
33370Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
33380Sstevel@tonic-gate {
33390Sstevel@tonic-gate ehci_pipe_private_t *pp;
33400Sstevel@tonic-gate ehci_trans_wrapper_t *prev, *next;
33410Sstevel@tonic-gate
33420Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
33436898Sfb209375 "ehci_remove_tw_from_timeout_list: tw = 0x%p", (void *)tw);
33440Sstevel@tonic-gate
33450Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
33460Sstevel@tonic-gate
33470Sstevel@tonic-gate /* Obtain the pipe private structure */
33480Sstevel@tonic-gate pp = tw->tw_pipe_private;
33490Sstevel@tonic-gate
33500Sstevel@tonic-gate if (pp->pp_timeout_list) {
33510Sstevel@tonic-gate if (pp->pp_timeout_list == tw) {
33520Sstevel@tonic-gate pp->pp_timeout_list = tw->tw_timeout_next;
33530Sstevel@tonic-gate
33540Sstevel@tonic-gate tw->tw_timeout_next = NULL;
33550Sstevel@tonic-gate } else {
33560Sstevel@tonic-gate prev = pp->pp_timeout_list;
33570Sstevel@tonic-gate next = prev->tw_timeout_next;
33580Sstevel@tonic-gate
33590Sstevel@tonic-gate while (next && (next != tw)) {
33600Sstevel@tonic-gate prev = next;
33610Sstevel@tonic-gate next = next->tw_timeout_next;
33620Sstevel@tonic-gate }
33630Sstevel@tonic-gate
33640Sstevel@tonic-gate if (next == tw) {
33650Sstevel@tonic-gate prev->tw_timeout_next =
33665441Ssl147100 next->tw_timeout_next;
33670Sstevel@tonic-gate tw->tw_timeout_next = NULL;
33680Sstevel@tonic-gate }
33690Sstevel@tonic-gate }
33700Sstevel@tonic-gate }
33710Sstevel@tonic-gate }
33720Sstevel@tonic-gate
33730Sstevel@tonic-gate
33740Sstevel@tonic-gate /*
33750Sstevel@tonic-gate * ehci_start_timer:
33760Sstevel@tonic-gate *
33770Sstevel@tonic-gate * Start the pipe's timer
33780Sstevel@tonic-gate */
33790Sstevel@tonic-gate static void
ehci_start_timer(ehci_state_t * ehcip,ehci_pipe_private_t * pp)33800Sstevel@tonic-gate ehci_start_timer(
33810Sstevel@tonic-gate ehci_state_t *ehcip,
33820Sstevel@tonic-gate ehci_pipe_private_t *pp)
33830Sstevel@tonic-gate {
33840Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
33856898Sfb209375 "ehci_start_timer: ehcip = 0x%p, pp = 0x%p",
33866898Sfb209375 (void *)ehcip, (void *)pp);
33870Sstevel@tonic-gate
33880Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
33890Sstevel@tonic-gate
33900Sstevel@tonic-gate /*
33910Sstevel@tonic-gate * Start the pipe's timer only if currently timer is not
33920Sstevel@tonic-gate * running and if there are any transfers on the timeout
33930Sstevel@tonic-gate * list. This timer will be per pipe.
33940Sstevel@tonic-gate */
33950Sstevel@tonic-gate if ((!pp->pp_timer_id) && (pp->pp_timeout_list)) {
33960Sstevel@tonic-gate pp->pp_timer_id = timeout(ehci_xfer_timeout_handler,
33970Sstevel@tonic-gate (void *)(pp->pp_pipe_handle), drv_usectohz(1000000));
33980Sstevel@tonic-gate }
33990Sstevel@tonic-gate }
34000Sstevel@tonic-gate
34010Sstevel@tonic-gate /*
34020Sstevel@tonic-gate * ehci_deallocate_tw:
34030Sstevel@tonic-gate *
34040Sstevel@tonic-gate * Deallocate of a Transaction Wrapper (TW) and this involves the freeing of
34050Sstevel@tonic-gate * of DMA resources.
34060Sstevel@tonic-gate */
34070Sstevel@tonic-gate void
ehci_deallocate_tw(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)34080Sstevel@tonic-gate ehci_deallocate_tw(
34090Sstevel@tonic-gate ehci_state_t *ehcip,
34100Sstevel@tonic-gate ehci_pipe_private_t *pp,
34110Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
34120Sstevel@tonic-gate {
34130Sstevel@tonic-gate ehci_trans_wrapper_t *prev, *next;
34140Sstevel@tonic-gate
34150Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
34166898Sfb209375 "ehci_deallocate_tw: tw = 0x%p", (void *)tw);
34170Sstevel@tonic-gate
34180Sstevel@tonic-gate /*
34190Sstevel@tonic-gate * If the transfer wrapper has no Host Controller (HC)
34200Sstevel@tonic-gate * Transfer Descriptors (QTD) associated with it, then
34210Sstevel@tonic-gate * remove the transfer wrapper.
34220Sstevel@tonic-gate */
34230Sstevel@tonic-gate if (tw->tw_qtd_head) {
34240Sstevel@tonic-gate ASSERT(tw->tw_qtd_tail != NULL);
34250Sstevel@tonic-gate
34260Sstevel@tonic-gate return;
34270Sstevel@tonic-gate }
34280Sstevel@tonic-gate
34290Sstevel@tonic-gate ASSERT(tw->tw_qtd_tail == NULL);
34300Sstevel@tonic-gate
34310Sstevel@tonic-gate /* Make sure we return all the unused qtd's to the pool as well */
34320Sstevel@tonic-gate ehci_free_tw_td_resources(ehcip, tw);
34330Sstevel@tonic-gate
34340Sstevel@tonic-gate /*
34350Sstevel@tonic-gate * If pp->pp_tw_head and pp->pp_tw_tail are pointing to
34360Sstevel@tonic-gate * given TW then set the head and tail equal to NULL.
34370Sstevel@tonic-gate * Otherwise search for this TW in the linked TW's list
34380Sstevel@tonic-gate * and then remove this TW from the list.
34390Sstevel@tonic-gate */
34400Sstevel@tonic-gate if (pp->pp_tw_head == tw) {
34410Sstevel@tonic-gate if (pp->pp_tw_tail == tw) {
34420Sstevel@tonic-gate pp->pp_tw_head = NULL;
34430Sstevel@tonic-gate pp->pp_tw_tail = NULL;
34440Sstevel@tonic-gate } else {
34450Sstevel@tonic-gate pp->pp_tw_head = tw->tw_next;
34460Sstevel@tonic-gate }
34470Sstevel@tonic-gate } else {
34480Sstevel@tonic-gate prev = pp->pp_tw_head;
34490Sstevel@tonic-gate next = prev->tw_next;
34500Sstevel@tonic-gate
34510Sstevel@tonic-gate while (next && (next != tw)) {
34520Sstevel@tonic-gate prev = next;
34530Sstevel@tonic-gate next = next->tw_next;
34540Sstevel@tonic-gate }
34550Sstevel@tonic-gate
34560Sstevel@tonic-gate if (next == tw) {
34570Sstevel@tonic-gate prev->tw_next = next->tw_next;
34580Sstevel@tonic-gate
34590Sstevel@tonic-gate if (pp->pp_tw_tail == tw) {
34600Sstevel@tonic-gate pp->pp_tw_tail = prev;
34610Sstevel@tonic-gate }
34620Sstevel@tonic-gate }
34630Sstevel@tonic-gate }
34640Sstevel@tonic-gate
34650Sstevel@tonic-gate /*
34660Sstevel@tonic-gate * Make sure that, this TW has been removed
34670Sstevel@tonic-gate * from the timeout list.
34680Sstevel@tonic-gate */
34690Sstevel@tonic-gate ehci_remove_tw_from_timeout_list(ehcip, tw);
34700Sstevel@tonic-gate
34710Sstevel@tonic-gate /* Deallocate this TW */
34720Sstevel@tonic-gate ehci_free_tw(ehcip, pp, tw);
34730Sstevel@tonic-gate }
34740Sstevel@tonic-gate
34750Sstevel@tonic-gate
34760Sstevel@tonic-gate /*
34770Sstevel@tonic-gate * ehci_free_dma_resources:
34780Sstevel@tonic-gate *
34790Sstevel@tonic-gate * Free dma resources of a Transfer Wrapper (TW) and also free the TW.
34800Sstevel@tonic-gate *
34810Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
34820Sstevel@tonic-gate */
34830Sstevel@tonic-gate void
ehci_free_dma_resources(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)34840Sstevel@tonic-gate ehci_free_dma_resources(
34850Sstevel@tonic-gate ehci_state_t *ehcip,
34860Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
34870Sstevel@tonic-gate {
34880Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
34890Sstevel@tonic-gate ehci_trans_wrapper_t *head_tw = pp->pp_tw_head;
34900Sstevel@tonic-gate ehci_trans_wrapper_t *next_tw, *tw;
34910Sstevel@tonic-gate
34920Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
34930Sstevel@tonic-gate "ehci_free_dma_resources: ph = 0x%p", (void *)ph);
34940Sstevel@tonic-gate
34950Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
34960Sstevel@tonic-gate
34970Sstevel@tonic-gate /* Process the Transfer Wrappers */
34980Sstevel@tonic-gate next_tw = head_tw;
34990Sstevel@tonic-gate while (next_tw) {
35000Sstevel@tonic-gate tw = next_tw;
35010Sstevel@tonic-gate next_tw = tw->tw_next;
35020Sstevel@tonic-gate
35030Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
35040Sstevel@tonic-gate "ehci_free_dma_resources: Free TW = 0x%p", (void *)tw);
35050Sstevel@tonic-gate
35060Sstevel@tonic-gate ehci_free_tw(ehcip, pp, tw);
35070Sstevel@tonic-gate }
35080Sstevel@tonic-gate
35090Sstevel@tonic-gate /* Adjust the head and tail pointers */
35100Sstevel@tonic-gate pp->pp_tw_head = NULL;
35110Sstevel@tonic-gate pp->pp_tw_tail = NULL;
35120Sstevel@tonic-gate }
35130Sstevel@tonic-gate
35140Sstevel@tonic-gate
35150Sstevel@tonic-gate /*
35160Sstevel@tonic-gate * ehci_free_tw:
35170Sstevel@tonic-gate *
35180Sstevel@tonic-gate * Free the Transfer Wrapper (TW).
35190Sstevel@tonic-gate */
35200Sstevel@tonic-gate /*ARGSUSED*/
35210Sstevel@tonic-gate static void
ehci_free_tw(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)35220Sstevel@tonic-gate ehci_free_tw(
35230Sstevel@tonic-gate ehci_state_t *ehcip,
35240Sstevel@tonic-gate ehci_pipe_private_t *pp,
35250Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
35260Sstevel@tonic-gate {
35270Sstevel@tonic-gate int rval;
352812733SRaymond.Chen@Sun.COM usb_ep_descr_t *eptd = &pp->pp_pipe_handle->p_ep;
35290Sstevel@tonic-gate
35300Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
35316898Sfb209375 "ehci_free_tw: tw = 0x%p", (void *)tw);
35320Sstevel@tonic-gate
35330Sstevel@tonic-gate ASSERT(tw != NULL);
35340Sstevel@tonic-gate ASSERT(tw->tw_id != NULL);
35350Sstevel@tonic-gate
35360Sstevel@tonic-gate /* Free 32bit ID */
35370Sstevel@tonic-gate EHCI_FREE_ID((uint32_t)tw->tw_id);
35380Sstevel@tonic-gate
35392495Sgc161489 if (tw->tw_dmahandle != NULL) {
35402495Sgc161489 rval = ddi_dma_unbind_handle(tw->tw_dmahandle);
35412495Sgc161489 ASSERT(rval == DDI_SUCCESS);
35422495Sgc161489
35432495Sgc161489 ddi_dma_mem_free(&tw->tw_accesshandle);
35442495Sgc161489 ddi_dma_free_handle(&tw->tw_dmahandle);
35452495Sgc161489 }
35460Sstevel@tonic-gate
354712733SRaymond.Chen@Sun.COM /* interrupt ep will come to this point */
354812733SRaymond.Chen@Sun.COM if (EHCI_INTR_ENDPOINT(eptd)) {
354912733SRaymond.Chen@Sun.COM ehcip->ehci_periodic_req_count--;
355012733SRaymond.Chen@Sun.COM } else {
355112733SRaymond.Chen@Sun.COM ehcip->ehci_async_req_count--;
355212733SRaymond.Chen@Sun.COM }
355312733SRaymond.Chen@Sun.COM ehci_toggle_scheduler(ehcip);
355412733SRaymond.Chen@Sun.COM
35550Sstevel@tonic-gate /* Free transfer wrapper */
35560Sstevel@tonic-gate kmem_free(tw, sizeof (ehci_trans_wrapper_t));
35570Sstevel@tonic-gate }
35580Sstevel@tonic-gate
35590Sstevel@tonic-gate
35600Sstevel@tonic-gate /*
35610Sstevel@tonic-gate * Miscellaneous functions
35620Sstevel@tonic-gate */
35630Sstevel@tonic-gate
35640Sstevel@tonic-gate /*
35650Sstevel@tonic-gate * ehci_allocate_intr_in_resource
35660Sstevel@tonic-gate *
35670Sstevel@tonic-gate * Allocate interrupt request structure for the interrupt IN transfer.
35680Sstevel@tonic-gate */
35690Sstevel@tonic-gate /*ARGSUSED*/
35700Sstevel@tonic-gate int
ehci_allocate_intr_in_resource(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw,usb_flags_t flags)35710Sstevel@tonic-gate ehci_allocate_intr_in_resource(
35720Sstevel@tonic-gate ehci_state_t *ehcip,
35730Sstevel@tonic-gate ehci_pipe_private_t *pp,
35740Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
35750Sstevel@tonic-gate usb_flags_t flags)
35760Sstevel@tonic-gate {
35770Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
35780Sstevel@tonic-gate usb_intr_req_t *curr_intr_reqp;
35790Sstevel@tonic-gate usb_opaque_t client_periodic_in_reqp;
35800Sstevel@tonic-gate size_t length = 0;
35810Sstevel@tonic-gate
35820Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
35830Sstevel@tonic-gate "ehci_allocate_intr_in_resource:"
35846898Sfb209375 "pp = 0x%p tw = 0x%p flags = 0x%x", (void *)pp, (void *)tw, flags);
35850Sstevel@tonic-gate
35860Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
35870Sstevel@tonic-gate ASSERT(tw->tw_curr_xfer_reqp == NULL);
35880Sstevel@tonic-gate
35890Sstevel@tonic-gate /* Get the client periodic in request pointer */
35900Sstevel@tonic-gate client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
35910Sstevel@tonic-gate
35920Sstevel@tonic-gate /*
35930Sstevel@tonic-gate * If it a periodic IN request and periodic request is NULL,
35940Sstevel@tonic-gate * allocate corresponding usb periodic IN request for the
35950Sstevel@tonic-gate * current periodic polling request and copy the information
35960Sstevel@tonic-gate * from the saved periodic request structure.
35970Sstevel@tonic-gate */
35980Sstevel@tonic-gate if (client_periodic_in_reqp) {
35990Sstevel@tonic-gate
36000Sstevel@tonic-gate /* Get the interrupt transfer length */
36010Sstevel@tonic-gate length = ((usb_intr_req_t *)
36020Sstevel@tonic-gate client_periodic_in_reqp)->intr_len;
36030Sstevel@tonic-gate
36040Sstevel@tonic-gate curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
36050Sstevel@tonic-gate (usb_intr_req_t *)client_periodic_in_reqp, length, flags);
36060Sstevel@tonic-gate } else {
36070Sstevel@tonic-gate curr_intr_reqp = usb_alloc_intr_req(ph->p_dip, length, flags);
36080Sstevel@tonic-gate }
36090Sstevel@tonic-gate
36100Sstevel@tonic-gate if (curr_intr_reqp == NULL) {
36110Sstevel@tonic-gate
36120Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
36130Sstevel@tonic-gate "ehci_allocate_intr_in_resource: Interrupt"
36140Sstevel@tonic-gate "request structure allocation failed");
36150Sstevel@tonic-gate
36160Sstevel@tonic-gate return (USB_NO_RESOURCES);
36170Sstevel@tonic-gate }
36180Sstevel@tonic-gate
36190Sstevel@tonic-gate /* For polled mode */
36200Sstevel@tonic-gate if (client_periodic_in_reqp == NULL) {
36210Sstevel@tonic-gate curr_intr_reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK;
36220Sstevel@tonic-gate curr_intr_reqp->intr_len = ph->p_ep.wMaxPacketSize;
36230Sstevel@tonic-gate } else {
36240Sstevel@tonic-gate /* Check and save the timeout value */
36250Sstevel@tonic-gate tw->tw_timeout = (curr_intr_reqp->intr_attributes &
36260Sstevel@tonic-gate USB_ATTRS_ONE_XFER) ? curr_intr_reqp->intr_timeout: 0;
36270Sstevel@tonic-gate }
36280Sstevel@tonic-gate
36290Sstevel@tonic-gate tw->tw_curr_xfer_reqp = (usb_opaque_t)curr_intr_reqp;
36300Sstevel@tonic-gate tw->tw_length = curr_intr_reqp->intr_len;
36310Sstevel@tonic-gate
36320Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
36330Sstevel@tonic-gate ph->p_req_count++;
36340Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
36350Sstevel@tonic-gate
36360Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_ACTIVE;
36370Sstevel@tonic-gate
36380Sstevel@tonic-gate return (USB_SUCCESS);
36390Sstevel@tonic-gate }
36400Sstevel@tonic-gate
36410Sstevel@tonic-gate /*
36420Sstevel@tonic-gate * ehci_pipe_cleanup
36430Sstevel@tonic-gate *
36440Sstevel@tonic-gate * Cleanup ehci pipe.
36450Sstevel@tonic-gate */
36460Sstevel@tonic-gate void
ehci_pipe_cleanup(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)36470Sstevel@tonic-gate ehci_pipe_cleanup(
36480Sstevel@tonic-gate ehci_state_t *ehcip,
36490Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
36500Sstevel@tonic-gate {
36510Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
36520Sstevel@tonic-gate uint_t pipe_state = pp->pp_state;
36530Sstevel@tonic-gate usb_cr_t completion_reason;
36540Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
36550Sstevel@tonic-gate
36560Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
36576898Sfb209375 "ehci_pipe_cleanup: ph = 0x%p", (void *)ph);
36580Sstevel@tonic-gate
36590Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
36600Sstevel@tonic-gate
36610Sstevel@tonic-gate if (EHCI_ISOC_ENDPOINT(eptd)) {
36620Sstevel@tonic-gate ehci_isoc_pipe_cleanup(ehcip, ph);
36630Sstevel@tonic-gate
36640Sstevel@tonic-gate return;
36650Sstevel@tonic-gate }
36660Sstevel@tonic-gate
36670Sstevel@tonic-gate ASSERT(!servicing_interrupt());
36680Sstevel@tonic-gate
36690Sstevel@tonic-gate /*
36700Sstevel@tonic-gate * Set the QH's status to Halt condition.
36710Sstevel@tonic-gate * If another thread is halting this function will automatically
36720Sstevel@tonic-gate * wait. If a pipe close happens at this time
36730Sstevel@tonic-gate * we will be in lots of trouble.
36740Sstevel@tonic-gate * If we are in an interrupt thread, don't halt, because it may
36750Sstevel@tonic-gate * do a wait_for_sof.
36760Sstevel@tonic-gate */
36770Sstevel@tonic-gate ehci_modify_qh_status_bit(ehcip, pp, SET_HALT);
36780Sstevel@tonic-gate
36790Sstevel@tonic-gate /*
36800Sstevel@tonic-gate * Wait for processing all completed transfers and
36810Sstevel@tonic-gate * to send results to upstream.
36820Sstevel@tonic-gate */
36830Sstevel@tonic-gate ehci_wait_for_transfers_completion(ehcip, pp);
36840Sstevel@tonic-gate
36850Sstevel@tonic-gate /* Save the data toggle information */
36860Sstevel@tonic-gate ehci_save_data_toggle(ehcip, ph);
36870Sstevel@tonic-gate
36880Sstevel@tonic-gate /*
36890Sstevel@tonic-gate * Traverse the list of QTDs for this pipe using transfer
36900Sstevel@tonic-gate * wrapper. Process these QTDs depending on their status.
36910Sstevel@tonic-gate * And stop the timer of this pipe.
36920Sstevel@tonic-gate */
36930Sstevel@tonic-gate ehci_traverse_qtds(ehcip, ph);
36940Sstevel@tonic-gate
36950Sstevel@tonic-gate /* Make sure the timer is not running */
36960Sstevel@tonic-gate ASSERT(pp->pp_timer_id == 0);
36970Sstevel@tonic-gate
36980Sstevel@tonic-gate /* Do callbacks for all unfinished requests */
36990Sstevel@tonic-gate ehci_handle_outstanding_requests(ehcip, pp);
37000Sstevel@tonic-gate
37010Sstevel@tonic-gate /* Free DMA resources */
37020Sstevel@tonic-gate ehci_free_dma_resources(ehcip, ph);
37030Sstevel@tonic-gate
37040Sstevel@tonic-gate switch (pipe_state) {
37050Sstevel@tonic-gate case EHCI_PIPE_STATE_CLOSE:
37060Sstevel@tonic-gate completion_reason = USB_CR_PIPE_CLOSING;
37070Sstevel@tonic-gate break;
37080Sstevel@tonic-gate case EHCI_PIPE_STATE_RESET:
37090Sstevel@tonic-gate case EHCI_PIPE_STATE_STOP_POLLING:
37100Sstevel@tonic-gate /* Set completion reason */
37110Sstevel@tonic-gate completion_reason = (pipe_state ==
37120Sstevel@tonic-gate EHCI_PIPE_STATE_RESET) ?
37130Sstevel@tonic-gate USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
37140Sstevel@tonic-gate
37150Sstevel@tonic-gate /* Restore the data toggle information */
37160Sstevel@tonic-gate ehci_restore_data_toggle(ehcip, ph);
37170Sstevel@tonic-gate
37180Sstevel@tonic-gate /*
37190Sstevel@tonic-gate * Clear the halt bit to restart all the
37200Sstevel@tonic-gate * transactions on this pipe.
37210Sstevel@tonic-gate */
37220Sstevel@tonic-gate ehci_modify_qh_status_bit(ehcip, pp, CLEAR_HALT);
37230Sstevel@tonic-gate
37240Sstevel@tonic-gate /* Set pipe state to idle */
37250Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_IDLE;
37260Sstevel@tonic-gate
37270Sstevel@tonic-gate break;
37280Sstevel@tonic-gate }
37290Sstevel@tonic-gate
37300Sstevel@tonic-gate /*
37310Sstevel@tonic-gate * Do the callback for the original client
37320Sstevel@tonic-gate * periodic IN request.
37330Sstevel@tonic-gate */
37340Sstevel@tonic-gate if ((EHCI_PERIODIC_ENDPOINT(eptd)) &&
37350Sstevel@tonic-gate ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
37360Sstevel@tonic-gate USB_EP_DIR_IN)) {
37370Sstevel@tonic-gate
37380Sstevel@tonic-gate ehci_do_client_periodic_in_req_callback(
37390Sstevel@tonic-gate ehcip, pp, completion_reason);
37400Sstevel@tonic-gate }
37410Sstevel@tonic-gate }
37420Sstevel@tonic-gate
37430Sstevel@tonic-gate
37440Sstevel@tonic-gate /*
37450Sstevel@tonic-gate * ehci_wait_for_transfers_completion:
37460Sstevel@tonic-gate *
37470Sstevel@tonic-gate * Wait for processing all completed transfers and to send results
37480Sstevel@tonic-gate * to upstream.
37490Sstevel@tonic-gate */
37500Sstevel@tonic-gate static void
ehci_wait_for_transfers_completion(ehci_state_t * ehcip,ehci_pipe_private_t * pp)37510Sstevel@tonic-gate ehci_wait_for_transfers_completion(
37520Sstevel@tonic-gate ehci_state_t *ehcip,
37530Sstevel@tonic-gate ehci_pipe_private_t *pp)
37540Sstevel@tonic-gate {
37550Sstevel@tonic-gate ehci_trans_wrapper_t *next_tw = pp->pp_tw_head;
37560Sstevel@tonic-gate ehci_qtd_t *qtd;
37570Sstevel@tonic-gate
37580Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
37590Sstevel@tonic-gate ehcip->ehci_log_hdl,
37606898Sfb209375 "ehci_wait_for_transfers_completion: pp = 0x%p", (void *)pp);
37610Sstevel@tonic-gate
37620Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
37630Sstevel@tonic-gate
37640Sstevel@tonic-gate if ((ehci_state_is_operational(ehcip)) != USB_SUCCESS) {
37650Sstevel@tonic-gate
37660Sstevel@tonic-gate return;
37670Sstevel@tonic-gate }
37680Sstevel@tonic-gate
37690Sstevel@tonic-gate pp->pp_count_done_qtds = 0;
37700Sstevel@tonic-gate
37710Sstevel@tonic-gate /* Process the transfer wrappers for this pipe */
37720Sstevel@tonic-gate while (next_tw) {
37730Sstevel@tonic-gate qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
37740Sstevel@tonic-gate
37750Sstevel@tonic-gate /*
37760Sstevel@tonic-gate * Walk through each QTD for this transfer wrapper.
37770Sstevel@tonic-gate * If a QTD still exists, then it is either on done
37780Sstevel@tonic-gate * list or on the QH's list.
37790Sstevel@tonic-gate */
37800Sstevel@tonic-gate while (qtd) {
37810Sstevel@tonic-gate if (!(Get_QTD(qtd->qtd_ctrl) &
37820Sstevel@tonic-gate EHCI_QTD_CTRL_ACTIVE_XACT)) {
37830Sstevel@tonic-gate pp->pp_count_done_qtds++;
37840Sstevel@tonic-gate }
37850Sstevel@tonic-gate
37860Sstevel@tonic-gate qtd = ehci_qtd_iommu_to_cpu(ehcip,
37870Sstevel@tonic-gate Get_QTD(qtd->qtd_tw_next_qtd));
37880Sstevel@tonic-gate }
37890Sstevel@tonic-gate
37900Sstevel@tonic-gate next_tw = next_tw->tw_next;
37910Sstevel@tonic-gate }
37920Sstevel@tonic-gate
37930Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
37940Sstevel@tonic-gate "ehci_wait_for_transfers_completion: count_done_qtds = 0x%x",
37950Sstevel@tonic-gate pp->pp_count_done_qtds);
37960Sstevel@tonic-gate
37970Sstevel@tonic-gate if (!pp->pp_count_done_qtds) {
37980Sstevel@tonic-gate
37990Sstevel@tonic-gate return;
38000Sstevel@tonic-gate }
38010Sstevel@tonic-gate
380211066Srafael.vanoni@sun.com (void) cv_reltimedwait(&pp->pp_xfer_cmpl_cv, &ehcip->ehci_int_mutex,
380311066Srafael.vanoni@sun.com drv_usectohz(EHCI_XFER_CMPL_TIMEWAIT * 1000000), TR_CLOCK_TICK);
38040Sstevel@tonic-gate
38050Sstevel@tonic-gate if (pp->pp_count_done_qtds) {
38060Sstevel@tonic-gate
38070Sstevel@tonic-gate USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
38080Sstevel@tonic-gate "ehci_wait_for_transfers_completion:"
38090Sstevel@tonic-gate "No transfers completion confirmation received");
38100Sstevel@tonic-gate }
38110Sstevel@tonic-gate }
38120Sstevel@tonic-gate
38130Sstevel@tonic-gate /*
38140Sstevel@tonic-gate * ehci_check_for_transfers_completion:
38150Sstevel@tonic-gate *
38160Sstevel@tonic-gate * Check whether anybody is waiting for transfers completion event. If so, send
38170Sstevel@tonic-gate * this event and also stop initiating any new transfers on this pipe.
38180Sstevel@tonic-gate */
38190Sstevel@tonic-gate void
ehci_check_for_transfers_completion(ehci_state_t * ehcip,ehci_pipe_private_t * pp)38200Sstevel@tonic-gate ehci_check_for_transfers_completion(
38210Sstevel@tonic-gate ehci_state_t *ehcip,
38220Sstevel@tonic-gate ehci_pipe_private_t *pp)
38230Sstevel@tonic-gate {
38240Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
38250Sstevel@tonic-gate ehcip->ehci_log_hdl,
38266898Sfb209375 "ehci_check_for_transfers_completion: pp = 0x%p", (void *)pp);
38270Sstevel@tonic-gate
38280Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
38290Sstevel@tonic-gate
38300Sstevel@tonic-gate if ((pp->pp_state == EHCI_PIPE_STATE_STOP_POLLING) &&
38310Sstevel@tonic-gate (pp->pp_error == USB_CR_NO_RESOURCES) &&
38320Sstevel@tonic-gate (pp->pp_cur_periodic_req_cnt == 0)) {
38330Sstevel@tonic-gate
38340Sstevel@tonic-gate /* Reset pipe error to zero */
38350Sstevel@tonic-gate pp->pp_error = 0;
38360Sstevel@tonic-gate
38370Sstevel@tonic-gate /* Do callback for original request */
38380Sstevel@tonic-gate ehci_do_client_periodic_in_req_callback(
38390Sstevel@tonic-gate ehcip, pp, USB_CR_NO_RESOURCES);
38400Sstevel@tonic-gate }
38410Sstevel@tonic-gate
38420Sstevel@tonic-gate if (pp->pp_count_done_qtds) {
38430Sstevel@tonic-gate
38440Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
38450Sstevel@tonic-gate "ehci_check_for_transfers_completion:"
38460Sstevel@tonic-gate "count_done_qtds = 0x%x", pp->pp_count_done_qtds);
38470Sstevel@tonic-gate
38480Sstevel@tonic-gate /* Decrement the done qtd count */
38490Sstevel@tonic-gate pp->pp_count_done_qtds--;
38500Sstevel@tonic-gate
38510Sstevel@tonic-gate if (!pp->pp_count_done_qtds) {
38520Sstevel@tonic-gate
38530Sstevel@tonic-gate USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
38540Sstevel@tonic-gate "ehci_check_for_transfers_completion:"
38556898Sfb209375 "Sent transfers completion event pp = 0x%p",
38566898Sfb209375 (void *)pp);
38570Sstevel@tonic-gate
38580Sstevel@tonic-gate /* Send the transfer completion signal */
38590Sstevel@tonic-gate cv_signal(&pp->pp_xfer_cmpl_cv);
38600Sstevel@tonic-gate }
38610Sstevel@tonic-gate }
38620Sstevel@tonic-gate }
38630Sstevel@tonic-gate
38640Sstevel@tonic-gate
38650Sstevel@tonic-gate /*
38660Sstevel@tonic-gate * ehci_save_data_toggle:
38670Sstevel@tonic-gate *
38680Sstevel@tonic-gate * Save the data toggle information.
38690Sstevel@tonic-gate */
38700Sstevel@tonic-gate static void
ehci_save_data_toggle(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)38710Sstevel@tonic-gate ehci_save_data_toggle(
38720Sstevel@tonic-gate ehci_state_t *ehcip,
38730Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
38740Sstevel@tonic-gate {
38750Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
38760Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
38770Sstevel@tonic-gate uint_t data_toggle;
38780Sstevel@tonic-gate usb_cr_t error = pp->pp_error;
38790Sstevel@tonic-gate ehci_qh_t *qh = pp->pp_qh;
38800Sstevel@tonic-gate
38810Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
38820Sstevel@tonic-gate ehcip->ehci_log_hdl,
38836898Sfb209375 "ehci_save_data_toggle: ph = 0x%p", (void *)ph);
38840Sstevel@tonic-gate
38850Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
38860Sstevel@tonic-gate
38870Sstevel@tonic-gate /* Reset the pipe error value */
38880Sstevel@tonic-gate pp->pp_error = USB_CR_OK;
38890Sstevel@tonic-gate
38900Sstevel@tonic-gate /* Return immediately if it is a control pipe */
38910Sstevel@tonic-gate if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
38920Sstevel@tonic-gate USB_EP_ATTR_CONTROL) {
38930Sstevel@tonic-gate
38940Sstevel@tonic-gate return;
38950Sstevel@tonic-gate }
38960Sstevel@tonic-gate
38970Sstevel@tonic-gate /* Get the data toggle information from the endpoint (QH) */
38980Sstevel@tonic-gate data_toggle = (Get_QH(qh->qh_status) &
38990Sstevel@tonic-gate EHCI_QH_STS_DATA_TOGGLE)? DATA1:DATA0;
39000Sstevel@tonic-gate
39010Sstevel@tonic-gate /*
39020Sstevel@tonic-gate * If error is STALL, then, set
39030Sstevel@tonic-gate * data toggle to zero.
39040Sstevel@tonic-gate */
39050Sstevel@tonic-gate if (error == USB_CR_STALL) {
39060Sstevel@tonic-gate data_toggle = DATA0;
39070Sstevel@tonic-gate }
39080Sstevel@tonic-gate
39090Sstevel@tonic-gate /*
39100Sstevel@tonic-gate * Save the data toggle information
39110Sstevel@tonic-gate * in the usb device structure.
39120Sstevel@tonic-gate */
39130Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
39140Sstevel@tonic-gate usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
39150Sstevel@tonic-gate data_toggle);
39160Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
39170Sstevel@tonic-gate }
39180Sstevel@tonic-gate
39190Sstevel@tonic-gate
39200Sstevel@tonic-gate /*
39210Sstevel@tonic-gate * ehci_restore_data_toggle:
39220Sstevel@tonic-gate *
39230Sstevel@tonic-gate * Restore the data toggle information.
39240Sstevel@tonic-gate */
39250Sstevel@tonic-gate void
ehci_restore_data_toggle(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)39260Sstevel@tonic-gate ehci_restore_data_toggle(
39270Sstevel@tonic-gate ehci_state_t *ehcip,
39280Sstevel@tonic-gate usba_pipe_handle_data_t *ph)
39290Sstevel@tonic-gate {
39300Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
39310Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
39320Sstevel@tonic-gate uint_t data_toggle = 0;
39330Sstevel@tonic-gate
39340Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
39350Sstevel@tonic-gate ehcip->ehci_log_hdl,
39366898Sfb209375 "ehci_restore_data_toggle: ph = 0x%p", (void *)ph);
39370Sstevel@tonic-gate
39380Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
39390Sstevel@tonic-gate
39400Sstevel@tonic-gate /* Return immediately if it is a control pipe */
39410Sstevel@tonic-gate if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
39420Sstevel@tonic-gate USB_EP_ATTR_CONTROL) {
39430Sstevel@tonic-gate
39440Sstevel@tonic-gate return;
39450Sstevel@tonic-gate }
39460Sstevel@tonic-gate
39470Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
39480Sstevel@tonic-gate
39490Sstevel@tonic-gate data_toggle = usba_hcdi_get_data_toggle(ph->p_usba_device,
39500Sstevel@tonic-gate ph->p_ep.bEndpointAddress);
39510Sstevel@tonic-gate usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
39520Sstevel@tonic-gate 0);
39530Sstevel@tonic-gate
39540Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
39550Sstevel@tonic-gate
39560Sstevel@tonic-gate /*
39570Sstevel@tonic-gate * Restore the data toggle bit depending on the
39580Sstevel@tonic-gate * previous data toggle information.
39590Sstevel@tonic-gate */
39600Sstevel@tonic-gate if (data_toggle) {
39610Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_status,
39620Sstevel@tonic-gate Get_QH(pp->pp_qh->qh_status) | EHCI_QH_STS_DATA_TOGGLE);
39630Sstevel@tonic-gate } else {
39640Sstevel@tonic-gate Set_QH(pp->pp_qh->qh_status,
39650Sstevel@tonic-gate Get_QH(pp->pp_qh->qh_status) & (~EHCI_QH_STS_DATA_TOGGLE));
39660Sstevel@tonic-gate }
39670Sstevel@tonic-gate }
39680Sstevel@tonic-gate
39690Sstevel@tonic-gate
39700Sstevel@tonic-gate /*
39710Sstevel@tonic-gate * ehci_handle_outstanding_requests
39720Sstevel@tonic-gate *
39730Sstevel@tonic-gate * Deallocate interrupt request structure for the interrupt IN transfer.
39740Sstevel@tonic-gate * Do the callbacks for all unfinished requests.
39750Sstevel@tonic-gate *
39760Sstevel@tonic-gate * NOTE: This function is also called from POLLED MODE.
39770Sstevel@tonic-gate */
39780Sstevel@tonic-gate void
ehci_handle_outstanding_requests(ehci_state_t * ehcip,ehci_pipe_private_t * pp)39790Sstevel@tonic-gate ehci_handle_outstanding_requests(
39800Sstevel@tonic-gate ehci_state_t *ehcip,
39810Sstevel@tonic-gate ehci_pipe_private_t *pp)
39820Sstevel@tonic-gate {
39830Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
39840Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
39850Sstevel@tonic-gate ehci_trans_wrapper_t *curr_tw;
39860Sstevel@tonic-gate ehci_trans_wrapper_t *next_tw;
39870Sstevel@tonic-gate usb_opaque_t curr_xfer_reqp;
39880Sstevel@tonic-gate
39890Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
39900Sstevel@tonic-gate ehcip->ehci_log_hdl,
39916898Sfb209375 "ehci_handle_outstanding_requests: pp = 0x%p", (void *)pp);
39920Sstevel@tonic-gate
39930Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
39940Sstevel@tonic-gate
39950Sstevel@tonic-gate /* Deallocate all pre-allocated interrupt requests */
39960Sstevel@tonic-gate next_tw = pp->pp_tw_head;
39970Sstevel@tonic-gate
39980Sstevel@tonic-gate while (next_tw) {
39990Sstevel@tonic-gate curr_tw = next_tw;
40000Sstevel@tonic-gate next_tw = curr_tw->tw_next;
40010Sstevel@tonic-gate
40020Sstevel@tonic-gate curr_xfer_reqp = curr_tw->tw_curr_xfer_reqp;
40030Sstevel@tonic-gate
40040Sstevel@tonic-gate /* Deallocate current interrupt request */
40050Sstevel@tonic-gate if (curr_xfer_reqp) {
40060Sstevel@tonic-gate
40070Sstevel@tonic-gate if ((EHCI_PERIODIC_ENDPOINT(eptd)) &&
40080Sstevel@tonic-gate (curr_tw->tw_direction == EHCI_QTD_CTRL_IN_PID)) {
40090Sstevel@tonic-gate
40100Sstevel@tonic-gate /* Decrement periodic in request count */
40110Sstevel@tonic-gate pp->pp_cur_periodic_req_cnt--;
40120Sstevel@tonic-gate
40130Sstevel@tonic-gate ehci_deallocate_intr_in_resource(
40140Sstevel@tonic-gate ehcip, pp, curr_tw);
40150Sstevel@tonic-gate } else {
40160Sstevel@tonic-gate ehci_hcdi_callback(ph, curr_tw, USB_CR_FLUSHED);
40170Sstevel@tonic-gate }
40180Sstevel@tonic-gate }
40190Sstevel@tonic-gate }
40200Sstevel@tonic-gate }
40210Sstevel@tonic-gate
40220Sstevel@tonic-gate
40230Sstevel@tonic-gate /*
40240Sstevel@tonic-gate * ehci_deallocate_intr_in_resource
40250Sstevel@tonic-gate *
40260Sstevel@tonic-gate * Deallocate interrupt request structure for the interrupt IN transfer.
40270Sstevel@tonic-gate */
40280Sstevel@tonic-gate void
ehci_deallocate_intr_in_resource(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)40290Sstevel@tonic-gate ehci_deallocate_intr_in_resource(
40300Sstevel@tonic-gate ehci_state_t *ehcip,
40310Sstevel@tonic-gate ehci_pipe_private_t *pp,
40320Sstevel@tonic-gate ehci_trans_wrapper_t *tw)
40330Sstevel@tonic-gate {
40340Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
40350Sstevel@tonic-gate uchar_t ep_attr = ph->p_ep.bmAttributes;
40360Sstevel@tonic-gate usb_opaque_t curr_xfer_reqp;
40370Sstevel@tonic-gate
40380Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
40390Sstevel@tonic-gate ehcip->ehci_log_hdl,
40400Sstevel@tonic-gate "ehci_deallocate_intr_in_resource: "
40416898Sfb209375 "pp = 0x%p tw = 0x%p", (void *)pp, (void *)tw);
40420Sstevel@tonic-gate
40430Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
40440Sstevel@tonic-gate ASSERT((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR);
40450Sstevel@tonic-gate
40460Sstevel@tonic-gate curr_xfer_reqp = tw->tw_curr_xfer_reqp;
40470Sstevel@tonic-gate
40480Sstevel@tonic-gate /* Check the current periodic in request pointer */
40490Sstevel@tonic-gate if (curr_xfer_reqp) {
40500Sstevel@tonic-gate
40510Sstevel@tonic-gate tw->tw_curr_xfer_reqp = NULL;
40520Sstevel@tonic-gate
40530Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
40540Sstevel@tonic-gate ph->p_req_count--;
40550Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
40560Sstevel@tonic-gate
40570Sstevel@tonic-gate /* Free pre-allocated interrupt requests */
40580Sstevel@tonic-gate usb_free_intr_req((usb_intr_req_t *)curr_xfer_reqp);
40590Sstevel@tonic-gate
40600Sstevel@tonic-gate /* Set periodic in pipe state to idle */
40610Sstevel@tonic-gate pp->pp_state = EHCI_PIPE_STATE_IDLE;
40620Sstevel@tonic-gate }
40630Sstevel@tonic-gate }
40640Sstevel@tonic-gate
40650Sstevel@tonic-gate
40660Sstevel@tonic-gate /*
40670Sstevel@tonic-gate * ehci_do_client_periodic_in_req_callback
40680Sstevel@tonic-gate *
40690Sstevel@tonic-gate * Do callback for the original client periodic IN request.
40700Sstevel@tonic-gate */
40710Sstevel@tonic-gate void
ehci_do_client_periodic_in_req_callback(ehci_state_t * ehcip,ehci_pipe_private_t * pp,usb_cr_t completion_reason)40720Sstevel@tonic-gate ehci_do_client_periodic_in_req_callback(
40730Sstevel@tonic-gate ehci_state_t *ehcip,
40740Sstevel@tonic-gate ehci_pipe_private_t *pp,
40750Sstevel@tonic-gate usb_cr_t completion_reason)
40760Sstevel@tonic-gate {
40770Sstevel@tonic-gate usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
40780Sstevel@tonic-gate usb_ep_descr_t *eptd = &ph->p_ep;
40790Sstevel@tonic-gate
40800Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_LISTS,
40810Sstevel@tonic-gate ehcip->ehci_log_hdl,
40820Sstevel@tonic-gate "ehci_do_client_periodic_in_req_callback: "
40836898Sfb209375 "pp = 0x%p cc = 0x%x", (void *)pp, completion_reason);
40840Sstevel@tonic-gate
40850Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
40860Sstevel@tonic-gate
40870Sstevel@tonic-gate /*
40880Sstevel@tonic-gate * Check for Interrupt/Isochronous IN, whether we need to do
40890Sstevel@tonic-gate * callback for the original client's periodic IN request.
40900Sstevel@tonic-gate */
40910Sstevel@tonic-gate if (pp->pp_client_periodic_in_reqp) {
40920Sstevel@tonic-gate ASSERT(pp->pp_cur_periodic_req_cnt == 0);
40930Sstevel@tonic-gate if (EHCI_ISOC_ENDPOINT(eptd)) {
40940Sstevel@tonic-gate ehci_hcdi_isoc_callback(ph, NULL, completion_reason);
40950Sstevel@tonic-gate } else {
40960Sstevel@tonic-gate ehci_hcdi_callback(ph, NULL, completion_reason);
40970Sstevel@tonic-gate }
40980Sstevel@tonic-gate }
40990Sstevel@tonic-gate }
41000Sstevel@tonic-gate
41010Sstevel@tonic-gate
41020Sstevel@tonic-gate /*
41030Sstevel@tonic-gate * ehci_hcdi_callback()
41040Sstevel@tonic-gate *
41050Sstevel@tonic-gate * Convenience wrapper around usba_hcdi_cb() other than root hub.
41060Sstevel@tonic-gate */
41070Sstevel@tonic-gate void
ehci_hcdi_callback(usba_pipe_handle_data_t * ph,ehci_trans_wrapper_t * tw,usb_cr_t completion_reason)41080Sstevel@tonic-gate ehci_hcdi_callback(
41090Sstevel@tonic-gate usba_pipe_handle_data_t *ph,
41100Sstevel@tonic-gate ehci_trans_wrapper_t *tw,
41110Sstevel@tonic-gate usb_cr_t completion_reason)
41120Sstevel@tonic-gate {
41130Sstevel@tonic-gate ehci_state_t *ehcip = ehci_obtain_state(
41145441Ssl147100 ph->p_usba_device->usb_root_hub_dip);
41150Sstevel@tonic-gate ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private;
41160Sstevel@tonic-gate usb_opaque_t curr_xfer_reqp;
41170Sstevel@tonic-gate uint_t pipe_state = 0;
41180Sstevel@tonic-gate
41190Sstevel@tonic-gate USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
41200Sstevel@tonic-gate "ehci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
41216898Sfb209375 (void *)ph, (void *)tw, completion_reason);
41220Sstevel@tonic-gate
41230Sstevel@tonic-gate ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
41240Sstevel@tonic-gate
41250Sstevel@tonic-gate /* Set the pipe state as per completion reason */
41260Sstevel@tonic-gate switch (completion_reason) {
41270Sstevel@tonic-gate case USB_CR_OK:
41280Sstevel@tonic-gate pipe_state = pp->pp_state;
41290Sstevel@tonic-gate break;
41300Sstevel@tonic-gate case USB_CR_NO_RESOURCES:
41310Sstevel@tonic-gate case USB_CR_NOT_SUPPORTED:
41320Sstevel@tonic-gate case USB_CR_PIPE_RESET:
41330Sstevel@tonic-gate case USB_CR_STOPPED_POLLING:
41340Sstevel@tonic-gate pipe_state = EHCI_PIPE_STATE_IDLE;
41350Sstevel@tonic-gate break;
41360Sstevel@tonic-gate case USB_CR_PIPE_CLOSING:
41370Sstevel@tonic-gate break;
41380Sstevel@tonic-gate default:
41390Sstevel@tonic-gate /* Set the pipe state to error */
41400Sstevel@tonic-gate pipe_state = EHCI_PIPE_STATE_ERROR;
41410Sstevel@tonic-gate pp->pp_error = completion_reason;
41420Sstevel@tonic-gate break;
41430Sstevel@tonic-gate
41440Sstevel@tonic-gate }
41450Sstevel@tonic-gate
41460Sstevel@tonic-gate pp->pp_state = pipe_state;
41470Sstevel@tonic-gate
41480Sstevel@tonic-gate if (tw && tw->tw_curr_xfer_reqp) {
41490Sstevel@tonic-gate curr_xfer_reqp = tw->tw_curr_xfer_reqp;
41500Sstevel@tonic-gate tw->tw_curr_xfer_reqp = NULL;
41510Sstevel@tonic-gate } else {
41520Sstevel@tonic-gate ASSERT(pp->pp_client_periodic_in_reqp != NULL);
41530Sstevel@tonic-gate
41540Sstevel@tonic-gate curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
41550Sstevel@tonic-gate pp->pp_client_periodic_in_reqp = NULL;
41560Sstevel@tonic-gate }
41570Sstevel@tonic-gate
41580Sstevel@tonic-gate ASSERT(curr_xfer_reqp != NULL);
41590Sstevel@tonic-gate
41600Sstevel@tonic-gate mutex_exit(&ehcip->ehci_int_mutex);
41610Sstevel@tonic-gate
41620Sstevel@tonic-gate usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
41630Sstevel@tonic-gate
41640Sstevel@tonic-gate mutex_enter(&ehcip->ehci_int_mutex);
41650Sstevel@tonic-gate }
4166