diff --git a/qmk-cmd.go b/qmk-cmd.go index e2178fd..25815f9 100644 --- a/qmk-cmd.go +++ b/qmk-cmd.go @@ -3,11 +3,20 @@ package qmkenc import ( "encoding/binary" "fmt" + "math/rand" + "time" ) var magicHeader = []byte{0x03, 0xFF} type QEncCmd byte +type QEncInfoFct func(string) +type QEncEndFct func() +type QEncErrFct func(error) + +var QEncInfoHandler QEncInfoFct +var QEncEndHandler QEncEndFct +var QEncErrHandler QEncErrFct const ( QENC_CMD_RESET QEncCmd = 0x00 @@ -24,8 +33,49 @@ const ( QENC_CMD_SET_KEYS QEncCmd = 0x0B QENC_CMD_GET_CFG QEncCmd = 0x0C QENC_CMD_INITIALIZE QEncCmd = 0x0D + QENC_CMD_NONE QEncCmd = 0x0E + QENC_CMD_SET_BUFFER QEncCmd = 0x0F ) +func (e QEncCmd) String() string { + switch e { + case QENC_CMD_RESET: + return "Reset cmd" + case QENC_CMD_ENCRYPT: + return "Encrypt cmd" + case QENC_CMD_DECRYPT: + return "Decrypt cmd" + case QENC_CMD_MORE_DATA: + return "More data cmd" + case QENC_CMD_UNLOCK: + return "Unlock cmd" + case QENC_CMD_LOCK: + return "Lock cmd" + case QENC_CMD_SET_CFG: + return "Set cfg cmd" + case QENC_CMD_GET_MODE: + return "Get mode cmd" + case QENC_CMD_GET_BUFFER: + return "Get buffer cmd" + case QENC_CMD_GET_KEYS: + return "Get keys cmd" + case QENC_CMD_GET_BUFFSIZE: + return "Get buffsize cmd" + case QENC_CMD_SET_KEYS: + return "Set keys cmd" + case QENC_CMD_GET_CFG: + return "Get cfg cmd" + case QENC_CMD_INITIALIZE: + return "Initialize cmd" + case QENC_CMD_NONE: + return "None cmd" + case QENC_CMD_SET_BUFFER: + return "Set buffer cmd" + default: + return "Unknown cmd" + } +} + type QEncMode byte const ( @@ -35,6 +85,21 @@ const ( QENC_MODE_INIT QEncMode = 0x03 ) +func (e QEncMode) String() string { + switch e { + case QENC_MODE_CLOSED: + return "Closed" + case QENC_MODE_OPEN: + return "Open" + case QENC_MODE_LOAD: + return "Load" + case QENC_MODE_INIT: + return "Init" + default: + return "Unknown" + } +} + type QEncSubMode byte const ( @@ -44,6 +109,21 @@ const ( QENC_SUB_MODE_VERIFY_PASSWORD QEncSubMode = 0x03 ) +func (e QEncSubMode) String() string { + switch e { + case QENC_SUB_MODE_NONE: + return "None" + case QENC_SUB_MODE_SEED: + return "Seed" + case QENC_SUB_MODE_PASSWORD: + return "Password" + case QENC_SUB_MODE_VERIFY_PASSWORD: + return "Verify Password" + default: + return "Unknown" + } +} + type QEncCfg byte const ( @@ -91,71 +171,55 @@ func (q *QCfg) Initialized() bool { } func QCmdReset(dev *QEncDevice) (err error) { - buf, err := initBuffer(dev, QENC_CMD_RESET) - if err != nil { - return err - } - - _, err = dev.SendBuffer(buf) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + _, err = sendDataWrapper(dev, []byte{}, QENC_CMD_RESET, id) return err } func QCmdUnlock(dev *QEncDevice) (err error) { - buf, err := initBuffer(dev, QENC_CMD_UNLOCK) - if err != nil { - return err - } - - _, err = dev.SendBuffer(buf) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + _, err = sendDataWrapper(dev, []byte{}, QENC_CMD_UNLOCK, id) return err } func QCmdLock(dev *QEncDevice) (err error) { - buf, err := initBuffer(dev, QENC_CMD_LOCK) - if err != nil { - return err - } - - _, err = dev.SendBuffer(buf) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + _, err = sendDataWrapper(dev, []byte{}, QENC_CMD_LOCK, id) return err } func QCmdInitialize(dev *QEncDevice) (err error) { - buf, err := initBuffer(dev, QENC_CMD_INITIALIZE) - if err != nil { - return err - } - - _, err = dev.SendBuffer(buf) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + _, err = sendDataWrapper(dev, []byte{}, QENC_CMD_INITIALIZE, id) return err } -func QCmdEncrypt(dev *QEncDevice, data []byte) (err error) { - return sendData(dev, data, QENC_CMD_ENCRYPT) +func QCmdEncrypt(dev *QEncDevice, data []byte) (ret []byte, err error) { + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + return sendDataWrapper(dev, data, QENC_CMD_ENCRYPT, id) } -func QCmdDecrypt(dev *QEncDevice, data []byte) (err error) { - return sendData(dev, data, QENC_CMD_DECRYPT) +func QCmdDecrypt(dev *QEncDevice, data []byte) (ret []byte, err error) { + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + return sendDataWrapper(dev, data, QENC_CMD_DECRYPT, id) } func QCmdGetMode(dev *QEncDevice) (retm QEncMode, retsm QEncSubMode, err error) { - buf, err := initBuffer(dev, QENC_CMD_GET_MODE) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + rbuf, err := sendDataWrapper(dev, []byte{}, QENC_CMD_GET_MODE, id) if err != nil { return retm, retsm, err } - rbuf, err := dev.SendBuffer(buf) - if err != nil { - return retm, retsm, err - } - - dataLen := binary.LittleEndian.Uint16(rbuf) - if int(dataLen) != 2 { - return retm, retsm, fmt.Errorf("Invalid data") - } - - retm = QEncMode(rbuf[3]) - retsm = QEncSubMode(rbuf[4]) + retm = QEncMode(rbuf[0]) + retsm = QEncSubMode(rbuf[1]) return retm, retsm, nil } @@ -184,119 +248,126 @@ func QCmdSetCfgTimeout(dev *QEncDevice, val uint8) (err error) { return setCfg(dev, QENC_CFG_TIMEOUT, val) } -func QCmdGetBuffer(dev *QEncDevice) (ret []byte, err error) { - return receiveData(dev, QENC_CMD_GET_BUFFER) -} - func QCmdGetKeys(dev *QEncDevice) (ret []byte, err error) { - return receiveData(dev, QENC_CMD_GET_KEYS) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + return sendDataWrapper(dev, []byte{}, QENC_CMD_GET_KEYS, id) } func QCmdSetKeys(dev *QEncDevice, data []byte) (err error) { - return sendData(dev, data, QENC_CMD_SET_KEYS) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + _, err = sendDataWrapper(dev, data, QENC_CMD_SET_KEYS, id) + return err } func QCmdGetCfg(dev *QEncDevice) (ret *QCfg, err error) { - buf, err := initBuffer(dev, QENC_CMD_GET_CFG) + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + rbuf, err := sendDataWrapper(dev, []byte{}, QENC_CMD_GET_CFG, id) if err != nil { return ret, err } - rbuf, err := dev.SendBuffer(buf) - if err != nil { - return ret, err - } - - dataLen := binary.LittleEndian.Uint16(rbuf) - if int(dataLen) != 6 { - return ret, fmt.Errorf("Invalid data") - } - ret = &QCfg{} ret.val = make([]byte, 6) - copy(ret.val, rbuf[3:3+dataLen]) + copy(ret.val, rbuf[0:6]) return ret, nil } func setCfg(dev *QEncDevice, cfg QEncCfg, val uint8) (err error) { - buf, err := initBuffer(dev, QENC_CMD_SET_CFG) - if err != nil { - return err - } + data := []byte{byte(cfg), val} - binary.LittleEndian.PutUint16(buf[3:], uint16(2)) - - buf[5] = byte(cfg) - buf[6] = val - - if _, err = dev.SendBuffer(buf); err != nil { - return err - } - return nil + rand.Seed(time.Now().UnixNano()) + id := rand.Uint32() + _, err = sendDataWrapper(dev, data, QENC_CMD_SET_CFG, id) + return err } -func sendData(dev *QEncDevice, data []byte, cmd QEncCmd) (err error) { - buf, err := initBuffer(dev, cmd) - if err != nil { - return err - } +func sendDataWrapper(dev *QEncDevice, data []byte, cmd QEncCmd, id uint32) (ret []byte, err error) { + done := make(chan bool) + msgc := make(chan string) - dataLen := len(data) - binary.LittleEndian.PutUint16(buf[3:], uint16(dataLen)) - - if dataLen <= (int(dev.GetBufSize()) - 5) { - copy(buf[5:], data) - if _, err = dev.SendBuffer(buf); err != nil { - return err + go func() { + defer func() { done <- true }() + ret, err = sendData(dev, data, cmd, id, msgc) + if err != nil { + if QEncErrHandler != nil { + QEncErrHandler(err) + } + return } - return nil - } + }() - dataPos := 0 - copy(buf[5:], data[:dev.GetBufSize()-5]) - if _, err := dev.SendBuffer(buf); err != nil { - return err - } - dataPos += int(dev.GetBufSize()) - 5 - - buf[2] = byte(QENC_CMD_MORE_DATA) + end := false for { - copy(buf[3:], data[dataPos:]) - if _, err := dev.SendBuffer(buf); err != nil { - return nil + if end { + if QEncEndHandler != nil { + QEncEndHandler() + } + break } - if (dataLen - dataPos) <= (int(dev.GetBufSize()) - 3) { - return nil + select { + case <-done: + end = true + if err != nil { + return ret, err + } + case msg := <-msgc: + if QEncInfoHandler != nil { + QEncInfoHandler(msg) + } + default: + time.Sleep(500 * time.Millisecond) } - dataPos += int(dev.GetBufSize()) - 3 } + + return ret, err } -func receiveData(dev *QEncDevice, cmd QEncCmd) (ret []byte, err error) { +func sendData(dev *QEncDevice, data []byte, cmd QEncCmd, id uint32, msg chan string) (ret []byte, err error) { buf, err := initBuffer(dev, cmd) if err != nil { return ret, err } - for { - rbuf, err := dev.SendBuffer(buf) - if err != nil { - return ret, err - } + dataLen := len(data) + binary.LittleEndian.PutUint16(buf[3:], uint16(dataLen)) + binary.LittleEndian.PutUint32(buf[5:], id) - if rbuf[0] == 1 { - dataLen := binary.LittleEndian.Uint16(rbuf[1:3]) - if int(dataLen) > int(dev.GetBufSize())-3 { + dataPos := 0 + + for { + copy(buf[9:], data[dataPos:]) + rbuf, err := dev.SendBuffer(buf, msg) + qerr := ToQEncError(err) + if err != nil && qerr.errCode != QENC_ERR_MORE_DATA { + return ret, err + } else if qerr.errCode == QENC_ERR_MORE_DATA { + resDataLen := binary.LittleEndian.Uint16(rbuf[1:3]) + if int(resDataLen) > int(dev.GetBufSize())-3 { return ret, fmt.Errorf("Invalid data") } - b := make([]byte, dataLen) + b := make([]byte, resDataLen) copy(b, rbuf[3:]) ret = append(ret, b...) - return ret, nil + continue } - ret = append(ret, rbuf[1:]...) + resDataLen := binary.LittleEndian.Uint16(rbuf[0:2]) + if int(resDataLen) > int(dev.GetBufSize())-2 { + return ret, fmt.Errorf("Invalid data") + } + + b := make([]byte, resDataLen) + copy(b, rbuf[2:]) + ret = append(ret, b...) + + if (dataLen - dataPos) <= (int(dev.GetBufSize()) - 9) { + return ret, err + } + dataPos += int(dev.GetBufSize()) - 9 } } diff --git a/qmk-dev.go b/qmk-dev.go index 04cda75..f8bcfda 100644 --- a/qmk-dev.go +++ b/qmk-dev.go @@ -45,7 +45,7 @@ func (d *QEncDevice) Close() (err error) { return d.dev.Close() } -func (d *QEncDevice) SendBuffer(data []byte) (ret []byte, err error) { +func (d *QEncDevice) SendBuffer(data []byte, msg chan string) (ret []byte, err error) { startTime := time.Now() for { ret = make([]byte, d.bufSize) @@ -62,12 +62,22 @@ func (d *QEncDevice) SendBuffer(data []byte) (ret []byte, err error) { return ret, fmt.Errorf("Failed to read: '%w'", err) } + var dataLen uint16 ret, err = QEncCheckResponse(ret) if err != nil { qerr := ToQEncError(err) if qerr.errCode != QENC_ERR_RETRY { return ret, err } + fmt.Println("RETRY") + dataLen = binary.LittleEndian.Uint16(ret[:2]) + if int(dataLen) == 5 { + if msg != nil { + timeout := binary.LittleEndian.Uint32(ret[4:8]) + cmd := QEncCmd(ret[3]) + msg <- fmt.Sprintf("Allow request for '%s' (timeout: %ds)", cmd, timeout) + } + } time.Sleep(time.Second * time.Duration(d.retryWait)) } else { return ret, err @@ -114,7 +124,7 @@ func (d *QEncDevice) setBufSize() (err error) { return fmt.Errorf("Invalid data") } - d.bufSize = buf[3] + d.bufSize = buf[2] return nil } return fmt.Errorf("Unsupported device: '%w'", err) diff --git a/qmk-err.go b/qmk-err.go index cfc71cc..a71e29a 100644 --- a/qmk-err.go +++ b/qmk-err.go @@ -10,6 +10,7 @@ const ( QENC_ERR_INVALID QEncErr = 0x04 QENC_ERR_RETRY QEncErr = 0x05 QENC_ERR_HW_SUPPORT QEncErr = 0x06 + QENC_ERR_MORE_DATA QEncErr = 0x07 // device retry timeout QENC_ERR_TIMEOUT QEncErr = 0xFF ) @@ -32,6 +33,8 @@ func (e *QEncError) Error() string { return "Retry request" case QENC_ERR_HW_SUPPORT: return "Not supported by hardware" + case QENC_ERR_MORE_DATA: + return "More data available" case QENC_ERR_TIMEOUT: return "Timeout reached" default: @@ -47,6 +50,12 @@ func QEncCheckResponse(data []byte) (ret []byte, err error) { rerr := new(QEncError) rerr.errCode = QEncErr(data[0]) + if data[0] == byte(QENC_ERR_RETRY) { + ret = data[1:] + } else { + ret = data + } + return ret, rerr }