1
1
package usbgadget
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
"os"
7
+ "reflect"
8
+ "time"
6
9
)
7
10
8
11
var keyboardConfig = gadgetConfigItem {
@@ -36,6 +39,7 @@ var keyboardReportDesc = []byte{
36
39
0x81 , 0x03 , /* INPUT (Cnst,Var,Abs) */
37
40
0x95 , 0x05 , /* REPORT_COUNT (5) */
38
41
0x75 , 0x01 , /* REPORT_SIZE (1) */
42
+
39
43
0x05 , 0x08 , /* USAGE_PAGE (LEDs) */
40
44
0x19 , 0x01 , /* USAGE_MINIMUM (Num Lock) */
41
45
0x29 , 0x05 , /* USAGE_MAXIMUM (Kana) */
@@ -54,13 +58,139 @@ var keyboardReportDesc = []byte{
54
58
0xc0 , /* END_COLLECTION */
55
59
}
56
60
57
- func (u * UsbGadget ) keyboardWriteHidFile (data []byte ) error {
58
- if u .keyboardHidFile == nil {
59
- var err error
60
- u .keyboardHidFile , err = os .OpenFile ("/dev/hidg0" , os .O_RDWR , 0666 )
61
- if err != nil {
62
- return fmt .Errorf ("failed to open hidg0: %w" , err )
61
+ const (
62
+ hidReadBufferSize = 8
63
+ // https://www.usb.org/sites/default/files/documents/hid1_11.pdf
64
+ // https://www.usb.org/sites/default/files/hut1_2.pdf
65
+ KeyboardLedMaskNumLock = 1 << 0
66
+ KeyboardLedMaskCapsLock = 1 << 1
67
+ KeyboardLedMaskScrollLock = 1 << 2
68
+ KeyboardLedMaskCompose = 1 << 3
69
+ KeyboardLedMaskKana = 1 << 4
70
+ ValidKeyboardLedMasks = KeyboardLedMaskNumLock | KeyboardLedMaskCapsLock | KeyboardLedMaskScrollLock | KeyboardLedMaskCompose | KeyboardLedMaskKana
71
+ )
72
+
73
+ // Synchronization between LED states and CAPS LOCK, NUM LOCK, SCROLL LOCK,
74
+ // COMPOSE, and KANA events is maintained by the host and NOT the keyboard. If
75
+ // using the keyboard descriptor in Appendix B, LED states are set by sending a
76
+ // 5-bit absolute report to the keyboard via a Set_Report(Output) request.
77
+ type KeyboardState struct {
78
+ NumLock bool `json:"num_lock"`
79
+ CapsLock bool `json:"caps_lock"`
80
+ ScrollLock bool `json:"scroll_lock"`
81
+ Compose bool `json:"compose"`
82
+ Kana bool `json:"kana"`
83
+ }
84
+
85
+ func getKeyboardState (b byte ) KeyboardState {
86
+ // should we check if it's the correct usage page?
87
+ return KeyboardState {
88
+ NumLock : b & KeyboardLedMaskNumLock != 0 ,
89
+ CapsLock : b & KeyboardLedMaskCapsLock != 0 ,
90
+ ScrollLock : b & KeyboardLedMaskScrollLock != 0 ,
91
+ Compose : b & KeyboardLedMaskCompose != 0 ,
92
+ Kana : b & KeyboardLedMaskKana != 0 ,
93
+ }
94
+ }
95
+
96
+ func (u * UsbGadget ) updateKeyboardState (b byte ) {
97
+ u .keyboardStateLock .Lock ()
98
+ defer u .keyboardStateLock .Unlock ()
99
+
100
+ if b &^ValidKeyboardLedMasks != 0 {
101
+ u .log .Trace ().Uint8 ("b" , b ).Msg ("contains invalid bits, ignoring" )
102
+ return
103
+ }
104
+
105
+ newState := getKeyboardState (b )
106
+ if reflect .DeepEqual (u .keyboardState , newState ) {
107
+ return
108
+ }
109
+ u .log .Info ().Interface ("old" , u .keyboardState ).Interface ("new" , newState ).Msg ("keyboardState updated" )
110
+ u .keyboardState = newState
111
+
112
+ if u .onKeyboardStateChange != nil {
113
+ (* u .onKeyboardStateChange )(newState )
114
+ }
115
+ }
116
+
117
+ func (u * UsbGadget ) SetOnKeyboardStateChange (f func (state KeyboardState )) {
118
+ u .onKeyboardStateChange = & f
119
+ }
120
+
121
+ func (u * UsbGadget ) GetKeyboardState () KeyboardState {
122
+ u .keyboardStateLock .Lock ()
123
+ defer u .keyboardStateLock .Unlock ()
124
+
125
+ return u .keyboardState
126
+ }
127
+
128
+ func (u * UsbGadget ) listenKeyboardEvents () {
129
+ var path string
130
+ if u .keyboardHidFile != nil {
131
+ path = u .keyboardHidFile .Name ()
132
+ }
133
+ l := u .log .With ().Str ("listener" , "keyboardEvents" ).Str ("path" , path ).Logger ()
134
+ l .Trace ().Msg ("starting" )
135
+
136
+ go func () {
137
+ buf := make ([]byte , hidReadBufferSize )
138
+ for {
139
+ select {
140
+ case <- u .keyboardStateCtx .Done ():
141
+ l .Info ().Msg ("context done" )
142
+ return
143
+ default :
144
+ l .Trace ().Msg ("reading from keyboard" )
145
+ if u .keyboardHidFile == nil {
146
+ l .Error ().Msg ("keyboardHidFile is nil" )
147
+ time .Sleep (time .Second )
148
+ continue
149
+ }
150
+ n , err := u .keyboardHidFile .Read (buf )
151
+ if err != nil {
152
+ l .Error ().Err (err ).Msg ("failed to read" )
153
+ continue
154
+ }
155
+ l .Trace ().Int ("n" , n ).Bytes ("buf" , buf ).Msg ("got data from keyboard" )
156
+ if n != 1 {
157
+ l .Trace ().Int ("n" , n ).Msg ("expected 1 byte, got" )
158
+ continue
159
+ }
160
+ u .updateKeyboardState (buf [0 ])
161
+ }
63
162
}
163
+ }()
164
+ }
165
+
166
+ func (u * UsbGadget ) openKeyboardHidFile () error {
167
+ if u .keyboardHidFile != nil {
168
+ return nil
169
+ }
170
+
171
+ var err error
172
+ u .keyboardHidFile , err = os .OpenFile ("/dev/hidg0" , os .O_RDWR , 0666 )
173
+ if err != nil {
174
+ return fmt .Errorf ("failed to open hidg0: %w" , err )
175
+ }
176
+
177
+ if u .keyboardStateCancel != nil {
178
+ u .keyboardStateCancel ()
179
+ }
180
+
181
+ u .keyboardStateCtx , u .keyboardStateCancel = context .WithCancel (context .Background ())
182
+ u .listenKeyboardEvents ()
183
+
184
+ return nil
185
+ }
186
+
187
+ func (u * UsbGadget ) OpenKeyboardHidFile () error {
188
+ return u .openKeyboardHidFile ()
189
+ }
190
+
191
+ func (u * UsbGadget ) keyboardWriteHidFile (data []byte ) error {
192
+ if err := u .openKeyboardHidFile (); err != nil {
193
+ return err
64
194
}
65
195
66
196
_ , err := u .keyboardHidFile .Write (data )
0 commit comments