qmkenc/qmk-dev.go

228 lines
4.9 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
productId uint16
vendorId uint16
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) GetProductId() uint16 { return d.productId }
func (d *QEncDevice) GetVendorId() uint16 { return d.vendorId }
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,
productId: info.ProductID,
vendorId: info.VendorID,
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()
}