Skip to content

Commit d800419

Browse files
authored
Merge pull request #178 from Code-Hex/add/usb
support USB Controllers
2 parents 472d28a + 22ef17e commit d800419

File tree

5 files changed

+410
-0
lines changed

5 files changed

+410
-0
lines changed

configuration.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package vz
66
# include "virtualization_11.h"
77
# include "virtualization_12.h"
88
# include "virtualization_13.h"
9+
# include "virtualization_15.h"
910
*/
1011
import "C"
1112
import (
@@ -40,6 +41,7 @@ type VirtualMachineConfiguration struct {
4041

4142
networkDeviceConfiguration []*VirtioNetworkDeviceConfiguration
4243
storageDeviceConfiguration []StorageDeviceConfiguration
44+
usbControllerConfiguration []USBControllerConfiguration
4345
}
4446

4547
// NewVirtualMachineConfiguration creates a new configuration.
@@ -277,6 +279,28 @@ func (v *VirtualMachineConfiguration) SetConsoleDevicesVirtualMachineConfigurati
277279
C.setConsoleDevicesVZVirtualMachineConfiguration(objc.Ptr(v), objc.Ptr(array))
278280
}
279281

282+
// SetUSBControllerConfiguration sets list of USB controllers. Empty by default.
283+
//
284+
// This is only supported on macOS 15 and newer. Older versions do nothing.
285+
func (v *VirtualMachineConfiguration) SetUSBControllersVirtualMachineConfiguration(us []USBControllerConfiguration) {
286+
if err := macOSAvailable(15); err != nil {
287+
return
288+
}
289+
ptrs := make([]objc.NSObject, len(us))
290+
for i, val := range us {
291+
ptrs[i] = val
292+
}
293+
array := objc.ConvertToNSMutableArray(ptrs)
294+
C.setUSBControllersVZVirtualMachineConfiguration(objc.Ptr(v), objc.Ptr(array))
295+
v.usbControllerConfiguration = us
296+
}
297+
298+
// USBControllers return the list of usb controller configuration configured in this virtual machine configuration.
299+
// Return an empty array if no usb controller configuration is set.
300+
func (v *VirtualMachineConfiguration) USBControllers() []USBControllerConfiguration {
301+
return v.usbControllerConfiguration
302+
}
303+
280304
// VirtualMachineConfigurationMinimumAllowedMemorySize returns minimum
281305
// amount of memory required by virtual machines.
282306
func VirtualMachineConfigurationMinimumAllowedMemorySize() uint64 {

usb.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package vz
2+
3+
/*
4+
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5+
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization
6+
# include "virtualization_15.h"
7+
*/
8+
import "C"
9+
import (
10+
"runtime/cgo"
11+
"unsafe"
12+
13+
"github.com/Code-Hex/vz/v3/internal/objc"
14+
)
15+
16+
// NewUSBMassStorageDevice initialize the runtime USB Mass Storage device object.
17+
//
18+
// This is only supported on macOS 15 and newer, error will
19+
// be returned on older versions.
20+
func NewUSBMassStorageDevice(config *USBMassStorageDeviceConfiguration) (USBDevice, error) {
21+
if err := macOSAvailable(15); err != nil {
22+
return nil, err
23+
}
24+
ptr := C.newVZUSBMassStorageDeviceWithConfiguration(objc.Ptr(config))
25+
return newUSBDevice(ptr), nil
26+
}
27+
28+
// USBControllerConfiguration for a usb controller configuration.
29+
type USBControllerConfiguration interface {
30+
objc.NSObject
31+
32+
usbControllerConfiguration()
33+
}
34+
35+
type baseUSBControllerConfiguration struct{}
36+
37+
func (*baseUSBControllerConfiguration) usbControllerConfiguration() {}
38+
39+
// XHCIControllerConfiguration is a configuration of the USB XHCI controller.
40+
//
41+
// This configuration creates a USB XHCI controller device for the guest.
42+
// see: https://developer.apple.com/documentation/virtualization/vzxhcicontrollerconfiguration?language=objc
43+
type XHCIControllerConfiguration struct {
44+
*pointer
45+
46+
*baseUSBControllerConfiguration
47+
}
48+
49+
var _ USBControllerConfiguration = (*XHCIControllerConfiguration)(nil)
50+
51+
// NewXHCIControllerConfiguration creates a new XHCIControllerConfiguration.
52+
//
53+
// This is only supported on macOS 15 and newer, error will
54+
// be returned on older versions.
55+
func NewXHCIControllerConfiguration() (*XHCIControllerConfiguration, error) {
56+
if err := macOSAvailable(15); err != nil {
57+
return nil, err
58+
}
59+
60+
config := &XHCIControllerConfiguration{
61+
pointer: objc.NewPointer(C.newVZXHCIControllerConfiguration()),
62+
}
63+
64+
objc.SetFinalizer(config, func(self *XHCIControllerConfiguration) {
65+
objc.Release(self)
66+
})
67+
return config, nil
68+
}
69+
70+
// USBController is representing a USB controller in a virtual machine.
71+
type USBController struct {
72+
dispatchQueue unsafe.Pointer
73+
*pointer
74+
}
75+
76+
func newUSBController(ptr, dispatchQueue unsafe.Pointer) *USBController {
77+
return &USBController{
78+
dispatchQueue: dispatchQueue,
79+
pointer: objc.NewPointer(ptr),
80+
}
81+
}
82+
83+
//export usbAttachDetachCompletionHandler
84+
func usbAttachDetachCompletionHandler(cgoHandleUintptr C.uintptr_t, errPtr unsafe.Pointer) {
85+
cgoHandle := cgo.Handle(cgoHandleUintptr)
86+
87+
handler := cgoHandle.Value().(func(error))
88+
89+
if err := newNSError(errPtr); err != nil {
90+
handler(err)
91+
} else {
92+
handler(nil)
93+
}
94+
}
95+
96+
// Attach attaches a USB device.
97+
//
98+
// This is only supported on macOS 15 and newer, error will
99+
// be returned on older versions.
100+
//
101+
// If the device is successfully attached to the controller, it will appear in the usbDevices property,
102+
// its usbController property will be set to point to the USB controller that it is attached to
103+
// and completion handler will return nil.
104+
// If the device was previously attached to this or another USB controller, attach function will fail
105+
// with the `vz.ErrorDeviceAlreadyAttached`. If the device cannot be initialized correctly, attach
106+
// function will fail with `vz.ErrorDeviceInitializationFailure`.
107+
func (u *USBController) Attach(device USBDevice) error {
108+
if err := macOSAvailable(15); err != nil {
109+
return err
110+
}
111+
h, errCh := makeHandler()
112+
handle := cgo.NewHandle(h)
113+
defer handle.Delete()
114+
C.attachDeviceVZUSBController(
115+
objc.Ptr(u),
116+
objc.Ptr(device),
117+
u.dispatchQueue,
118+
C.uintptr_t(handle),
119+
)
120+
return <-errCh
121+
}
122+
123+
// Detach detaches a USB device.
124+
//
125+
// This is only supported on macOS 15 and newer, error will
126+
// be returned on older versions.
127+
//
128+
// If the device is successfully detached from the controller, it will disappear from the usbDevices property,
129+
// its usbController property will be set to nil and completion handler will return nil.
130+
// If the device wasn't attached to the controller at the time of calling detach method, it will fail
131+
// with the `vz.ErrorDeviceNotFound` error.
132+
func (u *USBController) Detach(device USBDevice) error {
133+
if err := macOSAvailable(15); err != nil {
134+
return err
135+
}
136+
h, errCh := makeHandler()
137+
handle := cgo.NewHandle(h)
138+
defer handle.Delete()
139+
C.detachDeviceVZUSBController(
140+
objc.Ptr(u),
141+
objc.Ptr(device),
142+
u.dispatchQueue,
143+
C.uintptr_t(handle),
144+
)
145+
return <-errCh
146+
}
147+
148+
// USBDevices return a list of USB devices attached to controller.
149+
//
150+
// This is only supported on macOS 15 and newer, nil will
151+
// be returned on older versions.
152+
func (u *USBController) USBDevices() []USBDevice {
153+
if err := macOSAvailable(15); err != nil {
154+
return nil
155+
}
156+
nsArray := objc.NewNSArray(
157+
C.usbDevicesVZUSBController(objc.Ptr(u)),
158+
)
159+
ptrs := nsArray.ToPointerSlice()
160+
usbDevices := make([]USBDevice, len(ptrs))
161+
for i, ptr := range ptrs {
162+
usbDevices[i] = newUSBDevice(ptr)
163+
}
164+
return usbDevices
165+
}
166+
167+
// USBDevice is an interface that represents a USB device in a VM.
168+
type USBDevice interface {
169+
objc.NSObject
170+
171+
UUID() string
172+
173+
usbDevice()
174+
}
175+
176+
func newUSBDevice(ptr unsafe.Pointer) *usbDevice {
177+
return &usbDevice{
178+
pointer: objc.NewPointer(ptr),
179+
}
180+
}
181+
182+
type usbDevice struct {
183+
*pointer
184+
}
185+
186+
func (*usbDevice) usbDevice() {}
187+
188+
var _ USBDevice = (*usbDevice)(nil)
189+
190+
// UUID returns the device UUID.
191+
func (u *usbDevice) UUID() string {
192+
cs := (*char)(C.getUUIDUSBDevice(objc.Ptr(u)))
193+
return cs.String()
194+
}

virtualization.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package vz
66
# include "virtualization_11.h"
77
# include "virtualization_12.h"
88
# include "virtualization_13.h"
9+
# include "virtualization_15.h"
910
*/
1011
import "C"
1112
import (
@@ -186,6 +187,26 @@ func (v *VirtualMachine) SocketDevices() []*VirtioSocketDevice {
186187
return socketDevices
187188
}
188189

190+
// USBControllers return the list of USB controllers configured on this virtual machine. Return an empty array if no USB controller is configured.
191+
//
192+
// This is only supported on macOS 15 and newer, nil will
193+
// be returned on older versions.
194+
func (v *VirtualMachine) USBControllers() []*USBController {
195+
if err := macOSAvailable(15); err != nil {
196+
return nil
197+
}
198+
nsArray := objc.NewNSArray(
199+
C.VZVirtualMachine_usbControllers(objc.Ptr(v)),
200+
)
201+
ptrs := nsArray.ToPointerSlice()
202+
usbControllers := make([]*USBController, len(ptrs))
203+
for i, ptr := range ptrs {
204+
usbControllers[i] = newUSBController(ptr, v.dispatchQueue)
205+
}
206+
return usbControllers
207+
}
208+
209+
189210
//export changeStateOnObserver
190211
func changeStateOnObserver(newStateRaw C.int, cgoHandleUintptr C.uintptr_t) {
191212
stateHandle := cgo.Handle(cgoHandleUintptr)

virtualization_15.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//
22
// virtualization_15.h
3+
//
4+
// Created by codehex.
5+
//
36

47
#pragma once
58

@@ -10,6 +13,17 @@
1013
#import "virtualization_helper.h"
1114
#import <Virtualization/Virtualization.h>
1215

16+
/* exported from cgo */
17+
void usbAttachDetachCompletionHandler(uintptr_t cgoHandle, void *errPtr);
18+
1319
/* macOS 15 API */
1420
bool isNestedVirtualizationSupported();
1521
void setNestedVirtualizationEnabled(void *config, bool nestedVirtualizationEnabled);
22+
void *newVZXHCIControllerConfiguration();
23+
void setUSBControllersVZVirtualMachineConfiguration(void *config, void *usbControllers);
24+
const char *getUUIDUSBDevice(void *usbDevice);
25+
void *usbDevicesVZUSBController(void *usbController);
26+
void *VZVirtualMachine_usbControllers(void *machine);
27+
void attachDeviceVZUSBController(void *usbController, void *usbDevice, void *queue, uintptr_t cgoHandle);
28+
void detachDeviceVZUSBController(void *usbController, void *usbDevice, void *queue, uintptr_t cgoHandle);
29+
void *newVZUSBMassStorageDeviceWithConfiguration(void *config);

0 commit comments

Comments
 (0)