-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodule.go
More file actions
181 lines (146 loc) · 5.49 KB
/
Copy pathmodule.go
File metadata and controls
181 lines (146 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package windowsserialmouse
import (
"context"
"errors"
"fmt"
generic "go.viam.com/rdk/components/generic"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/resource"
"golang.org/x/sys/windows/registry"
)
var (
Disable = resource.NewModel("viam-soleng", "windows-serialmouse", "disable")
)
const sermouseKeyPath = `System\CurrentControlSet\Services\sermouse`
func init() {
resource.RegisterComponent(generic.API, Disable,
resource.Registration[resource.Resource, *Config]{
Constructor: newWindowsSerialmouseDisable,
},
)
}
type Config struct {
}
func (cfg *Config) Validate(path string) ([]string, []string, error) {
return nil, nil, nil
}
type windowsSerialmouseDisable struct {
resource.AlwaysRebuild
name resource.Name
logger logging.Logger
cfg *Config
cancelCtx context.Context
cancelFunc func()
}
func newWindowsSerialmouseDisable(ctx context.Context, deps resource.Dependencies, rawConf resource.Config, logger logging.Logger) (resource.Resource, error) {
conf, err := resource.NativeConfig[*Config](rawConf)
if err != nil {
return nil, err
}
return NewDisable(ctx, deps, rawConf.ResourceName(), conf, logger)
}
func NewDisable(ctx context.Context, deps resource.Dependencies, name resource.Name, conf *Config, logger logging.Logger) (resource.Resource, error) {
cancelCtx, cancelFunc := context.WithCancel(context.Background())
s := &windowsSerialmouseDisable{
name: name,
logger: logger,
cfg: conf,
cancelCtx: cancelCtx,
cancelFunc: cancelFunc,
}
return s, nil
}
func (s *windowsSerialmouseDisable) Name() resource.Name {
return s.name
}
func (s *windowsSerialmouseDisable) DoCommand(ctx context.Context, cmd map[string]interface{}) (map[string]interface{}, error) {
// https://paulhutch.blog/2019/06/24/disable-serial-mouse-detection/
// GPS will be on the serial line, not a serial mouse
startChanged, err := s.disableSermouseService()
if err != nil {
return nil, err
}
// Also stop Windows from polling the built-in serial port(s) for a serial
// mouse during enumeration, otherwise GPS data on the line can be
// misdetected. PNP0501 is the ACPI ID for the built-in 16550 serial port.
enumerationDisabledPorts, err := s.skipSerialPortEnumeration()
if err != nil {
return nil, err
}
message := "sermouse service was already disabled (Start was 4); serial port enumeration polling disabled"
if startChanged {
message = "sermouse service disabled (Start changed from 3 to 4); serial port enumeration polling disabled"
}
return map[string]interface{}{
"start": fmt.Sprintf(`%s\Start : 4`, sermouseKeyPath),
"sermouse-changed": startChanged,
"message": message,
"enumeration_disabled_ports": enumerationDisabledPorts,
}, nil
}
// disableSermouseService sets the sermouse service Start value to 4 (disabled).
// It reports whether the value was actually changed.
func (s *windowsSerialmouseDisable) disableSermouseService() (bool, error) {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, sermouseKeyPath, registry.QUERY_VALUE|registry.SET_VALUE)
if err != nil {
return false, fmt.Errorf("failed to open registry key: %w", err)
}
defer k.Close()
val, _, err := k.GetIntegerValue("Start")
if err != nil {
return false, fmt.Errorf("failed to read Start value: %w", err)
}
// No change required
if val == 4 {
return false, nil
}
if err := k.SetDWordValue("Start", 4); err != nil {
return false, fmt.Errorf("failed to set Start value: %w", err)
}
s.logger.Info("Windows serial mouse Start registry value changed from 3 to 4")
return true, nil
}
// skipSerialPortEnumeration writes a SkipEnumerations DWORD of 0xffffffff to
// every built-in serial port instance under
// SYSTEM\CurrentControlSet\Enum\ACPI\PNP0501, preventing Windows from polling
// those COM ports for a serial mouse. It returns the instance paths it updated.
func (s *windowsSerialmouseDisable) skipSerialPortEnumeration() ([]string, error) {
const enumPath = `System\CurrentControlSet\Enum\ACPI\PNP0501`
const skipValue = 0xffffffff
parent, err := registry.OpenKey(registry.LOCAL_MACHINE, enumPath, registry.ENUMERATE_SUB_KEYS)
if errors.Is(err, registry.ErrNotExist) {
s.logger.Debugf("registry key %s does not exist, skipping serial port enumeration", enumPath)
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("failed to open %s: %w", enumPath, err)
}
defer parent.Close()
instances, err := parent.ReadSubKeyNames(-1)
if err != nil {
return nil, fmt.Errorf("failed to enumerate serial port instances under %s: %w", enumPath, err)
}
enumerationDisabledPorts := make([]string, 0, len(instances))
for _, instance := range instances {
paramsPath := fmt.Sprintf(`%s\%s\Device Parameters`, enumPath, instance)
// CreateKey opens the key, creating it (and the Device Parameters subkey)
// if it does not already exist.
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, paramsPath, registry.SET_VALUE)
if err != nil {
return nil, fmt.Errorf("failed to open %s: %w", paramsPath, err)
}
if err := k.SetDWordValue("SkipEnumerations", skipValue); err != nil {
k.Close()
return nil, fmt.Errorf("failed to set SkipEnumerations on %s: %w", paramsPath, err)
}
k.Close()
s.logger.Infof("Set SkipEnumerations=0xffffffff on %s", paramsPath)
enumerationDisabledPorts = append(enumerationDisabledPorts,
fmt.Sprintf(`%s\SkipEnumerations : 0xffffffff`, paramsPath))
}
return enumerationDisabledPorts, nil
}
func (s *windowsSerialmouseDisable) Close(context.Context) error {
s.cancelFunc()
return nil
}