mirror of
https://github.com/jetkvm/kvm.git
synced 2025-09-16 08:38:14 +00:00
Pre-open HID files during initialization to minimize I/O overhead during operation. Remove mouse event throttling mechanism to improve input responsiveness. Keep HID files open on write errors to avoid repeated file operations.
235 lines
5.5 KiB
Go
235 lines
5.5 KiB
Go
package usbgadget
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
)
|
|
|
|
type gadgetConfigItem struct {
|
|
order uint
|
|
device string
|
|
path []string
|
|
attrs gadgetAttributes
|
|
configAttrs gadgetAttributes
|
|
configPath []string
|
|
reportDesc []byte
|
|
}
|
|
|
|
type gadgetAttributes map[string]string
|
|
|
|
type gadgetConfigItemWithKey struct {
|
|
key string
|
|
item gadgetConfigItem
|
|
}
|
|
|
|
type orderedGadgetConfigItems []gadgetConfigItemWithKey
|
|
|
|
var defaultGadgetConfig = map[string]gadgetConfigItem{
|
|
"base": {
|
|
order: 0,
|
|
attrs: gadgetAttributes{
|
|
"bcdUSB": "0x0200", // USB 2.0
|
|
"idVendor": "0x1d6b", // The Linux Foundation
|
|
"idProduct": "0x0104", // Multifunction Composite Gadget
|
|
"bcdDevice": "0x0100", // USB2
|
|
},
|
|
configAttrs: gadgetAttributes{
|
|
"MaxPower": "250", // in unit of 2mA
|
|
},
|
|
},
|
|
"base_info": {
|
|
order: 1,
|
|
path: []string{"strings", "0x409"},
|
|
configPath: []string{"strings", "0x409"},
|
|
attrs: gadgetAttributes{
|
|
"serialnumber": "",
|
|
"manufacturer": "JetKVM",
|
|
"product": "JetKVM USB Emulation Device",
|
|
},
|
|
configAttrs: gadgetAttributes{
|
|
"configuration": "Config 1: HID",
|
|
},
|
|
},
|
|
// keyboard HID
|
|
"keyboard": keyboardConfig,
|
|
// mouse HID
|
|
"absolute_mouse": absoluteMouseConfig,
|
|
// relative mouse HID
|
|
"relative_mouse": relativeMouseConfig,
|
|
// mass storage
|
|
"mass_storage_base": massStorageBaseConfig,
|
|
"mass_storage_lun0": massStorageLun0Config,
|
|
// audio
|
|
"audio": {
|
|
order: 4000,
|
|
device: "uac1.usb0",
|
|
path: []string{"functions", "uac1.usb0"},
|
|
configPath: []string{"uac1.usb0"},
|
|
attrs: gadgetAttributes{
|
|
"p_chmask": "3",
|
|
"p_srate": "48000",
|
|
"p_ssize": "2",
|
|
"p_volume_present": "0",
|
|
"c_chmask": "3",
|
|
"c_srate": "48000",
|
|
"c_ssize": "2",
|
|
"c_volume_present": "0",
|
|
},
|
|
},
|
|
}
|
|
|
|
func (u *UsbGadget) isGadgetConfigItemEnabled(itemKey string) bool {
|
|
switch itemKey {
|
|
case "absolute_mouse":
|
|
return u.enabledDevices.AbsoluteMouse
|
|
case "relative_mouse":
|
|
return u.enabledDevices.RelativeMouse
|
|
case "keyboard":
|
|
return u.enabledDevices.Keyboard
|
|
case "mass_storage_base":
|
|
return u.enabledDevices.MassStorage
|
|
case "mass_storage_lun0":
|
|
return u.enabledDevices.MassStorage
|
|
case "audio":
|
|
return u.enabledDevices.Audio
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (u *UsbGadget) loadGadgetConfig() {
|
|
if u.customConfig.isEmpty {
|
|
u.log.Trace().Msg("using default gadget config")
|
|
return
|
|
}
|
|
|
|
u.configMap["base"].attrs["idVendor"] = u.customConfig.VendorId
|
|
u.configMap["base"].attrs["idProduct"] = u.customConfig.ProductId
|
|
|
|
u.configMap["base_info"].attrs["serialnumber"] = u.customConfig.SerialNumber
|
|
u.configMap["base_info"].attrs["manufacturer"] = u.customConfig.Manufacturer
|
|
u.configMap["base_info"].attrs["product"] = u.customConfig.Product
|
|
}
|
|
|
|
func (u *UsbGadget) SetGadgetConfig(config *Config) {
|
|
u.configLock.Lock()
|
|
defer u.configLock.Unlock()
|
|
|
|
if config == nil {
|
|
return // nothing to do
|
|
}
|
|
|
|
u.customConfig = *config
|
|
u.loadGadgetConfig()
|
|
}
|
|
|
|
func (u *UsbGadget) SetGadgetDevices(devices *Devices) {
|
|
u.configLock.Lock()
|
|
defer u.configLock.Unlock()
|
|
|
|
if devices == nil {
|
|
return // nothing to do
|
|
}
|
|
|
|
u.enabledDevices = *devices
|
|
}
|
|
|
|
// GetConfigPath returns the path to the config item.
|
|
func (u *UsbGadget) GetConfigPath(itemKey string) (string, error) {
|
|
item, ok := u.configMap[itemKey]
|
|
if !ok {
|
|
return "", fmt.Errorf("config item %s not found", itemKey)
|
|
}
|
|
return joinPath(u.kvmGadgetPath, item.configPath), nil
|
|
}
|
|
|
|
// GetPath returns the path to the item.
|
|
func (u *UsbGadget) GetPath(itemKey string) (string, error) {
|
|
item, ok := u.configMap[itemKey]
|
|
if !ok {
|
|
return "", fmt.Errorf("config item %s not found", itemKey)
|
|
}
|
|
return joinPath(u.kvmGadgetPath, item.path), nil
|
|
}
|
|
|
|
// OverrideGadgetConfig overrides the gadget config for the given item and attribute.
|
|
// It returns an error if the item is not found or the attribute is not found.
|
|
// It returns true if the attribute is overridden, false otherwise.
|
|
func (u *UsbGadget) OverrideGadgetConfig(itemKey string, itemAttr string, value string) (error, bool) {
|
|
u.configLock.Lock()
|
|
defer u.configLock.Unlock()
|
|
|
|
// get it as a pointer
|
|
_, ok := u.configMap[itemKey]
|
|
if !ok {
|
|
return fmt.Errorf("config item %s not found", itemKey), false
|
|
}
|
|
|
|
if u.configMap[itemKey].attrs[itemAttr] == value {
|
|
return nil, false
|
|
}
|
|
|
|
u.configMap[itemKey].attrs[itemAttr] = value
|
|
u.log.Info().Str("itemKey", itemKey).Str("itemAttr", itemAttr).Str("value", value).Msg("overriding gadget config")
|
|
|
|
return nil, true
|
|
}
|
|
|
|
func mountConfigFS(path string) error {
|
|
err := exec.Command("mount", "-t", "configfs", "none", path).Run()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to mount configfs: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *UsbGadget) Init() error {
|
|
u.configLock.Lock()
|
|
defer u.configLock.Unlock()
|
|
|
|
u.loadGadgetConfig()
|
|
|
|
udcs := getUdcs()
|
|
if len(udcs) < 1 {
|
|
return u.logWarn("no udc found, skipping USB stack init", nil)
|
|
}
|
|
|
|
u.udc = udcs[0]
|
|
|
|
err := u.configureUsbGadget(false)
|
|
if err != nil {
|
|
return u.logError("unable to initialize USB stack", err)
|
|
}
|
|
|
|
// Pre-open HID files to reduce input latency
|
|
u.PreOpenHidFiles()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *UsbGadget) UpdateGadgetConfig() error {
|
|
u.configLock.Lock()
|
|
defer u.configLock.Unlock()
|
|
|
|
u.loadGadgetConfig()
|
|
|
|
err := u.configureUsbGadget(true)
|
|
if err != nil {
|
|
return u.logError("unable to update gadget config", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *UsbGadget) configureUsbGadget(resetUsb bool) error {
|
|
return u.WithTransaction(func() error {
|
|
u.tx.MountConfigFS()
|
|
u.tx.CreateConfigPath()
|
|
u.tx.WriteGadgetConfig()
|
|
if resetUsb {
|
|
u.tx.RebindUsb(true)
|
|
}
|
|
return nil
|
|
})
|
|
}
|