]>
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"; | |
7583d156 | 45 | } elsif ($machine !~ /q35/) { # FIXME: combine this and machine_type_is_q35 |
d559309f | 46 | $pciaddr = print_pci_addr("piix3", $bridges, $arch, $machine); |
de3d4ac4 DC |
47 | push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2"; |
48 | ||
49 | my $use_usb2 = 0; | |
50 | for (my $i = 0; $i < $max_usb_devices; $i++) { | |
51 | next if !$conf->{"usb$i"}; | |
52 | my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{"usb$i"}) }; | |
53 | next if !$d || $d->{usb3}; # do not add usb2 controller if we have only usb3 devices | |
54 | $use_usb2 = 1; | |
55 | } | |
56 | # include usb device config | |
57 | push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2; | |
58 | } | |
59 | ||
60 | # add usb3 controller if needed | |
61 | ||
62 | my $use_usb3 = 0; | |
63 | for (my $i = 0; $i < $max_usb_devices; $i++) { | |
64 | next if !$conf->{"usb$i"}; | |
65 | my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{"usb$i"}) }; | |
66 | next if !$d || !$d->{usb3}; | |
67 | $use_usb3 = 1; | |
68 | } | |
69 | ||
d559309f | 70 | $pciaddr = print_pci_addr("xhci", $bridges, $arch, $machine); |
de3d4ac4 DC |
71 | push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr" if $use_usb3; |
72 | ||
73 | return @$devices; | |
74 | } | |
75 | ||
76 | sub get_usb_devices { | |
2141a802 | 77 | my ($conf, $format, $max_usb_devices, $features, $bootorder) = @_; |
de3d4ac4 DC |
78 | |
79 | my $devices = []; | |
80 | ||
81 | for (my $i = 0; $i < $max_usb_devices; $i++) { | |
2141a802 SR |
82 | my $devname = "usb$i"; |
83 | next if !$conf->{$devname}; | |
84 | my $d = eval { PVE::JSONSchema::parse_property_string($format,$conf->{$devname}) }; | |
de3d4ac4 DC |
85 | next if !$d; |
86 | ||
87 | if (defined($d->{host})) { | |
88 | my $hostdevice = parse_usb_device($d->{host}); | |
89 | $hostdevice->{usb3} = $d->{usb3}; | |
47717a90 | 90 | if ($hostdevice->{spice}) { |
ae36393d AL |
91 | # usb redir support for spice |
92 | my $bus = 'ehci'; | |
93 | $bus = 'xhci' if $hostdevice->{usb3} && $features->{spice_usb3}; | |
94 | ||
de3d4ac4 | 95 | push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir"; |
ae36393d | 96 | push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=$bus.0"; |
2141a802 SR |
97 | |
98 | warn "warning: spice usb port set as bootdevice, ignoring\n" if $bootorder->{$devname}; | |
de3d4ac4 | 99 | } else { |
2141a802 | 100 | push @$devices, '-device', print_usbdevice_full($conf, $devname, $hostdevice, $bootorder); |
de3d4ac4 DC |
101 | } |
102 | } | |
103 | } | |
104 | ||
105 | return @$devices; | |
106 | } | |
107 | ||
108 | sub print_usbdevice_full { | |
2141a802 | 109 | my ($conf, $deviceid, $device, $bootorder) = @_; |
de3d4ac4 DC |
110 | |
111 | return if !$device; | |
112 | my $usbdevice = "usb-host"; | |
113 | ||
114 | # if it is a usb3 device, attach it to the xhci controller, else omit the bus option | |
115 | if($device->{usb3}) { | |
116 | $usbdevice .= ",bus=xhci.0"; | |
117 | } | |
118 | ||
119 | if (defined($device->{vendorid}) && defined($device->{productid})) { | |
120 | $usbdevice .= ",vendorid=0x$device->{vendorid},productid=0x$device->{productid}"; | |
121 | } elsif (defined($device->{hostbus}) && defined($device->{hostport})) { | |
122 | $usbdevice .= ",hostbus=$device->{hostbus},hostport=$device->{hostport}"; | |
123 | } | |
124 | ||
125 | $usbdevice .= ",id=$deviceid"; | |
2141a802 | 126 | $usbdevice .= ",bootindex=$bootorder->{$deviceid}" if $bootorder->{$deviceid}; |
de3d4ac4 DC |
127 | return $usbdevice; |
128 | } | |
129 | ||
130 | 1; |