#!/bin/bash # global variables: # g_cap_len capacity register length # g_dboff Doorbell offset # g_max_ports Number of max ports # g_max_slots Number of max slots # g_num_intrs Number of interrupts # g_op_base Base offset of Opertational registers # g_rtsoff Runtime Space offset # g_xbase xHCI base address # g_xecp xHCI extended capabilities pointer # g_xver xHCI version ### define register offset HCSPARAM1=0x04 HCSPARAM2=0x08 HCSPARAM3=0x0C HCCPARAM=0x10 DBOFF=0x14 RTSOFF=0x18 debug() { [ "$V" = "1" ] && echo "DEBUG: $@" || true; } debugf() { [ "$V" = "1" ] && printf "DEBUG: $@" || true; } echof() { printf "$@" || true; } # # $1: register offset # xhci_readw() { devmem2 $((g_xbase + $1)) | tail -1 | cut -d: -f2; } # # get bitfield value in decimal # $1: input value to read bitfield from # $2: inclusive LSB of the bitfield # $3: inclusive SMB of the bitfield # get_bits_value() { local _val=$1 local _start=$2 local _end=$3 local _ones=$((_end - _start + 1)) local _mask _mask=$(((1 << _ones) - 1)) _mask=$((_mask << _start)) echo $(((_val & _mask) >> _start)) } # # get bitfield value in hexdecimal # $1: input value to read bitfield from # $2: inclusive LSB of the bitfield # $3: inclusive SMB of the bitfield # get_bits_hex_value() { printf "0x%x" $(get_bit_value $1 $2 $3); } check_system() { # TODO check devmem2 # TODO check xhci/dwc3 controller debug "check system ok" } get_xhci_baseaddress() { local _str ### TODO handling instance index _str=$(find /sys/bus/platform/devices/ -iname 'xhci-hcd*' | head -1) ### TODO error handling _str=$(readlink $_str) _str=${_str%/*} # cut off the trail filename _str=${_str##*/} # cut off leading dir names g_xbase=0x${_str%.*} # cut off '.usb' to get base address debug "xHCI Base Address: $g_xbase" } # parset xHCI extended capabilities, xHCI Spec CH7 parse_xECP() { local _offset local _next=0 [[ $g_xecp -ne 0 ]] || { echo "Error: g_xecp is 0."; return; } _offset=$((g_xecp << 2)) while true; do _offset=$((_offset + (_next << 2))) _reg=$(xhci_readw $_offset) _next=$(get_bits_value $_reg 8 15) echof "xECP(0x%04x) -> [0x%08x]: \n" $_offset $_reg [[ $_next -ne 0 ]] || break; done } query_cap_registers() { local _reg local _var # parse CAPLENGTH _reg=$(xhci_readw 0) debugf "CAPLENGTH: 0x%08x\n" $_reg g_cap_len=$(get_bits_value $_reg 0 7) g_op_base=$g_cap_len _var=$(get_bits_value $_reg 16 23) g_xver=$(get_bits_value $_reg 24 31).${_var} # parse HCSPARAM1 _reg=$(xhci_readw $HCSPARAM1) debugf "HCSPARAM1: 0x%08x\n" $_reg g_max_slots=$(get_bits_value $_reg 0 7) g_num_intrs=$(get_bits_value $_reg 8 18) g_max_ports=$(get_bits_value $_reg 24 31) # parse HCSPARAM2 _reg=$(xhci_readw $HCSPARAM2) debugf "HCSPARAM2: 0x%08x\n" $_reg # TODO parse HCSPARAM2 # parse HCSPARAM3 _reg=$(xhci_readw $HCSPARAM3) debugf "HCSPARAM3: 0x%08x\n" $_reg # TODO parse HCSPARAM3 g_dboff=$(xhci_readw $DBOFF) g_rtsoff=$(xhci_readw $RTSOFF) echo -n "xHCI version: $g_xver, max slots: $g_max_slots, " echo "num of interrupters $g_num_intrs, max ports: $g_max_ports" echof "Operational base: 0x%08x, " $g_op_base echo "DoorBell offset: $g_dboff, Runtime Space offset: $g_rtsoff" # parse HCCPARAM, xHCI Spec 5.3.6 _reg=$(xhci_readw $HCCPARAM) echof "HCCPARAM [0x%08x]: " $_reg # [b.31-16] xHCI Extended Capabilities Pointer g_xecp=$(get_bits_value $_reg 16 31) echof "xECP=0x%04x " $g_xecp # [b.15-12] Max Primary Stream Array Size _var=$(get_bits_value $_reg 12 15) echo -n "MaxPSASize="$_var" " # [b.7] No Secondary SID Support _var=$(get_bits_value $_reg 7 7) [[ $_var -eq 1 ]] && echo -n "| NSS " || echo -n "| nss " # [b.6] Latency Tolerance Messaging Capability _var=$(get_bits_value $_reg 6 6) [[ $_var -eq 1 ]] && echo -n "LTC " || echo -n "ltc " # [b.5] Light HC Reset Capability _var=$(get_bits_value $_reg 5 5) [[ $_var -eq 1 ]] && echo -n "LHRC " || echo -n "lhrc " # [b.4] Port Indicator _var=$(get_bits_value $_reg 4 4) [[ $_var -eq 1 ]] && echo -n "PIND " || echo -n "pind " # [b.3] Port Power Control _var=$(get_bits_value $_reg 3 3) [[ $_var -eq 1 ]] && echo -n "PPC " || echo -n "ppc " # [b.2] Context Size _var=$(get_bits_value $_reg 2 2) [[ $_var -eq 1 ]] && echo -n "CSZ64 " || echo -n "csz32 " # [b.1] BW negotiation capability _var=$(get_bits_value $_reg 1 1) [[ $_var -eq 1 ]] && echo -n "BNC " || echo -n "bnc " # [b.0] 64-bit addressing capability _var=$(get_bits_value $_reg 0 0) [[ $_var -eq 1 ]] && echo "AC64" || echo "ac64" parse_xECP } # query USBCMD register query_op_usbcmd() { local _reg local _var _reg=$(xhci_readw $((g_op_base + 0))) echof "USBCMD [0x%08x]: " $_reg # [b.11] Enable U3 MFINDEX Stop _var=$(get_bits_value $_reg 11 11) [[ $_var -eq 1 ]] && echo -n "EU3S " || true # [b.10 ] Enable Wrap Event _var=$(get_bits_value $_reg 10 10) [[ $_var -eq 1 ]] && echo -n "EWE " || true # [b.9 ] Controller Restore State _var=$(get_bits_value $_reg 9 9) [[ $_var -eq 1 ]] && echo -n "CRS " || true # [b.8 ] Controller Save State _var=$(get_bits_value $_reg 8 8) [[ $_var -eq 1 ]] && echo -n "CSS " || true # [b.7 ] Light Host Controller Reset _var=$(get_bits_value $_reg 7 7) [[ $_var -eq 1 ]] && echo -n "LHCRST " || true # [b.3 ] Host System Error Enable _var=$(get_bits_value $_reg 3 3) [[ $_var -eq 1 ]] && echo -n "HSEE " || true # [b.2 ] Interrupter Enable _var=$(get_bits_value $_reg 2 2) [[ $_var -eq 1 ]] && echo -n "INTE " || true # [b.1 ] Host Controller Reset _var=$(get_bits_value $_reg 1 1) [[ $_var -eq 1 ]] && echo -n "HCRST " || true # [b.0] Run/Stop _var=$(get_bits_value $_reg 0 0) [[ $_var -eq 1 ]] && echo "Run" || echo "Stop" } # query USBSTS register query_op_usbsts() { local _reg local _var _reg=$(xhci_readw $((g_op_base + 4))) echof "USBSTS [0x%08x]: " $_reg # [b.12] Host Controller Error _var=$(get_bits_value $_reg 12 12) [[ $_var -eq 1 ]] && echo -n "HCE " || true # [b.11] Controller Not Ready _var=$(get_bits_value $_reg 11 11) [[ $_var -eq 1 ]] && echo -n "CNR " || true # [b.10 ] Save/Restore Error _var=$(get_bits_value $_reg 10 10) [[ $_var -eq 1 ]] && echo -n "SRE " || true # [b.9 ] Restore State Status _var=$(get_bits_value $_reg 9 9) [[ $_var -eq 1 ]] && echo -n "RSS " || true # [b.8 ] Save State Status _var=$(get_bits_value $_reg 8 8) [[ $_var -eq 1 ]] && echo -n "SSS " || true # [b.4 ] Port Change Detect _var=$(get_bits_value $_reg 4 4) [[ $_var -eq 1 ]] && echo -n "PCD " || true # [b.3 ] Event Interrupt _var=$(get_bits_value $_reg 3 3) [[ $_var -eq 1 ]] && echo -n "EINT " || true # [b.2 ] Host System Error _var=$(get_bits_value $_reg 2 2) [[ $_var -eq 1 ]] && echo -n "HSE " || true # [b.0] HC Halted _var=$(get_bits_value $_reg 0 0) [[ $_var -eq 1 ]] && echo "HCH" || echo } # query PAGESIZE register query_op_pagesize() { local _reg local _var _reg=$(xhci_readw $((g_op_base + 8))) # [b.15:0] PageSize: 2^(n+12), if bit n is set _var=$(get_bits_value $_reg 0 15) echof "PAGESIZE [0x%08x]\n" $_var } # query CONFIG register query_op_config() { local _reg local _var _reg=$(xhci_readw $((g_op_base + 0x38))) # [b.7:0] Max device slots enabled _var=$(get_bits_value $_reg 0 7) echof "CONFIG [0x%08x]: MaxSlotsEn:%d\n" $_reg $_var } # query PORTSC register query_op_portsc() { local _reg local _var local _idx=1 while [[ $_idx -le $g_max_ports ]]; do _reg=$(xhci_readw $((g_op_base + 0x400 + 0x10 * (_idx -1)))) echof "PORTSC[%d] [0x%08x]: " $_idx $_reg : $((_idx += 1)) # parse portsc # [b.30] Device Removable _var=$(get_bits_value $_reg 30 30) [[ $_var -eq 1 ]] && echo -n "DR " || echo -n "dr " # [b.27] Wake on Over-current Enable _var=$(get_bits_value $_reg 27 27) [[ $_var -eq 1 ]] && echo -n "WOE " || echo -n "woe " # [b.26] Wake on Disconnect Enable _var=$(get_bits_value $_reg 26 26) [[ $_var -eq 1 ]] && echo -n "WDE " || echo -n "wde " # [b.25] Wake on Connect Enable _var=$(get_bits_value $_reg 25 25) [[ $_var -eq 1 ]] && echo -n "WCE " || echo -n "wce " # [b.24] Cold Attach Status _var=$(get_bits_value $_reg 24 24) [[ $_var -eq 1 ]] && echo -n "CAS | " || echo -n "cas | " # [b.23] Port Config Error Change _var=$(get_bits_value $_reg 23 23) [[ $_var -eq 1 ]] && echo -n "CEC " || echo -n "cec " # [b.22] Port Link State Change _var=$(get_bits_value $_reg 22 22) [[ $_var -eq 1 ]] && echo -n "PLC " || echo -n "plc " # [b.21] Port Reset Change _var=$(get_bits_value $_reg 21 21) [[ $_var -eq 1 ]] && echo -n "PRC " || echo -n "prc " # [b.20] Over-current Change _var=$(get_bits_value $_reg 20 20) [[ $_var -eq 1 ]] && echo -n "OCC " || echo -n "occ " # [b.19] Warm Port Reset Change _var=$(get_bits_value $_reg 19 19) [[ $_var -eq 1 ]] && echo -n "WRC " || echo -n "wrc " # [b.18] Port Enabled/Disabled Change _var=$(get_bits_value $_reg 18 18) [[ $_var -eq 1 ]] && echo -n "PEC " || echo -n "pec " # [b.17] Connect Status Change _var=$(get_bits_value $_reg 17 17) [[ $_var -eq 1 ]] && echo -n "CSC " || echo -n "csc " # [b.16] Port Link State Write Strobe _var=$(get_bits_value $_reg 16 16) [[ $_var -eq 1 ]] && echo -n "LWS | " || echo -n "lws | " # [b.15:14] Port Indicator Control _var=$(get_bits_value $_reg 14 15) case $_var in 0) echo -n "PIC:off ";; 1) echo -n "PIC:amber ";; 2) echo -n "PIC:green ";; 3) echo -n "PIC:undefined ";; esac # [b.13:10] Port Speed _var=$(get_bits_value $_reg 10 13) echof "PSI:%d " $_var # [b.9] Port Power _var=$(get_bits_value $_reg 9 9) [[ $_var -eq 1 ]] && echo -n "PP | " || echo -n "pp | " # [b.8:5] Port Link State _var=$(get_bits_value $_reg 5 8) case $_var in [0-3]) echo -n "PLS:U${_var} ";; 4) echo -n "PLS:Disabled ";; 5) echo -n "PLS:RxDetect ";; 6) echo -n "PLS:Inactive ";; 7) echo -n "PLS:Polling ";; 8) echo -n "PLS:Recovery ";; 9) echo -n "PLS:Hot_Reset ";; 10) echo -n "PLS:Compliance_mode ";; [12-14]) echo -n "PLS:rsvd ";; 15) echo -n "PLS:Resume ";; esac # [b.4] Port Reset _var=$(get_bits_value $_reg 4 4) [[ $_var -eq 1 ]] && echo -n "PR " || echo -n "pr " # [b.3] Over-current Active _var=$(get_bits_value $_reg 3 3) [[ $_var -eq 1 ]] && echo -n "OCA " || echo -n "oca " # [b.1] Port Enabled/Disabled _var=$(get_bits_value $_reg 1 1) [[ $_var -eq 1 ]] && echo -n "PED " || echo -n "ped " # [b.0] Current Connect Status _var=$(get_bits_value $_reg 0 0) [[ $_var -eq 1 ]] && echo "CCS " || echo "ccs " done } # query PORTPMSC register query_op_portpmsc() { local _reg local _var local _idx=1 while [[ $_idx -le $g_max_ports ]]; do _reg=$(xhci_readw $((g_op_base + 0x404 + 0x10 * (_idx -1)))) echof "PORTPMSC[%d] [0x%08x]: " $_idx $_reg [[ $_reg -eq 0 ]] || { echo } echo : $((_idx += 1)) done } ### main ### check_system get_xhci_baseaddress query_cap_registers query_op_usbcmd query_op_usbsts query_op_pagesize query_op_config query_op_portsc query_op_portpmsc