/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include /* ****************************************************************** */ /* extenal definition */ typedef struct mdb_ctf_id { void *_opaque[2]; } mdb_ctf_id_t; extern int mdb_ctf_lookup_by_name(const char *, mdb_ctf_id_t *); extern int mdb_devinfo2driver(uintptr_t, char *, size_t); extern int mdb_devinfo2statep(uintptr_t, char *, uintptr_t *); extern char *mdb_ddi_pathname(uintptr_t, char *, size_t); /* ****************************************************************** */ /* internal definition */ #define OPT_TREE 0x01 #define OPT_VERB 0x02 #define STRLEN 256 #define BYTE_OFFSET 8 typedef struct usb_descr_item { uint_t nlen; /* if it's an byte array, nlen += BYTE_OFFSET */ char *name; /* descriptor item name */ } usb_descr_item_t; /* define the known descriptor items */ static usb_descr_item_t usb_cfg_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {2, "wTotalLength"}, {1, "bNumInterfaces"}, {1, "bConfigurationValue"}, {1, "iConfiguration"}, {1, "bmAttributes"}, {1, "bMaxPower"}, }; static uint_t usb_cfg_item = 8; static usb_descr_item_t usb_ia_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bFirstInterface"}, {1, "bInterfaceCount"}, {1, "bFunctionClass"}, {1, "bFunctionSubClass"}, {1, "bFunctionProtocol"}, {1, "iFunction"}, }; static uint_t usb_ia_item = 8; static usb_descr_item_t usb_if_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bInterfaceNumber"}, {1, "bAlternateSetting"}, {1, "bNumEndpoints"}, {1, "bInterfaceClass"}, {1, "bInterfaceSubClass"}, {1, "bInterfaceProtocol"}, {1, "iInterface"}, }; static uint_t usb_if_item = 9; static usb_descr_item_t usb_ep_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bEndpointAddress"}, {1, "bmAttributes"}, {2, "wMaxPacketSize"}, {1, "bInterval"}, }; 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"}, {2, "bcdUSB"}, {1, "bDeviceClass"}, {1, "bDeviceSubClass"}, {1, "bDeviceProtocol"}, {1, "bMaxPacketSize0"}, {1, "bNumConfigurations"}, {1, "bReserved"}, }; static uint_t usb_qlf_item = 9; static usb_descr_item_t usb_str_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bString"}, }; static uint_t usb_str_item = 3; static usb_descr_item_t usb_wa_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {2, "bcdWAVersion"}, {1, "bNumPorts"}, {1, "bmAttributes"}, {2, "wNumRPipes"}, {2, "wRPipeMaxBlock"}, {1, "bRPipeBlockSize"}, {1, "bPwrOn2PwrGood"}, {1, "bNumMMCIEs"}, {1, "DeviceRemovable"}, }; static uint_t usb_wa_item = 11; static usb_descr_item_t usb_hid_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {2, "bcdHID"}, {1, "bCountryCode"}, {1, "bNumDescriptors"}, {1, "bReportDescriptorType"}, {2, "wReportDescriptorLength"}, }; static uint_t usb_hid_item = 7; static usb_descr_item_t usb_ac_header_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {2, "bcdADC"}, {2, "wTotalLength"}, {1, "blnCollection"}, {1, "baInterfaceNr"}, }; static uint_t usb_ac_header_item = 7; static usb_descr_item_t usb_ac_input_term_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bTerminalID"}, {2, "wTerminalType"}, {1, "bAssocTerminal"}, {1, "bNrChannels"}, {2, "wChannelConfig"}, {1, "iChannelNames"}, {1, "iTerminal"}, }; static uint_t usb_ac_input_term_item = 10; static usb_descr_item_t usb_ac_output_term_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bTerminalID"}, {2, "wTerminalType"}, {1, "bAssocTerminal"}, {1, "bSourceID"}, {1, "iTerminal"}, }; static uint_t usb_ac_output_term_item = 8; static usb_descr_item_t usb_ac_mixer_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {1, "bNrInPins"}, {1, "baSourceID"}, }; static uint_t usb_ac_mixer_item = 6; static usb_descr_item_t usb_ac_selector_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {1, "bNrInPins"}, {1, "baSourceID"}, }; static uint_t usb_ac_selector_item = 6; static usb_descr_item_t usb_ac_feature_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {1, "bSourceID"}, {1, "bControlSize"}, {1, "bmaControls"}, }; static uint_t usb_ac_feature_item = 7; static usb_descr_item_t usb_ac_processing_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {1, "wProcessType"}, {1, "bNrInPins"}, {1, "baSourceID"}, }; static uint_t usb_ac_processing_item = 7; static usb_descr_item_t usb_ac_extension_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "wExtensionCode"}, {1, "bUnitID"}, {1, "bNrInPins"}, {1, "baSourceID"}, }; static uint_t usb_ac_extension_item = 7; static usb_descr_item_t usb_as_ep_descr[] = { {1, "blength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bmAttributes"}, {1, "bLockDelayUnits"}, {2, "wLockDelay"}, }; static uint_t usb_as_ep_item = 6; static usb_descr_item_t usb_as_if_descr[] = { {1, "blength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bTerminalLink"}, {1, "bDelay"}, {2, "wFormatTag"}, }; static uint_t usb_as_if_item = 6; static usb_descr_item_t usb_as_format_descr[] = { {1, "blength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bFormatType"}, {1, "bNrChannels"}, {1, "bSubFrameSize"}, {1, "bBitResolution"}, {1, "bSamFreqType"}, {1, "bSamFreqs"}, }; static uint_t usb_as_format_item = 9; static usb_descr_item_t usb_vc_header_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubtype"}, {2, "bcdUVC"}, {2, "wTotalLength"}, {4, "dwClockFrequency"}, {1, "bInCollection"}, }; static uint_t usb_vc_header_item = 7; static usb_descr_item_t usb_vc_input_term_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bTerminalID"}, {2, "wTerminalType"}, {1, "AssocTerminal"}, {1, "iTerminal"}, }; static uint_t usb_vc_input_term_item = 7; static usb_descr_item_t usb_vc_output_term_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bTerminalID"}, {2, "wTerminalType"}, {1, "AssocTerminal"}, {1, "bSourceID"}, {1, "iTerminal"}, }; static uint_t usb_vc_output_term_item = 8; static usb_descr_item_t usb_vc_processing_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {1, "bSourceID"}, {2, "wMaxMultiplier"}, {1, "bControlSize"}, {1, "bmControls"}, }; static uint_t usb_vc_processing_item = 8; static usb_descr_item_t usb_vc_selector_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {1, "bNrInPins"}, }; static uint_t usb_vc_selector_item = 5; static usb_descr_item_t usb_vc_extension_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bUnitID"}, {16 + BYTE_OFFSET, "guidExtensionCode[16]"}, {1, "bNumControls"}, {1, "bNrInPins"}, }; static uint_t usb_vc_extension_item = 7; static usb_descr_item_t usb_vs_input_header_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bNumFormats"}, {2, "wTotalLength"}, {1, "bEndpointAddress"}, {1, "bmInfo"}, {1, "bTerminalLink"}, {1, "bStillCaptureMethod"}, {1, "bTriggerSupport"}, {1, "bTriggerUsage"}, {1, "bControlSize"}, {1, "bmaControls"}, }; static uint_t usb_vs_input_header_item = 13; static usb_descr_item_t usb_vs_output_header_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bNumFormats"}, {2, "wTotalLength"}, {1, "bEndpointAddress"}, {1, "bTerminalLink"}, {1, "bControlSize"}, {1, "bmaControls"}, }; static uint_t usb_vs_output_header_item = 9; static usb_descr_item_t usb_vs_still_image_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bEndpointAddress"}, {1, "bNumImageSizePatterns"}, {2, "wWidth"}, {2, "wHeight"}, }; static uint_t usb_vs_still_image_item = 7; static usb_descr_item_t usb_vs_color_matching_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubtype"}, {1, "bColorPrimaries"}, {1, "bTransferCharacteristics"}, {1, "bMatrixCoefficients"}, }; static uint_t usb_vs_color_matching_item = 6; static usb_descr_item_t usb_vs_2frame_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bFrameIndex"}, {1, "bmCapabilities"}, {2, "wWidth"}, {2, "wHeight"}, {4, "dwMinBitRate"}, {4, "dwMaxBitRate"}, {4, "dwMaxVideoFrameBufferSize"}, {4, "dwDefaultFrameInterval"}, {1, "bFrameIntervalType"}, }; static uint_t usb_vs_2frame_item = 12; static usb_descr_item_t usb_vs_format_mjpeg_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bFormatIndex"}, {1, "bNumFrameDescriptors"}, {1, "bmFlags"}, {1, "bDefaultFrameIndex"}, {1, "bAspectRatioX"}, {1, "bAspectRatioY"}, {1, "bmInterlaceFlags"}, {1, "bCopyProtect"}, }; static uint_t usb_vs_format_mjpeg_item = 11; static usb_descr_item_t usb_vs_format_uncps_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bFormatIndex"}, {1, "bNumFrameDescriptors"}, {16 + BYTE_OFFSET, "guidFormat[16]"}, {1, "bBitsPerPixel"}, {1, "bDefaultFrameIndex"}, {1, "bAspectRatioX"}, {1, "bAspectRatioY"}, {1, "bmInterlaceFlags"}, {1, "bCopyProtect"}, }; static uint_t usb_vs_format_uncps_item = 12; static usb_descr_item_t usb_vs_format_mp2ts_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bFormatIndex"}, {1, "bDataOffset"}, {1, "bPacketLength"}, {1, "bStrideLength"}, {16 + BYTE_OFFSET, "guidStrideFormat[16]"}, }; static uint_t usb_vs_format_mp2ts_item = 8; static usb_descr_item_t usb_vs_format_dv_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {1, "bDescriptorSubType"}, {1, "bFormatIndex"}, {4, "dwMaxVideoFrameBufferSize"}, {1, "bFormatType"}, }; static uint_t usb_vs_format_dv_item = 6; static usb_descr_item_t usb_ccid_descr[] = { {1, "bLength"}, {1, "bDescriptorType"}, {2, "bcdCCID"}, {1, "bMaxSlotIndex"}, {1, "bVoltageSupport"}, {4, "dwProtocols"}, {4, "dwDefaultClock"}, {4, "dwMaximumClock"}, {1, "bNumClockSupported"}, {4, "dwDataRate"}, {4, "dwMaxDataRate"}, {1, "bNumDataRatesSupported"}, {4, "dwMaxIFSD"}, {4, "dwSyncProtocols"}, {4, "dwMechanical"}, {4, "dwFeatures"}, {4, "dwMaxCCIDMessageLength"}, {1, "bClassGetResponse"}, {1, "bClassEnvelope"}, {2, "wLcdLayout"}, {1, "bPinSupport"}, {1, "bMaxCCIDBusySlots"} }; static uint_t usb_ccid_item = ARRAY_SIZE(usb_ccid_descr); /* ****************************************************************** */ typedef struct hci_state { void *hci_dip; uint_t hci_instance; void *hci_hcdi_ops; uint_t hci_flags; uint16_t vendor_id; uint16_t device_id; } hci_state_t; static int prt_usb_tree(uintptr_t paddr, uint_t flag); static int prt_usb_tree_node(uintptr_t paddr); static void prt_usb_hid_item(uintptr_t paddr); static void prt_usb_hid_item_params(entity_item_t *item); static void prt_usb_hid_item_attrs(uintptr_t paddr); static void prt_usb_hid_item_tags(uint_t tag); static void prt_usb_hid_item_data(uintptr_t paddr, uint_t len); static int prt_usb_desc(uintptr_t usb_cfg, uint_t cfg_len); static int prt_usb_ac_desc(uintptr_t paddr, uint_t nlen); static int prt_usb_as_desc(uintptr_t paddr, uint_t nlen); static int prt_usb_vc_desc(uintptr_t paddr, uint_t nlen); static int prt_usb_vs_desc(uintptr_t paddr, uint_t nlen); static int print_descr(uintptr_t, uint_t, usb_descr_item_t *, uint_t); static int print_struct(uintptr_t, uint_t, mdb_arg_t *); static int prt_usb_buf(uintptr_t, uint_t); /* ****************************************************************** */ /* exported functions */ void prt_usb_usage(void); int prtusb(uintptr_t, uint_t, int, const mdb_arg_t *); /* ****************************************************************** */ /* help of prtusb */ void prt_usb_usage(void) { mdb_printf("%-8s : %s\n", "-v", "print all descriptors"); mdb_printf("%-8s : %s\n", "-t", "print device trees"); mdb_printf("%-8s : %s\n", "-i index", "print the device by index"); } /* the entry of ::prtusb */ int prtusb(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { static int count = 1; uint64_t sel_num = 0; uint_t usb_flag = 0; usba_device_t usb_dev; usb_dev_descr_t dev_desc; struct dev_info usb_dip; char strbuf[STRLEN]; /* print all usba devices if no address assigned */ if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("usba_device", "prtusb", argc, argv) == -1) { mdb_warn("failed to walk usba_device"); return (DCMD_ERR); } return (DCMD_OK); } /* for the first device, print head */ if (DCMD_HDRSPEC(flags)) { count = 1; mdb_printf("%%-8s%-12s%-6s%-14s%-5s%-12s%-20s%\n", "INDEX", "DRIVER", "INST", "NODE", "GEN", "VID.PID", "PRODUCT"); } if (mdb_getopts(argc, argv, 'i', MDB_OPT_UINT64, &sel_num, 't', MDB_OPT_SETBITS, OPT_TREE, &usb_flag, 'v', MDB_OPT_SETBITS, OPT_VERB, &usb_flag, NULL) != argc) { return (DCMD_USAGE); } if (mdb_vread(&usb_dev, sizeof (usba_device_t), addr) == -1) { mdb_warn("Failed to read usba_device!\n"); return (DCMD_ERR); } if (mdb_vread(&usb_dip, sizeof (struct dev_info), (uintptr_t)usb_dev.usb_dip) == -1) { mdb_warn("Failed to read dev_info!\n"); return (DCMD_ERR); } /* process the "-i" */ if (sel_num && sel_num != count) { count++; return (DCMD_OK); } /* index number of device node */ mdb_printf("%-8x", count++); /* driver and instance */ mdb_devinfo2driver((uintptr_t)usb_dev.usb_dip, strbuf, STRLEN); mdb_printf("%-12s%-6d", strbuf, usb_dip.devi_instance); /* node name */ if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dip.devi_node_name) != -1) { mdb_printf("%-14s", strbuf); } else { mdb_printf("%-14s", "No Node Name"); } 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); } /* product string */ if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dev.usb_product_str) != -1) { mdb_printf("%s\n", strbuf); } else { mdb_printf("%s\n", "No Product String"); } /* tree, print usb device tree info */ if (usb_flag & OPT_TREE) { mdb_printf("\nusba_device: 0x%lx\n", addr); mdb_printf("mfg_prod_sn: "); if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dev.usb_mfg_str) != -1) { mdb_printf("%s - ", strbuf); } else { mdb_printf("NULL - "); } if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dev.usb_product_str) != -1) { mdb_printf("%s - ", strbuf); } else { mdb_printf("NULL -"); } if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dev.usb_serialno_str) != -1) { mdb_printf("%s", strbuf); } else { mdb_printf("NULL"); } mdb_printf("\n\n"); prt_usb_tree((uintptr_t)usb_dev.usb_dip, 0); } /* verbose, print all descriptors */ if (usb_flag & OPT_VERB) { int i; uintptr_t cfg_buf; uint16_t cfg_len; mdb_printf("\n"); /* device descriptor */ prt_usb_desc((uintptr_t)usb_dev.usb_dev_descr, 18); /* config cloud descriptors */ if (usb_dev.usb_n_cfgs == 1) { mdb_inc_indent(4); mdb_printf("-- Active Config Index 0\n"); mdb_dec_indent(4); prt_usb_desc((uintptr_t)usb_dev.usb_cfg, usb_dev.usb_cfg_length); } else { /* multiple configs */ for (i = 0; i < usb_dev.usb_n_cfgs; i++) { if ((mdb_vread(&cfg_len, sizeof (uint16_t), (uintptr_t)(usb_dev.usb_cfg_array_len + i)) != -1) && (mdb_vread(&cfg_buf, sizeof (uintptr_t), (uintptr_t)(usb_dev.usb_cfg_array + i)) != -1)) { mdb_inc_indent(4); if (cfg_buf == (uintptr_t)usb_dev.usb_cfg) { mdb_printf("-- Active Config" " Index %x\n", i); } else { mdb_printf("-- Inactive Config" " Index %x\n", i); } mdb_dec_indent(4); prt_usb_desc(cfg_buf, cfg_len); } } } } if (usb_flag) { mdb_printf("%%-72s%\n", " "); } return (DCMD_OK); } /* print the info required by "-t" */ static int prt_usb_tree(uintptr_t paddr, uint_t flag) { struct dev_info usb_dip; if (mdb_vread(&usb_dip, sizeof (struct dev_info), paddr) == -1) { mdb_warn("prt_usb_tree: Failed to read dev_info!\n"); return (DCMD_ERR); } prt_usb_tree_node(paddr); if (usb_dip.devi_child) { mdb_printf("{\n"); mdb_inc_indent(4); prt_usb_tree((uintptr_t)usb_dip.devi_child, 1); mdb_dec_indent(4); mdb_printf("}\n\n"); } if (usb_dip.devi_sibling && flag == 1) { /* print the sibling if flag == 1 */ prt_usb_tree((uintptr_t)usb_dip.devi_sibling, 1); } return (DCMD_OK); } static int prt_usb_tree_node(uintptr_t paddr) { struct dev_info usb_dip; uintptr_t statep; uint_t errlevel; char driver_name[STRLEN] = ""; char strbuf[STRLEN] = ""; if (mdb_vread(&usb_dip, sizeof (struct dev_info), paddr) == -1) { mdb_warn("prt_usb_tree_node: Failed to read dev_info!\n"); return (DCMD_ERR); } /* node name */ if (mdb_readstr(strbuf, STRLEN, (uintptr_t)usb_dip.devi_node_name) != -1) { mdb_printf("%s, ", strbuf); } else { mdb_printf("%s, ", "node_name"); } /* instance */ mdb_printf("instance #%d ", usb_dip.devi_instance); /* driver name */ if (DDI_CF2(&usb_dip)) { mdb_devinfo2driver(paddr, driver_name, STRLEN); mdb_printf("(driver name: %s)\n", driver_name); } else { mdb_printf("(driver not attached)\n"); } /* device path */ mdb_ddi_pathname(paddr, strbuf, STRLEN); mdb_printf(" %s\n", strbuf); /* dip addr */ mdb_printf(" dip: 0x%lx\n", paddr); /* softe_sate */ mdb_snprintf(strbuf, STRLEN, "%s_statep", driver_name); if (mdb_devinfo2statep(paddr, strbuf, &statep) != -1) { mdb_printf(" %s: 0x%lx\n", strbuf, statep); } /* error level */ mdb_snprintf(strbuf, STRLEN, "%s_errlevel", driver_name); if (mdb_readvar(&errlevel, strbuf) != -1) { mdb_printf(" %s: 0x%x\n", strbuf, errlevel); } if (strcmp(driver_name, "ehci") == 0) { mdb_arg_t argv[] = { {MDB_TYPE_STRING, {"ehci_state_t"}}, {MDB_TYPE_STRING, {"ehci_root_hub.rh_descr"}} }; mdb_call_dcmd("print", statep, DCMD_ADDRSPEC, 2, argv); } if (strcmp(driver_name, "ohci") == 0) { mdb_arg_t argv[] = { {MDB_TYPE_STRING, {"ohci_state_t"}}, {MDB_TYPE_STRING, {"ohci_root_hub.rh_descr"}} }; mdb_call_dcmd("print", statep, DCMD_ADDRSPEC, 2, argv); } if (strcmp(driver_name, "uhci") == 0) { mdb_arg_t argv[] = { {MDB_TYPE_STRING, {"uhci_state_t"}}, {MDB_TYPE_STRING, {"uhci_root_hub.rh_descr"}} }; mdb_call_dcmd("print", statep, DCMD_ADDRSPEC, 2, argv); } if (strcmp(driver_name, "hubd") == 0) { mdb_arg_t argv[] = { {MDB_TYPE_STRING, {"hubd_t"}}, {MDB_TYPE_STRING, {"h_ep1_xdescr.uex_ep"}} }; mdb_call_dcmd("print", statep, DCMD_ADDRSPEC, 2, argv); } if (strcmp(driver_name, "hid") == 0) { hid_state_t hidp; if (mdb_vread(&hidp, sizeof (hid_state_t), statep) != -1) { hidparser_handle hid_report; if (mdb_vread(&hid_report, sizeof (hidparser_handle), (uintptr_t)hidp.hid_report_descr) != -1) { mdb_inc_indent(2); mdb_printf("\n"); prt_usb_hid_item((uintptr_t) hid_report.hidparser_handle_parse_tree); mdb_dec_indent(2); } } } mdb_printf("\n"); return (DCMD_OK); } /* print hid report descriptor */ static void prt_usb_hid_item(uintptr_t paddr) { entity_item_t item; if (mdb_vread(&item, sizeof (entity_item_t), paddr) != -1) { prt_usb_hid_item_attrs((uintptr_t)item.entity_item_attributes); prt_usb_hid_item_params(&item); if (item.info.child) { mdb_inc_indent(4); prt_usb_hid_item((uintptr_t)item.info.child); mdb_dec_indent(4); } if (item.entity_item_right_sibling) { prt_usb_hid_item((uintptr_t) item.entity_item_right_sibling); } } } static void prt_usb_hid_item_params(entity_item_t *item) { switch (item->entity_item_type) { case 0x80: mdb_printf("INPUT "); break; case 0x90: mdb_printf("OUTPUT "); break; case 0xA0: mdb_printf("COLLECTION "); break; case 0xB0: mdb_printf("FEATURE "); break; case 0xC0: mdb_printf("END_COLLECTION "); break; default: mdb_printf("MAIN_ITEM "); break; } prt_usb_hid_item_data((uintptr_t)item->entity_item_params, item->entity_item_params_leng); mdb_printf("\n"); } static void prt_usb_hid_item_attrs(uintptr_t paddr) { entity_attribute_t attr; if (mdb_vread(&attr, sizeof (entity_attribute_t), paddr) != -1) { prt_usb_hid_item_tags(attr.entity_attribute_tag); prt_usb_hid_item_data((uintptr_t)attr.entity_attribute_value, attr.entity_attribute_length); mdb_printf("\n"); if (attr.entity_attribute_next) { prt_usb_hid_item_attrs((uintptr_t) attr.entity_attribute_next); } } } static void prt_usb_hid_item_data(uintptr_t paddr, uint_t len) { char data[4]; int i; if (len > 4) { mdb_warn("Incorrect entity_item_length: 0x%x\n", len); return; } if (mdb_vread(data, len, paddr) != -1) { mdb_printf("( "); for (i = 0; i < len; i++) { mdb_printf("0x%02x ", data[i] & 0xff); } mdb_printf(")"); } } static void prt_usb_hid_item_tags(uint_t tag) { switch (tag) { case 0x04: mdb_printf("usage page "); break; case 0x14: mdb_printf("logical minimum "); break; case 0x24: mdb_printf("logical maximum "); break; case 0x34: mdb_printf("physical minimum "); break; case 0x44: mdb_printf("physical maximum "); break; case 0x54: mdb_printf("exponent "); break; case 0x64: mdb_printf("unit "); break; case 0x74: mdb_printf("report size "); break; case 0x84: mdb_printf("report id "); break; case 0x94: mdb_printf("report count "); break; case 0x08: mdb_printf("usage "); break; case 0x18: mdb_printf("usage min "); break; case 0x28: mdb_printf("usage max "); break; default: mdb_printf("tag "); } } /* print the info required by "-v" */ static int prt_usb_desc(uintptr_t usb_cfg, uint_t cfg_len) { uintptr_t paddr = usb_cfg; uintptr_t pend = usb_cfg + cfg_len; uchar_t desc_type, nlen; usb_if_descr_t usb_if; ulong_t indent = 0; mdb_arg_t argv = {MDB_TYPE_STRING, {"usb_dev_descr_t"}}; if (mdb_vread(&nlen, 1, paddr) == -1) { return (DCMD_ERR); } while ((paddr + nlen <= pend) && (nlen > 0)) { if (mdb_vread(&desc_type, 1, paddr + 1) == -1) { return (DCMD_ERR); } switch (desc_type) { case USB_DESCR_TYPE_DEV: mdb_printf("Device Descriptor\n"); print_struct(paddr, nlen, &argv); break; case USB_DESCR_TYPE_CFG: indent = 4; mdb_inc_indent(indent); mdb_printf("Configuration Descriptor\n"); print_descr(paddr, nlen, usb_cfg_descr, usb_cfg_item); mdb_dec_indent(indent); break; case USB_DESCR_TYPE_STRING: mdb_printf("String Descriptor\n"); print_descr(paddr, nlen, usb_str_descr, usb_str_item); break; case USB_DESCR_TYPE_IF: indent = 8; mdb_inc_indent(indent); mdb_printf("Interface Descriptor\n"); print_descr(paddr, nlen, usb_if_descr, usb_if_item); mdb_dec_indent(indent); mdb_vread(&usb_if, sizeof (usb_if_descr_t), paddr); break; case USB_DESCR_TYPE_EP: indent = 8; mdb_inc_indent(indent); mdb_printf("Endpoint Descriptor\n"); print_descr(paddr, nlen, usb_ep_descr, usb_ep_item); 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); break; case USB_DESCR_TYPE_OTHER_SPEED_CFG: indent = 4; mdb_inc_indent(indent); mdb_printf("Other_Speed_Configuration Descriptor\n"); print_descr(paddr, nlen, usb_cfg_descr, usb_cfg_item); mdb_dec_indent(indent); break; case USB_DESCR_TYPE_IA: indent = 6; mdb_inc_indent(indent); mdb_printf("Interface_Association Descriptor\n"); print_descr(paddr, nlen, usb_ia_descr, usb_ia_item); mdb_dec_indent(indent); break; case 0x21: /* hid descriptor */ indent = 12; mdb_inc_indent(indent); if (usb_if.bInterfaceClass == 0xe0 && usb_if.bInterfaceSubClass == 0x02) { mdb_printf("WA Descriptor\n"); print_descr(paddr, nlen, usb_wa_descr, usb_wa_item); } else if (usb_if.bInterfaceClass == USB_CLASS_CCID && usb_if.bInterfaceSubClass == 0x0) { mdb_printf("CCID Descriptor\n"); print_descr(paddr, nlen, usb_ccid_descr, usb_ccid_item); } else { mdb_printf("HID Descriptor\n"); print_descr(paddr, nlen, usb_hid_descr, usb_hid_item); } mdb_dec_indent(indent); break; case 0x24: /* class specific interfce descriptor */ indent = 12; mdb_inc_indent(indent); if (usb_if.bInterfaceClass == 1 && usb_if.bInterfaceSubClass == 1) { mdb_printf("AudioControl_Interface: "); prt_usb_ac_desc(paddr, nlen); } else if (usb_if.bInterfaceClass == 1 && usb_if.bInterfaceSubClass == 2) { mdb_printf("AudioStream_Interface: "); prt_usb_as_desc(paddr, nlen); } else if (usb_if.bInterfaceClass == 0x0E && usb_if.bInterfaceSubClass == 1) { mdb_printf("VideoControl_Interface: "); prt_usb_vc_desc(paddr, nlen); } else if (usb_if.bInterfaceClass == 0x0E && usb_if.bInterfaceSubClass == 2) { mdb_printf("VideoStream_Interface: "); prt_usb_vs_desc(paddr, nlen); } else { mdb_printf("Unknown_Interface:" "0x%x\n", desc_type); prt_usb_buf(paddr, nlen); } mdb_dec_indent(indent); break; case 0x25: /* class specific endpoint descriptor */ indent = 12; mdb_inc_indent(indent); if (usb_if.bInterfaceClass == 0x01) { mdb_printf("AudioEndpoint:\n"); print_descr(paddr, nlen, usb_as_ep_descr, usb_as_ep_item); } else if (usb_if.bInterfaceClass == 0x0E) { mdb_printf("VideoEndpoint:\n"); print_descr(paddr, nlen, usb_ep_descr, usb_ep_item); } else { mdb_printf("Unknown_Endpoint:" "0x%x\n", desc_type); prt_usb_buf(paddr, nlen); } mdb_dec_indent(indent); break; default: mdb_inc_indent(indent); mdb_printf("Unknown Descriptor: 0x%x\n", desc_type); prt_usb_buf(paddr, nlen); mdb_dec_indent(indent); break; } paddr += nlen; if (mdb_vread(&nlen, 1, paddr) == -1) { return (DCMD_ERR); } }; return (DCMD_OK); } /* print audio class specific control descriptor */ static int prt_usb_ac_desc(uintptr_t addr, uint_t nlen) { uchar_t sub_type; if (mdb_vread(&sub_type, 1, addr + 2) == -1) { return (DCMD_ERR); } switch (sub_type) { case 0x01: mdb_printf("header Descriptor\n"); print_descr(addr, nlen, usb_ac_header_descr, usb_ac_header_item); break; case 0x02: mdb_printf("input_terminal Descriptor\n"); print_descr(addr, nlen, usb_ac_input_term_descr, usb_ac_input_term_item); break; case 0x03: mdb_printf("output_terminal Descriptor\n"); print_descr(addr, nlen, usb_ac_output_term_descr, usb_ac_output_term_item); break; case 0x04: mdb_printf("mixer_unit Descriptor\n"); print_descr(addr, nlen, usb_ac_mixer_descr, usb_ac_mixer_item); break; case 0x05: mdb_printf("selector_unit Descriptor\n"); print_descr(addr, nlen, usb_ac_selector_descr, usb_ac_selector_item); break; case 0x06: mdb_printf("feature_unit Descriptor\n"); print_descr(addr, nlen, usb_ac_feature_descr, usb_ac_feature_item); break; case 0x07: mdb_printf("processing_unit Descriptor\n"); print_descr(addr, nlen, usb_ac_processing_descr, usb_ac_processing_item); break; case 0x08: mdb_printf("extension_unit Descriptor\n"); print_descr(addr, nlen, usb_ac_extension_descr, usb_ac_extension_item); break; default: mdb_printf("Unknown AC sub-descriptor 0x%x\n", sub_type); prt_usb_buf(addr, nlen); break; } return (DCMD_OK); } /* print audio class specific stream descriptor */ static int prt_usb_as_desc(uintptr_t addr, uint_t nlen) { uchar_t sub_type; if (mdb_vread(&sub_type, 1, addr + 2) == -1) { return (DCMD_ERR); } switch (sub_type) { case 0x01: mdb_printf("general_interface Descriptor\n"); print_descr(addr, nlen, usb_as_if_descr, usb_as_if_item); break; case 0x02: mdb_printf("format_type Descriptor\n"); print_descr(addr, nlen, usb_as_format_descr, usb_as_format_item); break; default: mdb_printf("Unknown AS sub-descriptor 0x%x\n", sub_type); prt_usb_buf(addr, nlen); break; } return (DCMD_OK); } /* print video class specific control descriptor */ static int prt_usb_vc_desc(uintptr_t addr, uint_t nlen) { uchar_t sub_type; if (mdb_vread(&sub_type, 1, addr + 2) == -1) { return (DCMD_ERR); } switch (sub_type) { case 0x01: mdb_printf("header Descriptor\n"); print_descr(addr, nlen, usb_vc_header_descr, usb_vc_header_item); break; case 0x02: mdb_printf("input_terminal Descriptor\n"); print_descr(addr, nlen, usb_vc_input_term_descr, usb_vc_input_term_item); break; case 0x03: mdb_printf("output_terminal Descriptor\n"); print_descr(addr, nlen, usb_vc_output_term_descr, usb_vc_output_term_item); break; case 0x04: mdb_printf("selector_unit Descriptor\n"); print_descr(addr, nlen, usb_vc_selector_descr, usb_vc_selector_item); break; case 0x05: mdb_printf("processing_unit Descriptor\n"); print_descr(addr, nlen, usb_vc_processing_descr, usb_vc_processing_item); break; case 0x06: mdb_printf("extension_unit Descriptor\n"); print_descr(addr, nlen, usb_vc_extension_descr, usb_vc_extension_item); break; default: mdb_printf("Unknown VC sub-descriptor 0x%x\n", sub_type); prt_usb_buf(addr, nlen); break; } return (DCMD_OK); } /* print video class specific stream descriptor */ static int prt_usb_vs_desc(uintptr_t addr, uint_t nlen) { uchar_t sub_type; if (mdb_vread(&sub_type, 1, addr + 2) == -1) { return (DCMD_ERR); } switch (sub_type) { case 0x01: mdb_printf("input_header Descriptor\n"); print_descr(addr, nlen, usb_vs_input_header_descr, usb_vs_input_header_item); break; case 0x02: mdb_printf("output_header Descriptor\n"); print_descr(addr, nlen, usb_vs_output_header_descr, usb_vs_output_header_item); break; case 0x03: mdb_printf("still_image_frame Descriptor\n"); print_descr(addr, nlen, usb_vs_still_image_descr, usb_vs_still_image_item); break; case 0x04: mdb_printf("format_uncompressed Descriptor\n"); print_descr(addr, nlen, usb_vs_format_uncps_descr, usb_vs_format_uncps_item); break; case 0x05: mdb_printf("frame_uncompressed Descriptor\n"); print_descr(addr, nlen, usb_vs_2frame_descr, usb_vs_2frame_item); break; case 0x06: mdb_printf("format_mjpeg Descriptor\n"); print_descr(addr, nlen, usb_vs_format_mjpeg_descr, usb_vs_format_mjpeg_item); break; case 0x07: mdb_printf("frame_mjpeg Descriptor\n"); print_descr(addr, nlen, usb_vs_2frame_descr, usb_vs_2frame_item); break; case 0x0A: mdb_printf("format_mpeg2ts Descriptor\n"); print_descr(addr, nlen, usb_vs_format_mp2ts_descr, usb_vs_format_mp2ts_item); break; case 0x0C: mdb_printf("format_dv Descriptor\n"); print_descr(addr, nlen, usb_vs_format_dv_descr, usb_vs_format_dv_item); break; case 0x0D: mdb_printf("color_matching Descriptor\n"); print_descr(addr, nlen, usb_vs_color_matching_descr, usb_vs_color_matching_item); break; default: mdb_printf("Unknown VS sub-descriptor 0x%x\n", sub_type); prt_usb_buf(addr, nlen); break; } return (DCMD_OK); } /* parse and print the descriptor items */ static int print_descr(uintptr_t addr, uint_t nlen, usb_descr_item_t *item, uint_t nitem) { int i, j; uint8_t buf[8]; uint64_t value; uintptr_t paddr = addr; usb_descr_item_t *p = item; mdb_printf("{"); for (i = 0; (i < nitem) && (paddr < addr + nlen); i++) { mdb_printf("\n %s =", p->name); switch (p->nlen) { case 1: /* uint8_t */ if (mdb_vread(buf, 1, paddr) == -1) { return (DCMD_ERR); } value = buf[0]; break; case 2: /* uint16_t */ if (mdb_vread(buf, 2, paddr) == -1) { return (DCMD_ERR); } value = buf[0] | (buf[1] << 8); break; case 4: /* uint32_t */ if (mdb_vread(buf, 4, paddr) == -1) { return (DCMD_ERR); } value = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); break; case 8: /* uint64_t */ if (mdb_vread(buf, 8, paddr) == -1) { return (DCMD_ERR); } value = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); value = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24) | (value << 32); break; default: /* byte array */ value = 0; /* print an array instead of a value */ for (j = 0; j < p->nlen - BYTE_OFFSET; j++) { if (mdb_vread(buf, 1, paddr + j) == -1) { break; } mdb_printf(" 0x%x", buf[0]); } break; } if (p->nlen > BYTE_OFFSET) { paddr += p->nlen - BYTE_OFFSET; } else { mdb_printf(" 0x%x", value); paddr += p->nlen; } p++; } /* print the unresolved bytes */ if (paddr < addr + nlen) { mdb_printf("\n ... ="); } while (paddr < addr + nlen) { if (mdb_vread(buf, 1, paddr++) == -1) { break; } mdb_printf(" 0x%x", buf[0]); } mdb_printf("\n}\n"); return (DCMD_OK); } /* print the buffer as a struct */ static int print_struct(uintptr_t addr, uint_t nlen, mdb_arg_t *arg) { mdb_ctf_id_t id; if (mdb_ctf_lookup_by_name(arg->a_un.a_str, &id) == 0) { mdb_call_dcmd("print", addr, DCMD_ADDRSPEC, 1, arg); } else { prt_usb_buf(addr, nlen); } return (DCMD_OK); } /* print the buffer as a byte array */ static int prt_usb_buf(uintptr_t addr, uint_t nlen) { int i; uchar_t val; mdb_printf("{\n"); for (i = 0; i < nlen; i++) { if (mdb_vread(&val, 1, addr + i) == -1) { break; } mdb_printf("%02x ", val); } if (nlen) { mdb_printf("\n"); } mdb_printf("}\n"); return (DCMD_OK); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2016 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Prototypes */ /* usba.c */ extern uintptr_t mdb_usba_get_usba_device(uintptr_t); extern uintptr_t mdb_usba_hcdi_get_hcdi(struct dev_info *); /* * Defines */ /* dcmd options */ #define USB_DUMP_VERBOSE 0x01 #define USB_DUMP_ACTIVE_PIPES 0x02 /* Hardcoded slop factor designed into debug buf logic */ #define USB_DEBUG_SIZE_EXTRA_ALLOC 8 /* * Callback arg struct for find_dip (callback func used in usba_device2devinfo). */ typedef struct usba_device2devinfo_data { uintptr_t u2d_target_usb_dev_p; /* one we're looking for */ uintptr_t *u2d_dip_addr; /* Where to store result */ boolean_t u2d_found; /* Match found */ } usba_device2devinfo_cbdata_t; /* * Callback for usba_device2dip. * Callback called from the devinfo_children walk invoked in usba_device2dip. * * For the current dip, get the (potential) pointer to its usba_device_t * struct. * See if this pointer matches the address of the usba_device_t we're looking * for (passed in as usb_dev_p). If so, stuff its value in u2d_dip_addr, * and terminate the walk. * * - dip_addr is the address in core of the dip currently being processed by the * walk * - local_dip is a pointer to a copy of the struct dev_info in local memory * - cb_data is the addr of the callback arg the walker was invoked with * (passed through transparently from walk invoker). * * Returns: * - WALK_NEXT on success (match not found yet) * - WALK_ERR on errors. * - WALK_DONE is returned, cb_data.found is set to TRUE, and * *cb_data.u2d_dip_addr is set to the matched dip addr if a dip corresponding * to the desired usba_device_t* is found. */ /*ARGSUSED*/ static int 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; if ((cur_usb_dev = mdb_usba_get_usba_device(dip_addr)) == 0) { /* * If there's no corresponding usba_device_t, this dip isn't * a usb node. Might be an sd node. Ignore it. */ return (WALK_NEXT); } if (cur_usb_dev == cb_data->u2d_target_usb_dev_p) { *cb_data->u2d_dip_addr = dip_addr; cb_data->u2d_found = TRUE; return (WALK_DONE); } return (WALK_NEXT); } /* * Given a usba_device pointer, figure out which dip is associated with it. * Relies on usba_device.usb_root_hub_dip being accurate. * * - usb_dev_addr is a pointer to a usba_device_t in core. * - dip_addr is the address of a uintptr_t to receive the address in core * of the found dip (if any). * * Returns: * 0 on success (no match found) * 1 on success (match found) * -1 on errors. */ static int usba_device2dip(uintptr_t usb_dev_addr, uintptr_t *dip_addr) { usba_device_t usb_dev; usba_device2devinfo_cbdata_t cb_data; /* * Walk all USB children of the root hub devinfo. * The callback func looks for a match on the usba_device address. */ cb_data.u2d_target_usb_dev_p = usb_dev_addr; cb_data.u2d_dip_addr = dip_addr; cb_data.u2d_found = FALSE; if (mdb_vread(&usb_dev, sizeof (usba_device_t), usb_dev_addr) == -1) { mdb_warn("failed to read usba_device struct"); return (-1); } /* * Walk devinfo children starting with the root hub node, * looking for a match on the usba_device pointer (which is what * find_dip does). * Result is placed in cb_data.dip_addr. */ if (mdb_pwalk("devinfo_children", find_dip, &cb_data, (uintptr_t)usb_dev.usb_root_hub_dip) != 0) { mdb_warn("failed to walk devinfo_children"); return (-1); } if (cb_data.u2d_found == TRUE) { return (1); } return (0); } /* * Generic walker usba_list_entry_t walker. * Works for any usba_list_entry_t list. */ int usba_list_walk_init(mdb_walk_state_t *wsp) { /* Must have a start addr. */ if (wsp->walk_addr == 0) { mdb_warn("not a global walk. Starting address required\n"); return (WALK_ERR); } return (WALK_NEXT); } /* * Generic list walker step routine. * NOTE: multiple walkers share this routine. */ int usba_list_walk_step(mdb_walk_state_t *wsp) { int status; usba_list_entry_t list_entry; if (mdb_vread(&list_entry, sizeof (usba_list_entry_t), (uintptr_t)wsp->walk_addr) == -1) { mdb_warn("failed to read usba_list_entry_t at %p", wsp->walk_addr); return (WALK_ERR); } status = wsp->walk_callback(wsp->walk_addr, &list_entry, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t)list_entry.next; /* Check if we're at the last element */ if (wsp->walk_addr == 0) { return (WALK_DONE); } return (status); } /* * usb_pipe_handle walker * Given a pointer to a usba_device_t, walk the array of endpoint * pipe_handle lists. * For each list, traverse the list, invoking the callback on each element. * * Note this function takes the address of a usba_device struct (which is * easily obtainable), but actually traverses a sub-portion of the struct * (which address is not so easily obtainable). */ int usb_pipe_handle_walk_init(mdb_walk_state_t *wsp) { if (wsp->walk_addr == 0) { mdb_warn("not a global walk; usba_device_t required\n"); return (WALK_ERR); } wsp->walk_data = mdb_alloc((sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS, UM_SLEEP | UM_GC); /* * Read the usb_ph_list array into local memory. * Set start address to first element/endpoint in usb_pipehandle_list */ if (mdb_vread(wsp->walk_data, (sizeof (usba_ph_impl_t)) * USBA_N_ENDPOINTS, (uintptr_t)((size_t)(wsp->walk_addr) + offsetof(usba_device_t, usb_ph_list))) == -1) { mdb_warn("failed to read usb_pipehandle_list at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_arg = 0; return (WALK_NEXT); } int usb_pipe_handle_walk_step(mdb_walk_state_t *wsp) { int status; usba_ph_impl_t *impl_list = (usba_ph_impl_t *)(wsp->walk_data); intptr_t index = (intptr_t)wsp->walk_arg; /* Find the first valid endpoint, starting from where we left off. */ while ((index < USBA_N_ENDPOINTS) && (impl_list[index].usba_ph_data == NULL)) { index++; } /* No more valid endpoints. */ if (index >= USBA_N_ENDPOINTS) { return (WALK_DONE); } status = wsp->walk_callback((uintptr_t)impl_list[index].usba_ph_data, wsp->walk_data, wsp->walk_cbdata); /* Set up to start at next pipe handle next time. */ wsp->walk_arg = (void *)(index + 1); return (status); } /* * Given the address of a usba_pipe_handle_data_t, dump summary info. */ /*ARGSUSED*/ int usb_pipe_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char *dir, *type, *state; usb_ep_descr_t ept_descr; usba_pipe_handle_data_t pipe_handle; usba_ph_impl_t ph_impl; if (!(flags & DCMD_ADDRSPEC)) { return (DCMD_USAGE); } if (mdb_vread(&pipe_handle, sizeof (usba_pipe_handle_data_t), addr) == -1) { mdb_warn("failed to read pipe handle at %p", addr); return (DCMD_ERR); } if (mdb_vread(&ph_impl, sizeof (usba_ph_impl_t), (uintptr_t)pipe_handle.p_ph_impl) == -1) { state = "*******"; } else { switch (ph_impl.usba_ph_state) { case USB_PIPE_STATE_CLOSED: state = "CLOSED "; break; case USB_PIPE_STATE_IDLE: state = "IDLE "; break; case USB_PIPE_STATE_ACTIVE: state = "ACTIVE "; break; case USB_PIPE_STATE_ERROR: state = "ERROR "; break; case USB_PIPE_STATE_CLOSING: state = "CLOSING"; break; default: state = "ILLEGAL"; break; } } bcopy(&pipe_handle.p_ep, &ept_descr, sizeof (usb_ep_descr_t)); if (DCMD_HDRSPEC(flags)) { mdb_printf("\n %%-3s %5s %3s %7s %-?s %-?s %-?s%\n", "EP", "TYPE ", "DIR", "STATE ", "P_HANDLE", "P_POLICY", "EP DESCR"); } dir = ((ept_descr.bEndpointAddress & USB_EP_DIR_MASK) & USB_EP_DIR_IN) ? "In " : "Out"; switch (ept_descr.bmAttributes & USB_EP_ATTR_MASK) { case USB_EP_ATTR_CONTROL: type = "Cntrl"; break; case USB_EP_ATTR_ISOCH: type = "Isoch"; break; case USB_EP_ATTR_BULK: type = "Bulk "; break; case USB_EP_ATTR_INTR: type = "Intr "; break; default: type = "*****"; break; } mdb_printf(" %3d %5s %3s %7s %-?p %-?p %-?p\n", ept_descr.bEndpointAddress & USB_EP_NUM_MASK, type, dir, state, addr, addr + offsetof(usba_pipe_handle_data_t, p_policy), addr + offsetof(usba_pipe_handle_data_t, p_ep)); return (DCMD_OK); } /* * usba_device walker: * * walks the chain of usba_device structs headed by usba_device_list in usba.c * NOTE: It uses the generic list walk step routine usba_list_walk_step. * No walk_fini routine is needed. */ int usba_device_walk_init(mdb_walk_state_t *wsp) { usba_list_entry_t list_entry; if (wsp->walk_addr != 0) { mdb_warn( "global walk only. Must be invoked without an address\n"); return (WALK_ERR); } if (mdb_readvar(&list_entry, "usba_device_list") == -1) { mdb_warn("failed to read usba_device_list"); return (WALK_ERR); } /* List head is not part of usba_device_t, get first usba_device_t */ wsp->walk_addr = (uintptr_t)list_entry.next; 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 * Given the address of a usba_device struct, dump summary info * -v: Print more (verbose) info * -p: Walk/dump all open pipes for this usba_device */ /*ARGSUSED*/ int usba_device(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int status; char pathname[MAXNAMELEN]; char dname[MODMAXNAMELEN] = ""; /* Driver name */ char drv_statep[MODMAXNAMELEN+ 10]; uint_t usb_flag = 0; boolean_t no_driver_attached = FALSE; uintptr_t dip_addr; struct dev_info devinfo; if (!(flags & DCMD_ADDRSPEC)) { /* Global walk */ if (mdb_walk_dcmd("usba_device", "usba_device", argc, argv) == -1) { mdb_warn("failed to walk usba_device"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_getopts(argc, argv, 'p', MDB_OPT_SETBITS, USB_DUMP_ACTIVE_PIPES, &usb_flag, 'v', MDB_OPT_SETBITS, USB_DUMP_VERBOSE, &usb_flag, NULL) != argc) { return (DCMD_USAGE); } if (usb_flag && !(DCMD_HDRSPEC(flags))) { mdb_printf("\n"); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-15s %4s %-?s %-42s%\n", "NAME", "INST", "DIP", "PATH "); } status = usba_device2dip(addr, &dip_addr); /* * -1 = error * 0 = no error, no match * 1 = no error, match */ if (status != 1) { if (status == -1) { mdb_warn("error looking for dip for usba_device %p", addr); } else { mdb_warn("failed to find dip for usba_device %p\n", addr); } mdb_warn("dip and statep unobtainable\n"); return (DCMD_ERR); } /* Figure out what driver (name) is attached to this node. */ (void) mdb_devinfo2driver(dip_addr, (char *)dname, sizeof (dname)); if (mdb_vread(&devinfo, sizeof (struct dev_info), dip_addr) == -1) { mdb_warn("failed to read devinfo"); return (DCMD_ERR); } if (!(DDI_CF2(&devinfo))) { no_driver_attached = TRUE; } (void) mdb_ddi_pathname(dip_addr, pathname, sizeof (pathname)); mdb_printf("%-15s %2d %-?p %s\n", dname, devinfo.devi_instance, dip_addr, pathname); if (usb_flag & USB_DUMP_VERBOSE) { int i; uintptr_t statep = 0; char *string_descr; char **config_cloud, **conf_str_descr; usb_dev_descr_t usb_dev_descr; usba_device_t usba_device_struct; if (mdb_vread(&usba_device_struct, sizeof (usba_device_t), addr) == -1) { mdb_warn("failed to read usba_device struct"); return (DCMD_ERR); } mdb_printf(" usba_device: %-16p\n\n", (usba_device_t *)addr); if (mdb_vread(&usb_dev_descr, sizeof (usb_dev_descr), (uintptr_t)usba_device_struct.usb_dev_descr) == -1) { mdb_warn("failed to read usb_dev_descr_t struct"); return (DCMD_ERR); } mdb_printf("\n idVendor: 0x%04x idProduct: 0x%04x " "usb_addr: 0x%02x\n", usb_dev_descr.idVendor, usb_dev_descr.idProduct, usba_device_struct.usb_addr); /* Get the string descriptor string into local space. */ string_descr = (char *)mdb_alloc(USB_MAXSTRINGLEN, UM_GC); if (usba_device_struct.usb_mfg_str == NULL) { (void) strcpy(string_descr, ""); } else { if (mdb_readstr(string_descr, USB_MAXSTRINGLEN, (uintptr_t)usba_device_struct.usb_mfg_str) == -1) { mdb_warn("failed to read manufacturer " "string descriptor"); (void) strcpy(string_descr, "???"); } } mdb_printf("\n Manufacturer String:\t%s\n", string_descr); if (usba_device_struct.usb_product_str == NULL) { (void) strcpy(string_descr, ""); } else { if (mdb_readstr(string_descr, USB_MAXSTRINGLEN, (uintptr_t)usba_device_struct.usb_product_str) == -1) { mdb_warn("failed to read product string " "descriptor"); (void) strcpy(string_descr, "???"); } } mdb_printf(" Product String:\t\t%s\n", string_descr); if (usba_device_struct.usb_serialno_str == NULL) { (void) strcpy(string_descr, ""); } else { if (mdb_readstr(string_descr, USB_MAXSTRINGLEN, (uintptr_t)usba_device_struct.usb_serialno_str) == -1) { mdb_warn("failed to read serial number string " "descriptor"); (void) strcpy(string_descr, "???"); } } mdb_printf(" SerialNumber String:\t%s\n", string_descr); if (no_driver_attached) { mdb_printf("\n"); } else { mdb_printf(" state_p: "); /* * Given the dip, find the associated statep. The * convention to generate this soft state anchor is: * _statep */ (void) mdb_snprintf(drv_statep, sizeof (drv_statep), "%s_statep", dname); if (mdb_devinfo2statep(dip_addr, drv_statep, &statep) == -1) { mdb_warn("failed to find %s state struct for " "dip %p", drv_statep, dip_addr); return (DCMD_ERR); } mdb_printf("%-?p\n", statep); } config_cloud = (char **)mdb_alloc(sizeof (void *) * usba_device_struct.usb_n_cfgs, UM_GC); conf_str_descr = (char **)mdb_alloc(sizeof (void *) * usba_device_struct.usb_n_cfgs, UM_GC); if ((usba_device_struct.usb_cfg_array) && (usba_device_struct.usb_cfg_str_descr)) { if ((mdb_vread(config_cloud, sizeof (void *) * usba_device_struct.usb_n_cfgs, (uintptr_t)usba_device_struct.usb_cfg_array) == -1) || (mdb_vread(conf_str_descr, sizeof (void *) * usba_device_struct.usb_n_cfgs, (uintptr_t) usba_device_struct.usb_cfg_str_descr)) == -1) { mdb_warn("failed to read config cloud " "pointers"); } else { mdb_printf("\n Device Config Clouds:\n" " Index\tConfig\t\tConfiguration " "String\n" " -----\t------\t\t" "--------------------\n"); for (i = 0; i < usba_device_struct.usb_n_cfgs; i++) { if (mdb_readstr(string_descr, USB_MAXSTRINGLEN, (uintptr_t)conf_str_descr[i]) == -1) { (void) strcpy(string_descr, ""); } mdb_printf(" %4d\t0x%p\t%s\n", i, config_cloud[i], string_descr); } } } mdb_printf("\n Active configuration index: %d\n", usba_device_struct.usb_active_cfg_ndx); } if (usb_flag & USB_DUMP_ACTIVE_PIPES) { if (mdb_pwalk_dcmd("usb_pipe_handle", "usb_pipe_handle", 0, NULL, addr) == -1) { mdb_warn("failed to walk usb_pipe_handle"); return (DCMD_ERR); } } return (DCMD_OK); } /* * Dump the contents of the usba_debug_buf, from the oldest to newest, * wrapping around if necessary. */ /*ARGSUSED*/ int usba_debug_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char *debug_buf_addr; /* addr in core */ char *local_debug_buf; /* local copy of buf */ int debug_buf_size; char *term_p; int being_cleared; if (flags & DCMD_ADDRSPEC) { return (DCMD_USAGE); } if (mdb_readvar(&being_cleared, "usba_clear_debug_buf_flag") == -1) { mdb_warn("failed to read usba_clear_debug_buf_flag"); return (DCMD_ERR); } if (being_cleared) { return (DCMD_OK); } if (mdb_readvar(&debug_buf_addr, "usba_debug_buf") == -1) { mdb_warn("failed to read usba_debug_buf"); return (DCMD_ERR); } if (debug_buf_addr == NULL) { mdb_warn("usba_debug_buf not allocated\n"); return (DCMD_OK); } if (mdb_readvar(&debug_buf_size, "usba_debug_buf_size") == -1) { mdb_warn("failed to read usba_debug_buf_size"); return (DCMD_ERR); } debug_buf_size += USB_DEBUG_SIZE_EXTRA_ALLOC; local_debug_buf = (char *)mdb_alloc(debug_buf_size, UM_SLEEP | UM_GC); if ((mdb_vread(local_debug_buf, debug_buf_size, (uintptr_t)debug_buf_addr)) == -1) { mdb_warn("failed to read usba_debug_buf at %p", local_debug_buf); return (DCMD_ERR); } local_debug_buf[debug_buf_size - 1] = '\0'; if (strlen(local_debug_buf) == 0) { return (DCMD_OK); } if ((term_p = strstr(local_debug_buf, ">>>>")) == NULL) { mdb_warn("failed to find terminator \">>>>\"\n"); return (DCMD_ERR); } /* * Print the chunk of buffer from the terminator to the end. * This will print a null string if no wrap has occurred yet. */ mdb_printf("%s", term_p+5); /* after >>>>\0 to end of buf */ mdb_printf("%s\n", local_debug_buf); /* beg of buf to >>>>\0 */ return (DCMD_OK); } /*ARGSUSED*/ int usba_clear_debug_buf( uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int clear = 1; /* stop the tracing */ if (mdb_writevar((void*)&clear, "usba_clear_debug_buf_flag") == -1) { mdb_warn("failed to set usba_clear_debug_buf_flag"); return (DCMD_ERR); } return (DCMD_OK); } /* prtusb entries */ extern int prtusb(uintptr_t, uint_t, int, const mdb_arg_t *); extern void prt_usb_usage(void); /* * MDB module linkage information: * * We declare a list of structures describing our dcmds, and a function * named _mdb_init to return a pointer to our module information. */ static const mdb_dcmd_t dcmds[] = { { "usb_pipe_handle", ":", "print a usb_pipe_handle struct", usb_pipe_handle, NULL}, { "usba_device", ": [-pv]", "print summary info for a usba_device_t struct", usba_device, NULL}, { "usba_debug_buf", NULL, "print usba_debug_buf", usba_debug_buf, NULL}, { "usba_clear_debug_buf", NULL, "clear usba_debug_buf", usba_clear_debug_buf, NULL}, { "prtusb", "?[-t] [-v] [-i index]", "print trees and descriptors for usba_device_t", prtusb, prt_usb_usage}, { NULL } }; static const mdb_walker_t walkers[] = { /* Generic list walker. */ { "usba_list_entry", "walk list of usba_list_entry_t structures", usba_list_walk_init, usba_list_walk_step, NULL, NULL }, { "usb_pipe_handle", "walk USB pipe handles, given a usba_device_t ptr", 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 } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Routines extracted from usr/src/uts/common/io/usb/usba/usba.c. */ #include #include #include #include #include #include #include /* * check whether this dip is the root hub. * dip_addr is address of the devinfo struct in the core image (not local space) */ static int mdb_usba_is_root_hub(struct dev_info *dip) { uintptr_t p = (uintptr_t)dip->devi_hw_prop_ptr; while (p != 0) { ddi_prop_t prop; char prop_name[128]; if (mdb_vread(&prop, sizeof (prop), p) == -1) { mdb_warn("failed to read property"); break; } if (mdb_readstr(prop_name, sizeof (prop_name), (uintptr_t)prop.prop_name) == -1) { mdb_warn("failed to read property name"); } if (strcmp(prop_name, "root-hub") == 0) { return (1); } p = (uintptr_t)prop.prop_next; } return (0); } /* * retrieve hcdi structure from the dip * * dip_addr is address of the devinfo struct in the core image (not local space) */ uintptr_t mdb_usba_hcdi_get_hcdi(struct dev_info *dip) { return ((uintptr_t)dip->devi_driver_data); } /* * get usba_device pointer in the devi * * dip_addr is address of the devinfo struct in the core image (not local space) */ uintptr_t mdb_usba_get_usba_device(uintptr_t dip_addr) { struct dev_info devinfo; if (mdb_vread(&devinfo, sizeof (struct dev_info), dip_addr) == -1) { mdb_warn("failed to read dev_info at %p", dip_addr); return (0); } /* * we cannot use parent_data in the usb node because its * bus parent (eg. PCI nexus driver) uses this data * * we cannot use driver data in the other usb nodes since * usb drivers may need to use this */ if (mdb_usba_is_root_hub(&devinfo)) { usba_hcdi_t hcdi_struct; uintptr_t hcdi_addr = mdb_usba_hcdi_get_hcdi(&devinfo); if (!hcdi_addr) { return (0); } /* Read hcdi struct into local address space. */ if (mdb_vread(&hcdi_struct, sizeof (usba_hcdi_t), hcdi_addr) == -1) { mdb_warn("failed to read hcdi struct"); return (0); } return ((uintptr_t)hcdi_struct.hcdi_usba_device); } else { struct dev_info devinfo; if (mdb_vread(&devinfo, sizeof (struct dev_info), dip_addr) == -1) { mdb_warn("failed to read dev_info at %p", dip_addr); return (0); } /* casts needed to keep lint happy */ return ((uintptr_t)devinfo.devi_parent_data); } }