blob: f80f26f2b7312c9db57c40f1cc3ccd3ccbb0ee5e [file]
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package hpsutil contains functionality used by the HPS tast tests.
package hpsutil
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"chromiumos/tast/common/testexec"
"chromiumos/tast/errors"
"chromiumos/tast/testing"
)
// CheckHpsError returns an error in case the HPS has seen an internal error.
func CheckHpsError(hctx *HpsContext) error {
value, err := GetRegisterValue(hctx, "6")
if err != nil {
// This could happen right after `hps mcu reset`, don't want to fail the test.
return nil
}
if value != 0 {
if hctx.FailOnHPSErrorRegister {
return errors.Errorf("error register is not zero (0x%x)", value)
}
}
return nil
}
// GetNumberOfPresenceOps return an increasing number of presence ops the HPS has executed.
// It's not guaranteed to start at 0 after power-cycle and is only valid if HPS is in presence mode.
func GetNumberOfPresenceOps(hctx *HpsContext) (uint16, error) {
return GetRegisterValue(hctx, "13")
}
// DecodePresenceResult decodes a result of a presence operation:
// it's positive when a person is detected, and negative when no person is detected.
func DecodePresenceResult(fullValue uint16) int {
value := int(fullValue & 0xff)
if value < 0x80 {
return value
}
return value - 256
}
// GetPresenceResult returns the decoded result of the last presence operation.
func GetPresenceResult(hctx *HpsContext, reg string) (int, error) {
raw, err := GetRegisterValue(hctx, reg)
if err != nil {
return 0, err
}
return DecodePresenceResult(raw), nil
}
// GetRegisterValue returns the integer value from the HPS register.
func GetRegisterValue(hctx *HpsContext, register string) (uint16, error) {
var output []byte
var err error
regex := regexp.MustCompile(`\b0x([0-9a-f]+)\b`)
args := []string{"hps", hctx.Device, "status", register}
if hctx.DutConn != nil {
output, err = hctx.DutConn.CommandContext(hctx.Ctx, args[0], args[1:]...).CombinedOutput()
} else {
output, err = testexec.CommandContext(hctx.Ctx, args[0], args[1:]...).CombinedOutput()
}
if err != nil {
return 0, err
}
result := regex.FindStringSubmatch(strings.ToLower(string(output)))
if len(result) < 2 {
return 0, errors.Errorf("Hex number pattern not found in %q (%q)", string(output), result)
}
value, err := strconv.ParseInt(result[1], 16, 64)
if err != nil {
return 0, errors.Wrapf(err, "Conversion failed: %q", result[1])
}
if value > 0xffff {
return 0, errors.Errorf("Register returned a value that doesn't fit in int16: 0x%x", value)
}
return uint16(value), nil
}
// RunBuiltinI2CDetect runs i2cdetect to check in what state the HPS is.
func RunBuiltinI2CDetect(hctx *HpsContext) error {
outDir := hctx.OutDir
cmdNum := &hctx.CmdNum
// 0x51 is system bootloader: if it responds we can flash stage0.
// Power-cycling gets us to address 51.
// Brya p2 -- 15, Taeko -- 14
if err := RunWithLog(hctx, outDir, "i2cdetect_0x51", cmdNum, "i2cdetect", "-y", "-r", "15", "0x51", "0x51"); err != nil {
return err
}
// 0x30 is hps firmware: after starting hpsd.
// Starting hpsd should get us from address 51 to address 30.
if err := RunWithLog(hctx, outDir, "i2cdetect_0x30", cmdNum, "i2cdetect", "-y", "-r", "15", "0x30", "0x30"); err != nil {
return err
}
return nil
}
// RunHpsTool runs the `hps device ...` command and writes both stdout and stderr to the log file.
func RunHpsTool(hctx *HpsContext, arg ...string) error {
arg2 := append([]string{"hps", hctx.Device}, arg...)
if err := RunWithLog(hctx, hctx.OutDir, "hps_tool", &hctx.CmdNum, arg2...); err != nil {
return err
}
return CheckHpsError(hctx)
}
// RunWithLog runs the specified command and writes both stdout and stderr to the log file.
// cmdNum is used to automatically generate a consistent numeric prefix.
func RunWithLog(hctx *HpsContext, outDir, logFilename string, cmdNum *int, args ...string) error {
ctx := hctx.Ctx
if hctx.DutConn != nil {
logFilename = fmt.Sprintf("%02d_%s.txt", *cmdNum, logFilename)
cmd := hctx.DutConn.CommandContext(hctx.Ctx, args[0], args[1:]...)
testing.ContextLog(ctx, cmd, " --> ", logFilename)
*cmdNum++
var output []byte
var err error
if output, err = cmd.Output(); err != nil {
testing.ContextLog(ctx, "Error for the command: ", (err))
}
log, fileerr := os.OpenFile(filepath.Join(outDir, logFilename),
os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if fileerr != nil {
return errors.Wrapf(err, "cannot open log file %s", logFilename)
}
defer log.Close()
if _, writeErr := log.WriteString(string(output)); writeErr != nil {
return errors.Wrap(writeErr, "Wrote logcommand failed")
}
// Writing error if there is any
if err != nil {
if _, writeErr := log.WriteString(err.Error()); writeErr != nil {
return errors.Wrap(writeErr, "Wrote logcommand failed")
}
return errors.Wrapf(err, "command failed, see %s for more details", logFilename)
}
} else {
logFilename = fmt.Sprintf("%02d_%s.txt", *cmdNum, logFilename)
cmd := testexec.CommandContext(hctx.Ctx, args[0], args[1:]...)
testing.ContextLog(ctx, cmd, " --> ", logFilename)
*cmdNum++
log, err := os.OpenFile(filepath.Join(outDir, logFilename),
os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
return errors.Wrapf(err, "cannot open log file %s", logFilename)
}
defer log.Close()
cmd.Stderr = log
cmd.Stdout = log
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "command failed, see %s for more details", logFilename)
}
}
return nil
}
// RunStatus runs the `hps status` command to help diagnose problems.
func RunStatus(hctx *HpsContext) error {
if err := RunHpsTool(hctx, "status", "0", "11"); err != nil {
return errors.Wrap(err, "error during getting status")
}
return nil
}