protocol.go 3.9 KB

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