gofastboot/fastboot/FastbootDevice.go

235 lines
4.4 KiB
Go
Raw Normal View History

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
}