protocol.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package via
  2. import (
  3. "fmt"
  4. "github.com/wobkey/rgb/internal/device"
  5. "github.com/wobkey/rgb/internal/hid"
  6. )
  7. type transport interface {
  8. SendReport(reportID byte, report []byte) (int, error)
  9. Read(buf []byte) (int, error)
  10. Close() error
  11. }
  12. // Protocol implements the VIA protocol for communication with a keyboard.
  13. type Protocol struct {
  14. handle transport
  15. kb device.Keyboard
  16. }
  17. // New creates a new VIA protocol handler for a connected device.
  18. func New(dev device.Device) (*Protocol, error) {
  19. h, err := hid.OpenPath(dev.Path)
  20. if err != nil {
  21. return nil, fmt.Errorf("open device %s: %w", dev.Path, err)
  22. }
  23. return &Protocol{
  24. handle: h,
  25. kb: dev.Keyboard,
  26. }, nil
  27. }
  28. // Close releases the device handle.
  29. func (p *Protocol) Close() error {
  30. if p.handle != nil {
  31. return p.handle.Close()
  32. }
  33. return nil
  34. }
  35. // Message defines VIA message types.
  36. type Message uint8
  37. const (
  38. CustomSet Message = 0x07
  39. CustomGet Message = 0x08
  40. Unhandled Message = 0xff
  41. )
  42. // LEDType defines QMK LED subsystem types.
  43. type LEDType uint8
  44. const (
  45. RGBLight LEDType = 0x02
  46. RGBMatrix LEDType = 0x03
  47. SideLight LEDType = 0x04
  48. )
  49. // SetValue sends a set value command for the given LED type and parameter.
  50. func (p *Protocol) SetValue(ledType LEDType, param uint8, value uint8) error {
  51. report := make([]byte, 32)
  52. report[0] = byte(CustomSet)
  53. report[1] = byte(ledType)
  54. report[2] = param
  55. report[3] = value
  56. if _, err := p.handle.SendReport(0x00, report); err != nil {
  57. return err
  58. }
  59. _, err := p.readResponse(CustomSet, ledType, param)
  60. return err
  61. }
  62. func (p *Protocol) DisableLighting() error {
  63. for _, channel := range []LEDType{RGBLight, RGBMatrix, SideLight} {
  64. if err := p.SetValue(channel, 0x02, 0x00); err != nil {
  65. return err
  66. }
  67. if err := p.SetValue(channel, 0x01, 0x00); err != nil {
  68. return err
  69. }
  70. }
  71. return nil
  72. }
  73. func (p *Protocol) EnableLighting() error {
  74. for _, channel := range []LEDType{RGBLight, RGBMatrix, SideLight} {
  75. if err := p.SetValue(channel, 0x02, 0x01); err != nil {
  76. return err
  77. }
  78. if err := p.SetValue(channel, 0x01, 160); err != nil {
  79. return err
  80. }
  81. }
  82. return nil
  83. }
  84. func (p *Protocol) SetColor(ledType LEDType, hue uint8, saturation uint8) error {
  85. report := make([]byte, 32)
  86. report[0] = byte(CustomSet)
  87. report[1] = byte(ledType)
  88. report[2] = 0x04
  89. report[3] = hue
  90. report[4] = saturation
  91. if _, err := p.handle.SendReport(0x00, report); err != nil {
  92. return err
  93. }
  94. _, err := p.readResponse(CustomSet, ledType, 0x04)
  95. return err
  96. }
  97. // GetValue sends a get value request and reads the response.
  98. func (p *Protocol) GetValue(ledType LEDType, param uint8) ([]byte, error) {
  99. report := make([]byte, 32)
  100. report[0] = byte(CustomGet)
  101. report[1] = byte(ledType)
  102. report[2] = param
  103. _, err := p.handle.SendReport(0x00, report)
  104. if err != nil {
  105. return nil, fmt.Errorf("send get request: %w", err)
  106. }
  107. buf, err := p.readResponse(CustomGet, ledType, param)
  108. if err != nil {
  109. return nil, err
  110. }
  111. valueSize := 1
  112. if param == 0x04 {
  113. valueSize = 2
  114. }
  115. return append([]byte(nil), buf[3:3+valueSize]...), nil
  116. }
  117. func (p *Protocol) readResponse(command Message, ledType LEDType, param uint8) ([]byte, error) {
  118. buf := make([]byte, 32)
  119. n, err := p.handle.Read(buf)
  120. if err != nil {
  121. return nil, fmt.Errorf("read response: %w", err)
  122. }
  123. if n != len(buf) {
  124. return nil, fmt.Errorf("short response: got %d bytes, want 32", n)
  125. }
  126. if buf[0] == byte(Unhandled) {
  127. return nil, fmt.Errorf("unhandled response")
  128. }
  129. if buf[0] != byte(command) {
  130. return nil, fmt.Errorf("unexpected response command: 0x%02x", buf[0])
  131. }
  132. if buf[1] != byte(ledType) || buf[2] != param {
  133. return nil, fmt.Errorf("response value mismatch: got channel 0x%02x value 0x%02x", buf[1], buf[2])
  134. }
  135. return buf, nil
  136. }