protocol.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. var h *hid.Device
  20. var err error
  21. if dev.Path != "" {
  22. h, err = hid.OpenPath(dev.Path)
  23. } else {
  24. h, err = hid.Open(dev.VendorID, dev.ProductID)
  25. }
  26. if err != nil {
  27. return nil, fmt.Errorf("open device: %w", err)
  28. }
  29. return &Protocol{
  30. handle: h,
  31. kb: dev.Keyboard,
  32. }, nil
  33. }
  34. // Close releases the device handle.
  35. func (p *Protocol) Close() error {
  36. if p.handle != nil {
  37. return p.handle.Close()
  38. }
  39. return nil
  40. }
  41. // Message defines VIA message types.
  42. type Message uint8
  43. const (
  44. CustomSet Message = 0x07
  45. CustomGet Message = 0x08
  46. Unhandled Message = 0xff
  47. )
  48. // LEDType defines QMK LED subsystem types.
  49. type LEDType uint8
  50. const (
  51. RGBLight LEDType = 0x02
  52. RGBMatrix LEDType = 0x03
  53. SideLight LEDType = 0x04
  54. )
  55. // SetValue sends a set value command for the given LED type and parameter.
  56. func (p *Protocol) SetValue(ledType LEDType, param uint8, value uint8) error {
  57. report := make([]byte, 32)
  58. report[0] = byte(CustomSet)
  59. report[1] = byte(ledType)
  60. report[2] = param
  61. report[3] = value
  62. if _, err := p.handle.SendReport(0x00, report); err != nil {
  63. return err
  64. }
  65. _, err := p.readResponse(CustomSet, ledType, param)
  66. return err
  67. }
  68. func (p *Protocol) DisableLighting() error {
  69. for _, channel := range []LEDType{RGBLight, RGBMatrix, SideLight} {
  70. if err := p.SetValue(channel, 0x02, 0x00); err != nil {
  71. return err
  72. }
  73. if err := p.SetValue(channel, 0x01, 0x00); err != nil {
  74. return err
  75. }
  76. }
  77. return nil
  78. }
  79. func (p *Protocol) EnableLighting() error {
  80. channels := []struct {
  81. channel LEDType
  82. effect uint8
  83. }{
  84. {RGBLight, 0x04},
  85. {RGBMatrix, 0x05},
  86. {SideLight, 0x04},
  87. }
  88. for _, channel := range channels {
  89. if err := p.SetValue(channel.channel, 0x02, channel.effect); err != nil {
  90. return err
  91. }
  92. if err := p.SetValue(channel.channel, 0x01, 160); err != nil {
  93. return err
  94. }
  95. }
  96. return nil
  97. }
  98. func (p *Protocol) SetLightingColor(hue uint8, saturation uint8) error {
  99. for _, channel := range []LEDType{RGBLight, RGBMatrix, SideLight} {
  100. if err := p.SetColor(channel, hue, saturation); err != nil {
  101. return err
  102. }
  103. }
  104. return nil
  105. }
  106. func (p *Protocol) SetColor(ledType LEDType, hue uint8, saturation uint8) error {
  107. report := make([]byte, 32)
  108. report[0] = byte(CustomSet)
  109. report[1] = byte(ledType)
  110. report[2] = 0x04
  111. report[3] = hue
  112. report[4] = saturation
  113. if _, err := p.handle.SendReport(0x00, report); err != nil {
  114. return err
  115. }
  116. _, err := p.readResponse(CustomSet, ledType, 0x04)
  117. return err
  118. }
  119. // GetValue sends a get value request and reads the response.
  120. func (p *Protocol) GetValue(ledType LEDType, param uint8) ([]byte, error) {
  121. report := make([]byte, 32)
  122. report[0] = byte(CustomGet)
  123. report[1] = byte(ledType)
  124. report[2] = param
  125. _, err := p.handle.SendReport(0x00, report)
  126. if err != nil {
  127. return nil, fmt.Errorf("send get request: %w", err)
  128. }
  129. buf, err := p.readResponse(CustomGet, ledType, param)
  130. if err != nil {
  131. return nil, err
  132. }
  133. valueSize := 1
  134. if param == 0x04 {
  135. valueSize = 2
  136. }
  137. return append([]byte(nil), buf[3:3+valueSize]...), nil
  138. }
  139. func (p *Protocol) readResponse(command Message, ledType LEDType, param uint8) ([]byte, error) {
  140. buf := make([]byte, 32)
  141. n, err := p.handle.Read(buf)
  142. if err != nil {
  143. return nil, fmt.Errorf("read response: %w", err)
  144. }
  145. if n != len(buf) {
  146. return nil, fmt.Errorf("short response: got %d bytes, want 32", n)
  147. }
  148. if buf[0] == byte(Unhandled) {
  149. return nil, fmt.Errorf("unhandled response")
  150. }
  151. if buf[0] != byte(command) {
  152. return nil, fmt.Errorf("unexpected response command: 0x%02x", buf[0])
  153. }
  154. if buf[1] != byte(ledType) || buf[2] != param {
  155. return nil, fmt.Errorf("response value mismatch: got channel 0x%02x value 0x%02x", buf[1], buf[2])
  156. }
  157. return buf, nil
  158. }