222 lines
4.7 KiB
Go
222 lines
4.7 KiB
Go
package qmkenc
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/sstallion/go-hid"
|
|
)
|
|
|
|
const encDevUsage uint16 = 0x61
|
|
const encDevUsagePage uint16 = 0xFF60
|
|
|
|
type QEncDevice struct {
|
|
path string
|
|
serial string
|
|
product string
|
|
manufacturer string
|
|
dev *hid.Device
|
|
bufSize uint8
|
|
timeout int
|
|
retryTimeout int
|
|
retryWait int
|
|
lockfile string
|
|
}
|
|
|
|
func (d *QEncDevice) GetPath() string { return d.path }
|
|
func (d *QEncDevice) GetSerial() string { return d.serial }
|
|
func (d *QEncDevice) GetProduct() string { return d.product }
|
|
func (d *QEncDevice) GetManufacturer() string { return d.manufacturer }
|
|
func (d *QEncDevice) GetBufSize() uint8 { return d.bufSize }
|
|
|
|
func (d *QEncDevice) Open() (err error) {
|
|
if d.dev, err = hid.OpenPath(d.path); err != nil {
|
|
return fmt.Errorf("Error opening device: '%w'", err)
|
|
}
|
|
|
|
if err = d.setBufSize(); err != nil {
|
|
d.Close()
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *QEncDevice) Close() (err error) {
|
|
if d.lockfile != "" {
|
|
os.Remove(d.lockfile)
|
|
}
|
|
return d.dev.Close()
|
|
}
|
|
|
|
func (d *QEncDevice) SetLockfile(lockfile string) {
|
|
d.lockfile = lockfile
|
|
}
|
|
|
|
func (d *QEncDevice) SendBuffer(data []byte, msg chan string) (ret []byte, err error) {
|
|
startTime := time.Now()
|
|
for {
|
|
ret = make([]byte, d.bufSize)
|
|
|
|
if len(data) != int(d.bufSize) {
|
|
return ret, fmt.Errorf("Invalid data size")
|
|
}
|
|
|
|
if _, err = d.dev.Write(data); err != nil {
|
|
return ret, fmt.Errorf("Failed to write: '%w'", err)
|
|
}
|
|
|
|
if _, err = d.dev.ReadWithTimeout(ret, time.Duration(time.Second*time.Duration(d.timeout))); err != nil {
|
|
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
|
|
}
|
|
if time.Now().Sub(startTime).Seconds() >= float64(d.retryTimeout) {
|
|
qerr := new(QEncError)
|
|
qerr.errCode = QENC_ERR_TIMEOUT
|
|
return ret, qerr
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *QEncDevice) setBufSize() (err error) {
|
|
if d.bufSize > 0 {
|
|
return nil
|
|
}
|
|
|
|
buf_sizes := []int{8, 16, 32, 64, 128}
|
|
|
|
for _, bs := range buf_sizes {
|
|
buf_max := bs
|
|
buf := make([]byte, buf_max)
|
|
sbuf := make([]byte, buf_max)
|
|
|
|
if copy(sbuf, magicHeader) != 2 {
|
|
return fmt.Errorf("Invalid magic Header")
|
|
}
|
|
sbuf[2] = byte(QENC_CMD_GET_BUFFSIZE)
|
|
|
|
if _, err = d.dev.Write(sbuf); err != nil {
|
|
return fmt.Errorf("Failed to write to device: '%w'", err)
|
|
}
|
|
|
|
_, err = d.dev.ReadWithTimeout(buf, time.Duration(time.Second*1))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if buf, err = QEncCheckResponse(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
data_len := binary.LittleEndian.Uint16(buf)
|
|
if int(data_len) != 1 {
|
|
return fmt.Errorf("Invalid data")
|
|
}
|
|
|
|
d.bufSize = buf[2]
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Unsupported device: '%w'", err)
|
|
}
|
|
|
|
func (d *QEncDevice) Lock() (err error) {
|
|
if d.lockfile == "" {
|
|
return nil
|
|
}
|
|
|
|
for {
|
|
if _, err := os.Stat(d.lockfile); err == nil {
|
|
time.Sleep(time.Second * 1)
|
|
continue
|
|
} else if os.IsNotExist(err) {
|
|
file, err := os.Create(d.lockfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
file.Close()
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *QEncDevice) UnLock() {
|
|
if d.lockfile == "" {
|
|
return
|
|
}
|
|
os.Remove(d.lockfile)
|
|
}
|
|
|
|
func QEncNewDevice(path string, bufSize uint8, timeout, retryTimeout, retryWait int, lockfile string) *QEncDevice {
|
|
return &QEncDevice{
|
|
path: path,
|
|
timeout: timeout,
|
|
retryTimeout: retryTimeout,
|
|
retryWait: retryWait,
|
|
bufSize: bufSize,
|
|
lockfile: lockfile,
|
|
}
|
|
}
|
|
|
|
func QEncScanDevices(ids ...uint16) (ret []*QEncDevice, err error) {
|
|
vid := uint16(hid.VendorIDAny)
|
|
pid := uint16(hid.ProductIDAny)
|
|
|
|
if len(ids) > 2 {
|
|
return ret, fmt.Errorf("To many ids given")
|
|
}
|
|
|
|
if len(ids) == 1 {
|
|
vid = ids[0]
|
|
}
|
|
if len(ids) == 2 {
|
|
vid = ids[0]
|
|
pid = ids[1]
|
|
}
|
|
|
|
hid.Enumerate(vid, pid,
|
|
func(info *hid.DeviceInfo) error {
|
|
if info.Usage == encDevUsage && info.UsagePage == encDevUsagePage {
|
|
d := QEncDevice{
|
|
path: info.Path,
|
|
serial: info.SerialNbr,
|
|
product: info.ProductStr,
|
|
manufacturer: info.MfrStr,
|
|
timeout: 5,
|
|
retryTimeout: 10,
|
|
retryWait: 1,
|
|
}
|
|
ret = append(ret, &d)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if len(ret) == 0 {
|
|
return ret, fmt.Errorf("No device found")
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func QEncExit() (err error) {
|
|
return hid.Exit()
|
|
}
|