]>
Commit | Line | Data |
---|---|---|
de3d4ac4 DC |
1 | package PVE::QemuServer::USB; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use PVE::QemuServer::PCI qw(print_pci_addr); | |
6 | use PVE::JSONSchema; | |
7 | use base 'Exporter'; | |
8 | ||
9 | our @EXPORT_OK = qw( | |
10 | parse_usb_device | |
11 | get_usb_controllers | |
12 | get_usb_devices | |
13 | ); | |
14 | ||
15 | sub parse_usb_device { | |
16 | my ($value) = @_; | |
17 | ||
18 | return undef if !$value; | |
19 | ||
20 | my $res = {}; | |
21 | if ($value =~ m/^(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) { | |
22 | $res->{vendorid} = $2; | |
23 | $res->{productid} = $4; | |
24 | } elsif ($value =~ m/^(\d+)\-(\d+(\.\d+)*)$/) { | |
25 | $res->{hostbus} = $1; | |
26 | $res->{hostport} = $2; | |
27 | } elsif ($value =~ m/^spice$/i) { | |
28 | $res->{spice} = 1; | |
29 | } else { | |
30 | return undef; | |
31 | } | |
32 | ||
33 | return $res; | |
34 | } | |
35 | ||
36 | sub get_usb_controllers { | |
d559309f | 37 | my ($conf, $bridges, $arch, $machine, $format, $max_usb_devices) = @_; |
de3d4ac4 DC |
38 | |
39 | my $devices = []; | |
40 | my $pciaddr = ""; | |
41 | ||
d559309f WB |
42 | if ($arch eq 'aarch64') { |
43 | $pciaddr = print_pci_addr('ehci', $bridges, $arch, $machine); | |
44 | push @$devices, '-device', "usb-ehci,id=ehci$pciaddr"; | |
88a7da83 | 45 | } elsif ($machine =~ /q35/) { # FIXME: combine this and machine_type_is_q35 |
de3d4ac4 DC |
46 | # the q35 chipset support native usb2, so we enable usb controller |
47 | # by default for this machine type | |
48 | push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg'; | |
49 | } else { | |
d559309f | 50 | $pciaddr = print_pci_addr("piix3", $bridges, $arch, $machine); |
de3d4ac4 DC |
51 | push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2"; |
52 | ||
53 | my $use_usb2 = 0; | |
54 | for (my $i = 0; $i < $max_usb_devices; $i++) { | |
55 | next if !$conf->{"usb$i"}; | |
56 | my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{"usb$i"}) }; | |
57 | next if !$d || $d->{usb3}; # do not add usb2 controller if we have only usb3 devices | |
58 | $use_usb2 = 1; | |
59 | } | |
60 | # include usb device config | |
61 | push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2; | |
62 | } | |
63 | ||
64 | # add usb3 controller if needed | |
65 | ||
66 | my $use_usb3 = 0; | |
67 | for (my $i = 0; $i < $max_usb_devices; $i++) { | |
68 | next if !$conf->{"usb$i"}; | |
69 | my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{"usb$i"}) }; | |
70 | next if !$d || !$d->{usb3}; | |
71 | $use_usb3 = 1; | |
72 | } | |
73 | ||
d559309f | 74 | $pciaddr = print_pci_addr("xhci", $bridges, $arch, $machine); |
de3d4ac4 DC |
75 | push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr" if $use_usb3; |
76 | ||
77 | return @$devices; | |
78 | } | |
79 | ||
80 | sub get_usb_devices { | |
81 | my ($conf, $format, $max_usb_devices) = @_; | |
82 | ||
83 | my $devices = []; | |
84 | ||
85 | for (my $i = 0; $i < $max_usb_devices; $i++) { | |
86 | next if !$conf->{"usb$i"}; | |
87 | my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{"usb$i"}) }; | |
88 | next if !$d; | |
89 | ||
90 | if (defined($d->{host})) { | |
91 | my $hostdevice = parse_usb_device($d->{host}); | |
92 | $hostdevice->{usb3} = $d->{usb3}; | |
93 | if (defined($hostdevice->{spice}) && $hostdevice->{spice}) { | |
94 | # usb redir support for spice, currently no usb3 | |
95 | push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir"; | |
96 | push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0"; | |
97 | } else { | |
98 | push @$devices, '-device', print_usbdevice_full($conf, "usb$i", $hostdevice); | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | return @$devices; | |
104 | } | |
105 | ||
106 | sub print_usbdevice_full { | |
107 | my ($conf, $deviceid, $device) = @_; | |
108 | ||
109 | return if !$device; | |
110 | my $usbdevice = "usb-host"; | |
111 | ||
112 | # if it is a usb3 device, attach it to the xhci controller, else omit the bus option | |
113 | if($device->{usb3}) { | |
114 | $usbdevice .= ",bus=xhci.0"; | |
115 | } | |
116 | ||
117 | if (defined($device->{vendorid}) && defined($device->{productid})) { | |
118 | $usbdevice .= ",vendorid=0x$device->{vendorid},productid=0x$device->{productid}"; | |
119 | } elsif (defined($device->{hostbus}) && defined($device->{hostport})) { | |
120 | $usbdevice .= ",hostbus=$device->{hostbus},hostport=$device->{hostport}"; | |
121 | } | |
122 | ||
123 | $usbdevice .= ",id=$deviceid"; | |
124 | return $usbdevice; | |
125 | } | |
126 | ||
127 | 1; |