server side for full gadget disable

starting on frontend

# Conflicts:
#	kvmapp/system/init.d/S03usbdev
This commit is contained in:
Thomas Pressnell
2025-05-24 15:34:55 +01:00
committed by wj-xiao
parent 5958fd5c6f
commit e684936d15
10 changed files with 186 additions and 14 deletions

View File

@@ -109,21 +109,20 @@ start_usb_dev(){
disk=$(cat /boot/usb.disk0)
if [ -z "${disk}" ]
then
echo "" > functions/mass_storage.disk0/lun.0/file
# if [ ! -e /mnt/usbdisk.img ]
# then
# fallocate -l 8G /mnt/usbdisk.img
# mkfs.vfat /mnt/usbdisk.img
# fi
echo /dev/mmcblk0p3 > functions/mass_storage.disk0/lun.0/file
else
cat /boot/usb.disk0 > functions/mass_storage.disk0/lun.0/file
fi
fi
# mount data partition
if [ -e /boot/usb.disk1 ]
then
mkdir functions/mass_storage.disk1
ln -s functions/mass_storage.disk1 configs/c.1/
echo /dev/mmcblk0p3 > functions/mass_storage.disk1/lun.0/file
if [ ! -e /boot/udc.disable ]; then
ls /sys/class/udc/ | cat > UDC
fi
ls /sys/class/udc/ | cat > UDC
echo device > /proc/cviusb/otg_role
}
@@ -135,7 +134,9 @@ start_usb_host(){
restart_usb_dev(){
echo > /sys/kernel/config/usb_gadget/g0/UDC
sleep 1
ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC
if [ ! -e /boot/udc.disable ]; then
ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC
fi
echo "USB Restart OK!"
}

View File

@@ -72,7 +72,9 @@ start_usb_dev(){
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\x05\\x01\\x09\\x38\\x15\\x81\\x25\\x7f\\x35\\x00\\x45\\x00\\x75\\x08\\x95\\x01\\x81\\x06\\xc0\\xc0 > functions/hid.GS2/report_desc
ln -s functions/hid.GS2 configs/c.1
ls /sys/class/udc/ | cat > UDC
if [ ! -e /boot/udc.disable ]; then
ls /sys/class/udc/ | cat > UDC
fi
echo device > /proc/cviusb/otg_role
}
@@ -84,7 +86,9 @@ start_usb_host(){
restart_usb_dev(){
echo > /sys/kernel/config/usb_gadget/g0/UDC
sleep 1
ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC
if [ ! -e /boot/udc.disable ]; then
ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC
fi
echo "USB Restart OK!"
}

View File

@@ -92,6 +92,10 @@ type GetGetHdmiStateRsp struct {
Enabled bool `json:"enabled"`
}
type GetUsbStateRsp struct {
Enabled bool `json:"enabled"`
}
type GetSSHStateRsp struct {
Enabled bool `json:"enabled"`
}

View File

@@ -35,6 +35,10 @@ func vmRouter(r *gin.Engine) {
api.GET("/vm/oled", service.GetOLED) // get OLED configuration
api.POST("/vm/oled", service.SetOLED) // set OLED configuration
api.GET("/vm/usb", service.GetUsbState) // get USB gadget state
api.POST("/vm/usb/enable", service.EnableUsb) // enable USB
api.POST("/vm/usb/disable", service.DisableUsb) // disable USB
// Only supported by PCIe version
api.GET("/vm/hdmi", service.GetHdmiState) // get HDMI state
api.POST("/vm/hdmi/reset", service.ResetHdmi) // reset hdmi

86
server/service/vm/usb.go Normal file
View File

@@ -0,0 +1,86 @@
package vm
import (
"NanoKVM-Server/proto"
"NanoKVM-Server/service/hid"
"os"
"os/exec"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)
const (
udcDisableFile = "/boot/udc.disable"
)
var (
udcEnableCommands = []string{
"rm /boot/udc.disable",
"/etc/init.d/S03usbdev restart",
}
udcDisableCommands = []string{
"touch /boot/udc.disable",
"/etc/init.d/S03usbdev restart",
}
)
func runCommands(commands []string) error {
h := hid.GetHid()
h.Lock()
h.CloseNoLock()
defer func() {
h.OpenNoLock()
h.Unlock()
}()
for _, command := range commands {
if err := exec.Command("sh", "-c", command).Run(); err != nil {
log.Errorf("failed to run command '%s': %v", command, err)
return err
}
}
return nil
}
func (s *Service) EnableUsb(c *gin.Context) {
var rsp proto.Response
err := runCommands(udcEnableCommands)
if err != nil {
rsp.ErrRsp(c, -1, "failed to enable USB")
return
}
rsp.OkRsp(c)
log.Debug("enable usb")
}
func (s *Service) DisableUsb(c *gin.Context) {
var rsp proto.Response
err := runCommands(udcDisableCommands)
if err != nil {
rsp.ErrRsp(c, -1, "failed to disable USB")
return
}
rsp.OkRsp(c)
log.Debug("disable usb")
}
func (s *Service) GetUsbState(c *gin.Context) {
var rsp proto.Response
usbEnabled := false
if _, err := os.Stat(udcDisableFile); err != nil {
usbEnabled = true
}
rsp.OkRspWithData(c, &proto.GetUsbStateRsp{
Enabled: usbEnabled,
})
log.Debug("get usb state")
}

16
web/src/api/usb-device.ts Normal file
View File

@@ -0,0 +1,16 @@
import { http } from '@/lib/http.ts';
// get virtual devices status
export function getUsbState() {
return http.get('/api/vm/usb');
}
// enable usb
export function enableUsb() {
return http.post('/api/vm/usb/enable');
}
// disable usb
export function disableUsb() {
return http.post('/api/vm/usb/disable');
}

View File

@@ -286,6 +286,8 @@ const en = {
networkDesc: 'Mount virtual network card on the remote host',
reboot: 'Reboot',
rebootDesc: 'Are you sure you want to reboot NanoKVM?',
usb: 'USB Interface',
usbDesc: 'Enable USB to host interface',
okBtn: 'Yes',
cancelBtn: 'No'
},

View File

@@ -15,7 +15,7 @@ import { Oled } from './oled.tsx';
import { Reboot } from './reboot.tsx';
import { Ssh } from './ssh.tsx';
import { Tls } from './tls.tsx';
import { VirtualDevices } from './virtual-devices.tsx';
import { Usb } from './usb/index.tsx';
import { Wifi } from './wifi.tsx';
export const Device = () => {
@@ -41,7 +41,7 @@ export const Device = () => {
<Mdns />
<Hdmi />
{hidMode === 'normal' ? <VirtualDevices /> : <HidMode />}
{hidMode === 'normal' ? <Usb/> : <HidMode />}
</div>
<Divider />

View File

@@ -0,0 +1,55 @@
import { useEffect, useState } from 'react';
import { Switch } from 'antd';
import { useTranslation } from 'react-i18next';
import { VirtualDevices } from './virtual-devices.tsx';
import * as api from '@/api/usb-device.ts';
export const Usb = () => {
const { t } = useTranslation();
const [isUsbOn, setIsUsbOn] = useState(false);
const [isLoading, setLoading] = useState(false);
useEffect(() => {
api.getUsbState().then((rsp) => {
if (rsp.code !== 0) {
return;
}
setIsUsbOn(rsp.data.enabled);
});
}, []);
function update() {
if (isLoading) return;
setLoading(true);
const func = isUsbOn ? api.disableUsb() : api.enableUsb();
func.then((rsp) => {
if (rsp.code !== 0) {
return;
}
setIsUsbOn(!isUsbOn);
})
.finally(() => {
setLoading(false);
});
}
return (
<>
<div className="flex items-center justify-between">
<div className="flex flex-col">
<span>{t('settings.device.usb')}</span>
<span className="text-xs text-neutral-500">{t('settings.device.usbDesc')}</span>
</div>
<Switch checked={isUsbOn} loading={isLoading} onChange={() => update()} />
</div>
{isUsbOn ? <VirtualDevices /> : ""}
</>
);
};