| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- package via
- import (
- "errors"
- "fmt"
- "netdome.biz/paul/qmk-rgb/internal/device"
- "netdome.biz/paul/qmk-rgb/internal/hid"
- )
- type transport interface {
- SendReport(reportID byte, report []byte) (int, error)
- Read(buf []byte) (int, error)
- Close() error
- }
- // Protocol implements the VIA protocol for communication with a keyboard.
- type Protocol struct {
- handle transport
- }
- // New creates a new VIA protocol handler for a connected device.
- func New(dev device.Device) (*Protocol, error) {
- h, err := hid.OpenPath(dev.Path)
- if err != nil {
- return nil, fmt.Errorf("open device: %w", err)
- }
- return &Protocol{handle: h}, nil
- }
- // Close releases the device handle.
- func (p *Protocol) Close() error {
- if p.handle != nil {
- return p.handle.Close()
- }
- return nil
- }
- // Message defines VIA message types.
- type Message uint8
- const (
- CustomSet Message = 0x07
- CustomGet Message = 0x08
- Unhandled Message = 0xff
- )
- // errUnhandled reports a channel or value ID the firmware does not implement.
- // It is a sentinel so a caller can tell "this does not exist here" from a
- // transport failure, which the two are otherwise indistinguishable in.
- var errUnhandled = errors.New("unhandled response")
- // SetValue sends a set value command for the given channel and parameter.
- func (p *Protocol) SetValue(ch Channel, param uint8, value uint8) error {
- report := make([]byte, 32)
- report[0] = byte(CustomSet)
- report[1] = byte(ch)
- report[2] = param
- report[3] = value
- if _, err := p.handle.SendReport(0x00, report); err != nil {
- return err
- }
- _, err := p.readResponse(CustomSet, byte(ch), param)
- return err
- }
- func (p *Protocol) SetColor(ch Channel, hue uint8, saturation uint8) error {
- report := make([]byte, 32)
- report[0] = byte(CustomSet)
- report[1] = byte(ch)
- report[2] = 0x04
- report[3] = hue
- report[4] = saturation
- if _, err := p.handle.SendReport(0x00, report); err != nil {
- return err
- }
- _, err := p.readResponse(CustomSet, byte(ch), 0x04)
- return err
- }
- // GetValue sends a get value request and reads the response.
- func (p *Protocol) GetValue(ch Channel, param uint8) ([]byte, error) {
- report := make([]byte, 32)
- report[0] = byte(CustomGet)
- report[1] = byte(ch)
- report[2] = param
- _, err := p.handle.SendReport(0x00, report)
- if err != nil {
- return nil, fmt.Errorf("send get request: %w", err)
- }
- buf, err := p.readResponse(CustomGet, byte(ch), param)
- if err != nil {
- return nil, err
- }
- valueSize := 1
- if param == 0x04 {
- valueSize = 2
- }
- return append([]byte(nil), buf[3:3+valueSize]...), nil
- }
- func (p *Protocol) readResponse(command Message, ch, param byte) ([]byte, error) {
- buf := make([]byte, 32)
- n, err := p.handle.Read(buf)
- if err != nil {
- return nil, fmt.Errorf("read response: %w", err)
- }
- if n != len(buf) {
- return nil, fmt.Errorf("short response: got %d bytes, want 32", n)
- }
- if buf[0] == byte(Unhandled) {
- return nil, errUnhandled
- }
- if buf[0] != byte(command) {
- return nil, fmt.Errorf("unexpected response command: 0x%02x", buf[0])
- }
- if buf[1] != ch || buf[2] != param {
- return nil, fmt.Errorf("response value mismatch: got channel 0x%02x value 0x%02x", buf[1], buf[2])
- }
- return buf, nil
- }
|