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