| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- package via
- import (
- "fmt"
- "netdome.biz/paul/impact-80/internal/device"
- "netdome.biz/paul/impact-80/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
- )
- // LEDType defines QMK LED subsystem types.
- type LEDType uint8
- const (
- RGBLight LEDType = 0x02
- RGBMatrix LEDType = 0x03
- SideLight LEDType = 0x04
- )
- // SetValue sends a set value command for the given LED type and parameter.
- func (p *Protocol) SetValue(ledType LEDType, param uint8, value uint8) error {
- report := make([]byte, 32)
- report[0] = byte(CustomSet)
- report[1] = byte(ledType)
- report[2] = param
- report[3] = value
- if _, err := p.handle.SendReport(0x00, report); err != nil {
- return err
- }
- _, err := p.readResponse(CustomSet, ledType, param)
- return err
- }
- func (p *Protocol) DisableLighting() error {
- for _, channel := range []LEDType{RGBLight, RGBMatrix, SideLight} {
- if err := p.SetValue(channel, 0x02, 0x00); err != nil {
- return err
- }
- if err := p.SetValue(channel, 0x01, 0x00); err != nil {
- return err
- }
- }
- return nil
- }
- func (p *Protocol) EnableLighting() error {
- channels := []struct {
- channel LEDType
- effect uint8
- }{
- {RGBLight, 0x04},
- {RGBMatrix, 0x05},
- {SideLight, 0x04},
- }
- for _, channel := range channels {
- if err := p.SetValue(channel.channel, 0x02, channel.effect); err != nil {
- return err
- }
- if err := p.SetValue(channel.channel, 0x01, 160); err != nil {
- return err
- }
- }
- return nil
- }
- func (p *Protocol) SetLightingColor(hue uint8, saturation uint8) error {
- for _, channel := range []LEDType{RGBLight, RGBMatrix, SideLight} {
- if err := p.SetColor(channel, hue, saturation); err != nil {
- return err
- }
- }
- return nil
- }
- func (p *Protocol) SetColor(ledType LEDType, hue uint8, saturation uint8) error {
- report := make([]byte, 32)
- report[0] = byte(CustomSet)
- report[1] = byte(ledType)
- report[2] = 0x04
- report[3] = hue
- report[4] = saturation
- if _, err := p.handle.SendReport(0x00, report); err != nil {
- return err
- }
- _, err := p.readResponse(CustomSet, ledType, 0x04)
- return err
- }
- // GetValue sends a get value request and reads the response.
- func (p *Protocol) GetValue(ledType LEDType, param uint8) ([]byte, error) {
- report := make([]byte, 32)
- report[0] = byte(CustomGet)
- report[1] = byte(ledType)
- 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, ledType, 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, ledType LEDType, param uint8) ([]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, fmt.Errorf("unhandled response")
- }
- if buf[0] != byte(command) {
- return nil, fmt.Errorf("unexpected response command: 0x%02x", buf[0])
- }
- if buf[1] != byte(ledType) || buf[2] != param {
- return nil, fmt.Errorf("response value mismatch: got channel 0x%02x value 0x%02x", buf[1], buf[2])
- }
- return buf, nil
- }
|