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