1// SPDX-License-Identifier: GPL-2.0-only 2package main 3 4import ( 5 "bufio" 6 "flag" 7 "fmt" 8 "log" 9 "os" 10 "path/filepath" 11 "regexp" 12 "review.coreboot.org/coreboot.git/util/hda-decoder/decoder" 13 "strconv" 14 "strings" 15) 16 17var indentLevel int = 0 18 19func indentedPrintf(format string, args ...interface{}) (n int, err error) { 20 s := fmt.Sprintf("%s%s", strings.Repeat("\t", indentLevel), format) 21 return fmt.Printf(s, args...) 22} 23 24func stringToUint32(s string) uint32 { 25 s = strings.Replace(s, "0x", "", -1) 26 v, err := strconv.ParseUint(s, 16, 32) 27 if err != nil { 28 log.Fatal(err) 29 } 30 return uint32(v) 31} 32 33func decodeConfig(config uint32) { 34 out := decoder.ToHumanReadable(decoder.Decode(config)) 35 36 indentedPrintf("%s,\n", out.PortConnectivity) 37 indentedPrintf("%s,\n", out.Location) 38 indentedPrintf("%s,\n", out.DefaultDevice) 39 indentedPrintf("%s,\n", out.ConnectionType) 40 indentedPrintf("%s,\n", out.Color) 41 indentedPrintf("%s,\n", out.Misc) 42 indentedPrintf("%s, %s\n", out.DefaultAssociation, out.Sequence) 43} 44 45func printDisconnectedPort(config uint32) { 46 // The value 0x411111f0 is not defined in the specification, but is a 47 // common value vendors use to indicate "not connected". 48 const nc uint32 = 0x411111f0 49 50 // Setting some values (e.g. 0x40000000) as `AZALIA_PIN_CFG_NC(0)` is 51 // probably harmless. However, we will stay on the safe side for now. 52 if (config & 0xfffffff0) != nc { 53 // Do not decode these values, as they would likely describe a 54 // bogus device which could be slighly confusing. 55 fmt.Printf("0x%08x), // does not describe a jack or internal device\n", config) 56 } else { 57 fmt.Printf("AZALIA_PIN_CFG_NC(%d)),\n", (config & 0x0000000f)) 58 } 59} 60 61func decodeFile(path string, codec uint32) { 62 file, err := os.Open(path) 63 if err != nil { 64 log.Fatal(err) 65 } 66 defer file.Close() 67 68 scanner := bufio.NewScanner(file) 69 70 for scanner.Scan() { 71 fields := strings.Fields(scanner.Text()) 72 pin := stringToUint32(fields[0]) 73 config := stringToUint32(fields[1]) 74 75 indentedPrintf("AZALIA_PIN_CFG(%d, 0x%02x, ", codec, pin) 76 if decoder.PortIsConnected(config) { 77 fmt.Printf("AZALIA_PIN_DESC(\n") 78 indentLevel += 1 79 decodeConfig(config) 80 indentLevel -= 1 81 indentedPrintf(")),\n") 82 } else { 83 printDisconnectedPort(config) 84 } 85 } 86} 87 88func getFileContents(path string) string { 89 contents, err := os.ReadFile(path) 90 if err != nil { 91 log.Fatal(err) 92 } 93 return strings.TrimSpace(string(contents)) 94} 95 96func getLineCount(path string) int { 97 return len(strings.Split(getFileContents(path), "\n")) 98} 99 100func decodeDeviceCodec(path string, codec uint32, isLastCodec bool, generate bool) { 101 if generate { 102 vendorId := getFileContents(path + "/vendor_id") 103 vendorName := getFileContents(path + "/vendor_name") 104 chipName := getFileContents(path + "/chip_name") 105 subsystemId := getFileContents(path + "/subsystem_id") 106 lineCount := getLineCount(path + "/init_pin_configs") 107 108 indentedPrintf("%s, // Vendor/Device ID: %s %s\n", vendorId, vendorName, chipName) 109 indentedPrintf("%s, // Subsystem ID\n", subsystemId) 110 indentedPrintf("%d,\n", lineCount+1) 111 indentedPrintf("AZALIA_SUBVENDOR(%d, %s),\n\n", codec, subsystemId) 112 } 113 114 decodeFile(path+"/init_pin_configs", codec) 115 if !isLastCodec { 116 fmt.Printf("\n") 117 } 118} 119 120func decodeDeviceCodecs(generate bool) { 121 matches, err := filepath.Glob("/sys/class/sound/hwC0D*") 122 if err != nil { 123 log.Fatal(err) 124 } 125 re := regexp.MustCompile(`D([0-9]+)$`) 126 127 for i, match := range matches { 128 codec := stringToUint32(re.FindStringSubmatch(match)[1]) 129 isLastCodec := (i + 1) == len(matches) 130 131 decodeDeviceCodec(match, codec, isLastCodec, generate) 132 } 133} 134 135func isFlagPassed(name string) bool { 136 found := false 137 138 flag.Visit(func(f *flag.Flag) { 139 if f.Name == name { 140 found = true 141 } 142 }) 143 return found 144} 145 146func main() { 147 codec := flag.Uint64("codec", 0, "Set the codec number when decoding a file\n"+ 148 "This flag is only meaningful in combination with the 'file' flag") 149 config := flag.Uint64("config", 0, "Decode a single configuration") 150 file := flag.String("file", "", "Decode configurations in a file\n"+ 151 "The decoder assumes each line in the file has the format: <pin> <config>") 152 generate := flag.Bool("generate", false, "Automatically generate hda_verb.c for the host device") 153 flag.Parse() 154 155 if isFlagPassed("config") { 156 decodeConfig(uint32(*config)) 157 } else if isFlagPassed("file") { 158 decodeFile(*file, uint32(*codec)) 159 } else { 160 if *generate { 161 fmt.Printf("/* SPDX-License-Identifier: GPL-2.0-only */\n\n") 162 fmt.Printf("#include <device/azalia_device.h>\n\n") 163 fmt.Printf("const u32 cim_verb_data[] = {\n") 164 indentLevel += 1 165 } 166 decodeDeviceCodecs(*generate) 167 if *generate { 168 indentLevel -= 1 169 fmt.Printf("};\n\n") 170 fmt.Printf("const u32 pc_beep_verbs[] = {};\n") 171 fmt.Printf("AZALIA_ARRAY_SIZES;\n") 172 } 173 } 174} 175