11package usbgadget
22
33import (
4+ "context"
45 "fmt"
56 "os"
7+ "reflect"
8+ "time"
69)
710
811var keyboardConfig = gadgetConfigItem {
@@ -36,6 +39,7 @@ var keyboardReportDesc = []byte{
3639 0x81 , 0x03 , /* INPUT (Cnst,Var,Abs) */
3740 0x95 , 0x05 , /* REPORT_COUNT (5) */
3841 0x75 , 0x01 , /* REPORT_SIZE (1) */
42+
3943 0x05 , 0x08 , /* USAGE_PAGE (LEDs) */
4044 0x19 , 0x01 , /* USAGE_MINIMUM (Num Lock) */
4145 0x29 , 0x05 , /* USAGE_MAXIMUM (Kana) */
@@ -54,13 +58,139 @@ var keyboardReportDesc = []byte{
5458 0xc0 , /* END_COLLECTION */
5559}
5660
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+ }
63162 }
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
64194 }
65195
66196 _ , err := u .keyboardHidFile .Write (data )
0 commit comments