/ util / hda-decoder / main.go
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  }