2024-06-14 16:14:31 +00:00
|
|
|
package fastboot
|
|
|
|
|
|
|
|
import (
|
2024-06-16 12:41:56 +00:00
|
|
|
"errors"
|
2024-06-14 16:14:31 +00:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/google/gousb"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FastbootResponseStatus string
|
|
|
|
|
|
|
|
var Status = struct {
|
|
|
|
OKAY FastbootResponseStatus
|
|
|
|
FAIL FastbootResponseStatus
|
|
|
|
DATA FastbootResponseStatus
|
|
|
|
INFO FastbootResponseStatus
|
|
|
|
}{
|
|
|
|
OKAY: "OKAY",
|
|
|
|
FAIL: "FAIL",
|
|
|
|
DATA: "DATA",
|
|
|
|
INFO: "INFO",
|
|
|
|
}
|
|
|
|
|
2024-06-16 12:41:56 +00:00
|
|
|
var Error = struct {
|
2024-06-17 07:33:38 +00:00
|
|
|
VarNotFound error
|
|
|
|
DeviceNotFound error
|
2024-06-16 12:41:56 +00:00
|
|
|
}{
|
2024-06-17 07:33:38 +00:00
|
|
|
VarNotFound: errors.New("variable not found"),
|
2024-06-21 12:27:31 +00:00
|
|
|
DeviceNotFound: errors.New("device not found"),
|
2024-06-16 12:41:56 +00:00
|
|
|
}
|
|
|
|
|
2024-06-14 16:14:31 +00:00
|
|
|
type FastbootDevice struct {
|
2024-06-21 12:27:31 +00:00
|
|
|
Device *gousb.Device
|
|
|
|
Context *gousb.Context
|
|
|
|
In *gousb.InEndpoint
|
|
|
|
Out *gousb.OutEndpoint
|
|
|
|
Unclaim func()
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
func FindDevices() ([]*FastbootDevice, error) {
|
|
|
|
ctx := gousb.NewContext()
|
|
|
|
var fastbootDevices []*FastbootDevice
|
2024-06-14 16:14:31 +00:00
|
|
|
devs, err := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool {
|
|
|
|
for _, cfg := range desc.Configs {
|
|
|
|
for _, ifc := range cfg.Interfaces {
|
|
|
|
for _, alt := range ifc.AltSettings {
|
|
|
|
return alt.Protocol == 0x03 && alt.Class == 0xff && alt.SubClass == 0x42
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-21 12:27:31 +00:00
|
|
|
return true
|
2024-06-14 16:14:31 +00:00
|
|
|
})
|
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
if err != nil && len(devs) == 0 {
|
2024-06-14 16:14:31 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, dev := range devs {
|
2024-06-21 12:27:31 +00:00
|
|
|
intf, done, err := dev.DefaultInterface()
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
inEndpoint, err := intf.InEndpoint(0x81)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
outEndpoint, err := intf.OutEndpoint(0x01)
|
2024-06-14 16:14:31 +00:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2024-06-21 12:27:31 +00:00
|
|
|
fastbootDevices = append(fastbootDevices, &FastbootDevice{
|
|
|
|
Device: dev,
|
|
|
|
Context: ctx,
|
|
|
|
In: inEndpoint,
|
|
|
|
Out: outEndpoint,
|
|
|
|
Unclaim: done,
|
|
|
|
})
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fastbootDevices, nil
|
|
|
|
}
|
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
func FindDevice(serial string) (*FastbootDevice, error) {
|
|
|
|
devs, err := FindDevices()
|
2024-06-14 16:14:31 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2024-06-21 12:27:31 +00:00
|
|
|
return &FastbootDevice{}, err
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, dev := range devs {
|
2024-06-21 12:27:31 +00:00
|
|
|
s, e := dev.Device.SerialNumber()
|
|
|
|
if e != nil {
|
2024-06-14 16:14:31 +00:00
|
|
|
continue
|
|
|
|
}
|
2024-06-21 12:27:31 +00:00
|
|
|
if serial != s {
|
2024-06-14 16:14:31 +00:00
|
|
|
continue
|
|
|
|
}
|
2024-06-21 12:27:31 +00:00
|
|
|
return dev, nil
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
return &FastbootDevice{}, Error.DeviceNotFound
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
func (d *FastbootDevice) Close() {
|
|
|
|
d.Unclaim()
|
|
|
|
d.Device.Close()
|
|
|
|
d.Context.Close()
|
|
|
|
}
|
2024-06-14 16:14:31 +00:00
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
func (d *FastbootDevice) Send(data []byte) error {
|
|
|
|
_, err := d.Out.Write(data)
|
2024-06-14 16:14:31 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *FastbootDevice) GetMaxPacketSize() (int, error) {
|
2024-06-21 12:27:31 +00:00
|
|
|
return d.Out.Desc.MaxPacketSize, nil
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *FastbootDevice) Recv() (FastbootResponseStatus, []byte, error) {
|
2024-06-21 12:27:31 +00:00
|
|
|
var data []byte
|
|
|
|
buf := make([]byte, d.In.Desc.MaxPacketSize)
|
|
|
|
n, err := d.In.Read(buf)
|
2024-06-14 16:14:31 +00:00
|
|
|
if err != nil {
|
2024-06-21 12:27:31 +00:00
|
|
|
return Status.FAIL, []byte{}, err
|
2024-06-14 16:14:31 +00:00
|
|
|
}
|
|
|
|
data = append(data, buf[:n]...)
|
|
|
|
var status FastbootResponseStatus
|
|
|
|
switch string(data[:4]) {
|
|
|
|
case "OKAY":
|
|
|
|
status = Status.OKAY
|
|
|
|
case "FAIL":
|
|
|
|
status = Status.FAIL
|
|
|
|
case "DATA":
|
|
|
|
status = Status.DATA
|
|
|
|
case "INFO":
|
|
|
|
status = Status.INFO
|
|
|
|
}
|
|
|
|
return status, data[4:], nil
|
|
|
|
}
|
|
|
|
|
2024-06-21 12:27:31 +00:00
|
|
|
func (d *FastbootDevice) GetVar(variable string) (string, error) {
|
|
|
|
err := d.Send([]byte(fmt.Sprintf("getvar:%s", variable)))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2024-06-16 12:41:56 +00:00
|
|
|
status, resp, err := d.Recv()
|
|
|
|
if status == Status.FAIL {
|
|
|
|
err = Error.VarNotFound
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(resp), nil
|
|
|
|
}
|
|
|
|
|
2024-06-14 16:14:31 +00:00
|
|
|
func (d *FastbootDevice) BootImage(data []byte) error {
|
2024-06-17 07:33:38 +00:00
|
|
|
err := d.Download(data)
|
2024-06-14 16:14:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = d.Send([]byte("boot"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
status, data, err := d.Recv()
|
|
|
|
switch {
|
|
|
|
case status != Status.OKAY:
|
2024-06-17 07:33:38 +00:00
|
|
|
return fmt.Errorf("failed to boot image: %s %s", status, data)
|
|
|
|
case err != nil:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *FastbootDevice) Flash(partition string, data []byte) error {
|
|
|
|
err := d.Download(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = d.Send([]byte(fmt.Sprintf("flash:%s", partition)))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
status, data, err := d.Recv()
|
|
|
|
switch {
|
|
|
|
case status != Status.OKAY:
|
|
|
|
return fmt.Errorf("failed to flash image: %s %s", status, data)
|
2024-06-14 16:14:31 +00:00
|
|
|
case err != nil:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-17 07:33:38 +00:00
|
|
|
func (d *FastbootDevice) Download(data []byte) error {
|
2024-06-14 16:14:31 +00:00
|
|
|
data_size := len(data)
|
|
|
|
err := d.Send([]byte(fmt.Sprintf("download:%08x", data_size)))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
status, _, err := d.Recv()
|
|
|
|
switch {
|
|
|
|
case status != Status.DATA:
|
2024-06-17 07:33:38 +00:00
|
|
|
return fmt.Errorf("failed to start data phase: %s", status)
|
2024-06-14 16:14:31 +00:00
|
|
|
case err != nil:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk_size := 0x40040
|
|
|
|
|
|
|
|
for i := 0; i < data_size; i += chunk_size {
|
|
|
|
end := i + chunk_size
|
|
|
|
if end > data_size {
|
|
|
|
end = data_size
|
|
|
|
}
|
|
|
|
err := d.Send(data[i:end])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
status, data, err = d.Recv()
|
|
|
|
switch {
|
|
|
|
case status != Status.OKAY:
|
2024-06-17 07:33:38 +00:00
|
|
|
return fmt.Errorf("failed to finish data phase: %s %s", status, data)
|
2024-06-14 16:14:31 +00:00
|
|
|
case err != nil:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|