diff options
| author | Robert Mustacchi <rm@joyent.com> | 2016-03-28 04:43:35 +0000 |
|---|---|---|
| committer | Robert Mustacchi <rm@joyent.com> | 2016-12-09 00:17:55 +0000 |
| commit | 1cb3ba547f23532c62f715007a21832205f7fdf7 (patch) | |
| tree | a3891916372f655da53eb47142019aa2a3f6775a /usr/src/cmd | |
| parent | 83390469eaf76687ae434504ed3e41fdbe4ae3b6 (diff) | |
| download | illumos-joyent-1cb3ba547f23532c62f715007a21832205f7fdf7.tar.gz | |
OS-1151 need support for USB v3.0
OS-5778 want usb_pipe_xopen(9F)
OS-5776 usbai burst macros for endpoint descriptor are wrong
OS-5367 usba_hcdi_register() should fail if driver is using private data
OS-5368 failing to load the usba root hub module destroys driver parent private data
OS-5775 want ::hubd walker
OS-5774 ::prtusb should include version
OS-5777 usb_*_request(9S) manual pages should match structure names
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Bryan Cantrill <bryan@joyent.com>
Approved by: Dave Pacheco <dap@joyent.com>
Diffstat (limited to 'usr/src/cmd')
| -rw-r--r-- | usr/src/cmd/Makefile | 1 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/Makefile.common | 1 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/kmdb/mapfile_skel | 1 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/conf/mapfile-extern | 1 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/usba/prtusb.c | 39 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/usba/usb.c | 83 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/xhci/xhci.c | 893 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/intel/amd64/xhci/Makefile | 27 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/intel/ia32/xhci/Makefile | 25 | ||||
| -rw-r--r-- | usr/src/cmd/xhci/Makefile | 43 | ||||
| -rw-r--r-- | usr/src/cmd/xhci/xhci_portsc.c | 373 |
11 files changed, 1475 insertions, 12 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 9017a78b14..0138deef4d 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -455,6 +455,7 @@ COMMON_SUBDIRS= \ wracct \ write \ xargs \ + xhci \ xstr \ yes \ ypcmd \ diff --git a/usr/src/cmd/mdb/Makefile.common b/usr/src/cmd/mdb/Makefile.common index 142d314b07..a941dda810 100644 --- a/usr/src/cmd/mdb/Makefile.common +++ b/usr/src/cmd/mdb/Makefile.common @@ -103,6 +103,7 @@ COMMON_MODULES_KVM = \ sv \ ufs \ usba \ + xhci \ zfs CLOSED_COMMON_MODULES_KVM = \ diff --git a/usr/src/cmd/mdb/common/kmdb/mapfile_skel b/usr/src/cmd/mdb/common/kmdb/mapfile_skel index 2d55703c7d..086dc85856 100644 --- a/usr/src/cmd/mdb/common/kmdb/mapfile_skel +++ b/usr/src/cmd/mdb/common/kmdb/mapfile_skel @@ -58,6 +58,7 @@ SYMBOL_SCOPE { islower; ispunct; isspace; + strlcpy; mdb_tgt_aread; mdb_dis_create; diff --git a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern index 73de8f1e41..9371b4834d 100644 --- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern +++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern @@ -102,6 +102,7 @@ SYMBOL_SCOPE { mdb_get_lbolt { FLAGS = EXTERN }; mdb_get_pipe { FLAGS = EXTERN }; mdb_get_soft_state_byaddr { FLAGS = EXTERN }; + mdb_get_soft_state_byname { FLAGS = EXTERN }; mdb_get_state { FLAGS = EXTERN }; mdb_get_xdata { FLAGS = EXTERN }; mdb_gethrtime { FLAGS = EXTERN }; diff --git a/usr/src/cmd/mdb/common/modules/usba/prtusb.c b/usr/src/cmd/mdb/common/modules/usba/prtusb.c index 5efecf314b..9f6ffc4177 100644 --- a/usr/src/cmd/mdb/common/modules/usba/prtusb.c +++ b/usr/src/cmd/mdb/common/modules/usba/prtusb.c @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2016 Joyent, Inc. */ @@ -117,6 +119,15 @@ static usb_descr_item_t usb_ep_descr[] = { }; static uint_t usb_ep_item = 6; +static usb_descr_item_t usb_ep_ss_comp_descr[] = { + {1, "bLength"}, + {1, "bDescriptorType"}, + {1, "bMaxBurst"}, + {1, "bmAttributes"}, + {2, "wBytesPerInterval"} +}; +static uint_t usb_ep_ss_comp_item = 5; + static usb_descr_item_t usb_qlf_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, @@ -561,8 +572,9 @@ prtusb(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) /* for the first device, print head */ if (DCMD_HDRSPEC(flags)) { count = 1; - mdb_printf("%<u>%-8s%-12s%-6s%-16s%-12s%-20s%</u>\n", - "INDEX", "DRIVER", "INST", "NODE", "VID.PID", "PRODUCT"); + mdb_printf("%<u>%-8s%-12s%-6s%-14s%-5s%-12s%-20s%</u>\n", + "INDEX", "DRIVER", "INST", "NODE", "GEN", "VID.PID", + "PRODUCT"); } if (mdb_getopts(argc, argv, @@ -604,16 +616,21 @@ prtusb(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dip.devi_node_name) != -1) { - mdb_printf("%-16s", strbuf); + mdb_printf("%-14s", strbuf); } else { - mdb_printf("%-16s", "No Node Name"); + mdb_printf("%-14s", "No Node Name"); } - /* vid.pid */ + if (mdb_vread(&dev_desc, sizeof (usb_dev_descr_t), (uintptr_t)usb_dev.usb_dev_descr) != -1) { + /* gen (note we read this from the bcd) */ + mdb_printf("%01x.%01x ", dev_desc.bcdUSB >> 8, + (dev_desc.bcdUSB & 0xf0) >> 4); + + /* vid.pid */ mdb_printf("%04x.%04x ", dev_desc.idVendor, dev_desc.idProduct); } @@ -824,7 +841,7 @@ prt_usb_tree_node(uintptr_t paddr) if (strcmp(driver_name, "hubd") == 0) { mdb_arg_t argv[] = { {MDB_TYPE_STRING, {"hubd_t"}}, - {MDB_TYPE_STRING, {"h_hub_descr"}} + {MDB_TYPE_STRING, {"h_ep1_xdescr.uex_ep"}} }; mdb_call_dcmd("print", statep, DCMD_ADDRSPEC, 2, argv); } @@ -1075,6 +1092,16 @@ prt_usb_desc(uintptr_t usb_cfg, uint_t cfg_len) mdb_dec_indent(indent); break; + case USB_DESCR_TYPE_SS_EP_COMP: + indent = 12; + mdb_inc_indent(indent); + mdb_printf("SuperSpeed Endpoint Companion " + "Descriptor\n"); + print_descr(paddr, nlen, usb_ep_ss_comp_descr, + usb_ep_ss_comp_item); + mdb_dec_indent(indent); + + break; case USB_DESCR_TYPE_DEV_QLF: mdb_printf("Device_Qualifier Descriptor\n"); print_descr(paddr, nlen, usb_qlf_descr, usb_qlf_item); diff --git a/usr/src/cmd/mdb/common/modules/usba/usb.c b/usr/src/cmd/mdb/common/modules/usba/usb.c index 442f1732c4..6791ada63b 100644 --- a/usr/src/cmd/mdb/common/modules/usba/usb.c +++ b/usr/src/cmd/mdb/common/modules/usba/usb.c @@ -21,10 +21,10 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2016 Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stddef.h> #include <sys/mdb_modapi.h> #include <mdb/mdb_ks.h> @@ -34,6 +34,8 @@ #include <sys/usb/usba/usba_types.h> #include <sys/usb/usba/usba_impl.h> #include <sys/usb/usba/hcdi_impl.h> +#include <sys/usb/hubd/hub.h> +#include <sys/usb/hubd/hubdvar.h> #include <sys/file.h> #include <sys/sunndi.h> #include <unistd.h> @@ -96,7 +98,7 @@ find_dip(uintptr_t dip_addr, const void *local_dip, void *cb_arg) { uintptr_t cur_usb_dev; usba_device2devinfo_cbdata_t *cb_data = - (usba_device2devinfo_cbdata_t *)cb_arg; + (usba_device2devinfo_cbdata_t *)cb_arg; if ((cur_usb_dev = mdb_usba_get_usba_device(dip_addr)) == NULL) { /* @@ -244,7 +246,7 @@ usb_pipe_handle_walk_init(mdb_walk_state_t *wsp) } wsp->walk_data = mdb_alloc((sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS, - UM_SLEEP | UM_GC); + UM_SLEEP | UM_GC); /* * Read the usb_ph_list array into local memory. @@ -286,7 +288,7 @@ usb_pipe_handle_walk_step(mdb_walk_state_t *wsp) } status = wsp->walk_callback((uintptr_t)impl_list[index].usba_ph_data, - wsp->walk_data, wsp->walk_cbdata); + wsp->walk_data, wsp->walk_cbdata); /* Set up to start at next pipe handle next time. */ wsp->walk_arg = (void *)(index + 1); @@ -422,6 +424,72 @@ usba_device_walk_init(mdb_walk_state_t *wsp) return (WALK_NEXT); } +int +usba_hubd_walk_init(mdb_walk_state_t *wsp) +{ + if (wsp->walk_addr != 0) { + mdb_warn("hubd only supports global walks.\n"); + return (WALK_ERR); + } + + if (mdb_layered_walk("usba_device", wsp) == -1) { + mdb_warn("couldn't walk 'usba_device'"); + return (WALK_ERR); + } + + return (WALK_NEXT); +} + +/* + * Getting the hub state is annoying. The root hubs are stored on dev_info_t + * while the normal hubs are stored as soft state. + */ +int +usba_hubd_walk_step(mdb_walk_state_t *wsp) +{ + usba_device_t ud; + hubd_t hubd; + struct dev_info dev_info; + uintptr_t state_addr; + + if (mdb_vread(&ud, sizeof (ud), wsp->walk_addr) != sizeof (ud)) { + mdb_warn("failed to read usba_device_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + + if (ud.usb_root_hubd != NULL) { + if (mdb_vread(&hubd, sizeof (hubd), + (uintptr_t)ud.usb_root_hubd) != sizeof (hubd)) { + mdb_warn("failed to read hubd at %p", ud.usb_root_hubd); + return (WALK_ERR); + } + return (wsp->walk_callback((uintptr_t)ud.usb_root_hubd, &hubd, + wsp->walk_cbdata)); + } + + if (ud.usb_hubdi == NULL) + return (WALK_NEXT); + + /* + * For non-root hubs, the hubd_t is stored in the soft state. Figure out + * the instance from the dev_info_t and then get its soft state. + */ + if (mdb_vread(&dev_info, sizeof (struct dev_info), + (uintptr_t)ud.usb_dip) != sizeof (struct dev_info)) { + mdb_warn("failed to read dev_info_t for device %p at %p", + wsp->walk_addr, ud.usb_dip); + return (WALK_ERR); + } + + if (mdb_get_soft_state_byname("hubd_statep", dev_info.devi_instance, + &state_addr, &hubd, sizeof (hubd)) == -1) { + mdb_warn("failed to read hubd soft state for instance %d from " + "usb device %p", dev_info.devi_instance, wsp->walk_addr); + return (WALK_ERR); + } + + return (wsp->walk_callback(state_addr, &hubd, wsp->walk_cbdata)); +} /* * usba_device dcmd @@ -613,7 +681,8 @@ usba_device(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * usba_device_struct.usb_n_cfgs, (uintptr_t) usba_device_struct.usb_cfg_str_descr)) == -1) { - mdb_warn("failed to read config cloud pointers"); + mdb_warn("failed to read config cloud " + "pointers"); } else { @@ -789,6 +858,8 @@ static const mdb_walker_t walkers[] = { usb_pipe_handle_walk_init, usb_pipe_handle_walk_step, NULL, NULL }, { "usba_device", "walk global list of usba_device_t structures", usba_device_walk_init, usba_list_walk_step, NULL, NULL }, + { "hubd", "walk hubd instances", usba_hubd_walk_init, + usba_hubd_walk_step, NULL, NULL }, { NULL } }; diff --git a/usr/src/cmd/mdb/common/modules/xhci/xhci.c b/usr/src/cmd/mdb/common/modules/xhci/xhci.c new file mode 100644 index 0000000000..fbf3c42417 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/xhci/xhci.c @@ -0,0 +1,893 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +#include <sys/mdb_modapi.h> +#include <sys/usb/hcd/xhci/xhci.h> + +#define XHCI_MDB_TRB_INDENT 4 + +static const char *xhci_mdb_epctx_eptypes[] = { + "Not Valid", + "ISOCH OUT", + "BULK OUT", + "INTR OUT", + "CONTROL", + "ISOCH IN", + "BULK IN", + "INTR IN" +}; + +static const char *xhci_mdb_epctx_states[] = { + "Disabled", + "Running", + "Halted", + "Stopped", + "Error", + "<Unknown>", + "<Unknown>", + "<Unknown>" +}; + +static const mdb_bitmask_t xhci_mdb_trb_flags[] = { + { "C", XHCI_TRB_CYCLE, XHCI_TRB_CYCLE }, + { "ENT", XHCI_TRB_ENT, XHCI_TRB_ENT }, + { "ISP", XHCI_TRB_ISP, XHCI_TRB_ISP }, + { "NS", XHCI_TRB_NOSNOOP, XHCI_TRB_NOSNOOP }, + { "CH", XHCI_TRB_CHAIN, XHCI_TRB_CHAIN }, + { "IOC", XHCI_TRB_IOC, XHCI_TRB_IOC }, + { "IDT", XHCI_TRB_IDT, XHCI_TRB_IDT }, + { "BEI", XHCI_TRB_BEI, XHCI_TRB_BEI }, + { NULL, 0, 0 } +}; + +typedef struct xhci_mdb_walk_endpoint { + xhci_device_t xmwe_device; + uint_t xmwe_ep; +} xhci_mdb_walk_endpoint_t; + +static const char * +xhci_mdb_trb_code_to_str(int code) +{ + switch (code) { + case XHCI_CODE_INVALID: + return ("Invalid"); + case XHCI_CODE_SUCCESS: + return ("Success"); + case XHCI_CODE_DATA_BUF: + return ("Data Overrun or Underrun"); + case XHCI_CODE_BABBLE: + return ("Babble"); + case XHCI_CODE_TXERR: + return ("Transaction Error"); + case XHCI_CODE_TRB: + return ("Invalid TRB"); + case XHCI_CODE_STALL: + return ("Stall"); + case XHCI_CODE_RESOURCE: + return ("No Resources Available"); + case XHCI_CODE_BANDWIDTH: + return ("No Bandwidth Available"); + case XHCI_CODE_NO_SLOTS: + return ("No Slots Available"); + case XHCI_CODE_STREAM_TYPE: + return ("Stream Context Type Detected"); + case XHCI_CODE_SLOT_NOT_ON: + return ("Slot disabled"); + case XHCI_CODE_ENDP_NOT_ON: + return ("Endpoint disabled"); + case XHCI_CODE_SHORT_XFER: + return ("Short Transfer"); + case XHCI_CODE_RING_UNDERRUN: + return ("Isoch. Ring Underrun"); + case XHCI_CODE_RING_OVERRUN: + return ("Isoch. Ring Overrun"); + case XHCI_CODE_VF_RING_FULL: + return ("VF Ring Full"); + case XHCI_CODE_PARAMETER: + return ("Invalid Context Paramenter"); + case XHCI_CODE_BW_OVERRUN: + return ("Bandwidth Overrun"); + case XHCI_CODE_CONTEXT_STATE: + return ("Illegal Context Transition"); + case XHCI_CODE_NO_PING_RESP: + return ("Failed to Complete Periodic Transfer"); + case XHCI_CODE_EV_RING_FULL: + return ("Event Ring Full"); + case XHCI_CODE_INCOMPAT_DEV: + return ("Incompatible Device"); + case XHCI_CODE_MISSED_SRV: + return ("Missed Isoch. Service Window"); + case XHCI_CODE_CMD_RING_STOP: + return ("Command Ring Stop"); + case XHCI_CODE_CMD_ABORTED: + return ("Command Aborted"); + case XHCI_CODE_XFER_STOPPED: + return ("Transfer Stopped"); + case XHCI_CODE_XFER_STOPINV: + return ("Invalid Transfer Length"); + case XHCI_CODE_XFER_STOPSHORT: + return ("Stopped before End of Transfer Descriptor"); + case XHCI_CODE_MELAT: + return ("Max Exit Latency too large"); + case XHCI_CODE_RESERVED: + return ("Reserved"); + case XHCI_CODE_ISOC_OVERRUN: + return ("Isochronus Overrun"); + case XHCI_CODE_EVENT_LOST: + return ("Event Lost"); + case XHCI_CODE_UNDEFINED: + return ("Undefined Fatal Error"); + case XHCI_CODE_INVALID_SID: + return ("Invalid Stream ID"); + case XHCI_CODE_SEC_BW: + return ("Secondary Bandwith Allocation Failure"); + case XHCI_CODE_SPLITERR: + return ("USB2 SPlit Transaction Error"); + default: + break; + } + + if (code >= 192 && code <= 223) + return ("Vendor Defined Error"); + if (code >= 224 && code <= 255) + return ("Vendor Defined Info"); + + return ("Reserved"); +} + +static const char * +xhci_mdb_trb_type_to_str(int code) +{ + /* + * The macros for the types are all already shifted over based on their + * place in the TRB, so shift there again ourselves. + */ + switch (code << 10) { + case XHCI_TRB_TYPE_NORMAL: + return ("Normal"); + case XHCI_TRB_TYPE_SETUP: + return ("Setup"); + case XHCI_TRB_TYPE_DATA: + return ("Data"); + case XHCI_TRB_TYPE_STATUS: + return ("Status"); + case XHCI_TRB_TYPE_LINK: + return ("Link"); + case XHCI_TRB_TYPE_EVENT: + return ("Event"); + case XHCI_TRB_TYPE_NOOP: + return ("No-Op"); + case XHCI_CMD_ENABLE_SLOT: + return ("Enable Slot"); + case XHCI_CMD_DISABLE_SLOT: + return ("Disable Slot"); + case XHCI_CMD_ADDRESS_DEVICE: + return ("Address Device"); + case XHCI_CMD_CONFIG_EP: + return ("Configure Endpoint"); + case XHCI_CMD_EVAL_CTX: + return ("Evaluate Context"); + case XHCI_CMD_RESET_EP: + return ("Reset Endpoint"); + case XHCI_CMD_STOP_EP: + return ("Stop Endpoint"); + case XHCI_CMD_SET_TR_DEQ: + return ("Set Transfer Ring Dequeue Pointer"); + case XHCI_CMD_RESET_DEV: + return ("Reset Device"); + case XHCI_CMD_FEVENT: + return ("Force Event"); + case XHCI_CMD_NEG_BW: + return ("Negotiate Bandwidth"); + case XHCI_CMD_SET_LT: + return ("Set Latency Tolerance"); + case XHCI_CMD_GET_BW: + return ("Get Bandwidth"); + case XHCI_CMD_FHEADER: + return ("Force Header"); + case XHCI_CMD_NOOP: + return ("No-Op Command"); + case XHCI_EVT_XFER: + return ("Transfer Event"); + case XHCI_EVT_CMD_COMPLETE: + return ("Command Completion Event"); + case XHCI_EVT_PORT_CHANGE: + return ("Port Status Change Event"); + case XHCI_EVT_BW_REQUEST: + return ("Bandwidth Request Event"); + case XHCI_EVT_DOORBELL: + return ("Doorbell Event"); + case XHCI_EVT_HOST_CTRL: + return ("Host Controller Event"); + case XHCI_EVT_DEVICE_NOTIFY: + return ("Device Notification Event"); + case XHCI_EVT_MFINDEX_WRAP: + return ("MFINDEX Wrap Event"); + default: + break; + } + + if (code >= 43 && code <= 63) + return ("Vendor Defiend"); + return ("Reserved"); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_epctx(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uint32_t info, info2, txinfo; + xhci_endpoint_context_t epctx; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("::xhci_epctx requires an address\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&epctx, sizeof (epctx), addr) != sizeof (epctx)) { + mdb_warn("failed to read xhci_endpoint_context_t at %p", addr); + return (DCMD_ERR); + } + + info = LE_32(epctx.xec_info); + info2 = LE_32(epctx.xec_info2); + txinfo = LE_32(epctx.xec_txinfo); + + mdb_printf("Endpoint State: %s (%d)\n", + xhci_mdb_epctx_states[XHCI_EPCTX_STATE(info)], + XHCI_EPCTX_STATE(info)); + + mdb_printf("Mult: %d\n", XHCI_EPCTX_GET_MULT(info)); + mdb_printf("Max Streams: %d\n", XHCI_EPCTX_GET_MAXP_STREAMS(info)); + mdb_printf("LSA: %d\n", XHCI_EPCTX_GET_LSA(info)); + mdb_printf("Interval: %d\n", XHCI_EPCTX_GET_IVAL(info)); + mdb_printf("Max ESIT Hi: %d\n", XHCI_EPCTX_GET_MAX_ESIT_HI(info)); + + mdb_printf("CErr: %d\n", XHCI_EPCTX_GET_CERR(info2)); + mdb_printf("EP Type: %s (%d)\n", + xhci_mdb_epctx_eptypes[XHCI_EPCTX_GET_EPTYPE(info2)], + XHCI_EPCTX_GET_EPTYPE(info2)); + mdb_printf("Host Initiate Disable: %d\n", XHCI_EPCTX_GET_HID(info2)); + mdb_printf("Max Burst: %d\n", XHCI_EPCTX_GET_MAXB(info2)); + mdb_printf("Max Packet Size: %d\n", XHCI_EPCTX_GET_MPS(info2)); + + mdb_printf("Ring DCS: %d\n", LE_64(epctx.xec_dequeue) & 0x1); + mdb_printf("Ring PA: 0x%lx\n", LE_64(epctx.xec_dequeue) & ~0xf); + + mdb_printf("Average TRB Length: %d\n", XHCI_EPCTX_AVG_TRB_LEN(txinfo)); + mdb_printf("Max ESIT: %d\n", XHCI_EPCTX_GET_MAX_ESIT_PAYLOAD(txinfo)); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_slotctx(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uint32_t info, info2, tt, state; + xhci_slot_context_t sctx; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("::xhci_slotctx requires an address\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&sctx, sizeof (sctx), addr) != sizeof (sctx)) { + mdb_warn("failed to read xhci_slot_context_t at %p", addr); + return (DCMD_ERR); + } + + info = LE_32(sctx.xsc_info); + info2 = LE_32(sctx.xsc_info2); + tt = LE_32(sctx.xsc_tt); + state = LE_32(sctx.xsc_state); + + mdb_printf("Route: 0x%x\n", XHCI_SCTX_GET_ROUTE(info)); + + mdb_printf("Slot Speed: "); + switch (XHCI_SCTX_GET_SPEED(info)) { + case XHCI_SPEED_FULL: + mdb_printf("Full"); + break; + case XHCI_SPEED_LOW: + mdb_printf("Low"); + break; + case XHCI_SPEED_HIGH: + mdb_printf("High"); + break; + case XHCI_SPEED_SUPER: + mdb_printf("Super"); + break; + default: + mdb_printf("Unknown"); + break; + } + mdb_printf(" (%d)\n", XHCI_SCTX_GET_SPEED(info)); + + + mdb_printf("MTT: %d\n", XHCI_SCTX_GET_MTT(info)); + mdb_printf("HUB: %d\n", XHCI_SCTX_GET_HUB(info)); + mdb_printf("DCI: %d\n", XHCI_SCTX_GET_DCI(info)); + + mdb_printf("Max Exit Latency: %d\n", XHCI_SCTX_GET_MAX_EL(info2)); + mdb_printf("Root Hub Port: %d\n", XHCI_SCTX_GET_RHPORT(info2)); + mdb_printf("Hub Number of Ports: %d\n", XHCI_SCTX_GET_NPORTS(info2)); + + mdb_printf("TT Hub Slot id: %d\n", XHCI_SCTX_GET_TT_HUB_SID(tt)); + mdb_printf("TT Port Number: %d\n", XHCI_SCTX_GET_TT_PORT_NUM(tt)); + mdb_printf("TT Think Time: %d\n", XHCI_SCTX_GET_TT_THINK_TIME(tt)); + mdb_printf("IRQ Target: %d\n", XHCI_SCTX_GET_IRQ_TARGET(tt)); + + mdb_printf("Device Address: 0x%x\n", XHCI_SCTX_GET_DEV_ADDR(state)); + mdb_printf("Slot State: "); + switch (XHCI_SCTX_GET_SLOT_STATE(state)) { + case XHCI_SLOT_DIS_ENAB: + mdb_printf("Disabled/Enabled"); + break; + case XHCI_SLOT_DEFAULT: + mdb_printf("Default"); + break; + case XHCI_SLOT_ADDRESSED: + mdb_printf("Addressed"); + break; + case XHCI_SLOT_CONFIGURED: + mdb_printf("Configured"); + break; + default: + mdb_printf("Unknown"); + break; + } + mdb_printf(" (%d)\n", XHCI_SCTX_GET_SLOT_STATE(state)); + + return (DCMD_OK); +} + +static int +xhci_mdb_print_transfer_event(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("Transfer Length (Remain): %d\n", XHCI_TRB_REMAIN(status)); + mdb_printf("Completion Code: %s (%d)\n", + xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)), + XHCI_TRB_GET_CODE(status)); + + mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags)); + mdb_printf("Event Data: %d\n", XHCI_TRB_GET_ED(flags)); + mdb_printf("Endpoint ID: %d\n", XHCI_TRB_GET_EP(flags)); + mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +static int +xhci_mdb_print_command_event(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("Command Param: 0x%x\n", XHCI_TRB_REMAIN(status)); + mdb_printf("Completion Code: %s (%d)\n", + xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)), + XHCI_TRB_GET_CODE(status)); + + mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags)); + /* Skip VF ID as we don't support VFs */ + mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_psc(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("Port: %d\n", XHCI_TRB_PORTID(pa)); + mdb_printf("Completion Code: %s (%d)\n", + xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)), + XHCI_TRB_GET_CODE(status)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + return (DCMD_OK); +} + +static int +xhci_mdb_print_normal_trb(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("TRB Length: %d bytes\n", XHCI_TRB_LEN(status)); + mdb_printf("TRB TD Size: %d packets\n", XHCI_TRB_GET_TDREM(status)); + mdb_printf("TRB Interrupt: %d\n", XHCI_TRB_GET_INTR(status)); + mdb_printf("TRB Flags: %b (0x%x)\n", flags, xhci_mdb_trb_flags, + XHCI_TRB_GET_FLAGS(flags)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_trb(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + xhci_trb_t trb; + uint64_t pa; + uint32_t status, trbflags, type; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("::xhci_trb expects an address\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&trb, sizeof (trb), addr) != sizeof (trb)) { + mdb_warn("failed to read xhci_trb_t at 0x%x", addr); + return (DCMD_ERR); + } + + pa = LE_64(trb.trb_addr); + status = LE_32(trb.trb_status); + trbflags = LE_32(trb.trb_flags); + + type = XHCI_TRB_GET_TYPE(trbflags); + + if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST)) + mdb_printf("\n"); + + mdb_set_dot(addr + sizeof (xhci_trb_t)); + mdb_printf("%s TRB (%d)\n", xhci_mdb_trb_type_to_str(type), type); + mdb_inc_indent(XHCI_MDB_TRB_INDENT); + + switch (XHCI_RING_TYPE_SHIFT(type)) { + case XHCI_EVT_XFER: + return (xhci_mdb_print_transfer_event(pa, status, trbflags)); + case XHCI_EVT_CMD_COMPLETE: + return (xhci_mdb_print_command_event(pa, status, trbflags)); + case XHCI_EVT_PORT_CHANGE: + return (xhci_mdb_print_psc(pa, status, trbflags)); + case XHCI_TRB_TYPE_NORMAL: + return (xhci_mdb_print_normal_trb(pa, status, trbflags)); + } + + /* + * Just print generic information if we don't have a specific printer + * for that TRB type. + */ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("TRB Status: 0x%x\n", status); + mdb_printf("TRB Flags: 0x%x\n", trbflags); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +static int +xhci_mdb_walk_xhci_init(mdb_walk_state_t *wsp) +{ + GElf_Sym sym; + uintptr_t addr; + + if (wsp->walk_addr != 0) { + mdb_warn("::walk xhci only supports global walks\n"); + return (WALK_ERR); + } + + if (mdb_lookup_by_obj("xhci", "xhci_soft_state", &sym) != 0) { + mdb_warn("failed to find xhci_soft_state symbol"); + return (WALK_ERR); + } + + if (mdb_vread(&addr, sizeof (addr), sym.st_value) != sizeof (addr)) { + mdb_warn("failed to read xhci_soft_state at %p", addr); + return (WALK_ERR); + } + + wsp->walk_addr = addr; + if (mdb_layered_walk("softstate", wsp) != 0) { + mdb_warn("failed to walk softstate"); + return (WALK_ERR); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_walk_xhci_step(mdb_walk_state_t *wsp) +{ + xhci_t xhci; + + if (mdb_vread(&xhci, sizeof (xhci), wsp->walk_addr) != sizeof (xhci)) { + mdb_warn("failed to read xhci_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + + return (wsp->walk_callback(wsp->walk_addr, &xhci, wsp->walk_cbdata)); +} + +static int +xhci_mdb_walk_xhci_device_init(mdb_walk_state_t *wsp) +{ + uintptr_t addr; + + if (wsp->walk_addr == 0) { + mdb_warn("::walk xhci_device requires an xhci_t\n"); + return (WALK_ERR); + } + + addr = wsp->walk_addr; + addr += offsetof(xhci_t, xhci_usba); + addr += offsetof(xhci_usba_t, xa_devices); + wsp->walk_addr = (uintptr_t)addr; + if (mdb_layered_walk("list", wsp) != 0) { + mdb_warn("failed to walk list"); + return (WALK_ERR); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_walk_xhci_device_step(mdb_walk_state_t *wsp) +{ + xhci_device_t xd; + + if (mdb_vread(&xd, sizeof (xd), wsp->walk_addr) != sizeof (xd)) { + mdb_warn("failed to read xhci_device_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + + return (wsp->walk_callback(wsp->walk_addr, &xd, wsp->walk_cbdata)); +} + +static int +xhci_mdb_walk_xhci_endpoint_init(mdb_walk_state_t *wsp) +{ + xhci_mdb_walk_endpoint_t *xm; + xhci_device_t *xd; + + if (wsp->walk_addr == 0) { + mdb_warn("::walk xhci_endpoint requires an xhci_device_t\n"); + return (WALK_ERR); + } + + xm = mdb_alloc(sizeof (xhci_mdb_walk_endpoint_t), UM_SLEEP | UM_GC); + xm->xmwe_ep = 0; + xd = &xm->xmwe_device; + if (mdb_vread(xd, sizeof (*xd), wsp->walk_addr) != sizeof (*xd)) { + mdb_warn("failed to read xhci_endpoint_t at %p", + wsp->walk_addr); + return (WALK_ERR); + } + wsp->walk_data = xm; + + return (WALK_NEXT); +} + +static int +xhci_mdb_walk_xhci_endpoint_step(mdb_walk_state_t *wsp) +{ + int ret; + uintptr_t addr; + xhci_mdb_walk_endpoint_t *xm = wsp->walk_data; + + if (xm->xmwe_ep >= XHCI_NUM_ENDPOINTS) + return (WALK_DONE); + + addr = (uintptr_t)xm->xmwe_device.xd_endpoints[xm->xmwe_ep]; + if (addr != NULL) { + xhci_endpoint_t xe; + + if (mdb_vread(&xe, sizeof (xe), addr) != sizeof (xe)) { + mdb_warn("failed to read xhci_endpoint_t at %p", + xm->xmwe_device.xd_endpoints[xm->xmwe_ep]); + return (WALK_ERR); + } + + ret = wsp->walk_callback(addr, &xe, wsp->walk_cbdata); + } else { + ret = WALK_NEXT; + } + xm->xmwe_ep++; + + return (ret); +} + +typedef struct xhci_mdb_find { + int xmf_slot; + int xmf_ep; + uintptr_t xmf_addr; +} xhci_mdb_find_t; + +static int +xhci_mdb_find_endpoint_cb(uintptr_t addr, const void *data, void *arg) +{ + const xhci_endpoint_t *xep = data; + xhci_mdb_find_t *xmf = arg; + + /* + * The endpoints that are presented here are off by one from the actual + * endpoint ID in the xhci_endpoint_t, as we're really displaying the + * index into the device input context. + */ + if (xep->xep_num + 1 == xmf->xmf_ep) { + xmf->xmf_addr = addr; + return (WALK_DONE); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_find_device_cb(uintptr_t addr, const void *data, void *arg) +{ + const xhci_device_t *xd = data; + xhci_mdb_find_t *xmf = arg; + + if (xd->xd_slot == xmf->xmf_slot) { + if (xmf->xmf_ep == -1) { + xmf->xmf_addr = addr; + return (WALK_DONE); + } + + if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_find_endpoint_cb, + xmf, addr) == -1) { + mdb_warn("failed to walk xhci_endpoint at %p", addr); + return (WALK_ERR); + } + + return (WALK_DONE); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_find(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uintptr_t ep, slot; + boolean_t ep_set, slot_set; + xhci_mdb_find_t xmf; + + if ((flags & DCMD_ADDRSPEC) == 0) + return (DCMD_USAGE); + + ep_set = slot_set = B_FALSE; + if (mdb_getopts(argc, argv, 'e', MDB_OPT_UINTPTR_SET, &ep_set, &ep, + 's', MDB_OPT_UINTPTR_SET, &slot_set, &slot) != argc) + return (DCMD_USAGE); + + if (!slot_set) { + mdb_warn("-s is required\n"); + return (DCMD_USAGE); + } + + xmf.xmf_slot = (int)slot; + if (ep_set) + xmf.xmf_ep = (int)ep; + else + xmf.xmf_ep = -1; + xmf.xmf_addr = 0; + + if (mdb_pwalk("xhci`xhci_device", xhci_mdb_find_device_cb, + &xmf, addr) == -1) { + mdb_warn("failed to walk xhci_device at %p", addr); + return (DCMD_ERR); + } + + if (xmf.xmf_addr == 0) { + if (ep_set) { + mdb_warn("failed to find xhci_endpoint_t for slot %d " + "and endpoint %d\n", slot, ep); + } else { + mdb_warn("failed to find xhci_device_t for slot %d\n", + slot); + } + return (DCMD_ERR); + } + + mdb_printf("%p\n", xmf.xmf_addr); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_endpoint_count(uintptr_t addr, const void *ep, void *arg) +{ + int *countp = arg; + + *countp += 1; + return (WALK_NEXT); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_endpoint_summary(uintptr_t addr, const void *ep, void *arg) +{ + const xhci_device_t *xd = arg; + const xhci_endpoint_t *xep = ep; + const char *type; + const char *state; + xhci_endpoint_context_t epctx; + int eptype; + + if (mdb_vread(&epctx, sizeof (epctx), + (uintptr_t)xd->xd_endout[xep->xep_num]) != sizeof (epctx)) { + mdb_warn("failed to read endpoint context at %p", + xd->xd_endout[xep->xep_num]); + return (WALK_ERR); + } + + eptype = XHCI_EPCTX_GET_EPTYPE(LE_32(epctx.xec_info2)); + type = xhci_mdb_epctx_eptypes[eptype]; + state = xhci_mdb_epctx_states[XHCI_EPCTX_STATE(LE_32(epctx.xec_info))]; + + mdb_printf("%-4d %-10s %-10s 0x%-04x 0x%-04x\n", xep->xep_num, type, + state, xep->xep_ring.xr_head, xep->xep_ring.xr_tail); + + return (WALK_NEXT); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_device(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + int count; + xhci_device_t xd; + usba_device_t ud; + char product[256], mfg[256]; + + if (!(flags & DCMD_ADDRSPEC)) { + return (mdb_eval("::walk xhci`xhci | ::walk xhci`xhci_device | " + "::xhci_device")); + } + + if (mdb_vread(&xd, sizeof (xd), addr) != sizeof (xd)) { + mdb_warn("failed to read xhci_device_t at 0x%x", addr); + return (DCMD_ERR); + } + + if (mdb_vread(&ud, sizeof (ud), (uintptr_t)xd.xd_usbdev) != + sizeof (ud)) { + mdb_warn("failed to read usba_device_t at %p\n", xd.xd_usbdev); + return (DCMD_ERR); + } + + if (ud.usb_mfg_str == NULL || mdb_readstr(mfg, sizeof (mfg), + (uintptr_t)ud.usb_mfg_str) <= 0) { + (void) strlcpy(mfg, "Unknown Manufacturer", sizeof (mfg)); + } + + if (ud.usb_product_str == NULL || mdb_readstr(product, sizeof (product), + (uintptr_t)ud.usb_product_str) <= 0) { + (void) strlcpy(product, "Unknown Product", sizeof (product)); + } + + mdb_printf("%<b>%s - %s%</b>\n", mfg, product); + + count = 0; + if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_endpoint_count, &count, + addr) == -1) { + mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr); + return (DCMD_ERR); + } + + mdb_printf("Port %02d | Slot %02d | # Endpoints %02d\n", xd.xd_port, + xd.xd_slot, count); + mdb_printf("%<u>%-4s %-10s %-10s %-6s %-6s%</u>\n", "EP", "Type", + "State", "Head", "Tail"); + + if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_print_endpoint_summary, + &xd, addr) == -1) { + mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr); + return (DCMD_ERR); + } + + + mdb_printf("\n"); + + return (DCMD_OK); +} + +static int +xhci_mdb_find_trb(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + xhci_ring_t xr; + uint64_t base, max, target; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("missing required xhci_ring_t\n"); + return (DCMD_USAGE); + } + + if (argc == 0) { + mdb_warn("missing required PA of ring\n"); + return (DCMD_USAGE); + } + + if (argc > 1) { + mdb_warn("too many arguments\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&xr, sizeof (xr), addr) != sizeof (xr)) { + mdb_warn("failed to read xhci_ring_t at %p", addr); + return (DCMD_USAGE); + } + + if (argv[0].a_type == MDB_TYPE_IMMEDIATE) { + target = argv[0].a_un.a_val; + } else if (argv[0].a_type == MDB_TYPE_STRING) { + target = mdb_strtoull(argv[0].a_un.a_str); + } else { + mdb_warn("argument is an unknown supported type: %d\n", + argv[0].a_type); + return (DCMD_USAGE); + } + target = roundup(target, sizeof (xhci_trb_t)); + + base = xr.xr_dma.xdb_cookies[0].dmac_laddress; + max = base + xr.xr_ntrb * sizeof (xhci_trb_t); + + if (target < base || target > max) { + mdb_warn("target address %p is outside the range of PAs for " + "TRBs in the ring [%p, %p)", target, base, max); + return (DCMD_ERR); + } + target -= base; + mdb_printf("0x%" PRIx64 "\n", target + (uintptr_t)xr.xr_trb); + + return (DCMD_OK); +} + +static const mdb_dcmd_t xhci_dcmds[] = { + { "xhci_epctx", ":", "print endpoint context", + xhci_mdb_print_epctx, NULL }, + { "xhci_slotctx", ":", "print slot context", + xhci_mdb_print_slotctx, NULL }, + { "xhci_trb", ":", "print TRB", + xhci_mdb_print_trb, NULL }, + { "xhci_find", ": -s slot [-e endpiont]", + "find given xhci slot or endpoint", + xhci_mdb_find, NULL }, + { "xhci_device", ":", "device summary", + xhci_mdb_print_device, NULL }, + { "xhci_find_trb", ": pa", "find trb with PA in ring", + xhci_mdb_find_trb, NULL }, + { NULL } +}; + +static const mdb_walker_t xhci_walkers[] = { + { "xhci", "walk list of xhci_t structures", + xhci_mdb_walk_xhci_init, xhci_mdb_walk_xhci_step, NULL }, + { "xhci_device", "walk list of xhci_device_t structures", + xhci_mdb_walk_xhci_device_init, xhci_mdb_walk_xhci_device_step, + NULL }, + { "xhci_endpoint", "walk list of xhci_endpoint_t structures", + xhci_mdb_walk_xhci_endpoint_init, xhci_mdb_walk_xhci_endpoint_step, + NULL }, + { NULL } +}; + +static const mdb_modinfo_t xhci_modinfo = { + MDB_API_VERSION, xhci_dcmds, xhci_walkers +}; + +const mdb_modinfo_t * +_mdb_init(void) +{ + return (&xhci_modinfo); +} diff --git a/usr/src/cmd/mdb/intel/amd64/xhci/Makefile b/usr/src/cmd/mdb/intel/amd64/xhci/Makefile new file mode 100644 index 0000000000..0de7a701a7 --- /dev/null +++ b/usr/src/cmd/mdb/intel/amd64/xhci/Makefile @@ -0,0 +1,27 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + + +MODULE = xhci.so +MDBTGT = kvm + +MODSRCS = xhci.c + +include ../../../../Makefile.cmd +include ../../../../Makefile.cmd.64 +include ../../Makefile.amd64 +include ../../../Makefile.module + +CPPFLAGS += -I$(SRC)/uts/common diff --git a/usr/src/cmd/mdb/intel/ia32/xhci/Makefile b/usr/src/cmd/mdb/intel/ia32/xhci/Makefile new file mode 100644 index 0000000000..6fb72a3b2d --- /dev/null +++ b/usr/src/cmd/mdb/intel/ia32/xhci/Makefile @@ -0,0 +1,25 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +MODULE = xhci.so +MDBTGT = kvm + +MODSRCS = xhci.c + +include ../../../../Makefile.cmd +include ../../Makefile.ia32 +include ../../../Makefile.module + +CPPFLAGS += -I$(SRC)/uts/common diff --git a/usr/src/cmd/xhci/Makefile b/usr/src/cmd/xhci/Makefile new file mode 100644 index 0000000000..b0282ca5f9 --- /dev/null +++ b/usr/src/cmd/xhci/Makefile @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +PROG= xhci_portsc + +ROOTLIBXHCI = $(ROOTLIB)/xhci +ROOTLIBXHCIPROG = $(PROG:%=$(ROOTLIBXHCI)/%) + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I$(SRC)/uts/common/ +LDLIBS += -ldevinfo + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTLIBXHCIPROG) + +clean: + +lint: lint_PROG + +$(ROOTLIBXHCI): + $(INS.dir) + +$(ROOTLIBXHCI)/%: % $(ROOTLIBXHCI) + $(INS.file) + +include ../Makefile.targ diff --git a/usr/src/cmd/xhci/xhci_portsc.c b/usr/src/cmd/xhci/xhci_portsc.c new file mode 100644 index 0000000000..a656f76950 --- /dev/null +++ b/usr/src/cmd/xhci/xhci_portsc.c @@ -0,0 +1,373 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +/* + * This is a private utility that combines a number of minor debugging routines + * for xhci. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libdevinfo.h> +#include <sys/usb/hcd/xhci/xhci_ioctl.h> +#include <sys/usb/hcd/xhci/xhcireg.h> + +static char *xp_devpath = NULL; +static int xp_npaths; +static const char *xp_path; +static const char *xp_state = NULL; +static uint32_t xp_port; +static boolean_t xp_verbose = B_FALSE; +static boolean_t xp_clear = B_FALSE; +static boolean_t xp_list = B_FALSE; +extern const char *__progname; + +static int +xp_usage(const char *format, ...) +{ + if (format != NULL) { + va_list alist; + + va_start(alist, format); + vwarnx(format, alist); + va_end(alist); + } + + (void) fprintf(stderr, "usage: %s [-l] [-v] [-c] [-d path] [-p port] " + "[-s state]\n", __progname); + return (2); +} + +static const char *xp_pls_strings[] = { + "U0", + "U1", + "U2", + "U3 (suspended)", + "Disabled", + "RxDetect", + "Inactive", + "Polling", + "Recovery", + "Hot Reset", + "Compliance Mode", + "Test Mode", + "Reserved", + "Reserved", + "Reserved", + "Resume", + NULL +}; + +static void +xp_dump_verbose(uint32_t portsc) +{ + if (portsc & XHCI_PS_CCS) + (void) printf("\t\t\tCCS\n"); + if (portsc & XHCI_PS_PED) + (void) printf("\t\t\tPED\n"); + if (portsc & XHCI_PS_OCA) + (void) printf("\t\t\tOCA\n"); + if (portsc & XHCI_PS_PR) + (void) printf("\t\t\tPR\n"); + if (portsc & XHCI_PS_PP) { + (void) printf("\t\t\tPLS: %s (%d)\n", + xp_pls_strings[XHCI_PS_PLS_GET(portsc)], + XHCI_PS_PLS_GET(portsc)); + (void) printf("\t\t\tPP\n"); + } else { + (void) printf("\t\t\tPLS: undefined (No PP)\n"); + } + + if (XHCI_PS_SPEED_GET(portsc) != 0) { + (void) printf("\t\t\tPort Speed: "); + switch (XHCI_PS_SPEED_GET(portsc)) { + case 0: + (void) printf("Undefined "); + break; + case XHCI_SPEED_FULL: + (void) printf("Full "); + break; + case XHCI_SPEED_LOW: + (void) printf("Low "); + break; + case XHCI_SPEED_HIGH: + (void) printf("High "); + break; + case XHCI_SPEED_SUPER: + (void) printf("Super "); + break; + default: + (void) printf("Unknown "); + break; + } + (void) printf("(%d)\n", XHCI_PS_SPEED_GET(portsc)); + } + if (XHCI_PS_PIC_GET(portsc) != 0) + (void) printf("\t\t\tPIC: %d\n", XHCI_PS_PIC_GET(portsc)); + + if (portsc & XHCI_PS_LWS) + (void) printf("\t\t\tLWS\n"); + if (portsc & XHCI_PS_CSC) + (void) printf("\t\t\tCSC\n"); + if (portsc & XHCI_PS_PEC) + (void) printf("\t\t\tPEC\n"); + if (portsc & XHCI_PS_WRC) + (void) printf("\t\t\tWRC\n"); + if (portsc & XHCI_PS_OCC) + (void) printf("\t\t\tOCC\n"); + if (portsc & XHCI_PS_PRC) + (void) printf("\t\t\tPRC\n"); + if (portsc & XHCI_PS_PLC) + (void) printf("\t\t\tPLC\n"); + if (portsc & XHCI_PS_CEC) + (void) printf("\t\t\tCEC\n"); + if (portsc & XHCI_PS_CAS) + (void) printf("\t\t\tCAS\n"); + if (portsc & XHCI_PS_WCE) + (void) printf("\t\t\tWCE\n"); + if (portsc & XHCI_PS_WDE) + (void) printf("\t\t\tWDE\n"); + if (portsc & XHCI_PS_WOE) + (void) printf("\t\t\tWOE\n"); + if (portsc & XHCI_PS_DR) + (void) printf("\t\t\tDR\n"); + if (portsc & XHCI_PS_WPR) + (void) printf("\t\t\tWPR\n"); +} + +static void +xp_dump(const char *path) +{ + int fd, i; + xhci_ioctl_portsc_t xhi = { 0 }; + + fd = open(path, O_RDWR); + if (fd < 0) { + err(EXIT_FAILURE, "failed to open %s", path); + } + + if (ioctl(fd, XHCI_IOCTL_PORTSC, &xhi) != 0) + err(EXIT_FAILURE, "failed to get port status"); + + (void) close(fd); + + for (i = 1; i <= xhi.xhi_nports; i++) { + if (xp_port != 0 && i != xp_port) + continue; + + (void) printf("port %2d:\t0x%08x\n", i, xhi.xhi_portsc[i]); + if (xp_verbose == B_TRUE) + xp_dump_verbose(xhi.xhi_portsc[i]); + } +} + +static void +xp_set_pls(const char *path, uint32_t port, const char *state) +{ + int fd, i; + xhci_ioctl_setpls_t xis; + + fd = open(path, O_RDWR); + if (fd < 0) { + err(EXIT_FAILURE, "failed to open %s", path); + } + + xis.xis_port = port; + for (i = 0; xp_pls_strings[i] != NULL; i++) { + if (strcasecmp(state, xp_pls_strings[i]) == 0) + break; + } + + if (xp_pls_strings[i] == NULL) { + errx(EXIT_FAILURE, "unknown state string: %s\n", state); + } + + xis.xis_pls = i; + (void) printf("setting port %d with pls %d\n", port, xis.xis_pls); + + if (ioctl(fd, XHCI_IOCTL_SETPLS, &xis) != 0) + err(EXIT_FAILURE, "failed to set port status"); + + (void) close(fd); +} + +static void +xp_clear_change(const char *path, uint32_t port) +{ + int fd; + xhci_ioctl_clear_t xic; + + fd = open(path, O_RDWR); + if (fd < 0) { + err(EXIT_FAILURE, "failed to open %s", path); + } + + xic.xic_port = port; + (void) printf("clearing change bits on port %d\n", port); + if (ioctl(fd, XHCI_IOCTL_CLEAR, &xic) != 0) + err(EXIT_FAILURE, "failed to set port status"); + + (void) close(fd); +} + +/* ARGSUSED */ +static int +xp_devinfo_cb(di_node_t node, void *arg) +{ + char *drv; + di_minor_t minor; + boolean_t *do_print = arg; + + drv = di_driver_name(node); + if (drv == NULL) + return (DI_WALK_CONTINUE); + if (strcmp(drv, "xhci") != 0) + return (DI_WALK_CONTINUE); + + /* + * We have an instance of the xhci driver. We need to find the minor + * node for the hubd instance. These are all usually greater than + * HUBD_IS_ROOT_HUB. However, to avoid hardcoding that here, we instead + * rely on the fact that the minor node for the actual device has a + * :hubd as the intance. + */ + minor = DI_MINOR_NIL; + while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { + char *mname, *path; + + mname = di_minor_name(minor); + if (mname == NULL) + continue; + if (strcmp(mname, "hubd") != 0) + continue; + path = di_devfs_minor_path(minor); + if (*do_print == B_TRUE) { + (void) printf("/devices%s\n", path); + di_devfs_path_free(path); + } else { + xp_npaths++; + if (xp_devpath == NULL) + xp_devpath = path; + else + di_devfs_path_free(path); + } + } + + return (DI_WALK_PRUNECHILD); +} + +/* + * We need to find all minor nodes of instances of the xhci driver whose name is + * 'hubd'. + */ +static void +xp_find_devs(boolean_t print) +{ + di_node_t root; + + if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { + err(EXIT_FAILURE, "failed to initialize devices tree"); + } + + if (di_walk_node(root, DI_WALK_CLDFIRST, &print, xp_devinfo_cb) != 0) + err(EXIT_FAILURE, "failed to walk devices tree"); +} + +int +main(int argc, char *argv[]) +{ + int c; + char devpath[PATH_MAX]; + + while ((c = getopt(argc, argv, ":d:vlcp:s:")) != -1) { + switch (c) { + case 'c': + xp_clear = B_TRUE; + break; + case 'd': + xp_path = optarg; + break; + case 'l': + xp_list = B_TRUE; + break; + case 'v': + xp_verbose = B_TRUE; + break; + case 'p': + xp_port = atoi(optarg); + if (xp_port < 1 || xp_port > XHCI_PORTSC_NPORTS) + return (xp_usage("invalid port for -p: %d\n", + optarg)); + break; + case 's': + xp_state = optarg; + break; + case ':': + return (xp_usage("-%c requires an operand\n", optopt)); + case '?': + return (xp_usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + if (xp_list == B_TRUE && (xp_path != NULL || xp_clear == B_TRUE || + xp_port > 0 || xp_state != NULL)) { + return (xp_usage("-l cannot be used with other options\n")); + } + + if (xp_list == B_TRUE) { + xp_find_devs(B_TRUE); + return (0); + } + + if (xp_path == NULL) { + xp_find_devs(B_FALSE); + if (xp_npaths == 0) { + errx(EXIT_FAILURE, "no xhci devices found"); + } else if (xp_npaths > 1) { + errx(EXIT_FAILURE, "more than one xhci device found, " + "please specify device with -d, use -l to list"); + } + if (snprintf(devpath, sizeof (devpath), "/devices/%s", + xp_devpath) >= sizeof (devpath)) + errx(EXIT_FAILURE, "xhci path found at %s overflows " + "internal device path"); + di_devfs_path_free(xp_devpath); + xp_devpath = NULL; + xp_path = devpath; + } + + if (xp_clear == B_TRUE && xp_state != NULL) { + return (xp_usage("-c and -s can't be used together\n")); + } + + if (xp_state != NULL) { + xp_set_pls(xp_path, xp_port, xp_state); + } else if (xp_clear == B_TRUE) { + xp_clear_change(xp_path, xp_port); + } else { + xp_dump(xp_path); + } + + return (0); +} |
