// Package usbgadget provides a high-level interface to manage USB gadgets // THIS PACKAGE IS FOR INTERNAL USE ONLY AND ITS API MAY CHANGE WITHOUT NOTICE package usbgadget import ( "context" "os" "path" "sync" "time" "github.com/jetkvm/kvm/internal/logging" "github.com/rs/zerolog" ) // Devices is a struct that represents the USB devices that can be enabled on a USB gadget. type Devices struct { AbsoluteMouse bool `json:"absolute_mouse"` RelativeMouse bool `json:"relative_mouse"` Keyboard bool `json:"keyboard"` MassStorage bool `json:"mass_storage"` } // Config is a struct that represents the customizations for a USB gadget. // TODO: rename to something else that won't confuse with the USB gadget configuration type Config struct { VendorId string `json:"vendor_id"` ProductId string `json:"product_id"` SerialNumber string `json:"serial_number"` Manufacturer string `json:"manufacturer"` Product string `json:"product"` strictMode bool // when it's enabled, all warnings will be converted to errors isEmpty bool } var defaultUsbGadgetDevices = Devices{ AbsoluteMouse: true, RelativeMouse: true, Keyboard: true, MassStorage: true, } // UsbGadget is a struct that represents a USB gadget. type UsbGadget struct { name string udc string kvmGadgetPath string configC1Path string configMap map[string]gadgetConfigItem customConfig Config configLock sync.Mutex keyboardHidFile *os.File keyboardLock sync.Mutex absMouseHidFile *os.File absMouseLock sync.Mutex relMouseHidFile *os.File relMouseLock sync.Mutex keyboardState KeyboardState keyboardStateLock sync.Mutex keyboardStateCtx context.Context keyboardStateCancel context.CancelFunc enabledDevices Devices strictMode bool // only intended for testing for now absMouseAccumulatedWheelY float64 lastUserInput time.Time tx *UsbGadgetTransaction txLock sync.Mutex onKeyboardStateChange *func(state KeyboardState) log *zerolog.Logger } const configFSPath = "/sys/kernel/config" const gadgetPath = "/sys/kernel/config/usb_gadget" var defaultLogger = logging.GetSubsystemLogger("usbgadget") // NewUsbGadget creates a new UsbGadget. func NewUsbGadget(name string, enabledDevices *Devices, config *Config, logger *zerolog.Logger) *UsbGadget { return newUsbGadget(name, defaultGadgetConfig, enabledDevices, config, logger) } func newUsbGadget(name string, configMap map[string]gadgetConfigItem, enabledDevices *Devices, config *Config, logger *zerolog.Logger) *UsbGadget { if logger == nil { logger = defaultLogger } if enabledDevices == nil { enabledDevices = &defaultUsbGadgetDevices } if config == nil { config = &Config{isEmpty: true} } keyboardCtx, keyboardCancel := context.WithCancel(context.Background()) g := &UsbGadget{ name: name, kvmGadgetPath: path.Join(gadgetPath, name), configC1Path: path.Join(gadgetPath, name, "configs/c.1"), configMap: configMap, customConfig: *config, configLock: sync.Mutex{}, keyboardLock: sync.Mutex{}, absMouseLock: sync.Mutex{}, relMouseLock: sync.Mutex{}, txLock: sync.Mutex{}, keyboardStateCtx: keyboardCtx, keyboardStateCancel: keyboardCancel, keyboardState: KeyboardState{}, enabledDevices: *enabledDevices, lastUserInput: time.Now(), log: logger, strictMode: config.strictMode, absMouseAccumulatedWheelY: 0, } if err := g.Init(); err != nil { logger.Error().Err(err).Msg("failed to init USB gadget") return nil } return g }