#include <sys/usb/hcd/xhci/xhci.h>
#include <sys/cmn_err.h>
static void xhci_polled_panic(xhci_polled_t *xhci_polledp, const char *format,
...) __KVPRINTFLIKE(2) __NORETURN;
static void
xhci_polled_panic(xhci_polled_t *xhci_polledp, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vdev_err(xhci_polledp->xhci_polled_xhci->xhci_dip, CE_PANIC, format,
ap);
panic(__func__);
}
static void
xhci_polled_set_persistent_error(xhci_polled_t *xhci_polledp, int error)
{
if (error != USB_SUCCESS &&
xhci_polledp->xhci_polled_persistent_error == USB_SUCCESS) {
xhci_polledp->xhci_polled_persistent_error = error;
}
}
static int
xhci_polled_init(usba_pipe_handle_data_t *input_pipe_handle, xhci_t *xhcip,
usb_console_info_impl_t *console_info)
{
xhci_pipe_t *xp;
xhci_endpoint_t *xep;
xhci_polled_t *xhci_polledp;
ASSERT(mutex_owned(&xhcip->xhci_lock));
if (console_info->uci_private != NULL)
return (USB_SUCCESS);
xp = (xhci_pipe_t *)input_pipe_handle->p_hcd_private;
if (xp == NULL)
return (USB_FAILURE);
xep = xp->xp_ep;
if (xep->xep_type != USB_EP_ATTR_INTR)
return (USB_NOT_SUPPORTED);
xhci_polledp = kmem_zalloc(sizeof (xhci_polled_t), KM_SLEEP);
xhci_polledp->xhci_polled_xhci = xhcip;
xhci_polledp->xhci_polled_input_pipe_handle = input_pipe_handle;
xhci_polledp->xhci_polled_endpoint = xep;
console_info->uci_private = (usb_console_info_private_t)xhci_polledp;
return (USB_SUCCESS);
}
static xhci_endpoint_t *
xhci_polled_get_endpoint(xhci_t *xhcip, xhci_trb_t *trb)
{
int slot, endpoint;
xhci_device_t *xd;
endpoint = XHCI_TRB_GET_EP(LE_32(trb->trb_flags));
slot = XHCI_TRB_GET_SLOT(LE_32(trb->trb_flags));
xd = xhci_device_lookup_by_slot(xhcip, slot);
if (xd == NULL)
return (NULL);
return (xd->xd_endpoints[endpoint - 1]);
}
static int
xhci_polled_endpoint_transfer(xhci_polled_t *xhci_polledp, xhci_endpoint_t *xep,
xhci_trb_t *trb, uint_t *num_characters)
{
xhci_t *xhcip = xhci_polledp->xhci_polled_xhci;
xhci_device_t *xd = xep->xep_xd;
uint_t off;
int code;
xhci_transfer_t *xt;
size_t len;
xhci_transfer_t *rem;
int sched_err;
if ((xt = xhci_endpoint_determine_transfer(xhcip, xep, trb, &off)) ==
NULL) {
return (USB_FAILURE);
}
code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status));
if (code != XHCI_CODE_SUCCESS)
return (USB_FAILURE);
ASSERT(xep->xep_type == USB_EP_ATTR_INTR);
if (!xt->xt_data_tohost)
return (USB_SUCCESS);
if (xt->xt_short != 0)
return (USB_CR_DATA_UNDERRUN);
if (xhci_transfer_sync(xhcip, xt, DDI_DMA_SYNC_FORCPU) != DDI_FM_OK) {
xhci_polled_panic(xhci_polledp, "failed to process normal "
"transfer callback for endpoint %u of device on slot %d "
"and port %d: encountered fatal FM error synchronizing "
"DMA memory", xhcip, xep->xep_num, xd->xd_slot,
xd->xd_port);
}
len = xt->xt_buffer.xdb_len;
if (len > sizeof (xhci_polledp->xhci_polled_buf))
len = sizeof (xhci_polledp->xhci_polled_buf);
xhci_transfer_copy(xt, xhci_polledp->xhci_polled_buf, len, B_TRUE);
*num_characters = (uint_t)len;
VERIFY(xhci_ring_trb_consumed(&xep->xep_ring, LE_64(trb->trb_addr)));
rem = list_remove_head(&xep->xep_transfers);
VERIFY3P(rem, ==, xt);
xt->xt_short = 0;
xt->xt_cr = USB_CR_OK;
sched_err = xhci_endpoint_schedule(xhcip, xd, xep, xt, B_TRUE);
xhci_polled_set_persistent_error(xhci_polledp, sched_err);
return (USB_SUCCESS);
}
static int
xhci_polled_event_process(xhci_polled_t *xhci_polledp, uint_t *num_characters)
{
xhci_t *xhcip = xhci_polledp->xhci_polled_xhci;
xhci_ring_t *xrp = &xhcip->xhci_event.xev_ring;
uint_t nevents;
int ret;
uint64_t addr;
if (xhcip->xhci_state & XHCI_S_ERROR)
return (USB_HC_HARDWARE_ERROR);
VERIFY(xhcip->xhci_event.xev_segs != NULL);
XHCI_DMA_SYNC(xrp->xr_dma, DDI_DMA_SYNC_FORKERNEL);
ret = USB_SUCCESS;
for (nevents = 0; nevents < xrp->xr_ntrb; nevents++) {
xhci_trb_t *trb;
xhci_endpoint_t *xep;
uint32_t type;
if ((trb = xhci_ring_event_advance(xrp)) == NULL)
break;
xep = xhci_polled_get_endpoint(xhcip, trb);
if (xep == NULL) {
xhci_polled_set_persistent_error(xhci_polledp,
USB_HC_HARDWARE_ERROR);
return (USB_HC_HARDWARE_ERROR);
}
type = LE_32(trb->trb_flags) & XHCI_TRB_TYPE_MASK;
if (xep != xhci_polledp->xhci_polled_endpoint ||
type != XHCI_EVT_XFER) {
boolean_t processed;
mutex_exit(&xhcip->xhci_lock);
processed = xhci_event_process_trb(xhcip, trb);
mutex_enter(&xhcip->xhci_lock);
if (!processed && xhcip->xhci_state & XHCI_S_ERROR)
return (USB_HC_HARDWARE_ERROR);
continue;
}
ret = xhci_polled_endpoint_transfer(xhci_polledp, xep, trb,
num_characters);
if (ret != USB_SUCCESS) {
xhci_polled_set_persistent_error(xhci_polledp, ret);
break;
}
}
addr = xhci_dma_pa(&xrp->xr_dma) + sizeof (xhci_trb_t) * xrp->xr_tail;
addr |= XHCI_ERDP_BUSY;
xhci_put64(xhcip, XHCI_R_RUN, XHCI_ERDP(0), addr);
return (ret);
}
int
xhci_hcdi_console_input_init(usba_pipe_handle_data_t *pipe_handle,
uchar_t **polled_buf, usb_console_info_impl_t *console_input_info)
{
xhci_t *xhcip;
int ret;
xhci_polled_t *xhci_polledp;
xhcip = xhci_hcdi_get_xhcip_from_dev(pipe_handle->p_usba_device);
mutex_enter(&xhcip->xhci_lock);
ret = xhci_polled_init(pipe_handle, xhcip, console_input_info);
if (ret != USB_SUCCESS) {
mutex_exit(&xhcip->xhci_lock);
return (ret);
}
xhci_polledp = (xhci_polled_t *)console_input_info->uci_private;
*polled_buf = xhci_polledp->xhci_polled_buf;
mutex_exit(&xhcip->xhci_lock);
return (ret);
}
int
xhci_hcdi_console_input_fini(usb_console_info_impl_t *console_input_info)
{
xhci_polled_t *xhci_polledp;
xhci_polledp = (xhci_polled_t *)console_input_info->uci_private;
if (xhci_polledp != NULL) {
kmem_free(xhci_polledp, sizeof (xhci_polled_t));
console_input_info->uci_private = NULL;
}
return (USB_SUCCESS);
}
int
xhci_hcdi_console_input_enter(usb_console_info_impl_t *console_input_info)
{
xhci_polled_t *xhci_polledp;
xhci_t *xhcip;
xhci_endpoint_t *xep;
uint32_t status;
xhci_polledp = (xhci_polled_t *)console_input_info->uci_private;
xhcip = xhci_polledp->xhci_polled_xhci;
xep = xhci_polledp->xhci_polled_endpoint;
if (mutex_tryenter(&xhcip->xhci_lock) == 0)
return (USB_BUSY);
if (xhci_polledp->xhci_polled_entry > 0) {
xhci_polledp->xhci_polled_entry++;
ASSERT(xep->xep_state & XHCI_ENDPOINT_POLLED);
mutex_exit(&xhcip->xhci_lock);
return (USB_SUCCESS);
}
status = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBSTS);
if ((status & (XHCI_STS_HSE | XHCI_STS_SRE | XHCI_STS_HCE)) != 0) {
mutex_exit(&xhcip->xhci_lock);
xhci_polled_set_persistent_error(xhci_polledp,
USB_HC_HARDWARE_ERROR);
return (USB_HC_HARDWARE_ERROR);
}
if (xhci_polledp->xhci_polled_endpoint->xep_type != USB_EP_ATTR_INTR) {
mutex_exit(&xhcip->xhci_lock);
return (USB_NOT_SUPPORTED);
}
xhci_polledp->xhci_polled_persistent_error = USB_SUCCESS;
xhci_polledp->xhci_polled_entry++;
ASSERT(!(xep->xep_state & XHCI_ENDPOINT_POLLED));
xep->xep_state |= XHCI_ENDPOINT_POLLED;
mutex_exit(&xhcip->xhci_lock);
return (USB_SUCCESS);
}
int
xhci_hcdi_console_read(usb_console_info_impl_t *console_input_info,
uint_t *num_characters)
{
xhci_polled_t *xhci_polledp;
xhci_t *xhcip;
uint32_t status, iman;
int ret;
*num_characters = 0;
xhci_polledp = (xhci_polled_t *)console_input_info->uci_private;
VERIFY(xhci_polledp != NULL);
if (xhci_polledp->xhci_polled_persistent_error != USB_SUCCESS)
return (xhci_polledp->xhci_polled_persistent_error);
xhcip = xhci_polledp->xhci_polled_xhci;
if (mutex_tryenter(&xhcip->xhci_lock) == 0)
return (USB_BUSY);
status = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBSTS);
if ((status & (XHCI_STS_HSE | XHCI_STS_SRE | XHCI_STS_HCE)) != 0) {
xhci_polled_panic(xhci_polledp, "found fatal error bit in "
"status register, value: 0x%x", xhcip, status);
}
iman = xhci_get32(xhcip, XHCI_R_RUN, XHCI_IMAN(0));
ret = xhci_polled_event_process(xhci_polledp, num_characters);
if (ret == USB_SUCCESS)
xhci_put32(xhcip, XHCI_R_RUN, XHCI_IMAN(0), iman);
mutex_exit(&xhcip->xhci_lock);
return (ret);
}
int
xhci_hcdi_console_input_exit(usb_console_info_impl_t *console_input_info)
{
xhci_polled_t *xhci_polledp;
xhci_t *xhcip;
xhci_endpoint_t *xep;
xhci_polledp = (xhci_polled_t *)console_input_info->uci_private;
VERIFY(xhci_polledp != NULL);
xhcip = xhci_polledp->xhci_polled_xhci;
mutex_enter(&xhcip->xhci_lock);
xep = xhci_polledp->xhci_polled_endpoint;
ASSERT(xep->xep_state & XHCI_ENDPOINT_POLLED);
VERIFY(xhci_polledp->xhci_polled_entry > 0);
xhci_polledp->xhci_polled_entry--;
if (xhci_polledp->xhci_polled_entry > 0) {
mutex_exit(&xhcip->xhci_lock);
return (USB_SUCCESS);
}
xep->xep_state &= ~XHCI_ENDPOINT_POLLED;
if (xhci_polledp->xhci_polled_persistent_error != USB_SUCCESS) {
xhci_fm_runtime_reset(xhcip);
}
mutex_exit(&xhcip->xhci_lock);
return (USB_SUCCESS);
}
int
xhci_hcdi_console_output_init(usba_pipe_handle_data_t *pipe_handle,
usb_console_info_impl_t *console_output_info)
{
return (USB_NOT_SUPPORTED);
}
int
xhci_hcdi_console_output_fini(usb_console_info_impl_t *console_output_info)
{
return (USB_NOT_SUPPORTED);
}
int
xhci_hcdi_console_output_enter(usb_console_info_impl_t *console_output_info)
{
return (USB_NOT_SUPPORTED);
}
int
xhci_hcdi_console_write(usb_console_info_impl_t *console_output_info,
uchar_t *buf, uint_t num_characters, uint_t *num_characters_written)
{
return (USB_NOT_SUPPORTED);
}
int
xhci_hcdi_console_output_exit(usb_console_info_impl_t *console_output_info)
{
return (USB_NOT_SUPPORTED);
}