diff --git a/dfu.go b/dfu.go index bfa1c81..47068c5 100644 --- a/dfu.go +++ b/dfu.go @@ -1,224 +1,227 @@ package main import ( - "errors" - "fmt" - "github.com/google/gousb" - "io/ioutil" - "log" - "time" -) + "errors" + "fmt" + "github.com/google/gousb" + "io/ioutil" + "runtime" + "log" + "time" + ) type status struct { - bStatus string - bwPollTimeout int - bState string - iString string + bStatus string + bwPollTimeout int + bState string + iString string } func dfuCommand(dev *gousb.Device, addr int, command int, status *status) (err error) { - var buf []byte - if command == setAddress { - buf = make([]byte, 5) - buf[0] = 0x21 - buf[1] = byte(addr & 0xff) - buf[2] = byte((addr >> 8) & 0xff) - buf[3] = byte((addr >> 16) & 0xff) - buf[4] = byte((addr >> 24) & 0xff) - } - if command == eraseAddress { - buf = make([]byte, 5) - buf[0] = 0x41 - buf[1] = byte(addr & 0xff) - buf[2] = byte((addr >> 8) & 0xff) - buf[3] = byte((addr >> 16) & 0xff) - buf[4] = byte((addr >> 24) & 0xff) - } - if command == eraseFlash { - buf = make([]byte, 1) - buf[0] = 0x41 - } - - _, err = dev.Control(33, 1, 0, 0, buf) - - err = dfuPollTimeout(dev, status) - - if err != nil { - return err - } - - return nil + var buf []byte + if command == setAddress { + buf = make([]byte, 5) + buf[0] = 0x21 + buf[1] = byte(addr & 0xff) + buf[2] = byte((addr >> 8) & 0xff) + buf[3] = byte((addr >> 16) & 0xff) + buf[4] = byte((addr >> 24) & 0xff) + } + if command == eraseAddress { + buf = make([]byte, 5) + buf[0] = 0x41 + buf[1] = byte(addr & 0xff) + buf[2] = byte((addr >> 8) & 0xff) + buf[3] = byte((addr >> 16) & 0xff) + buf[4] = byte((addr >> 24) & 0xff) + } + if command == eraseFlash { + buf = make([]byte, 1) + buf[0] = 0x41 + } + + _, err = dev.Control(33, 1, 0, 0, buf) + + err = dfuPollTimeout(dev, status) + + if err != nil { + return err + } + + return nil } func dfuPollTimeout(dev *gousb.Device, status *status) (err error) { - for i := 0; i < 3; i++ { - err = dfuGetStatus(dev, status) - time.Sleep(time.Duration(status.bwPollTimeout) * time.Millisecond) - } - return err + for i := 0; i < 3; i++ { + err = dfuGetStatus(dev, status) + time.Sleep(time.Duration(status.bwPollTimeout) * time.Millisecond) + } + return err } func dfuGetStatus(dev *gousb.Device, status *status) (err error) { - buf := make([]byte, 6) - stat, err := dev.Control(161, 3, 0, 0, buf) - if err != nil { - return err - } - if stat == 6 { - status.bStatus = string(buf[0]) - status.bwPollTimeout = int((0xff & buf[3] << 16) | (0xff & buf[2]) | 0xff&buf[1]) - status.bState = string(buf[4]) - status.iString = string(buf[5]) - } - return err + buf := make([]byte, 6) + stat, err := dev.Control(161, 3, 0, 0, buf) + if err != nil { + return err + } + if stat == 6 { + status.bStatus = string(buf[0]) + status.bwPollTimeout = int((0xff & buf[3] << 16) | (0xff & buf[2]) | 0xff&buf[1]) + status.bState = string(buf[4]) + status.iString = string(buf[5]) + } +return err } func dfuClearStatus(dev *gousb.Device) (err error) { - _, err = dev.Control(33, 4, 2, 0, nil) - return err + _, err = dev.Control(33, 4, 2, 0, nil) +return err } func dfuReboot(dev *gousb.Device, status *status) (err error) { - err = dfuPollTimeout(dev, status) - _, err = dev.Control(33, 1, 2, 0, nil) - time.Sleep(1000 * time.Millisecond) - err = dfuGetStatus(dev, status) - return err + err = dfuPollTimeout(dev, status) +_, err = dev.Control(33, 1, 2, 0, nil) +time.Sleep(1000 * time.Millisecond) +err = dfuGetStatus(dev, status) + return err } func extractSuffix(fileData []byte) (hasSuffix bool, data []byte, err error) { - fileSize := len(fileData) + fileSize := len(fileData) - suffix := fileData[fileSize-dfuSuffixLength : fileSize] - d := string(suffix[10]) - f := string(suffix[9]) - u := string(suffix[8]) + suffix := fileData[fileSize-dfuSuffixLength : fileSize] + d := string(suffix[10]) + f := string(suffix[9]) + u := string(suffix[8]) - if d == "D" && f == "F" && u == "U" { - vid := int((suffix[5] << 8) + suffix[4]) - pid := int((suffix[3] << 8) + suffix[2]) - if vid != dfuSuffixVendorID || pid != dfuSuffixProductID { - message := fmt.Sprintf("Invalid vendor or product id, expected %#x:%#x got %#x:%#x", dfuSuffixVendorID, dfuSuffixProductID, vid, pid) - err = errors.New(message) - return true, fileData, err +if d == "D" && f == "F" && u == "U" { +vid := int((suffix[5] << 8) + suffix[4]) + pid := int((suffix[3] << 8) + suffix[2]) + if vid != dfuSuffixVendorID || pid != dfuSuffixProductID { + message := fmt.Sprintf("Invalid vendor or product id, expected %#x:%#x got %#x:%#x", dfuSuffixVendorID, dfuSuffixProductID, vid, pid) + err = errors.New(message) + return true, fileData, err - } + } - return true, fileData[0 : fileSize-dfuSuffixLength], nil - } + return true, fileData[0 : fileSize-dfuSuffixLength], nil + } - return false, fileData, nil + return false, fileData, nil } func dfuFlash(firmwarePath string, s *state) { - dfuStatus := status{} - fileData, err := ioutil.ReadFile(firmwarePath) - if err != nil { - message := fmt.Sprintf("Error while opening firmware: %s", err) - log.Fatal(message) - return - } - - _, firmwareData, err := extractSuffix(fileData) - if err != nil { - message := fmt.Sprintf("Error while extracting DFU Suffix: %s", err) - log.Fatal(message) - return - } - - ctx := gousb.NewContext() - ctx.Debug(0) - defer ctx.Close() - var dev *gousb.Device - - // Get the list of device that match TMK's vendor id - for { - devs, _ := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { - if desc.Vendor == gousb.ID(dfuVendorID) && desc.Product == gousb.ID(dfuProductID) { - return true - } - return false - }) - - defer func() { - for _, d := range devs { - d.Close() - } - }() - - if len(devs) > 0 { - dev = devs[0] - break - } - time.Sleep(1 * time.Second) - } - - dev.SetAutoDetach(true) - - dev.ControlTimeout = 5 * time.Second - - cfg, err := dev.Config(1) - if err != nil { - message := fmt.Sprintf("Error while claiming the usb interface: %s", err) - log.Fatal(message) - } - defer cfg.Close() - - fileSize := len(firmwareData) - s.total = fileSize - - err = dfuClearStatus(dev) - if err != nil { - message := fmt.Sprintf("Error while clearing the device status: %s", err) - log.Fatal(message) - } - - s.step = 1 - - err = dfuCommand(dev, 0, eraseFlash, &dfuStatus) - if err != nil { - message := fmt.Sprintf("Error while erasing flash: %s", err) - log.Fatal(message) - return - } - - for page := 0; page < fileSize; page += planckBlockSize { - addr := planckStartAddress + page - chunckSize := planckBlockSize - - if page+chunckSize > fileSize { - chunckSize = fileSize - page - } - - err = dfuCommand(dev, addr, eraseAddress, &dfuStatus) - if err != nil { - message := fmt.Sprintf("Error while sending the erase address command: %s", err) - log.Fatal(message) - } - err = dfuCommand(dev, addr, setAddress, &dfuStatus) - if err != nil { - message := fmt.Sprintf("Error while sending the set address command: %s", err) - log.Fatal(message) - } - - buf := firmwareData[page : page+chunckSize] - bytes, err := dev.Control(33, 1, 2, 0, buf) - - if err != nil { - message := fmt.Sprintf("Error while sending firmware bytes: %s", err) - log.Fatal(message) - } - - s.sent += bytes - } - - err = dfuReboot(dev, &dfuStatus) - if err != nil { - message := fmt.Sprintf("Error while rebooting device: %s", err) - log.Fatal(message) - return - } - - s.step = 2 + dfuStatus := status{} + fileData, err := ioutil.ReadFile(firmwarePath) + if err != nil { + message := fmt.Sprintf("Error while opening firmware: %s", err) + log.Fatal(message) + return + } + + _, firmwareData, err := extractSuffix(fileData) + if err != nil { + message := fmt.Sprintf("Error while extracting DFU Suffix: %s", err) + log.Fatal(message) + return + } + + ctx := gousb.NewContext() + ctx.Debug(0) + defer ctx.Close() + var dev *gousb.Device + + // Get the list of device that match TMK's vendor id + for { + devs, _ := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { + if desc.Vendor == gousb.ID(dfuVendorID) && desc.Product == gousb.ID(dfuProductID) { + return true + } + return false + }) + + defer func() { + for _, d := range devs { + d.Close() + } + }() + + if len(devs) > 0 { + dev = devs[0] + break + } + time.Sleep(1 * time.Second) + } + + if runtime.GOOS != "darwin" { + dev.SetAutoDetach(true) + } + + dev.ControlTimeout = 5 * time.Second + + cfg, err := dev.Config(1) + if err != nil { + message := fmt.Sprintf("Error while claiming the usb interface: %s", err) + log.Fatal(message) + } + defer cfg.Close() + + fileSize := len(firmwareData) + s.total = fileSize + + err = dfuClearStatus(dev) + if err != nil { + message := fmt.Sprintf("Error while clearing the device status: %s", err) + log.Fatal(message) + } + + s.step = 1 + + err = dfuCommand(dev, 0, eraseFlash, &dfuStatus) + if err != nil { + message := fmt.Sprintf("Error while erasing flash: %s", err) + log.Fatal(message) + return + } + + for page := 0; page < fileSize; page += planckBlockSize { + addr := planckStartAddress + page + chunckSize := planckBlockSize + + if page+chunckSize > fileSize { + chunckSize = fileSize - page + } + + err = dfuCommand(dev, addr, eraseAddress, &dfuStatus) + if err != nil { + message := fmt.Sprintf("Error while sending the erase address command: %s", err) + log.Fatal(message) + } + err = dfuCommand(dev, addr, setAddress, &dfuStatus) + if err != nil { + message := fmt.Sprintf("Error while sending the set address command: %s", err) + log.Fatal(message) + } + + buf := firmwareData[page : page+chunckSize] + bytes, err := dev.Control(33, 1, 2, 0, buf) + + if err != nil { + message := fmt.Sprintf("Error while sending firmware bytes: %s", err) + log.Fatal(message) + } + + s.sent += bytes + } + + err = dfuReboot(dev, &dfuStatus) + if err != nil { + message := fmt.Sprintf("Error while rebooting device: %s", err) + log.Fatal(message) + return + } + + s.step = 2 } diff --git a/teensy.go b/teensy.go index 7b5854c..7e84076 100644 --- a/teensy.go +++ b/teensy.go @@ -1,115 +1,117 @@ package main import ( - "fmt" - "github.com/google/gousb" - "github.com/marcinbor85/gohex" - "log" - "os" - "time" -) + "fmt" + "github.com/google/gousb" + "github.com/marcinbor85/gohex" + "runtime" + "log" + "os" + "time" + ) // TeensyFlash: Flashes Teensy boards. // It opens the firmware file at the provided path, checks it's integrity, wait for the keyboard to be in Flash mode, flashes it and reboots the board. func teensyFlash(firmwarePath string, s *state) { - file, err := os.Open(firmwarePath) - if err != nil { - message := fmt.Sprintf("Error while opening firmware: %s", err) - log.Fatal(message) - return - } - defer file.Close() - - s.total = ergodoxCodeSize - - firmware := gohex.NewMemory() - err = firmware.ParseIntelHex(file) - if err != nil { - message := fmt.Sprintf("Error while parsing firmware: %s", err) - log.Fatal(message) - return - } - - ctx := gousb.NewContext() - ctx.Debug(0) - defer ctx.Close() - var dev *gousb.Device - - // Loop until a keyboard is ready to flash - for { - devs, _ := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { - if desc.Vendor == gousb.ID(halfKayVendorID) && desc.Product == gousb.ID(halfKayProductID) { - return true - } - return false - }) - - defer func() { - for _, d := range devs { - d.Close() - } - }() - - if len(devs) > 0 { - dev = devs[0] - break - } - time.Sleep(1 * time.Second) - } - - // Detach keyboard from the kernel - dev.SetAutoDetach(true) - - // Claim usb device - cfg, err := dev.Config(1) - defer cfg.Close() - if err != nil { - message := fmt.Sprintf("Error while claiming the usb interface: %s", err) - log.Fatal(message) - return - } - - s.step = 1 - - // Loop on the firmware data and program - var addr uint32 - for addr = 0; addr < ergodoxCodeSize; addr += ergodoxBlockSize { - // set a longer timeout when writing the first block - if addr == 0 { - dev.ControlTimeout = 5 * time.Second - } else { - dev.ControlTimeout = 500 * time.Millisecond - } - // Prepare and write a firmware block - // https://www.pjrc.com/teensy/halfkay_protocol.html - buf := make([]byte, ergodoxBlockSize+2) - buf[0] = byte(addr & 255) - buf[1] = byte((addr >> 8) & 255) - block := firmware.ToBinary(addr, ergodoxBlockSize, 255) - for index := range block { - buf[index+2] = block[index] - } - - bytes, err := dev.Control(0x21, 9, 0x0200, 0, buf) - if err != nil { - message := fmt.Sprintf("Error while sending firmware bytes: %s", err) - log.Fatal(message) - return - } - - s.sent += bytes - } - - buf := make([]byte, ergodoxBlockSize+2) - buf[0] = byte(0xFF) - buf[1] = byte(0xFF) - buf[2] = byte(0xFF) - _, err = dev.Control(0x21, 9, 0x0200, 0, buf) - - if err != nil { - message := fmt.Sprintf("Error while rebooting device: %s", err) - log.Fatal(message) - return - } - s.step = 2 + file, err := os.Open(firmwarePath) + if err != nil { + message := fmt.Sprintf("Error while opening firmware: %s", err) + log.Fatal(message) + return + } + defer file.Close() + + s.total = ergodoxCodeSize + + firmware := gohex.NewMemory() + err = firmware.ParseIntelHex(file) + if err != nil { + message := fmt.Sprintf("Error while parsing firmware: %s", err) + log.Fatal(message) + return + } + + ctx := gousb.NewContext() + ctx.Debug(0) + defer ctx.Close() + var dev *gousb.Device + + // Loop until a keyboard is ready to flash + for { + devs, _ := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { + if desc.Vendor == gousb.ID(halfKayVendorID) && desc.Product == gousb.ID(halfKayProductID) { + return true + } + return false + }) + + defer func() { + for _, d := range devs { + d.Close() + } + }() + + if len(devs) > 0 { + dev = devs[0] + break + } + time.Sleep(1 * time.Second) + } + + if runtime.GOOS != "darwin" { + dev.SetAutoDetach(true) + } + + // Claim usb device + cfg, err := dev.Config(1) + defer cfg.Close() + if err != nil { + message := fmt.Sprintf("Error while claiming the usb interface: %s", err) + log.Fatal(message) + return + } + + s.step = 1 + + // Loop on the firmware data and program + var addr uint32 + for addr = 0; addr < ergodoxCodeSize; addr += ergodoxBlockSize { + // set a longer timeout when writing the first block + if addr == 0 { + dev.ControlTimeout = 5 * time.Second + } else { + dev.ControlTimeout = 500 * time.Millisecond + } + // Prepare and write a firmware block + // https://www.pjrc.com/teensy/halfkay_protocol.html + buf := make([]byte, ergodoxBlockSize+2) + buf[0] = byte(addr & 255) + buf[1] = byte((addr >> 8) & 255) + block := firmware.ToBinary(addr, ergodoxBlockSize, 255) + for index := range block { + buf[index+2] = block[index] + } + + bytes, err := dev.Control(0x21, 9, 0x0200, 0, buf) + if err != nil { + message := fmt.Sprintf("Error while sending firmware bytes: %s", err) + log.Fatal(message) + return + } + + s.sent += bytes + } + + buf := make([]byte, ergodoxBlockSize+2) + buf[0] = byte(0xFF) + buf[1] = byte(0xFF) + buf[2] = byte(0xFF) + _, err = dev.Control(0x21, 9, 0x0200, 0, buf) + + if err != nil { + message := fmt.Sprintf("Error while rebooting device: %s", err) + log.Fatal(message) + return + } + s.step = 2 }