This tool helps take off the burden of manually decoding default configuration registers. Using decoded values can make code more self-documenting compared to shrouding it with magic numbers. This is also written as a module which allows easy integration with other tools written in Go (e.g. autoport). Change-Id: Ib4fb652e178517b2b7aceaac8be005c5b2d3b03e Signed-off-by: Nicholas Sudsgaard <devel+coreboot@nsudsgaard.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/80470 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Felix Singer <service+coreboot-gerrit@felixsinger.de> Reviewed-by: Michael Niewöhner <foss@mniewoehner.de>
		
			
				
	
	
		
			175 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"regexp"
 | |
| 	"review.coreboot.org/coreboot.git/util/hda-decoder/decoder"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| var indentLevel int = 0
 | |
| 
 | |
| func indentedPrintf(format string, args ...interface{}) (n int, err error) {
 | |
| 	s := fmt.Sprintf("%s%s", strings.Repeat("\t", indentLevel), format)
 | |
| 	return fmt.Printf(s, args...)
 | |
| }
 | |
| 
 | |
| func stringToUint32(s string) uint32 {
 | |
| 	s = strings.Replace(s, "0x", "", -1)
 | |
| 	v, err := strconv.ParseUint(s, 16, 32)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	return uint32(v)
 | |
| }
 | |
| 
 | |
| func decodeConfig(config uint32) {
 | |
| 	out := decoder.ToHumanReadable(decoder.Decode(config))
 | |
| 
 | |
| 	indentedPrintf("%s,\n", out.PortConnectivity)
 | |
| 	indentedPrintf("%s,\n", out.Location)
 | |
| 	indentedPrintf("%s,\n", out.DefaultDevice)
 | |
| 	indentedPrintf("%s,\n", out.ConnectionType)
 | |
| 	indentedPrintf("%s,\n", out.Color)
 | |
| 	indentedPrintf("%s,\n", out.Misc)
 | |
| 	indentedPrintf("%s, %s\n", out.DefaultAssociation, out.Sequence)
 | |
| }
 | |
| 
 | |
| func printDisconnectedPort(config uint32) {
 | |
| 	// The value 0x411111f0 is not defined in the specification, but is a
 | |
| 	// common value vendors use to indicate "not connected".
 | |
| 	const nc uint32 = 0x411111f0
 | |
| 
 | |
| 	// Setting some values (e.g. 0x40000000) as `AZALIA_PIN_CFG_NC(0)` is
 | |
| 	// probably harmless. However, we will stay on the safe side for now.
 | |
| 	if (config & 0xfffffff0) != nc {
 | |
| 		// Do not decode these values, as they would likely describe a
 | |
| 		// bogus device which could be slighly confusing.
 | |
| 		fmt.Printf("0x%08x), // does not describe a jack or internal device\n", config)
 | |
| 	} else {
 | |
| 		fmt.Printf("AZALIA_PIN_CFG_NC(%d)),\n", (config & 0x0000000f))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func decodeFile(path string, codec uint32) {
 | |
| 	file, err := os.Open(path)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	defer file.Close()
 | |
| 
 | |
| 	scanner := bufio.NewScanner(file)
 | |
| 
 | |
| 	for scanner.Scan() {
 | |
| 		fields := strings.Fields(scanner.Text())
 | |
| 		pin := stringToUint32(fields[0])
 | |
| 		config := stringToUint32(fields[1])
 | |
| 
 | |
| 		indentedPrintf("AZALIA_PIN_CFG(%d, 0x%02x, ", codec, pin)
 | |
| 		if decoder.PortIsConnected(config) {
 | |
| 			fmt.Printf("AZALIA_PIN_DESC(\n")
 | |
| 			indentLevel += 1
 | |
| 			decodeConfig(config)
 | |
| 			indentLevel -= 1
 | |
| 			indentedPrintf(")),\n")
 | |
| 		} else {
 | |
| 			printDisconnectedPort(config)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getFileContents(path string) string {
 | |
| 	contents, err := os.ReadFile(path)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	return strings.TrimSpace(string(contents))
 | |
| }
 | |
| 
 | |
| func getLineCount(path string) int {
 | |
| 	return len(strings.Split(getFileContents(path), "\n"))
 | |
| }
 | |
| 
 | |
| func decodeDeviceCodec(path string, codec uint32, isLastCodec bool, generate bool) {
 | |
| 	if generate {
 | |
| 		vendorId := getFileContents(path + "/vendor_id")
 | |
| 		vendorName := getFileContents(path + "/vendor_name")
 | |
| 		chipName := getFileContents(path + "/chip_name")
 | |
| 		subsystemId := getFileContents(path + "/subsystem_id")
 | |
| 		lineCount := getLineCount(path + "/init_pin_configs")
 | |
| 
 | |
| 		indentedPrintf("%s, // Vendor/Device ID: %s %s\n", vendorId, vendorName, chipName)
 | |
| 		indentedPrintf("%s, // Subsystem ID\n", subsystemId)
 | |
| 		indentedPrintf("%d,\n", lineCount+1)
 | |
| 		indentedPrintf("AZALIA_SUBVENDOR(%d, %s),\n\n", codec, subsystemId)
 | |
| 	}
 | |
| 
 | |
| 	decodeFile(path+"/init_pin_configs", codec)
 | |
| 	if !isLastCodec {
 | |
| 		fmt.Printf("\n")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func decodeDeviceCodecs(generate bool) {
 | |
| 	matches, err := filepath.Glob("/sys/class/sound/hwC0D*")
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	re := regexp.MustCompile(`D([0-9]+)$`)
 | |
| 
 | |
| 	for i, match := range matches {
 | |
| 		codec := stringToUint32(re.FindStringSubmatch(match)[1])
 | |
| 		isLastCodec := (i + 1) == len(matches)
 | |
| 
 | |
| 		decodeDeviceCodec(match, codec, isLastCodec, generate)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func isFlagPassed(name string) bool {
 | |
| 	found := false
 | |
| 
 | |
| 	flag.Visit(func(f *flag.Flag) {
 | |
| 		if f.Name == name {
 | |
| 			found = true
 | |
| 		}
 | |
| 	})
 | |
| 	return found
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	codec := flag.Uint64("codec", 0, "Set the codec number when decoding a file\n"+
 | |
| 		"This flag is only meaningful in combination with the 'file' flag")
 | |
| 	config := flag.Uint64("config", 0, "Decode a single configuration")
 | |
| 	file := flag.String("file", "", "Decode configurations in a file\n"+
 | |
| 		"The decoder assumes each line in the file has the format: <pin> <config>")
 | |
| 	generate := flag.Bool("generate", false, "Automatically generate hda_verb.c for the host device")
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	if isFlagPassed("config") {
 | |
| 		decodeConfig(uint32(*config))
 | |
| 	} else if isFlagPassed("file") {
 | |
| 		decodeFile(*file, uint32(*codec))
 | |
| 	} else {
 | |
| 		if *generate {
 | |
| 			fmt.Printf("/* SPDX-License-Identifier: GPL-2.0-only */\n\n")
 | |
| 			fmt.Printf("#include <device/azalia_device.h>\n\n")
 | |
| 			fmt.Printf("const u32 cim_verb_data[] = {\n")
 | |
| 			indentLevel += 1
 | |
| 		}
 | |
| 		decodeDeviceCodecs(*generate)
 | |
| 		if *generate {
 | |
| 			indentLevel -= 1
 | |
| 			fmt.Printf("};\n\n")
 | |
| 			fmt.Printf("const u32 pc_beep_verbs[] = {};\n")
 | |
| 			fmt.Printf("AZALIA_ARRAY_SIZES;\n")
 | |
| 		}
 | |
| 	}
 | |
| }
 |