/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Scott Long * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_thunderbolt.h" /* PCIe bridge for Thunderbolt */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include tb_string_t nhi_outmailcmd_opmode[] = { { 0x000, "Safe Mode" }, { 0x100, "Authentication Mode" }, { 0x200, "Endpoint Mode" }, { 0x300, "Connection Manager Fully Functional" }, { 0, NULL } }; tb_string_t nhi_frame_pdf[] = { { 0x01, "PDF_READ" }, { 0x02, "PDF_WRITE" }, { 0x03, "PDF_NOTIFY" }, { 0x04, "PDF_NOTIFY_ACK" }, { 0x05, "PDF_HOTPLUG" }, { 0x06, "PDF_XDOMAIN_REQ" }, { 0x07, "PDF_XDOMAIN_RESP" }, { 0x0a, "PDF_CM_EVENT" }, { 0x0b, "PDF_CM_REQ" }, { 0x0c, "PDF_CM_RESP" }, { 0, NULL } }; tb_string_t tb_security_level[] = { { TBSEC_NONE, "None" }, { TBSEC_USER, "User" }, { TBSEC_SECURE, "Secure Authorization" }, { TBSEC_DP, "Display Port" }, { TBSEC_UNKNOWN,"Unknown" }, { 0, NULL } }; tb_string_t tb_mbox_connmode[] = { { INMAILCMD_SETMODE_CERT_TB_1ST_DEPTH, "Certified/1st" }, { INMAILCMD_SETMODE_ANY_TB_1ST_DEPTH, "Any/1st" }, { INMAILCMD_SETMODE_CERT_TB_ANY_DEPTH, "Certified/Any" }, { INMAILCMD_SETMODE_ANY_TB_ANY_DEPTH, "Any/Any" }, { 0, NULL } }; tb_string_t tb_device_power[] = { { 0x0, "Self-powered" }, { 0x1, "Normal power" }, { 0x2, "High power" }, { 0x3, "Unknown power draw" }, { 0, NULL } }; tb_string_t tb_notify_code[] = { { 0x03, "DEVCONN" }, { 0x04, "DISCONN" }, { 0x05, "DPCONN" }, { 0x06, "DOMCONN" }, { 0x07, "DOMDISCONN" }, { 0x08, "DPCHANGE" }, { 0x09, "I2C" }, { 0x0a, "RTD3" }, { 0, NULL } }; tb_string_t tb_adapter_type[] = { { ADP_CS2_UNSUPPORTED, "Unsupported Adapter" }, { ADP_CS2_LANE, "Lane Adapter" }, { ADP_CS2_HOSTIF, "Host Interface Adapter" }, { ADP_CS2_PCIE_DFP, "Downstream PCIe Adapter" }, { ADP_CS2_PCIE_UFP, "Upstream PCIe Adapter" }, { ADP_CS2_DP_OUT, "DP OUT Adapter" }, { ADP_CS2_DP_IN, "DP IN Adapter" }, { ADP_CS2_USB3_DFP, "Downstream USB3 Adapter" }, { ADP_CS2_USB3_UFP, "Upstream USB3 Adapter" }, { 0, NULL } }; tb_string_t tb_adapter_state[] = { { CAP_LANE_STATE_DISABLE, "Disabled" }, { CAP_LANE_STATE_TRAINING, "Training" }, { CAP_LANE_STATE_CL0, "CL0" }, { CAP_LANE_STATE_TXCL0, "TX CL0s" }, { CAP_LANE_STATE_RXCL0, "RX CL0s" }, { CAP_LANE_STATE_CL1, "CL1" }, { CAP_LANE_STATE_CL2, "CL2" }, { CAP_LANE_STATE_CLD, "CLd" }, { 0, NULL } }; tb_string_t tb_notify_event[] = { { TB_CFG_ERR_CONN, "Connection error" }, { TB_CFG_ERR_LINK, "Link error" }, { TB_CFG_ERR_ADDR, "Addressing error" }, { TB_CFG_ERR_ADP, "Invalid adapter" }, { TB_CFG_ERR_ENUM, "Enumeration error" }, { TB_CFG_ERR_NUA, "Adapter not enumerated" }, { TB_CFG_ERR_LEN, "Invalid request length" }, { TB_CFG_ERR_HEC, "Invalid packet header" }, { TB_CFG_ERR_FC, "Flow control error" }, { TB_CFG_ERR_PLUG, "Hot plug error" }, { TB_CFG_ERR_LOCK, "Adapter locked" }, { TB_CFG_HP_ACK, "Hotplug acknowledgement" }, { TB_CFG_DP_BW, "Display port bandwidth change" }, { 0, NULL } }; const char * tb_get_string(uintmax_t key, tb_string_t *table) { if (table == NULL) return (""); while (table->value != NULL) { if (table->key == key) return (table->value); table++; } return (""); } static struct tb_debug_string { char *name; int flag; } tb_debug_strings[] = { {"info", DBG_INFO}, {"init", DBG_INIT}, {"info", DBG_INFO}, {"rxq", DBG_RXQ}, {"txq", DBG_TXQ}, {"intr", DBG_INTR}, {"tb", DBG_TB}, {"mbox", DBG_MBOX}, {"bridge", DBG_BRIDGE}, {"cfg", DBG_CFG}, {"router", DBG_ROUTER}, {"port", DBG_PORT}, {"hcm", DBG_HCM}, {"extra", DBG_EXTRA}, {"noisy", DBG_NOISY}, {"full", DBG_FULL} }; enum tb_debug_level_combiner { COMB_NONE, COMB_ADD, COMB_SUB }; int tb_debug_sysctl(SYSCTL_HANDLER_ARGS) { struct sbuf *sbuf; #if defined (THUNDERBOLT_DEBUG) && (THUNDERBOLT_DEBUG > 0) struct tb_debug_string *string; char *buffer; size_t sz; u_int *debug; int i, len; #endif int error; error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); #if defined (THUNDERBOLT_DEBUG) && (THUNDERBOLT_DEBUG > 0) debug = (u_int *)arg1; sbuf_printf(sbuf, "%#x", *debug); sz = sizeof(tb_debug_strings) / sizeof(tb_debug_strings[0]); for (i = 0; i < sz; i++) { string = &tb_debug_strings[i]; if (*debug & string->flag) sbuf_printf(sbuf, ",%s", string->name); } error = sbuf_finish(sbuf); sbuf_delete(sbuf); if (error || req->newptr == NULL) return (error); len = req->newlen - req->newidx; if (len == 0) return (0); buffer = malloc(len, M_THUNDERBOLT, M_ZERO|M_WAITOK); error = SYSCTL_IN(req, buffer, len); tb_parse_debug(debug, buffer); free(buffer, M_THUNDERBOLT); #else sbuf_printf(sbuf, "debugging unavailable"); error = sbuf_finish(sbuf); sbuf_delete(sbuf); #endif return (error); } void tb_parse_debug(u_int *debug, char *list) { struct tb_debug_string *string; enum tb_debug_level_combiner op; char *token, *endtoken; size_t sz; int flags, i; if (list == NULL || *list == '\0') return; if (*list == '+') { op = COMB_ADD; list++; } else if (*list == '-') { op = COMB_SUB; list++; } else op = COMB_NONE; if (*list == '\0') return; flags = 0; sz = sizeof(tb_debug_strings) / sizeof(tb_debug_strings[0]); while ((token = strsep(&list, ":,")) != NULL) { /* Handle integer flags */ flags |= strtol(token, &endtoken, 0); if (token != endtoken) continue; /* Handle text flags */ for (i = 0; i < sz; i++) { string = &tb_debug_strings[i]; if (strcasecmp(token, string->name) == 0) { flags |= string->flag; break; } } } switch (op) { case COMB_NONE: *debug = flags; break; case COMB_ADD: *debug |= flags; break; case COMB_SUB: *debug &= (~flags); break; } return; } void tbdbg_dprintf(device_t dev, u_int debug, u_int val, const char *fmt, ...) { #if defined(THUNDERBOLT_DEBUG) && (THUNDERBOLT_DEBUG > 0) va_list ap; u_int lvl, dbg; lvl = debug & 0xc0000000; dbg = debug & 0x3fffffff; va_start(ap, fmt); if ((lvl >= (val & 0xc0000000)) && ((dbg & (val & 0x3fffffff)) != 0)) { device_printf(dev, ""); vprintf(fmt, ap); } va_end(ap); #endif }