]> git.proxmox.com Git - qemu-server.git/blame - PVE/QemuServer/USB.pm
usb: refactor usb code and move some into USB module
[qemu-server.git] / PVE / QemuServer / USB.pm
CommitLineData
de3d4ac4
DC
1package PVE::QemuServer::USB;
2
3use strict;
4use warnings;
5use PVE::QemuServer::PCI qw(print_pci_addr);
3deccbd7 6use PVE::QemuServer::Machine;
4862922a 7use PVE::QemuServer::Helpers qw(min_version windows_version);
de3d4ac4
DC
8use PVE::JSONSchema;
9use base 'Exporter';
10
11our @EXPORT_OK = qw(
12parse_usb_device
13get_usb_controllers
14get_usb_devices
15);
16
0c3d18ef 17my $OLD_MAX_USB = 5;
0cf8d56c
DC
18our $MAX_USB_DEVICES = 14;
19
20
21my $USB_ID_RE = qr/(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})/;
22my $USB_PATH_RE = qr/(\d+)\-(\d+(\.\d+)*)/;
23
24my $usb_fmt = {
25 host => {
26 default_key => 1,
27 type => 'string',
28 pattern => qr/(?:(?:$USB_ID_RE)|(?:$USB_PATH_RE)|[Ss][Pp][Ii][Cc][Ee])/,
29 format_description => 'HOSTUSBDEVICE|spice',
30 description => <<EODESCR,
31The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
32
33 'bus-port(.port)*' (decimal numbers) or
34 'vendor_id:product_id' (hexadeciaml numbers) or
35 'spice'
36
37You can use the 'lsusb -t' command to list existing usb devices.
38
39NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
40machines - use with special care.
41
42The value 'spice' can be used to add a usb redirection devices for spice.
43EODESCR
44 },
45 usb3 => {
46 optional => 1,
47 type => 'boolean',
48 description => "Specifies whether if given host option is a USB3 device or port."
49 ." For modern guests (machine version >= 7.1 and ostype l26 and windows > 7), this flag"
50 ." is irrelevant (all devices are plugged into a xhci controller).",
51 default => 0,
52 },
53};
54
55PVE::JSONSchema::register_format('pve-qm-usb', $usb_fmt);
56
57our $usbdesc = {
58 optional => 1,
59 type => 'string', format => $usb_fmt,
60 description => "Configure an USB device (n is 0 to 4, for machine version >= 7.1 and ostype"
61 ." l26 or windows > 7, n can be up to 14).",
62};
63PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
0c3d18ef 64
de3d4ac4
DC
65sub parse_usb_device {
66 my ($value) = @_;
67
d1c1af4b 68 return if !$value;
de3d4ac4
DC
69
70 my $res = {};
0cf8d56c 71 if ($value =~ m/^$USB_ID_RE$/) {
de3d4ac4
DC
72 $res->{vendorid} = $2;
73 $res->{productid} = $4;
0cf8d56c 74 } elsif ($value =~ m/^$USB_PATH_RE$/) {
de3d4ac4
DC
75 $res->{hostbus} = $1;
76 $res->{hostport} = $2;
77 } elsif ($value =~ m/^spice$/i) {
78 $res->{spice} = 1;
79 } else {
d1c1af4b 80 return;
de3d4ac4
DC
81 }
82
83 return $res;
84}
85
871ebe17 86my sub assert_usb_index_is_useable {
0c3d18ef
DC
87 my ($index, $use_qemu_xhci) = @_;
88
89 die "using usb$index is only possible with machine type >= 7.1 and ostype l26 or windows > 7\n"
90 if $index >= $OLD_MAX_USB && !$use_qemu_xhci;
91
92 return undef;
93}
94
de3d4ac4 95sub get_usb_controllers {
0cf8d56c 96 my ($conf, $bridges, $arch, $machine, $machine_version) = @_;
de3d4ac4
DC
97
98 my $devices = [];
99 my $pciaddr = "";
100
4862922a
DC
101 my $ostype = $conf->{ostype};
102
103 my $use_qemu_xhci = min_version($machine_version, 7, 1)
104 && defined($ostype) && ($ostype eq 'l26' || windows_version($ostype) > 7);
f6b24f42 105 my $is_q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
4862922a 106
d559309f
WB
107 if ($arch eq 'aarch64') {
108 $pciaddr = print_pci_addr('ehci', $bridges, $arch, $machine);
109 push @$devices, '-device', "usb-ehci,id=ehci$pciaddr";
f6b24f42 110 } elsif (!$is_q35) {
d559309f 111 $pciaddr = print_pci_addr("piix3", $bridges, $arch, $machine);
de3d4ac4 112 push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2";
de3d4ac4
DC
113 }
114
e68881e4
TL
115 my ($use_usb2, $use_usb3) = 0;
116 my $any_usb = 0;
0cf8d56c 117 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
de3d4ac4 118 next if !$conf->{"usb$i"};
871ebe17 119 assert_usb_index_is_useable($i, $use_qemu_xhci);
0cf8d56c 120 my $d = eval { PVE::JSONSchema::parse_property_string($usb_fmt, $conf->{"usb$i"}) } or next;
e68881e4 121 $any_usb = 1;
4862922a 122 $use_usb3 = 1 if $d->{usb3};
e68881e4
TL
123 $use_usb2 = 1 if !$d->{usb3};
124 }
125
f6b24f42 126 if (!$use_qemu_xhci && !$is_q35 && $use_usb2 && $arch ne 'aarch64') {
e68881e4
TL
127 # include usb device config if still on x86 before-xhci machines and if USB 3 is not used
128 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg';
de3d4ac4
DC
129 }
130
d559309f 131 $pciaddr = print_pci_addr("xhci", $bridges, $arch, $machine);
e68881e4 132 if ($use_qemu_xhci && $any_usb) {
4862922a 133 push @$devices, '-device', print_qemu_xhci_controller($pciaddr);
e68881e4
TL
134 } elsif ($use_usb3) {
135 push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr";
4862922a 136 }
de3d4ac4
DC
137
138 return @$devices;
139}
140
141sub get_usb_devices {
0cf8d56c 142 my ($conf, $features, $bootorder, $machine_version) = @_;
de3d4ac4
DC
143
144 my $devices = [];
145
4862922a
DC
146 my $ostype = $conf->{ostype};
147 my $use_qemu_xhci = min_version($machine_version, 7, 1)
148 && defined($ostype) && ($ostype eq 'l26' || windows_version($ostype) > 7);
149
0cf8d56c 150 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
2141a802
SR
151 my $devname = "usb$i";
152 next if !$conf->{$devname};
871ebe17 153 assert_usb_index_is_useable($i, $use_qemu_xhci);
0cf8d56c 154 my $d = eval { PVE::JSONSchema::parse_property_string($usb_fmt, $conf->{$devname}) };
de3d4ac4
DC
155 next if !$d;
156
342f0493 157 my $port = $use_qemu_xhci ? $i + 1 : undef;
4862922a 158
0cf8d56c
DC
159 if ($d->{host} && $d->{host} =~ m/^spice$/) {
160 # usb redir support for spice
161 my $bus = 'ehci';
162 $bus = 'xhci' if ($d->{usb3} && $features->{spice_usb3}) || $use_qemu_xhci;
163
164 push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
165 push @$devices, '-device', print_spice_usbdevice($i, $bus, $port);
166
167 warn "warning: spice usb port set as bootdevice, ignoring\n" if $bootorder->{$devname};
168 } else {
169 push @$devices, '-device', print_usbdevice_full($conf, $devname, $d, $bootorder, $port);
de3d4ac4
DC
170 }
171 }
172
173 return @$devices;
174}
175
4862922a
DC
176sub print_qemu_xhci_controller {
177 my ($pciaddr) = @_;
178 return "qemu-xhci,p2=15,p3=15,id=xhci$pciaddr";
179}
180
181sub print_spice_usbdevice {
182 my ($index, $bus, $port) = @_;
183 my $device = "usb-redir,chardev=usbredirchardev$index,id=usbredirdev$index,bus=$bus.0";
184 if (defined($port)) {
185 $device .= ",port=$port";
186 }
187 return $device;
188}
189
de3d4ac4 190sub print_usbdevice_full {
4862922a 191 my ($conf, $deviceid, $device, $bootorder, $port) = @_;
de3d4ac4
DC
192
193 return if !$device;
194 my $usbdevice = "usb-host";
195
4862922a 196 # if it is a usb3 device or with newer qemu, attach it to the xhci controller, else omit the bus option
342f0493 197 if ($device->{usb3} || defined($port)) {
de3d4ac4 198 $usbdevice .= ",bus=xhci.0";
4862922a 199 $usbdevice .= ",port=$port" if defined($port);
de3d4ac4
DC
200 }
201
0cf8d56c
DC
202 my $parsed = parse_usb_device($device->{host});
203
204 if (defined($parsed->{vendorid}) && defined($parsed->{productid})) {
205 $usbdevice .= ",vendorid=0x$parsed->{vendorid},productid=0x$parsed->{productid}";
206 } elsif (defined($parsed->{hostbus}) && defined($parsed->{hostport})) {
207 $usbdevice .= ",hostbus=$parsed->{hostbus},hostport=$parsed->{hostport}";
b06a2492
DC
208 } else {
209 die "no usb id or path given\n";
de3d4ac4
DC
210 }
211
212 $usbdevice .= ",id=$deviceid";
2141a802 213 $usbdevice .= ",bootindex=$bootorder->{$deviceid}" if $bootorder->{$deviceid};
de3d4ac4
DC
214 return $usbdevice;
215}
216
2171;