#include "opt_thunderbolt.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
#include <sys/gsb_crc32.h>
#include <sys/endian.h>
#include <machine/bus.h>
#include <machine/stdarg.h>
#include <dev/thunderbolt/nhi_reg.h>
#include <dev/thunderbolt/nhi_var.h>
#include <dev/thunderbolt/tb_reg.h>
#include <dev/thunderbolt/tb_var.h>
#include <dev/thunderbolt/tbcfg_reg.h>
#include <dev/thunderbolt/tb_debug.h>
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 ("<null>");
while (table->value != NULL) {
if (table->key == key)
return (table->value);
table++;
}
return ("<unknown>");
}
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) {
flags |= strtol(token, &endtoken, 0);
if (token != endtoken)
continue;
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
}