mirror of
https://github.com/lima-vm/lima.git
synced 2026-10-28 19:54:35 +08:00
feat:add virtio-sound interface
Signed-off-by: Aneesh-Hegde <aneeshhegde7110@gmail.com>
This commit is contained in:
12 files changed
+93
-22
No files matched your search
@@ -116,6 +116,9 @@ func RegisterCreate(cmd *cobra.Command, commentPrefix string) {
|
||||
_ = cmd.RegisterFlagCompletionFunc("port-forward", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"8080:80", "3000:3000", "8080:80,static=true"}, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
|
||||
flags.String("audio-device", "", commentPrefix+"Audio device backend (e.g., 'pa', 'coreaudio', 'none')")
|
||||
flags.String("audio-interface", "", commentPrefix+"Audio virtual hardware interface ('hda' or 'virtio')")
|
||||
}
|
||||
|
||||
func defaultExprFunc(expr string) func(v *flag.Flag) ([]string, error) {
|
||||
@@ -373,6 +376,8 @@ func YQExpressions(flags *flag.FlagSet, newInstance bool, params map[string]stri
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{"audio-device", d(".audio.device = %q"), false, true},
|
||||
{"audio-interface", d(".audio.interface = %q"), false, true},
|
||||
{"ssh-port", d(".ssh.localPort = %s"), false, false},
|
||||
{"arch", d(".arch = %q"), true, false},
|
||||
{
|
||||
|
||||
Vendored
+1
-1
@@ -32,7 +32,7 @@ func NewDriverClient(socketPath string, logger *logrus.Logger) (*DriverClient, e
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
}
|
||||
|
||||
//nolint:staticcheck // grpc.Dial is used for compatibility reasons
|
||||
//nolint:staticcheck,nolintlint // grpc.Dial is used for compatibility reasons
|
||||
conn, err := grpc.Dial("unix://"+socketPath, opts...)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to dial gRPC driver client connection: %v", err)
|
||||
|
||||
+33
-12
@@ -375,6 +375,7 @@ func adjustMemBytesDarwinARM64HVF(memBytes int64, accel string) int64 {
|
||||
return memBytes
|
||||
}
|
||||
macOSProductVersion, err := osutil.ProductVersion()
|
||||
//nolint:staticcheck,nolintlint // SA4023: always true on Linux, but valid on macOS
|
||||
if err != nil {
|
||||
logrus.Warn(err)
|
||||
return memBytes
|
||||
@@ -818,19 +819,39 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
|
||||
input := "mouse"
|
||||
|
||||
// Sound
|
||||
if *y.Audio.Device != "" {
|
||||
id := "default"
|
||||
// audio device
|
||||
audiodev := *y.Audio.Device
|
||||
if audiodev == "default" {
|
||||
audiodev = audioDevice()
|
||||
if y.Audio.Device != nil && y.Audio.Interface != nil {
|
||||
audioDev := *y.Audio.Device
|
||||
audioInterface := *y.Audio.Interface
|
||||
|
||||
if audioDev == "" || audioDev == "none" {
|
||||
args = append(args, "-audiodev", "none,id=snd0")
|
||||
} else {
|
||||
if audioDev == "default" {
|
||||
audioDev = audioDevice()
|
||||
}
|
||||
args = append(args, "-audiodev", fmt.Sprintf("%s,id=snd0", audioDev))
|
||||
if audioInterface == "" {
|
||||
audioInterface = "hda"
|
||||
}
|
||||
switch audioInterface {
|
||||
case "virtio":
|
||||
minVirtioVersion := semver.New("8.2.0")
|
||||
if version.LessThan(*minVirtioVersion) {
|
||||
logrus.Warnf("virtio-sound requires QEMU 8.2.0 or newer. Falling back to intel-hda.")
|
||||
// We append the HDA hardware directly here instead of mutating the variable
|
||||
args = append(args, "-device", "ich9-intel-hda", "-device", "hda-output,audiodev=snd0")
|
||||
} else {
|
||||
args = append(args, "-device", "virtio-sound-pci,audiodev=snd0")
|
||||
}
|
||||
case "hda":
|
||||
args = append(args,
|
||||
"-device", "ich9-intel-hda",
|
||||
"-device", "hda-output,audiodev=snd0",
|
||||
)
|
||||
default:
|
||||
return "", nil, fmt.Errorf("unknown audio.interface %q", audioInterface)
|
||||
}
|
||||
}
|
||||
audiodev += fmt.Sprintf(",id=%s", id)
|
||||
args = append(args, "-audiodev", audiodev)
|
||||
// audio controller
|
||||
args = append(args, "-device", "ich9-intel-hda")
|
||||
// audio codec
|
||||
args = append(args, "-device", fmt.Sprintf("hda-output,audiodev=%s", id))
|
||||
}
|
||||
// Graphics
|
||||
if *y.Video.Display != "" {
|
||||
|
||||
@@ -765,15 +765,30 @@ func attachFolderMounts(inst *limatype.Instance, vmConfig *vz.VirtualMachineConf
|
||||
}
|
||||
|
||||
func attachAudio(inst *limatype.Instance, config *vz.VirtualMachineConfiguration) error {
|
||||
switch *inst.Config.Audio.Device {
|
||||
y := inst.Config
|
||||
|
||||
if y.Audio.Device == nil || y.Audio.Interface == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
audioDev := *y.Audio.Device
|
||||
audioInterface := *y.Audio.Interface
|
||||
|
||||
// If the user explicitly asks for HDA, we gracefully ignore it.
|
||||
if audioInterface == "hda" {
|
||||
logrus.Warn("VZ driver natively uses virtio-sound. Ignoring 'hda' interface request.")
|
||||
}
|
||||
|
||||
switch audioDev {
|
||||
case "vz", "default":
|
||||
outputStream, err := vz.NewVirtioSoundDeviceHostOutputStreamConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create host audio output stream: %w", err)
|
||||
}
|
||||
|
||||
soundDeviceConfiguration, err := vz.NewVirtioSoundDeviceConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create virtio sound device configuration: %w", err)
|
||||
}
|
||||
soundDeviceConfiguration.SetStreams(outputStream)
|
||||
config.SetAudioDevicesVirtualMachineConfiguration([]vz.AudioDeviceConfiguration{
|
||||
|
||||
@@ -233,7 +233,9 @@ func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) {
|
||||
// Probably never supportable for WSL2
|
||||
logrus.Warn(".ssh.overVsock is not supported for WSL2 driver")
|
||||
}
|
||||
|
||||
if l.Instance.Config.Audio.Interface != nil {
|
||||
logrus.Warn("`audio.interface` is ignored when using the WSL2 driver")
|
||||
}
|
||||
logrus.Infof("Starting WSL VM")
|
||||
status, err := getWslStatus(ctx, l.Instance.Name)
|
||||
if err != nil {
|
||||
|
||||
@@ -44,7 +44,7 @@ func (c *secureTC) OverrideServerName(serverNameOverride string) error {
|
||||
// SA1019: c.info.ServerName is deprecated:
|
||||
// Users should use grpc.WithAuthority to override the authority on a channel instead of configuring the credentials.
|
||||
|
||||
//nolint:staticcheck // c.info.ServerName is used for compatibility reason
|
||||
//nolint:staticcheck,nolintlint // c.info.ServerName is used for compatibility reason
|
||||
c.info.ServerName = serverNameOverride
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -220,6 +220,8 @@ type Firmware struct {
|
||||
type Audio struct {
|
||||
// Device is a QEMU audiodev string
|
||||
Device *string `yaml:"device,omitempty" json:"device,omitempty" jsonschema:"nullable"`
|
||||
// Interface is the virtual hardware presentation
|
||||
Interface *string `yaml:"interface,omitempty" json:"interface,omitempty" jsonschema:"nullable"`
|
||||
}
|
||||
|
||||
type VNCOptions struct {
|
||||
|
||||
@@ -296,6 +296,16 @@ func FillDefault(ctx context.Context, y, d, o *limatype.LimaYAML, filePath strin
|
||||
y.Audio.Device = ptr.Of("")
|
||||
}
|
||||
|
||||
if y.Audio.Interface == nil {
|
||||
y.Audio.Interface = d.Audio.Interface
|
||||
}
|
||||
if o.Audio.Interface != nil {
|
||||
y.Audio.Interface = o.Audio.Interface
|
||||
}
|
||||
if y.Audio.Interface == nil {
|
||||
y.Audio.Interface = ptr.Of("")
|
||||
}
|
||||
|
||||
if y.Video.Display == nil {
|
||||
y.Video.Display = d.Video.Display
|
||||
}
|
||||
|
||||
@@ -99,7 +99,8 @@ func TestFillDefault(t *testing.T) {
|
||||
LegacyBIOS: ptr.Of(false),
|
||||
},
|
||||
Audio: limatype.Audio{
|
||||
Device: ptr.Of(""),
|
||||
Device: ptr.Of(""),
|
||||
Interface: ptr.Of(""),
|
||||
},
|
||||
Video: limatype.Video{
|
||||
Display: ptr.Of("none"),
|
||||
@@ -344,7 +345,8 @@ func TestFillDefault(t *testing.T) {
|
||||
// Remove driver-specific firmware images from defaults
|
||||
},
|
||||
Audio: limatype.Audio{
|
||||
Device: ptr.Of("coreaudio"),
|
||||
Device: ptr.Of("coreaudio"),
|
||||
Interface: ptr.Of("virtio"),
|
||||
},
|
||||
Video: limatype.Video{
|
||||
Display: ptr.Of("cocoa"),
|
||||
@@ -556,7 +558,8 @@ func TestFillDefault(t *testing.T) {
|
||||
LegacyBIOS: ptr.Of(true),
|
||||
},
|
||||
Audio: limatype.Audio{
|
||||
Device: ptr.Of("coreaudio"),
|
||||
Device: ptr.Of("coreaudio"),
|
||||
Interface: ptr.Of("hda"),
|
||||
},
|
||||
Video: limatype.Video{
|
||||
Display: ptr.Of("cocoa"),
|
||||
|
||||
@@ -432,6 +432,12 @@ func Validate(y *limatype.LimaYAML, warn bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
if y.Audio.Interface != nil {
|
||||
if *y.Audio.Interface != "" && *y.Audio.Interface != "hda" && *y.Audio.Interface != "virtio" {
|
||||
return fmt.Errorf("invalid audio.interface %q, must be \"hda\" or \"virtio\"", *y.Audio.Interface)
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -623,6 +629,9 @@ func warnExperimental(y *limatype.LimaYAML) {
|
||||
}
|
||||
if y.Audio.Device != nil && *y.Audio.Device != "" {
|
||||
logrus.Warn("`audio.device` is experimental")
|
||||
if y.Audio.Interface != nil && *y.Audio.Interface != "" {
|
||||
logrus.Warn("`audio.interface` is experimental")
|
||||
}
|
||||
}
|
||||
if y.MountInotify != nil && *y.MountInotify {
|
||||
logrus.Warn("`mountInotify` is experimental")
|
||||
|
||||
@@ -448,6 +448,10 @@ audio:
|
||||
# that is still visible in the guest but not connected to the host.
|
||||
# 🟢 Builtin default: ""
|
||||
device: null
|
||||
# The virtual hardware presentation for QEMU driver. Options are "hda" (legacy) or "virtio" (paravirtualized, requires QEMU >= 8.2).
|
||||
# VZ driver always uses virtio-sound regardless of this setting.
|
||||
# 🟢 Builtin default: "" (resolves to "hda" when audio.device is set)
|
||||
interface: null
|
||||
|
||||
video:
|
||||
# QEMU display, e.g., "none", "cocoa", "sdl", "gtk", "vnc", "default".
|
||||
|
||||
@@ -10,7 +10,7 @@ The following features are experimental and subject to change:
|
||||
- `vmType: wsl2` and relevant configurations (`mountType: wsl2`)
|
||||
- `arch`: `riscv64`, `armv7l`, `s390x`, and `ppc64le`
|
||||
- `video.display: vnc` and relevant configuration (`video.vnc.display`)
|
||||
- `audio.device`
|
||||
- `audio.device` and `audio.interface`
|
||||
- `mountInotify: true`
|
||||
- `tpm: true`
|
||||
- `External drivers`: building and using drivers as separate executables (see [Virtual Machine Drivers](../dev/drivers))
|
||||
|
||||
Reference in new issue
Block a user