CFT: move servo_lib out of firmwareservice

No reason to bury this library so deep in the firmwareservice.

See what this library does in README.md

BUG=None
TEST=local testing

Change-Id: I8a36c216d1292fc2fd65b104d023e5b9c002ff29
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/3345755
Auto-Submit: Sergey Frolov <sfrolov@google.com>
Reviewed-by: Jaques Clapauch <jaquesc@google.com>
Tested-by: Sergey Frolov <sfrolov@google.com>
Reviewed-by: Otabek Kasimov <otabek@google.com>
Commit-Queue: Jaques Clapauch <jaquesc@google.com>
diff --git a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_config.go b/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_config.go
deleted file mode 100644
index 45003c5..0000000
--- a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_config.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2021 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 servo_lib provides servo-related variables, such as dut-controls
-// to be run before and after flashing, and a programmer argument.
-package servo_lib
-
-import "fmt"
-
-type ServoConfig struct {
-	dut_on     [][]string
-	dut_off    [][]string
-	programmer string
-}
-
-type UnsupportedServoError struct {
-	servoType ServoType
-	board     string
-}
-
-func (m *UnsupportedServoError) Error() string {
-	return fmt.Sprintf("board %v does not support servo %v", m.board, m.servoType.string)
-}
-
-func GetServoConfig(board, servoSerial string, servoType ServoType) (*ServoConfig, error) {
-	dut_on := [][]string{{"cpu_fw_spi:on"}}
-	dut_off := [][]string{{"cpu_fw_spi:off"}}
-	programmer := ""
-	if servoType.IsV2() {
-		programmer = fmt.Sprintf("ft2232_spi:type=google-servo-v2,serial=%v", servoSerial)
-	} else if servoType.IsMicro() || servoType.IsC2D2() {
-		programmer = fmt.Sprintf("raiden_debug_spi:serial=%v", servoSerial)
-	} else if servoType.IsCCD() {
-		dut_on = nil
-		dut_off = nil
-		programmer = fmt.Sprintf("raiden_debug_spi:target=AP,serial=%v", servoSerial)
-	} else {
-		return nil, &UnsupportedServoError{servoType, board}
-	}
-	return &ServoConfig{dut_on, dut_off, programmer}, nil
-}
diff --git a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_type.go b/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_type.go
deleted file mode 100644
index 70f1871..0000000
--- a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_type.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2021 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 servo_lib
-
-import "strings"
-
-type ServoType struct {
-	string
-}
-
-func NewServoType(servo_type string) ServoType {
-	return ServoType{servo_type}
-}
-
-func (s *ServoType) IsV2() bool {
-	return strings.Contains(s.string, "servo_v2")
-}
-func (s *ServoType) IsV4() bool {
-	return strings.Contains(s.string, "servo_v4")
-}
-func (s *ServoType) IsC2D2() bool {
-	return strings.Contains(s.string, "c2d2")
-}
-func (s *ServoType) IsCCD() bool {
-	return strings.Contains(s.string, "ccd")
-}
-func (s *ServoType) IsMicro() bool {
-	return strings.Contains(s.string, "servo_micro")
-}
-
-func (s *ServoType) GetSerialNumberOption() string {
-	if s.IsV4() && s.IsCCD() {
-		return "ccd_serialname"
-	}
-	if s.IsV4() && s.IsMicro() {
-		return "servo_micro_serialname"
-	}
-	return "serialname"
-}
diff --git a/src/chromiumos/test/provision/lib/servo_lib/OWNERS b/src/chromiumos/test/provision/lib/servo_lib/OWNERS
new file mode 100644
index 0000000..5ea8372
--- /dev/null
+++ b/src/chromiumos/test/provision/lib/servo_lib/OWNERS
@@ -0,0 +1,3 @@
+sfrolov@google.com
+
+include ../../OWNERS
diff --git a/src/chromiumos/test/provision/lib/servo_lib/README.md b/src/chromiumos/test/provision/lib/servo_lib/README.md
new file mode 100644
index 0000000..d5386a9
--- /dev/null
+++ b/src/chromiumos/test/provision/lib/servo_lib/README.md
@@ -0,0 +1,16 @@
+# servo lib
+
+This module allows users to get correct servo-related variables for
+flashing, specifically, dut-controls to be run before and after flashing
+and the programmer argument.
+
+These arguments mostly* depend on type of the servo in use. Thus, this
+module provides utilities to parse the type of the servo.
+
+\* - However, some boards require special arguments, configs for which may
+be found
+[here](https://source.corp.google.com/chromeos_public/chromite/lib/firmware/ap_firmware_config/).
+TODO: get labstations to install those configs via either `cros` tool
+(requires cros_sdk) or simply by installing an
+[ebuild](https://source.corp.google.com/chromeos_public/src/third_party/chromiumos-overlay/sys-firmware/ap-firmware-config/ap-firmware-config-0.0.1-r238.ebuild;l=1)
+and use them.
diff --git a/src/chromiumos/test/provision/lib/servo_lib/servo_config.go b/src/chromiumos/test/provision/lib/servo_lib/servo_config.go
new file mode 100644
index 0000000..decb05d
--- /dev/null
+++ b/src/chromiumos/test/provision/lib/servo_lib/servo_config.go
@@ -0,0 +1,62 @@
+// 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 servo_lib provides servo-related variables, such as dut-controls
+// to be run before and after flashing, and a programmer argument.
+package servo_lib
+
+import "fmt"
+
+// ServoConfig structure holds the servo-related variables that are necessary
+// for provisioning firmware over Servo.
+type ServoConfig struct {
+	// dut-controls to run before flashing.
+	DutOn [][]string
+	// dut-controls to run after flashing.
+	DutOff [][]string
+	// programmer (-p) argument for futility.
+	Programmer string
+	// extra arguments to provide to futility, such as --fast or --force.
+	ExtraArgs []string
+	// servo_type in use.
+	ServoType ServoType
+}
+
+type UnsupportedServoError struct {
+	ServoType ServoType
+	Board     string
+}
+
+func (m *UnsupportedServoError) Error() string {
+	return fmt.Sprintf("board %v does not support servo %v", m.Board, m.ServoType.string)
+}
+
+// GetServoConfig returns ServoConfig, that depends on the |board| and |servoType|,
+// and contains variables, necessary to flash that setup over Servo. One of those
+// variables is a programmer argument, which needs to include |servoSerial| - serial
+// number of the given DUT.
+// Returns an error if a given |board| cannot be flashed using given |servoType|.
+func GetServoConfig(board, servoSerial string, servoType ServoType) (*ServoConfig, error) {
+	dutOn := [][]string{{"cpu_fw_spi:on"}}
+	dutOff := [][]string{{"cpu_fw_spi:off"}}
+	programmer := ""
+	extraArgs := []string{}
+	if servoType.IsV2() {
+		programmer = fmt.Sprintf("ft2232_spi:type=google-servo-v2,serial=%v", servoSerial)
+	} else if servoType.IsMicro() || servoType.IsC2D2() {
+		programmer = fmt.Sprintf("raiden_debug_spi:serial=%v", servoSerial)
+	} else if servoType.IsCCD() {
+		dutOn = nil
+		dutOff = nil
+		programmer = fmt.Sprintf("raiden_debug_spi:target=AP,serial=%v", servoSerial)
+		// By default, futility will verify images by re-reading them,
+		// but this is extremely slow on CCDs (roughly over 30 mins),
+		// so we turn verification off with "--fast".
+		// Verification doesn't save DUTs from bricking.
+		extraArgs = []string{"--fast"}
+	} else {
+		return nil, &UnsupportedServoError{servoType, board}
+	}
+	return &ServoConfig{dutOn, dutOff, programmer, extraArgs, servoType}, nil
+}
diff --git a/src/chromiumos/test/provision/lib/servo_lib/servo_type.go b/src/chromiumos/test/provision/lib/servo_lib/servo_type.go
new file mode 100644
index 0000000..b0506ed
--- /dev/null
+++ b/src/chromiumos/test/provision/lib/servo_lib/servo_type.go
@@ -0,0 +1,82 @@
+// 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 servo_lib
+
+import (
+	"fmt"
+	"strings"
+)
+
+// ServoType is the used servo type, as reported by `dut-control servo_type`.
+// ServoType determines arguments to use with futility when flashing.
+type ServoType struct {
+	string
+}
+
+func NewServoType(servo_type string) ServoType {
+	return ServoType{servo_type}
+}
+
+func (s *ServoType) IsV2() bool {
+	return strings.Contains(s.string, "servo_v2")
+}
+func (s *ServoType) IsV4() bool {
+	return strings.Contains(s.string, "servo_v4")
+}
+func (s *ServoType) IsC2D2() bool {
+	return strings.Contains(s.string, "c2d2")
+}
+func (s *ServoType) IsCCD() bool {
+	return strings.Contains(s.string, "ccd")
+}
+func (s *ServoType) IsMicro() bool {
+	return strings.Contains(s.string, "servo_micro")
+}
+
+func (s *ServoType) GetSerialNumberOption() string {
+	if s.IsV4() && s.IsCCD() {
+		return "ccd_serialname"
+	}
+	if s.IsV4() && s.IsMicro() {
+		return "servo_micro_serialname"
+	}
+	return "serialname"
+}
+
+func (s *ServoType) IsMultipleServos() bool {
+	return strings.Contains(s.string, "_and_")
+}
+
+// PickServoSubtype allows to pick a single servo from dual servo types,
+// such as "servo_v4p1_with_servo_micro_and_ccd_cr50".
+// PickServoSubtype assumes that the servo is dual, use IsMultipleServos()
+// function to check for that.
+// |preferCCD| tells the function to pick the CCD servo, otherwise other servo
+// type (likely servo_micro) will be chosen. Example:
+//   If preferCCD is true, string above will return "servo_v4p1_with_ccd_cr50".
+//   If preferCCD is false, it will return "servo_v4p1_with_servo_micro".
+func (s *ServoType) PickServoSubtype(preferCCD bool) string {
+	withSplit := strings.Split(s.string, "_with_")
+	if len(withSplit) <= 1 {
+		return strings.Split(s.string, "_and_")[0]
+	}
+
+	// for "servo_v4p1_with_servo_micro_and_ccd_cr50" this would return "servo_v4p1"
+	servoVer := withSplit[0]
+
+	servoSubtypes := strings.Split(withSplit[1], "_and_")
+	subtypeToUse := ""
+	for _, servoSubType := range servoSubtypes {
+		subtypeToUse = servoSubType
+		isCCD := strings.Contains(servoSubType, "ccd")
+		if isCCD && preferCCD {
+			break
+		}
+		if !isCCD && !preferCCD {
+			break
+		}
+	}
+
+	return fmt.Sprintf("%v_with_%v", servoVer, subtypeToUse)
+}
diff --git a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_type_test.go b/src/chromiumos/test/provision/lib/servo_lib/servo_type_test.go
similarity index 78%
rename from src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_type_test.go
rename to src/chromiumos/test/provision/lib/servo_lib/servo_type_test.go
index c53e29d..3c648e0 100644
--- a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib/servo_type_test.go
+++ b/src/chromiumos/test/provision/lib/servo_lib/servo_type_test.go
@@ -1,10 +1,10 @@
-// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// 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 servo_lib_test
 
 import (
-	servo_lib "chromiumos/test/provision/cmd/provisionserver/bootstrap/services/firmwareservice/servo/lib"
+	"chromiumos/test/provision/lib/servo_lib"
 	"testing"
 )
 
@@ -98,3 +98,19 @@
 		}
 	}
 }
+
+func TestPickServoSubtype(t *testing.T) {
+	dualServoType := servo_lib.NewServoType("servo_v4p1_with_servo_micro_and_ccd_cr50")
+
+	expectedPreferCCD := "servo_v4p1_with_ccd_cr50"
+	expectedNoPreferCCD := "servo_v4p1_with_servo_micro"
+
+	if gotPreferCCD := dualServoType.PickServoSubtype(true /*prefer CCD*/); gotPreferCCD != expectedPreferCCD {
+		t.Errorf("%v PickOneFromDual(true) expected: %v, got: %v",
+			dualServoType, expectedPreferCCD, gotPreferCCD)
+	}
+	if gotNoPreferCCD := dualServoType.PickServoSubtype(false /*prefer CCD*/); gotNoPreferCCD != expectedNoPreferCCD {
+		t.Errorf("%v PickOneFromDual(true) expected: %v, got: %v",
+			dualServoType, expectedNoPreferCCD, gotNoPreferCCD)
+	}
+}