XEN PVUSB: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
(40 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
=== PVUSB mit XEN 3.4 === | === PVUSB mit XEN 3.4 === | ||
Bei mir gab es Probleme mit USB per PCI-Delegation unter XEN 3.4.1 auf Lenny (die Netzwerkkarten gingen, ''lsusb'' hat auch die USB Hostcontroller angezeigt, aber es wurden keine USB-Devices sichtbar), deshalb habe ich die seit Xen 3.4 neue USB-Virtualisierung '''PVUSB''' (paravirtualized USB support for Xen, Details siehe [http:// | Bei mir gab es Probleme mit USB per PCI-Delegation unter XEN 3.4.1 auf Lenny (die Netzwerkkarten gingen, ''lsusb'' hat auch die USB Hostcontroller angezeigt, aber es wurden keine USB-Devices sichtbar), deshalb habe ich die seit Xen 3.4 neue USB-Virtualisierung '''PVUSB''' (paravirtualized USB support for Xen, Details siehe [http://lists.xensource.com/archives/html/xen-devel/2009-03/msg01084.html hier]) getestet und unter Lenny eingebaut. Dabei ist folgendes Skript herausgekommen, welches ich in '/etc/rc.local' als '''pvusb -b''' aufrufe, um beim booten des Host-Systems den USB-Drucker an meine Printer-Domain durchzureichen. | ||
File ''/ | '''Beispiele''' | ||
PVUSB Konfigfile zum automatischen Verbinden von USB-Devices z.B. beim booten des XEN Hostsystems. | |||
Ich verbinde meinen USB-Drucker nach dem Start mit meinen CUPS Printserver (srv), und USB-Sticks z.B. mit dem Webserver in der DMZ (dmz). | |||
Die USB DeviceIDs (z.B. '04a9:1093') kann man mit dem Komamndo ''lsusb'' ermitteln. Die USB-Ports (z.B. '3-2') werden beim Anstecken der Devices auf der Systemkonsole (Dom0) angezeigt. | |||
File '''/etc/xen/pvusb.conf''' | |||
<pre> | |||
# Configfile for pvusb | |||
# Dev_Id <domain> <Comment> | |||
# USBPort <domain> <Comment> | |||
# 0000:0000 <domain> <Comment> | |||
04a9:1093 srv Canon Printer IP4000 | |||
5-1 dmz Anything on USB Port 5-1 | |||
0000:0000 vm01 initialize 'vm01' for PVUSB | |||
</pre> | |||
File '''/etc/rc.local''' | |||
Für die Option ''-t'' ("Trigger Domain") muss SSH Access per SSH-Keys funktionieren, da mit ''ssh <domain> <Befehle>'' versucht wird, Remote-Kommandos innerhalb der Domain auszuführen. | |||
<pre>#!/bin/sh | <pre>#!/bin/sh | ||
... | |||
/usr/local/sbin/pvusb -b | |||
... | |||
</pre> | |||
Beispiele für ''pvusb'' | |||
<pre> | |||
m450:~# pvusb -h | |||
usage: pvusb -b [-t] | -i | |||
pvusb -s <domain> -a [ -t ] | |||
pvusb -d <device_id> -s <domain> [-c <comment>] -w [ -t ] | |||
pvusb -u <usb-port> -s <domain> [-c <comment>] -w [ -t ] | |||
pvusb -l | -r | |||
pvusb -x <usb-port>:<domain>:0:<vport> | |||
------------ | |||
-a # activate PVUSB (for -s <domain>) | |||
-b # boot/initialise PVUSB with hotplug rules (/etc/xen/pvusb.conf) | |||
-i # initialize PVUSB without hotplug rules (/etc/xen/pvusb.conf) | |||
-s <domain> # server domain_name or domain_id | |||
-u <usb-port> # USB-PORT e.g. '3-2' | |||
-d <device_id># USB device_id e.g. '0912:1234' (use 'lsusb' to get the ID) | |||
-c <comment> # e.g. "Canon IP4000 Printer" | |||
-l # list grabbed PVUSB Devices | |||
-r # read PVUSB hotplug rules | |||
-w # write/activate PVUSB hotplug rule | |||
-x # delete PVUSB hotplug rule (use copy/paste from -r list) | |||
-t # try to trigger all domains (per ssh) to init PVUSB | |||
-q # be quiet | |||
-D # Debug option (set -x) | |||
m450:~# pvusb -l | |||
PVUSB: 3-2 on srv (3) 04a9:1093 Canon, Inc. PIXMA iP4000 | |||
m450:~# pvusb -r | |||
5-1:5:0:2 | |||
3-2:3:0:3 | |||
5-2:3:0:2 | |||
m450:~# pvusb -x 5-2:3:0:2 | |||
m450:~# pvusb -r | |||
5-1:5:0:2 | |||
3-2:3:0:3 | |||
</pre> | |||
Dieses Skript funktioniert nicht mit Xen 3.4.3, wegen Änderungen im xenstore. Ab XEN 4.0 kann im Konfigfile die Option ''vusb = [ 'usbver=2, numports=8, port_1=5-8' ]'' verwendet werden. | |||
Skript für '''XEN 3.4.3''' siehe weiter unten! | |||
Das Skript ''/usr/local/sbin/'''''pvusb''' für '''Xen 3.4.2''' | |||
<pre> | |||
#!/bin/sh | |||
# | # | ||
# paravirtualized USB Support | # paravirtualized USB Support (XEN 3.4.2) | ||
# | # | ||
# Version: $Revision$ | # Version: $Revision: 1.15 $ | ||
# Datum: $Date$ | # Datum: $Date: 2009/08/31 07:28:49 $ | ||
# | # | ||
# Author: neobiker | # Author: neobiker | ||
# | # | ||
# $Log$ | # $Log: pvusb,v $ | ||
# Revision 1.15 2009/08/31 07:28:49 root | |||
# init_xenstore (): call init_backend () if needed | |||
# | |||
# Revision 1.14 2009/08/26 17:15:17 root | |||
# updated usage () and some info's | |||
# | |||
# Revision 1.13 2009/08/26 07:12:35 root | |||
# bugfix: get_free_vusb_port () | |||
# check if xenstore is initialized | |||
# | |||
# Revision 1.12 2009/08/25 20:05:43 root | |||
# some optimization and error handling | |||
# | |||
# Revision 1.11 2009/08/24 19:01:36 root | |||
# rename _trigger_backend () to _connect_backend () | |||
# | |||
# Revision 1.10 2009/08/24 18:52:53 root | |||
# bugfix: lsusb test fixed, ohci_hcd added | |||
# | |||
# Revision 1.9 2009/08/23 23:50:35 root | |||
# bugfix with domain not started | |||
# | |||
# Revision 1.8 2009/08/23 23:14:05 root | |||
# use -t to trigger domain needed | |||
# | |||
# Revision 1.7 2009/08/23 21:41:27 root | |||
# optimzed trigger_backend | |||
# | |||
# Revision 1.6 2009/08/23 21:01:21 root | |||
# read USB-Ports from $CONFIG | |||
# | |||
# Revision 1.5 2009/08/23 20:28:30 root | |||
# new options -t and -i | |||
# | |||
# Revision 1.4 2009/08/23 17:06:54 root | |||
# optimization and code readability | |||
# | |||
# Revision 1.3 2009/08/22 19:35:40 root | |||
# bugfix convert dom_id+dom_nam | |||
# | |||
# Revision 1.2 2009/08/21 22:24:27 root | |||
# bugfix: rename $server to $domain | |||
# | |||
# Revision 1.1 2009/08/20 20:28:14 root | |||
# Initial revision | |||
# | |||
LSUSB=$(lsusb 2>/dev/null | grep -v '0000:0000' || echo "Error") | |||
if [ "$LSUSB" = "Error" ]; then | |||
echo "Error: Missing command 'lsusb', please install usbutils" | |||
exit 1 | |||
fi | |||
set -e | set -e | ||
usage () | usage () | ||
{ | { | ||
cat <<-EOT | cat <<-EOT | ||
usage: $(basename $0) -b | usage: $(basename $0) -b [-t] | -i | ||
$(basename $0) -d device_id -s domain [-c comment] -w | $(basename $0) -s <domain> -a [ -t ] | ||
$(basename $0) -u usb-port | $(basename $0) -d <device_id> -s <domain> [-c <comment>] -w [ -t ] | ||
$(basename $0) -u <usb-port> -s <domain> [-c <comment>] -w [ -t ] | |||
$(basename $0) -l | -r | |||
$(basename $0) -x <usb-port>:<domain>:0:<vport> | |||
------------ | ------------ | ||
-b # boot | -a # activate PVUSB (for -s <domain>) | ||
-s domain | -b # boot/initialise PVUSB with hotplug rules ($CONFIG_FILE) | ||
-u usb-port | -i # initialize PVUSB without hotplug rules ($CONFIG_FILE) | ||
-d device_id | -s <domain> # server domain_name or domain_id | ||
-c comment | -u <usb-port> # USB-PORT e.g. '3-2' | ||
-l # list | -d <device_id># USB device_id e.g. '0912:1234' (use 'lsusb' to get the ID) | ||
- | -c <comment> # e.g. "Canon IP4000 Printer" | ||
-w # write/activate PVUSB rule | -l # list grabbed PVUSB Devices | ||
-r # read PVUSB hotplug rules | |||
-w # write/activate PVUSB hotplug rule | |||
-x # delete PVUSB hotplug rule (use copy/paste from -r list) | |||
-t # try to trigger all domains (per ssh) to init PVUSB | |||
-q # be quiet | |||
-D # Debug option (set -x) | |||
EOT | EOT | ||
} | } | ||
Zeile 38: | Zeile 168: | ||
init () | init () | ||
{ | { | ||
NUM_PORTS=8 # Max 16 PVUSB ports. | |||
VUSB_BUS=0 # virtual USB Bus in <domain> (we use always usb bus 0) | |||
CONFIG_FILE=/etc/xen/pvusb.conf | |||
[ -e $CONFIG_FILE ] || cat >$CONFIG_FILE <<-EOT | |||
# Configfile for pvusb | |||
# 'pvusb -b' connects <Device_Id> / <USB-Port> to <Domain> | |||
# 'pvusb -i' initializes <Domain> for PVUSB | |||
# <Device_Id> <Domain> <Comment> | |||
# 1234:5678 server USB-Printer on an USB-Port | |||
# <USB-Port> <Domain> <Comment> | |||
# 3-2 server Any Device on Port 3-2 | |||
# <Device_Id> <Domain> <Comment> | |||
# | # 0000:0000 server initialize 'server' for PVUSB | ||
EOT | EOT | ||
CONFIG=$(cat $CONFIG_FILE | sed -e 's/#.*$//' -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' -e '/^$/ d' -e '/^[[:graph:]]*$/ d') | |||
} | } | ||
Zeile 57: | Zeile 200: | ||
# based on init_xs.sh by Noboru Iwamatsu | # based on init_xs.sh by Noboru Iwamatsu | ||
dom_id=$1 | |||
vusb_bus=$2 # vusb_bus (typical 0) | |||
# check if xenstore is already initialized | |||
[ -e /sys/devices/xen-backend/vusb-$dom_id-$vusb_bus ] && return | |||
# check if PVUSB is already initialized | |||
[ -e /sys/bus/xen-backend/drivers/vusb ] || pvusb_init_backend | |||
XSWRITE=/usr/bin/xenstore-write | XSWRITE=/usr/bin/xenstore-write | ||
Zeile 67: | Zeile 213: | ||
# Write backend information into the location that frontend look for. | # Write backend information into the location that frontend look for. | ||
$XSWRITE /local/domain/$ | $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id 0 | ||
$XSWRITE /local/domain/$ | $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend \ | ||
/local/domain/0/backend/vusb/$dom_id/$vusb_bus | |||
# Write frontend information into the location that backend look for. | # Write frontend information into the location that backend look for. | ||
$XSWRITE /local/domain/0/backend/ | $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id $dom_id | ||
$XSWRITE /local/domain/0/backend/ | $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend \ | ||
/local/domain/$dom_id/device/vusb/$vusb_bus | |||
# Write virtual root hub field. | # Write virtual root hub field. | ||
$XSWRITE /local/domain/0/backend/ | $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports $NUM_PORTS | ||
for i in $(seq 1 $NUM_PORTS) | for i in $(seq 1 $NUM_PORTS) | ||
do | do | ||
# Set all port to disconnected state | # Set all port to disconnected state | ||
$XSWRITE /local/domain/0/backend/ | $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port-$i "0" | ||
done | done | ||
# Set permission | # Set permission | ||
$XSCHMOD /local/domain/$ | $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus n$dom_id r0 | ||
$XSCHMOD /local/domain/$ | $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id n$dom_id r0 | ||
$XSCHMOD /local/domain/$ | $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend n$dom_id r0 | ||
$XSCHMOD /local/domain/0/backend/ | $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus n0 r$dom_id | ||
$XSCHMOD /local/domain/0/backend/ | $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id n0 r$dom_id | ||
$XSCHMOD /local/domain/0/backend/ | $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend n0 r$dom_id | ||
$XSCHMOD /local/domain/0/backend/ | $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports n0 r$dom_id | ||
for i in $(seq 1 $NUM_PORTS) | for i in $(seq 1 $NUM_PORTS) | ||
do | do | ||
$XSCHMOD /local/domain/0/backend/ | $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port-$i n0 r$dom_id | ||
done | done | ||
# Set state to XenbusStateInitialising | # Set state to XenbusStateInitialising | ||
$XSWRITE /local/domain/$ | $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/state 1 | ||
$XSCHMOD /local/domain/$ | $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/state n$dom_id r0 | ||
$XSWRITE /local/domain/0/backend/ | $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state 1 | ||
$XSCHMOD /local/domain/0/backend/ | $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state n0 r$dom_id | ||
} | } | ||
pvusb_init_backend () | |||
{ | { | ||
[ "$ | # initialise backend only once | ||
[ "$PVUSB_INIT" = "1" ] && { PVUSB_INIT=1; return; } | |||
# 1) First, you have to start from the state that no device is connected. | # 1) First, you have to start from the state that no device is connected. | ||
Zeile 113: | Zeile 259: | ||
usb_mods="$(lsmod | grep usb | grep -v usbcore | grep -v usbbk | awk '/^usb/ {print $1}')" | usb_mods="$(lsmod | grep usb | grep -v usbcore | grep -v usbbk | awk '/^usb/ {print $1}')" | ||
[ -n "$usb_mods" ] && rmmod $usb_mods | [ -n "$usb_mods" ] && rmmod $usb_mods | ||
# load usb (backend) modules | # load usb (backend) modules | ||
Zeile 121: | Zeile 264: | ||
} | } | ||
pvusb_connect_backend () | |||
{ | { | ||
# connect USB devices to backend (and restart udevd afterwards) | # connect USB devices to backend (and restart udevd afterwards) | ||
# XXX: better way to connect USB devices dynamically | |||
rmmod uhci_hcd ehci_hcd | # stop udevd fetching events before the backend driver | ||
modprobe -a uhci_hcd ehci_hcd | udevadm control --stop_exec_queue | ||
# XXX: find better way to connect USB devices dynamically!!! | |||
rmmod uhci_hcd ehci_hcd ohci_hcd 2>/dev/null || true | |||
modprobe -a uhci_hcd ehci_hcd ohci_hcd || true | |||
# start udevd again after we have finished | # start udevd again after we have finished | ||
Zeile 132: | Zeile 279: | ||
} | } | ||
pvusb_write_rule () | |||
{ | { | ||
domain=$1 # domain to connect PVUSB Port | |||
usb_port=$2 # where is usb device connected | |||
comment=$3 # a name for usb device | comment=$3 # a name for usb device | ||
if [ -z "$ | if [ -z "$domain" ]; then | ||
echo "missing option -s < | echo "missing option -s <domain>" | ||
return | return | ||
fi | fi | ||
if [ -z "$ | if [ -z "$usb_port" ]; then | ||
echo "missing option -u <usb_port>" | echo "missing option -u <usb_port>" | ||
return | return | ||
fi | fi | ||
# initialise paravirtualized USB Support | if [ "$domain" = "$(echo $domain | tr -d [:alpha:])" ]; then | ||
pvusb_init_xenstore $dom_id $ | dom_name=$(xm domname $domain) | ||
dom_id=$domain | |||
else | |||
dom_name=$domain | |||
dom_id=$(xm domid $domain) | |||
fi | |||
# get the next free USB Port in <domain> | |||
vusb_port=$(get_free_vusb_port $dom_id) | |||
# 1) initialise paravirtualized USB Support for dom_id | |||
pvusb_init_xenstore $dom_id $VUSB_BUS | |||
# | # 2a) test and remove existing hotplug-rule | ||
actual_vport=$(cat /sys/bus/usb/drivers/usbback/vports | awk "/$ | actual_vport=$(cat /sys/bus/usb/drivers/usbback/vports | awk "/$usb_port/ {print}") | ||
[ -n "$actual_vport" ] && echo "$actual_vport" > /sys/bus/usb/drivers/usbback/remove_vport | [ -n "$actual_vport" ] && echo "$actual_vport" > /sys/bus/usb/drivers/usbback/remove_vport | ||
# | # 2b) Write the hotplug-rule through the sysfs interface of the backenend driver | ||
echo "$ | echo "$usb_port:$dom_id:$VUSB_BUS:$vusb_port" > /sys/bus/usb/drivers/usbback/new_vport | ||
# tell me what happened | # tell me what happened | ||
actual_vport=$(cat /sys/bus/usb/drivers/usbback/vports | awk "/$ | if [ -z "$QUIET" ]; then | ||
actual_vport=$(cat /sys/bus/usb/drivers/usbback/vports | awk "/$usb_port/"'{print}') | |||
echo "xen PVUSB Rule written: $actual_vport $comment on domain $dom_name ($dom_id)" | |||
fi | |||
} | |||
get_free_vusb_port () | |||
{ | |||
# get new vusb_port between 2 .. $NUM_PORTS | |||
dom_id=$1 | |||
vports=$(cat /sys/bus/usb/drivers/usbback/vports | awk -v FS=: "/^.*:$dom_id:.*:.*/ {print \$4}") | |||
if [ -z "$vports" ]; then | |||
echo "2" | |||
return | |||
else | |||
for ((i=2; $i<=$NUM_PORTS; i++)); do | |||
for vport in $vports; do | |||
[ "$vport" -eq $i ] && break | |||
done | |||
[ $vport -ne $i ] && { echo "$i"; return; } | |||
done | |||
fi | |||
echo "Error: no virtual USB-Port free for $dom_id" >&2 | |||
exit 1 | |||
} | } | ||
get_usb_port () | get_usb_port () | ||
{ | { | ||
usb_dev= | # find out where USB-Device is connected | ||
usb_dev=$1 | |||
usb_port="" | usb_port="" | ||
usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" | usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" | ||
usb_bus=$( | usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') | ||
usb_adr=$( | usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') | ||
for port in $(echo "$usb_ports"|grep | for port in $(echo "$usb_ports" | grep $usb_bus || true); do | ||
adr=$(cat /sys/bus/usb/devices/$port/devnum) | adr=$(cat /sys/bus/usb/devices/$port/devnum) | ||
if [ "$adr" = "$usb_adr" ]; then | if [ "$adr" = "$usb_adr" ]; then | ||
echo "$port" | |||
return | |||
fi | fi | ||
done | done | ||
echo "$ | echo "Error: no USB-Port found for $usb_dev" >&2 | ||
} | |||
pvusb_init_domains_from_configfile () | |||
{ | |||
domains=$(echo "$CONFIG" | awk '/^0000:0000 / {print $2}') | |||
for domain in $domains; do | |||
dom_id=$(xm domid $domain || continue) | |||
[ -n "$dom_id" ] || continue | |||
pvusb_init_xenstore $dom_id $VUSB_BUS | |||
# remember domain to be triggered | |||
PVUSB_DOMAINS=$(cat <<-EOT | sort -u | |||
$PVUSB_DOMAINS | |||
$domain) | |||
done | |||
} | } | ||
pvusb_write_defined_ports_from_configfile () | |||
{ | { | ||
# | # $CONFIG: where to connect the USB-Ports | ||
# --------------------------------------- | |||
ports=$(echo "$CONFIG" | awk '/^[0-9]-[0-9] / {print $1}') | |||
for usb_port in $ports; do | |||
# read static definitions from $CONFIG | |||
pvusb_conf=$(echo "$CONFIG" | grep $usb_port | tail -1) | |||
if [ -n "$pvusb_conf" ]; then | |||
domain=$(echo $pvusb_conf | awk '{print $2}') | |||
comment=$(echo $pvusb_conf | cut -d\ -f3-) | |||
[ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue | |||
else | |||
continue | |||
fi | |||
# PVUSB write xen backend rules for usb device | |||
# -------------------------------------------- | |||
pvusb_write_rule $domain $usb_port "$comment" | |||
# remember domain to be triggered | |||
PVUSB_DOMAINS=$(cat <<-EOT | sort -u | |||
$PVUSB_DOMAINS | |||
$domain) | |||
done | |||
} | |||
pvusb_write_connected_devices_from_configfile () | |||
{ | |||
# $CONFIG: where to connect my USB-Devices | |||
# ---------------------------------------- | |||
# see dmesg (syslog) where device ist connected: | # see dmesg (syslog) where device ist connected: | ||
# e.g.: usb 3-2: new full speed USB device using uhci_hcd and address 4 | # e.g.: usb 3-2: new full speed USB device using uhci_hcd and address 4 | ||
devices="$1 | # get all connected USB devices | ||
usb_devices=$(echo "$LSUSB" | grep -v '0000:0000' | awk '{print $6}') | |||
# ready if no devices are found | |||
if [ -z "$usb_devices" ]; then | |||
echo "$(basename $0): No connected USB Devices found." | |||
return | |||
fi | |||
# XXX: only normal USB-Ports (1-1) are supported yet, no cascaded USB-Hubs (1-1.1) | |||
usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" | usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" | ||
for usb_dev in $ | for usb_dev in $usb_devices; do | ||
# where to connect my usb devices to? | # where to connect my usb devices to? | ||
# | # read static definitions from $CONFIG | ||
pvusb_conf=$( | pvusb_conf=$(echo "$CONFIG" | grep $usb_dev | tail -1) | ||
if [ -n "$pvusb_conf" ]; then | if [ -n "$pvusb_conf" ]; then | ||
domain=$(echo $pvusb_conf | awk '{print $2}') | |||
comment=$(echo $pvusb_conf | cut -d\ -f3-) | comment=$(echo $pvusb_conf | cut -d\ -f3-) | ||
# skip if <domain> isn't activ | |||
[ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue | |||
else | else | ||
continue | continue | ||
Zeile 208: | Zeile 443: | ||
# find out where USB-Device is connected | # find out where USB-Device is connected | ||
usb_bus=$( | usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') | ||
usb_adr=$( | usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') | ||
for port in $(echo "$usb_ports"|grep | for port in $(echo "$usb_ports" | grep $usb_bus || true); do | ||
adr=$(cat /sys/bus/usb/devices/$port/devnum) | adr=$(cat /sys/bus/usb/devices/$port/devnum) | ||
if [ "$adr" = "$usb_adr" ]; then | if [ "$adr" = "$usb_adr" ]; then | ||
Zeile 217: | Zeile 452: | ||
fi | fi | ||
done | done | ||
if [ -z "$usb_port" ]; then | |||
echo "Error: No USB Port found for $usb_dev" | |||
continue | |||
fi | |||
echo "found $comment ($usb_dev) on USB-Port $usb_port" | echo "found $comment ($usb_dev) on USB-Port $usb_port" | ||
# PVUSB write xen backend rules for usb device | # PVUSB write xen backend rules for usb device | ||
# -------------------------------------------- | # -------------------------------------------- | ||
pvusb_write_rule $domain $usb_port "$comment" | |||
# remember domain to be triggered | # remember domain to be triggered | ||
Zeile 230: | Zeile 469: | ||
} | } | ||
pvusb_trigger () | |||
{ | |||
# PVUSB trigger for all relevant domains | |||
# XXX: better way to trigger domU? | |||
for domain in $(echo "$1" | sort -u); do | |||
ssh $domain "[ -n \"\$(lsusb || true)\" ] && exit 0 ; | |||
rmmod xen_hcd usbcore || true ; | |||
modprobe xen_hcd | |||
" || true | |||
done | |||
} | |||
pvusb_list () | |||
{ | { | ||
# tell me what happened | |||
# --------------------- | |||
pvusb_grabbed=$(cat /sys/bus/usb/drivers/usbback/grabbed_devices) | |||
for pvusb in $pvusb_grabbed; do | |||
usb_port=$(echo $pvusb | awk -F: '{print $1}') | |||
dom_id=$(cat /sys/bus/usb/drivers/usbback/vports | awk -F: "/$usb_port/"' {print $2}') | |||
dev_id="$(cat /sys/bus/usb/devices/$usb_port/idVendor)" | |||
dev_id="$dev_id:$(cat /sys/bus/usb/devices/$usb_port/idProduct)" | |||
product=$(echo "$LSUSB" | grep $dev_id | cut -d\ -f6-) | |||
echo "PVUSB: $usb_port on $(xm domname $dom_id 2>/dev/null || echo "<inactiv>") ($dom_id) $product" | |||
done | |||
} | |||
# ------------------------ | |||
# Script starts here | |||
# ------------------------ | |||
init | |||
while getopts ":abc:d:Dhilqrs:tu:wx:" opt; do | |||
case $opt in | |||
a) # activate PVUSB for <domain> without any rules | |||
dom_id=$(xm domid $domain) | |||
pvusb_init_xenstore $dom_id $VUSB_BUS | |||
;; | |||
b) # boot host with setup rules in $CONFIG | |||
pvusb_init_backend | |||
pvusb_init_domains_from_configfile | |||
pvusb_write_connected_devices_from_configfile | |||
pvusb_write_defined_ports_from_configfile | |||
pvusb_connect_backend | |||
;; | |||
i) # initialise PVUSB without any rules | |||
pvusb_init_backend | |||
pvusb_init_domains_from_configfile | |||
pvusb_connect_backend | |||
;; | |||
c) # comment e.g. Canon IP4000 | |||
comment=$OPTARG | |||
;; | |||
d) # USB DEVICE_ID (0912:1234) | |||
usb_port=$(get_usb_port $OPTARG) | |||
;; | |||
h) usage | |||
;; | |||
l) # list PVUSB Ports | |||
LIST_PVUSB=1 | |||
;; | |||
q) # don't list PVUSB Ports | |||
QUIET=0 | |||
;; | |||
r) # list rules | |||
cat /sys/bus/usb/drivers/usbback/vports | |||
;; | |||
s) # Server Domain-name or -id | |||
domain=$(xm domname $OPTARG) # check if domain exists | |||
;; | |||
t) # try to trigger domain with ssh | |||
pvusb_trigger "$PVUSB_DOMAINS $domain" | |||
;; | |||
u) # USB-PORT (3-2) | |||
usb_port=$OPTARG | |||
;; | |||
w) # write rule PVUSB | |||
pvusb_write_rule $domain $usb_port "$comment" | |||
LIST_PVUSB=1 | |||
;; | |||
x) # delete PVUSB rule | |||
echo "$OPTARG" > /sys/bus/usb/drivers/usbback/remove_vport || true | |||
;; | |||
D) set -x | |||
;; | |||
\?) | |||
echo "Invalid option: -$OPTARG" >&2 | |||
;; | |||
esac | |||
done | |||
[ "$LIST_PVUSB" = "1" ] && pvusb_list | |||
</pre> | |||
Das Skript für '''XEN 3.4.3''' | |||
<pre>#!/bin/sh | |||
# | |||
# paravirtualized USB Support (XEN 3.4.3) | |||
# | |||
# Version: $Revision: 1.16 $ | |||
# Datum: $Date: 2010/06/26 15:42:21 $ | |||
# | |||
# Author: neobiker | |||
# | |||
# $Log: pvusb,v $ | |||
# Revision 1.16 2010/06/26 15:42:21 root | |||
# Changes for XEN 3.4.3 | |||
# | |||
# Revision 1.15 2009/08/31 07:28:49 root | |||
# init_xenstore (): call init_backend () if needed | |||
# | |||
# Revision 1.14 2009/08/26 17:15:17 root | |||
# updated usage () and some info's | |||
# | |||
# Revision 1.13 2009/08/26 07:12:35 root | |||
# bugfix: get_free_vusb_port () | |||
# check if xenstore is initialized | |||
# | |||
# Revision 1.12 2009/08/25 20:05:43 root | |||
# some optimization and error handling | |||
# | |||
# Revision 1.11 2009/08/24 19:01:36 root | |||
# rename _trigger_backend () to _connect_backend () | |||
# | |||
# Revision 1.10 2009/08/24 18:52:53 root | |||
# bugfix: lsusb test fixed, ohci_hcd added | |||
# | |||
# Revision 1.9 2009/08/23 23:50:35 root | |||
# bugfix with domain not started | |||
# | |||
# Revision 1.8 2009/08/23 23:14:05 root | |||
# use -t to trigger domain needed | |||
# | |||
# Revision 1.7 2009/08/23 21:41:27 root | |||
# optimzed trigger_backend | |||
# | |||
# Revision 1.6 2009/08/23 21:01:21 root | |||
# read USB-Ports from $CONFIG | |||
# | |||
# Revision 1.5 2009/08/23 20:28:30 root | |||
# new options -t and -i | |||
# | |||
# Revision 1.4 2009/08/23 17:06:54 root | |||
# optimization and code readability | |||
# | |||
# Revision 1.3 2009/08/22 19:35:40 root | |||
# bugfix convert dom_id+dom_nam | |||
# | |||
# Revision 1.2 2009/08/21 22:24:27 root | |||
# bugfix: rename $server to $domain | |||
# | |||
# Revision 1.1 2009/08/20 20:28:14 root | |||
# Initial revision | |||
# | |||
LSUSB=$(lsusb 2>/dev/null) | |||
if [ ! $? ]; then | |||
echo "Error: Missing command 'lsusb', please install usbutils" | |||
exit 1 | |||
fi | |||
XSREAD=/usr/bin/xenstore-read | |||
XSWRITE=/usr/bin/xenstore-write | |||
XSCHMOD=/usr/bin/xenstore-chmod | |||
set -e | |||
LSUSB=$(echo "$LSUSB" | grep -v '0000:0000') | |||
usage () | |||
{ | |||
# $(basename $0) -l | -r | |||
# $(basename $0) -x <usb-port>:<domain>:0:<vport> | |||
# -r # read PVUSB hotplug rules | |||
# -x # delete PVUSB hotplug rule (use copy/paste from -r list) | |||
cat <<-EOT | |||
usage: $(basename $0) -b [-t] | -i | |||
$(basename $0) -s <domain> -a [ -t ] | |||
$(basename $0) -d <device_id> -s <domain> [-c <comment>] -w [ -t ] | |||
$(basename $0) -u <usb-port> -s <domain> [-c <comment>] -w [ -t ] | |||
$(basename $0) -l | |||
------------ | |||
-a # activate PVUSB (for -s <domain>) | |||
-b # boot/initialise PVUSB with hotplug rules ($CONFIG_FILE) | |||
-i # initialize PVUSB without hotplug rules ($CONFIG_FILE) | |||
-s <domain> # server domain_name or domain_id | |||
-u <usb-port> # USB-PORT e.g. '3-2' | |||
-d <device_id># USB device_id e.g. '0912:1234' (use 'lsusb' to get the ID) | |||
-c <comment> # e.g. "Canon IP4000 Printer" | |||
-l # list grabbed PVUSB Devices | |||
-w # write/activate PVUSB hotplug rule | |||
-t # try to trigger all domains (per ssh) to init PVUSB | |||
-q # be quiet | |||
-D # Debug option (set -x) | |||
EOT | |||
} | |||
init () | |||
{ | |||
NUM_PORTS=8 # Max 16 PVUSB ports. | |||
VUSB_BUS=0 # virtual USB Bus in <domain> (we use usb bus 0) | |||
CONFIG_FILE=/etc/xen/pvusb.conf | |||
[ -e $CONFIG_FILE ] || cat >$CONFIG_FILE <<-EOT | |||
# Configfile for pvusb | |||
# 'pvusb -b' connects <Device_Id> / <USB-Port> to <Domain> | |||
# 'pvusb -i' initializes <Domain> for PVUSB | |||
# <Device_Id> <Domain> <Comment> | |||
# 1234:5678 server USB-Printer on an USB-Port | |||
# <USB-Port> <Domain> <Comment> | |||
# 3-2 server Any Device on Port 3-2 | |||
# <Device_Id> <Domain> <Comment> | |||
# 0000:0000 server initialize 'server' for PVUSB | |||
EOT | |||
CONFIG=$(cat $CONFIG_FILE | sed -e 's/#.*$//' -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' -e '/^$/ d' -e '/^[[:graph:]]*$/ d') | |||
} | |||
# | |||
# paravirtualized USB Support | |||
# --------------------------- | |||
pvusb_init_xenstore () | |||
{ | |||
# Setup and initialize the XenStore for PVUSB | |||
# based on init_xs.sh by Noboru Iwamatsu | |||
dom_id=$1 | |||
vusb_bus=$2 # vusb_bus (typical 0) | |||
# check if xenstore is already initialized | |||
[ -e /sys/bus/xen-backend/drivers/vusb/vusb-$dom_id-$vusb_bus ] && return | |||
# check if PVUSB is already initialized | |||
[ -e /sys/bus/xen-backend/drivers/vusb ] || pvusb_init_backend | |||
# Write backend information into the location that frontend look for. | |||
$XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id 0 | |||
$XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend \ | |||
/local/domain/0/backend/vusb/$dom_id/$vusb_bus | |||
# Write frontend information into the location that backend look for. | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id $dom_id | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend \ | |||
/local/domain/$dom_id/device/vusb/$vusb_bus | |||
# Write USB Spec version field. | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/usb-ver 2 | |||
# Write virtual root hub field. | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports $NUM_PORTS | |||
for i in $(seq 1 $NUM_PORTS) | |||
do | |||
# Set all port to disconnected state | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$i "" | |||
done | |||
# Set permission | |||
$XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus n$dom_id r0 | |||
$XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id n$dom_id r0 | |||
$XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend n$dom_id r0 | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus n0 r$dom_id | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id n0 r$dom_id | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend n0 r$dom_id | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/usb-ver n0 r$dom_id | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports n0 r$dom_id | |||
for i in $(seq 1 $NUM_PORTS) | |||
do | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$i n0 r0 | |||
done | |||
# Set state to XenbusStateInitialising | |||
$XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/state 1 | |||
$XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/state n$dom_id r0 | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state 1 | |||
$XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state n0 r$dom_id | |||
} | |||
pvusb_init_backend () | |||
{ | |||
# initialise backend only once | |||
[ "$PVUSB_INIT" = "1" ] && { PVUSB_INIT=1; return; } | |||
# 1) First, you have to start from the state that no device is connected. | |||
# remove all usb modules but usbbk | |||
usb_mods="$(lsmod | grep usb | grep -v usbcore | grep -v usbbk | awk '/^usb/ {print $1}')" | |||
[ -n "$usb_mods" ] && rmmod $usb_mods | |||
# load usb (backend) modules | |||
modprobe usbbk | |||
} | |||
pvusb_connect_backend () | |||
{ | |||
# connect USB devices to backend (and restart udevd afterwards) | |||
# stop udevd fetching events before the backend driver | |||
udevadm control --stop_exec_queue | |||
# XXX: find better way to connect USB devices dynamically!!! | |||
rmmod uhci_hcd ehci_hcd ohci_hcd 2>/dev/null || true | |||
modprobe -a uhci_hcd ehci_hcd ohci_hcd || true | |||
# start udevd again after we have finished | |||
udevadm control --start_exec_queue | |||
} | |||
pvusb_write_rule () | |||
{ | |||
domain=$1 # domain to connect PVUSB Port | |||
usb_port=$2 # where is usb device connected | |||
comment=$3 # a name for usb device | |||
if [ -z "$domain" ]; then | |||
echo "missing option -s <domain>" | |||
return | |||
fi | |||
if [ -z "$usb_port" ]; then | |||
echo "missing option -u <usb_port>" | |||
return | |||
fi | |||
if [ "$domain" = "$(echo $domain | tr -d [:alpha:])" ]; then | |||
dom_name=$(xm domname $domain) | |||
dom_id=$domain | |||
else | |||
dom_name=$domain | |||
dom_id=$(xm domid $domain) | |||
fi | |||
# 1) initialise paravirtualized USB Support for dom_id | |||
pvusb_init_xenstore $dom_id $VUSB_BUS | |||
# get the next free USB Port in <domain> | |||
vusb_port=$(get_free_vusb_port $dom_id) | |||
# 2) Write the hotplug-rule to xenstore | |||
$XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$vusb_port $usb_port | |||
# tell me what happened | |||
if [ -z "$QUIET" ]; then | |||
actual_vport=$($XSREAD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$vusb_port || true) | |||
echo "xen PVUSB Rule written: $actual_vport $comment on domain $dom_name ($dom_id)" | |||
fi | |||
} | |||
get_free_vusb_port () | |||
{ | |||
# get new vusb_port between 2 .. $NUM_PORTS | |||
dom_id=$1 | |||
for ((i=1; $i<=$NUM_PORTS; i++)); do | |||
vport=$($XSREAD /local/domain/0/backend/vusb/$dom_id/0/port/$i || true) | |||
[ -z "$vport" ] && { echo "$i"; return; } | |||
done | |||
echo "Error: no virtual USB-Port free for $dom_id" >&2 | |||
exit 1 | |||
} | |||
get_usb_port () | |||
{ | |||
# find out where USB-Device is connected | |||
usb_dev=$1 | |||
usb_port="" | |||
usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" | |||
usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') | |||
usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') | |||
for port in $(echo "$usb_ports" | grep $usb_bus || true); do | |||
adr=$(cat /sys/bus/usb/devices/$port/devnum) | |||
if [ "$adr" = "$usb_adr" ]; then | |||
echo "$port" | |||
return | |||
fi | |||
done | |||
echo "Error: no USB-Port found for $usb_dev" >&2 | |||
} | |||
pvusb_init_domains_from_configfile () | |||
{ | |||
domains=$(echo "$CONFIG" | awk '/^0000:0000 / {print $2}') | |||
for domain in $domains; do | |||
dom_id=$(xm domid $domain || continue) | |||
[ -n "$dom_id" ] || continue | |||
pvusb_init_xenstore $dom_id $VUSB_BUS | |||
# remember domain to be triggered | |||
PVUSB_DOMAINS=$(cat <<-EOT | sort -u | |||
$PVUSB_DOMAINS | |||
$domain) | |||
done | |||
} | |||
pvusb_write_defined_ports_from_configfile () | |||
{ | |||
# $CONFIG: where to connect the USB-Ports | |||
# --------------------------------------- | |||
ports=$(echo "$CONFIG" | awk '/^[0-9]-[0-9] / {print $1}') | |||
for usb_port in $ports; do | |||
# read static definitions from $CONFIG | |||
pvusb_conf=$(echo "$CONFIG" | grep $usb_port | tail -1) | |||
if [ -n "$pvusb_conf" ]; then | |||
domain=$(echo $pvusb_conf | awk '{print $2}') | |||
comment=$(echo $pvusb_conf | cut -d\ -f3-) | |||
[ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue | |||
else | |||
continue | |||
fi | |||
# PVUSB write xen backend rules for usb device | |||
# -------------------------------------------- | |||
pvusb_write_rule $domain $usb_port "$comment" | |||
# remember domain to be triggered | |||
PVUSB_DOMAINS=$(cat <<-EOT | sort -u | |||
$PVUSB_DOMAINS | |||
$domain) | |||
done | |||
} | |||
pvusb_write_connected_devices_from_configfile () | |||
{ | |||
# $CONFIG: where to connect my USB-Devices | |||
# ---------------------------------------- | |||
# see dmesg (syslog) where device ist connected: | |||
# e.g.: usb 3-2: new full speed USB device using uhci_hcd and address 4 | |||
# get all connected USB devices | # get all connected USB devices | ||
usb_devices=$( | usb_devices=$(echo "$LSUSB" | awk '{print $6}') | ||
# ready if no devices are found | # ready if no devices are found | ||
if [ -z "$usb_devices" ]; then | if [ -z "$usb_devices" ]; then | ||
echo "$(basename $0): No USB Devices found" | echo "$(basename $0): No connected USB Devices found." | ||
return | return | ||
fi | fi | ||
# | # XXX: only normal USB-Ports (1-1) are supported yet, no cascaded USB-Hubs (1-1.1) | ||
usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" | |||
# PVUSB write backend rules | for usb_dev in $usb_devices; do | ||
# where to connect my usb devices to? | |||
# read static definitions from $CONFIG | |||
pvusb_conf=$(echo "$CONFIG" | grep $usb_dev | tail -1) | |||
if [ -n "$pvusb_conf" ]; then | |||
domain=$(echo $pvusb_conf | awk '{print $2}') | |||
comment=$(echo $pvusb_conf | cut -d\ -f3-) | |||
# skip if <domain> isn't activ | |||
[ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue | |||
else | |||
continue | |||
fi | |||
# find out where USB-Device is connected | |||
usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') | |||
usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') | |||
for port in $(echo "$usb_ports" | grep $usb_bus || true); do | |||
adr=$(cat /sys/bus/usb/devices/$port/devnum) | |||
if [ "$adr" = "$usb_adr" ]; then | |||
usb_port=$port | |||
break | |||
fi | |||
done | |||
if [ -z "$usb_port" ]; then | |||
echo "Error: No USB Port found for $usb_dev" | |||
continue | |||
fi | |||
echo "found $comment ($usb_dev) on USB-Port $usb_port" | |||
# PVUSB write xen backend rules for usb device | |||
# -------------------------------------------- | |||
pvusb_write_rule $domain $usb_port "$comment" | |||
# remember domain to be triggered | |||
PVUSB_DOMAINS=$(cat <<-EOT | sort -u | |||
$PVUSB_DOMAINS | |||
$domain) | |||
done | |||
} | } | ||
Zeile 253: | Zeile 966: | ||
{ | { | ||
# PVUSB trigger for all relevant domains | # PVUSB trigger for all relevant domains | ||
# XXX: better way to trigger domU? | # XXX: better way to trigger domU? | ||
for | for domain in $(echo "$1" | sort -u); do | ||
ssh $ | ssh $domain "[ -n \"\$(lsusb || true)\" ] && exit 0 ; | ||
rmmod xen_hcd usbcore ; | rmmod xen_hcd usbcore || true ; | ||
modprobe xen_hcd | modprobe xen_hcd | ||
" | " || true | ||
done | done | ||
} | } | ||
Zeile 270: | Zeile 979: | ||
# tell me what happened | # tell me what happened | ||
# --------------------- | # --------------------- | ||
for vusb_dev in /sys/bus/xen-backend/drivers/vusb/vusb-*-*; do | |||
dom_id=$(basename "$vusb_dev" | cut -d- -f2) | |||
[ "$dom_id" == '*' ] && break | |||
for ((i=1; $i<=$NUM_PORTS; i++)); do | |||
port=$($XSREAD /local/domain/0/backend/vusb/$dom_id/0/port/$i || true) | |||
if [ -n "$port" ]; then | |||
usb_port=$port | |||
dev_id="$(cat /sys/bus/usb/devices/$usb_port/idVendor)" | |||
dev_id="$dev_id:$(cat /sys/bus/usb/devices/$usb_port/idProduct)" | |||
product=$(echo "$LSUSB" | grep $dev_id | cut -d\ -f6-) | |||
echo "PVUSB: $usb_port on $(xm domname $dom_id 2>/dev/null || echo "<inactiv>") ($dom_id) $product" | |||
fi | |||
done | |||
done | done | ||
#pvusb_grabbed=$(ls /sys/bus/xen-backend/drivers/vusb/vusb-*-*) | |||
#for pvusb in $pvusb_grabbed; do | |||
# usb_port=$(basename $pvusb | awk -F: '{print $1}') | |||
# dom_id=$(cat /sys/bus/usb/drivers/usbback/port_ids | awk -F: "/$usb_port/"' {print $2}') | |||
# dev_id="$(cat /sys/bus/usb/devices/$usb_port/idVendor)" | |||
# dev_id="$dev_id:$(cat /sys/bus/usb/devices/$usb_port/idProduct)" | |||
# product=$(echo "$LSUSB" | grep $dev_id | cut -d\ -f6-) | |||
# echo "PVUSB: $usb_port on $(xm domname $dom_id 2>/dev/null || echo "<inactiv>") ($dom_id) $product" | |||
#done | |||
} | } | ||
Zeile 286: | Zeile 1.011: | ||
init | init | ||
while getopts ": | #while getopts ":abc:d:Dhilqrs:tu:wx:" opt; do | ||
while getopts ":abc:d:Dhilqs:tu:w:" opt; do | |||
case $opt in | case $opt in | ||
a) # activate PVUSB for <domain> without any rules | |||
dom_id=$(xm domid $domain) | |||
pvusb_init_xenstore $dom_id $VUSB_BUS | |||
;; | ;; | ||
c) # | b) # boot host with setup rules in $CONFIG | ||
pvusb_init_backend | |||
pvusb_init_domains_from_configfile | |||
pvusb_write_connected_devices_from_configfile | |||
pvusb_write_defined_ports_from_configfile | |||
pvusb_connect_backend | |||
;; | |||
i) # initialise PVUSB without any rules | |||
pvusb_init_backend | |||
pvusb_init_domains_from_configfile | |||
pvusb_connect_backend | |||
;; | |||
c) # comment e.g. Canon IP4000 | |||
comment=$OPTARG | comment=$OPTARG | ||
;; | ;; | ||
Zeile 300: | Zeile 1.036: | ||
usb_port=$(get_usb_port $OPTARG) | usb_port=$(get_usb_port $OPTARG) | ||
;; | ;; | ||
s) # Server Domain name | h) usage | ||
domain=$OPTARG | ;; | ||
l) # list PVUSB Ports | |||
LIST_PVUSB=1 | |||
;; | |||
q) # don't list PVUSB Ports | |||
QUIET=0 | |||
;; | |||
r) # list rules | |||
#cat /sys/bus/usb/drivers/usbback/vports | |||
;; | |||
s) # Server Domain-name or -id | |||
domain=$(xm domname $OPTARG) # check if domain exists | |||
;; | |||
t) # try to trigger domain with ssh | |||
pvusb_trigger "$PVUSB_DOMAINS $domain" | |||
;; | ;; | ||
u) # USB-PORT (3-2) | u) # USB-PORT (3-2) | ||
Zeile 307: | Zeile 1.057: | ||
;; | ;; | ||
w) # write rule PVUSB | w) # write rule PVUSB | ||
pvusb_write_rule $domain $usb_port "$comment" | |||
LIST_PVUSB=1 | LIST_PVUSB=1 | ||
;; | ;; | ||
x) # delete PVUSB rule | |||
#echo "$OPTARG" > /sys/bus/usb/drivers/usbback/remove_vport || true | |||
;; | ;; | ||
D) set -x | |||
;; | ;; | ||
\?) | \?) | ||
Zeile 327: | Zeile 1.071: | ||
done | done | ||
[ $LIST_PVUSB | [ "$LIST_PVUSB" = "1" ] && pvusb_list | ||
</pre> | </pre> |
Aktuelle Version vom 26. Juni 2010, 19:59 Uhr
PVUSB mit XEN 3.4
Bei mir gab es Probleme mit USB per PCI-Delegation unter XEN 3.4.1 auf Lenny (die Netzwerkkarten gingen, lsusb hat auch die USB Hostcontroller angezeigt, aber es wurden keine USB-Devices sichtbar), deshalb habe ich die seit Xen 3.4 neue USB-Virtualisierung PVUSB (paravirtualized USB support for Xen, Details siehe hier) getestet und unter Lenny eingebaut. Dabei ist folgendes Skript herausgekommen, welches ich in '/etc/rc.local' als pvusb -b aufrufe, um beim booten des Host-Systems den USB-Drucker an meine Printer-Domain durchzureichen.
Beispiele PVUSB Konfigfile zum automatischen Verbinden von USB-Devices z.B. beim booten des XEN Hostsystems. Ich verbinde meinen USB-Drucker nach dem Start mit meinen CUPS Printserver (srv), und USB-Sticks z.B. mit dem Webserver in der DMZ (dmz).
Die USB DeviceIDs (z.B. '04a9:1093') kann man mit dem Komamndo lsusb ermitteln. Die USB-Ports (z.B. '3-2') werden beim Anstecken der Devices auf der Systemkonsole (Dom0) angezeigt.
File /etc/xen/pvusb.conf
# Configfile for pvusb # Dev_Id <domain> <Comment> # USBPort <domain> <Comment> # 0000:0000 <domain> <Comment> 04a9:1093 srv Canon Printer IP4000 5-1 dmz Anything on USB Port 5-1 0000:0000 vm01 initialize 'vm01' for PVUSB
File /etc/rc.local
Für die Option -t ("Trigger Domain") muss SSH Access per SSH-Keys funktionieren, da mit ssh <domain> <Befehle> versucht wird, Remote-Kommandos innerhalb der Domain auszuführen.
#!/bin/sh ... /usr/local/sbin/pvusb -b ...
Beispiele für pvusb
m450:~# pvusb -h usage: pvusb -b [-t] | -i pvusb -s <domain> -a [ -t ] pvusb -d <device_id> -s <domain> [-c <comment>] -w [ -t ] pvusb -u <usb-port> -s <domain> [-c <comment>] -w [ -t ] pvusb -l | -r pvusb -x <usb-port>:<domain>:0:<vport> ------------ -a # activate PVUSB (for -s <domain>) -b # boot/initialise PVUSB with hotplug rules (/etc/xen/pvusb.conf) -i # initialize PVUSB without hotplug rules (/etc/xen/pvusb.conf) -s <domain> # server domain_name or domain_id -u <usb-port> # USB-PORT e.g. '3-2' -d <device_id># USB device_id e.g. '0912:1234' (use 'lsusb' to get the ID) -c <comment> # e.g. "Canon IP4000 Printer" -l # list grabbed PVUSB Devices -r # read PVUSB hotplug rules -w # write/activate PVUSB hotplug rule -x # delete PVUSB hotplug rule (use copy/paste from -r list) -t # try to trigger all domains (per ssh) to init PVUSB -q # be quiet -D # Debug option (set -x) m450:~# pvusb -l PVUSB: 3-2 on srv (3) 04a9:1093 Canon, Inc. PIXMA iP4000 m450:~# pvusb -r 5-1:5:0:2 3-2:3:0:3 5-2:3:0:2 m450:~# pvusb -x 5-2:3:0:2 m450:~# pvusb -r 5-1:5:0:2 3-2:3:0:3
Dieses Skript funktioniert nicht mit Xen 3.4.3, wegen Änderungen im xenstore. Ab XEN 4.0 kann im Konfigfile die Option vusb = [ 'usbver=2, numports=8, port_1=5-8' ] verwendet werden. Skript für XEN 3.4.3 siehe weiter unten!
Das Skript /usr/local/sbin/pvusb für Xen 3.4.2
#!/bin/sh # # paravirtualized USB Support (XEN 3.4.2) # # Version: $Revision: 1.15 $ # Datum: $Date: 2009/08/31 07:28:49 $ # # Author: neobiker # # $Log: pvusb,v $ # Revision 1.15 2009/08/31 07:28:49 root # init_xenstore (): call init_backend () if needed # # Revision 1.14 2009/08/26 17:15:17 root # updated usage () and some info's # # Revision 1.13 2009/08/26 07:12:35 root # bugfix: get_free_vusb_port () # check if xenstore is initialized # # Revision 1.12 2009/08/25 20:05:43 root # some optimization and error handling # # Revision 1.11 2009/08/24 19:01:36 root # rename _trigger_backend () to _connect_backend () # # Revision 1.10 2009/08/24 18:52:53 root # bugfix: lsusb test fixed, ohci_hcd added # # Revision 1.9 2009/08/23 23:50:35 root # bugfix with domain not started # # Revision 1.8 2009/08/23 23:14:05 root # use -t to trigger domain needed # # Revision 1.7 2009/08/23 21:41:27 root # optimzed trigger_backend # # Revision 1.6 2009/08/23 21:01:21 root # read USB-Ports from $CONFIG # # Revision 1.5 2009/08/23 20:28:30 root # new options -t and -i # # Revision 1.4 2009/08/23 17:06:54 root # optimization and code readability # # Revision 1.3 2009/08/22 19:35:40 root # bugfix convert dom_id+dom_nam # # Revision 1.2 2009/08/21 22:24:27 root # bugfix: rename $server to $domain # # Revision 1.1 2009/08/20 20:28:14 root # Initial revision # LSUSB=$(lsusb 2>/dev/null | grep -v '0000:0000' || echo "Error") if [ "$LSUSB" = "Error" ]; then echo "Error: Missing command 'lsusb', please install usbutils" exit 1 fi set -e usage () { cat <<-EOT usage: $(basename $0) -b [-t] | -i $(basename $0) -s <domain> -a [ -t ] $(basename $0) -d <device_id> -s <domain> [-c <comment>] -w [ -t ] $(basename $0) -u <usb-port> -s <domain> [-c <comment>] -w [ -t ] $(basename $0) -l | -r $(basename $0) -x <usb-port>:<domain>:0:<vport> ------------ -a # activate PVUSB (for -s <domain>) -b # boot/initialise PVUSB with hotplug rules ($CONFIG_FILE) -i # initialize PVUSB without hotplug rules ($CONFIG_FILE) -s <domain> # server domain_name or domain_id -u <usb-port> # USB-PORT e.g. '3-2' -d <device_id># USB device_id e.g. '0912:1234' (use 'lsusb' to get the ID) -c <comment> # e.g. "Canon IP4000 Printer" -l # list grabbed PVUSB Devices -r # read PVUSB hotplug rules -w # write/activate PVUSB hotplug rule -x # delete PVUSB hotplug rule (use copy/paste from -r list) -t # try to trigger all domains (per ssh) to init PVUSB -q # be quiet -D # Debug option (set -x) EOT } init () { NUM_PORTS=8 # Max 16 PVUSB ports. VUSB_BUS=0 # virtual USB Bus in <domain> (we use always usb bus 0) CONFIG_FILE=/etc/xen/pvusb.conf [ -e $CONFIG_FILE ] || cat >$CONFIG_FILE <<-EOT # Configfile for pvusb # 'pvusb -b' connects <Device_Id> / <USB-Port> to <Domain> # 'pvusb -i' initializes <Domain> for PVUSB # <Device_Id> <Domain> <Comment> # 1234:5678 server USB-Printer on an USB-Port # <USB-Port> <Domain> <Comment> # 3-2 server Any Device on Port 3-2 # <Device_Id> <Domain> <Comment> # 0000:0000 server initialize 'server' for PVUSB EOT CONFIG=$(cat $CONFIG_FILE | sed -e 's/#.*$//' -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' -e '/^$/ d' -e '/^[[:graph:]]*$/ d') } # # paravirtualized USB Support # --------------------------- pvusb_init_xenstore () { # Setup and initialize the XenStore for PVUSB # based on init_xs.sh by Noboru Iwamatsu dom_id=$1 vusb_bus=$2 # vusb_bus (typical 0) # check if xenstore is already initialized [ -e /sys/devices/xen-backend/vusb-$dom_id-$vusb_bus ] && return # check if PVUSB is already initialized [ -e /sys/bus/xen-backend/drivers/vusb ] || pvusb_init_backend XSWRITE=/usr/bin/xenstore-write XSCHMOD=/usr/bin/xenstore-chmod # Write backend information into the location that frontend look for. $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id 0 $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend \ /local/domain/0/backend/vusb/$dom_id/$vusb_bus # Write frontend information into the location that backend look for. $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id $dom_id $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend \ /local/domain/$dom_id/device/vusb/$vusb_bus # Write virtual root hub field. $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports $NUM_PORTS for i in $(seq 1 $NUM_PORTS) do # Set all port to disconnected state $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port-$i "0" done # Set permission $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus n$dom_id r0 $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id n$dom_id r0 $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend n$dom_id r0 $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports n0 r$dom_id for i in $(seq 1 $NUM_PORTS) do $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port-$i n0 r$dom_id done # Set state to XenbusStateInitialising $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/state 1 $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/state n$dom_id r0 $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state 1 $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state n0 r$dom_id } pvusb_init_backend () { # initialise backend only once [ "$PVUSB_INIT" = "1" ] && { PVUSB_INIT=1; return; } # 1) First, you have to start from the state that no device is connected. # remove all usb modules but usbbk usb_mods="$(lsmod | grep usb | grep -v usbcore | grep -v usbbk | awk '/^usb/ {print $1}')" [ -n "$usb_mods" ] && rmmod $usb_mods # load usb (backend) modules modprobe usbbk } pvusb_connect_backend () { # connect USB devices to backend (and restart udevd afterwards) # stop udevd fetching events before the backend driver udevadm control --stop_exec_queue # XXX: find better way to connect USB devices dynamically!!! rmmod uhci_hcd ehci_hcd ohci_hcd 2>/dev/null || true modprobe -a uhci_hcd ehci_hcd ohci_hcd || true # start udevd again after we have finished udevadm control --start_exec_queue } pvusb_write_rule () { domain=$1 # domain to connect PVUSB Port usb_port=$2 # where is usb device connected comment=$3 # a name for usb device if [ -z "$domain" ]; then echo "missing option -s <domain>" return fi if [ -z "$usb_port" ]; then echo "missing option -u <usb_port>" return fi if [ "$domain" = "$(echo $domain | tr -d [:alpha:])" ]; then dom_name=$(xm domname $domain) dom_id=$domain else dom_name=$domain dom_id=$(xm domid $domain) fi # get the next free USB Port in <domain> vusb_port=$(get_free_vusb_port $dom_id) # 1) initialise paravirtualized USB Support for dom_id pvusb_init_xenstore $dom_id $VUSB_BUS # 2a) test and remove existing hotplug-rule actual_vport=$(cat /sys/bus/usb/drivers/usbback/vports | awk "/$usb_port/ {print}") [ -n "$actual_vport" ] && echo "$actual_vport" > /sys/bus/usb/drivers/usbback/remove_vport # 2b) Write the hotplug-rule through the sysfs interface of the backenend driver echo "$usb_port:$dom_id:$VUSB_BUS:$vusb_port" > /sys/bus/usb/drivers/usbback/new_vport # tell me what happened if [ -z "$QUIET" ]; then actual_vport=$(cat /sys/bus/usb/drivers/usbback/vports | awk "/$usb_port/"'{print}') echo "xen PVUSB Rule written: $actual_vport $comment on domain $dom_name ($dom_id)" fi } get_free_vusb_port () { # get new vusb_port between 2 .. $NUM_PORTS dom_id=$1 vports=$(cat /sys/bus/usb/drivers/usbback/vports | awk -v FS=: "/^.*:$dom_id:.*:.*/ {print \$4}") if [ -z "$vports" ]; then echo "2" return else for ((i=2; $i<=$NUM_PORTS; i++)); do for vport in $vports; do [ "$vport" -eq $i ] && break done [ $vport -ne $i ] && { echo "$i"; return; } done fi echo "Error: no virtual USB-Port free for $dom_id" >&2 exit 1 } get_usb_port () { # find out where USB-Device is connected usb_dev=$1 usb_port="" usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') for port in $(echo "$usb_ports" | grep $usb_bus || true); do adr=$(cat /sys/bus/usb/devices/$port/devnum) if [ "$adr" = "$usb_adr" ]; then echo "$port" return fi done echo "Error: no USB-Port found for $usb_dev" >&2 } pvusb_init_domains_from_configfile () { domains=$(echo "$CONFIG" | awk '/^0000:0000 / {print $2}') for domain in $domains; do dom_id=$(xm domid $domain || continue) [ -n "$dom_id" ] || continue pvusb_init_xenstore $dom_id $VUSB_BUS # remember domain to be triggered PVUSB_DOMAINS=$(cat <<-EOT | sort -u $PVUSB_DOMAINS $domain) done } pvusb_write_defined_ports_from_configfile () { # $CONFIG: where to connect the USB-Ports # --------------------------------------- ports=$(echo "$CONFIG" | awk '/^[0-9]-[0-9] / {print $1}') for usb_port in $ports; do # read static definitions from $CONFIG pvusb_conf=$(echo "$CONFIG" | grep $usb_port | tail -1) if [ -n "$pvusb_conf" ]; then domain=$(echo $pvusb_conf | awk '{print $2}') comment=$(echo $pvusb_conf | cut -d\ -f3-) [ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue else continue fi # PVUSB write xen backend rules for usb device # -------------------------------------------- pvusb_write_rule $domain $usb_port "$comment" # remember domain to be triggered PVUSB_DOMAINS=$(cat <<-EOT | sort -u $PVUSB_DOMAINS $domain) done } pvusb_write_connected_devices_from_configfile () { # $CONFIG: where to connect my USB-Devices # ---------------------------------------- # see dmesg (syslog) where device ist connected: # e.g.: usb 3-2: new full speed USB device using uhci_hcd and address 4 # get all connected USB devices usb_devices=$(echo "$LSUSB" | grep -v '0000:0000' | awk '{print $6}') # ready if no devices are found if [ -z "$usb_devices" ]; then echo "$(basename $0): No connected USB Devices found." return fi # XXX: only normal USB-Ports (1-1) are supported yet, no cascaded USB-Hubs (1-1.1) usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" for usb_dev in $usb_devices; do # where to connect my usb devices to? # read static definitions from $CONFIG pvusb_conf=$(echo "$CONFIG" | grep $usb_dev | tail -1) if [ -n "$pvusb_conf" ]; then domain=$(echo $pvusb_conf | awk '{print $2}') comment=$(echo $pvusb_conf | cut -d\ -f3-) # skip if <domain> isn't activ [ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue else continue fi # find out where USB-Device is connected usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') for port in $(echo "$usb_ports" | grep $usb_bus || true); do adr=$(cat /sys/bus/usb/devices/$port/devnum) if [ "$adr" = "$usb_adr" ]; then usb_port=$port break fi done if [ -z "$usb_port" ]; then echo "Error: No USB Port found for $usb_dev" continue fi echo "found $comment ($usb_dev) on USB-Port $usb_port" # PVUSB write xen backend rules for usb device # -------------------------------------------- pvusb_write_rule $domain $usb_port "$comment" # remember domain to be triggered PVUSB_DOMAINS=$(cat <<-EOT | sort -u $PVUSB_DOMAINS $domain) done } pvusb_trigger () { # PVUSB trigger for all relevant domains # XXX: better way to trigger domU? for domain in $(echo "$1" | sort -u); do ssh $domain "[ -n \"\$(lsusb || true)\" ] && exit 0 ; rmmod xen_hcd usbcore || true ; modprobe xen_hcd " || true done } pvusb_list () { # tell me what happened # --------------------- pvusb_grabbed=$(cat /sys/bus/usb/drivers/usbback/grabbed_devices) for pvusb in $pvusb_grabbed; do usb_port=$(echo $pvusb | awk -F: '{print $1}') dom_id=$(cat /sys/bus/usb/drivers/usbback/vports | awk -F: "/$usb_port/"' {print $2}') dev_id="$(cat /sys/bus/usb/devices/$usb_port/idVendor)" dev_id="$dev_id:$(cat /sys/bus/usb/devices/$usb_port/idProduct)" product=$(echo "$LSUSB" | grep $dev_id | cut -d\ -f6-) echo "PVUSB: $usb_port on $(xm domname $dom_id 2>/dev/null || echo "<inactiv>") ($dom_id) $product" done } # ------------------------ # Script starts here # ------------------------ init while getopts ":abc:d:Dhilqrs:tu:wx:" opt; do case $opt in a) # activate PVUSB for <domain> without any rules dom_id=$(xm domid $domain) pvusb_init_xenstore $dom_id $VUSB_BUS ;; b) # boot host with setup rules in $CONFIG pvusb_init_backend pvusb_init_domains_from_configfile pvusb_write_connected_devices_from_configfile pvusb_write_defined_ports_from_configfile pvusb_connect_backend ;; i) # initialise PVUSB without any rules pvusb_init_backend pvusb_init_domains_from_configfile pvusb_connect_backend ;; c) # comment e.g. Canon IP4000 comment=$OPTARG ;; d) # USB DEVICE_ID (0912:1234) usb_port=$(get_usb_port $OPTARG) ;; h) usage ;; l) # list PVUSB Ports LIST_PVUSB=1 ;; q) # don't list PVUSB Ports QUIET=0 ;; r) # list rules cat /sys/bus/usb/drivers/usbback/vports ;; s) # Server Domain-name or -id domain=$(xm domname $OPTARG) # check if domain exists ;; t) # try to trigger domain with ssh pvusb_trigger "$PVUSB_DOMAINS $domain" ;; u) # USB-PORT (3-2) usb_port=$OPTARG ;; w) # write rule PVUSB pvusb_write_rule $domain $usb_port "$comment" LIST_PVUSB=1 ;; x) # delete PVUSB rule echo "$OPTARG" > /sys/bus/usb/drivers/usbback/remove_vport || true ;; D) set -x ;; \?) echo "Invalid option: -$OPTARG" >&2 ;; esac done [ "$LIST_PVUSB" = "1" ] && pvusb_list
Das Skript für XEN 3.4.3
#!/bin/sh # # paravirtualized USB Support (XEN 3.4.3) # # Version: $Revision: 1.16 $ # Datum: $Date: 2010/06/26 15:42:21 $ # # Author: neobiker # # $Log: pvusb,v $ # Revision 1.16 2010/06/26 15:42:21 root # Changes for XEN 3.4.3 # # Revision 1.15 2009/08/31 07:28:49 root # init_xenstore (): call init_backend () if needed # # Revision 1.14 2009/08/26 17:15:17 root # updated usage () and some info's # # Revision 1.13 2009/08/26 07:12:35 root # bugfix: get_free_vusb_port () # check if xenstore is initialized # # Revision 1.12 2009/08/25 20:05:43 root # some optimization and error handling # # Revision 1.11 2009/08/24 19:01:36 root # rename _trigger_backend () to _connect_backend () # # Revision 1.10 2009/08/24 18:52:53 root # bugfix: lsusb test fixed, ohci_hcd added # # Revision 1.9 2009/08/23 23:50:35 root # bugfix with domain not started # # Revision 1.8 2009/08/23 23:14:05 root # use -t to trigger domain needed # # Revision 1.7 2009/08/23 21:41:27 root # optimzed trigger_backend # # Revision 1.6 2009/08/23 21:01:21 root # read USB-Ports from $CONFIG # # Revision 1.5 2009/08/23 20:28:30 root # new options -t and -i # # Revision 1.4 2009/08/23 17:06:54 root # optimization and code readability # # Revision 1.3 2009/08/22 19:35:40 root # bugfix convert dom_id+dom_nam # # Revision 1.2 2009/08/21 22:24:27 root # bugfix: rename $server to $domain # # Revision 1.1 2009/08/20 20:28:14 root # Initial revision # LSUSB=$(lsusb 2>/dev/null) if [ ! $? ]; then echo "Error: Missing command 'lsusb', please install usbutils" exit 1 fi XSREAD=/usr/bin/xenstore-read XSWRITE=/usr/bin/xenstore-write XSCHMOD=/usr/bin/xenstore-chmod set -e LSUSB=$(echo "$LSUSB" | grep -v '0000:0000') usage () { # $(basename $0) -l | -r # $(basename $0) -x <usb-port>:<domain>:0:<vport> # -r # read PVUSB hotplug rules # -x # delete PVUSB hotplug rule (use copy/paste from -r list) cat <<-EOT usage: $(basename $0) -b [-t] | -i $(basename $0) -s <domain> -a [ -t ] $(basename $0) -d <device_id> -s <domain> [-c <comment>] -w [ -t ] $(basename $0) -u <usb-port> -s <domain> [-c <comment>] -w [ -t ] $(basename $0) -l ------------ -a # activate PVUSB (for -s <domain>) -b # boot/initialise PVUSB with hotplug rules ($CONFIG_FILE) -i # initialize PVUSB without hotplug rules ($CONFIG_FILE) -s <domain> # server domain_name or domain_id -u <usb-port> # USB-PORT e.g. '3-2' -d <device_id># USB device_id e.g. '0912:1234' (use 'lsusb' to get the ID) -c <comment> # e.g. "Canon IP4000 Printer" -l # list grabbed PVUSB Devices -w # write/activate PVUSB hotplug rule -t # try to trigger all domains (per ssh) to init PVUSB -q # be quiet -D # Debug option (set -x) EOT } init () { NUM_PORTS=8 # Max 16 PVUSB ports. VUSB_BUS=0 # virtual USB Bus in <domain> (we use usb bus 0) CONFIG_FILE=/etc/xen/pvusb.conf [ -e $CONFIG_FILE ] || cat >$CONFIG_FILE <<-EOT # Configfile for pvusb # 'pvusb -b' connects <Device_Id> / <USB-Port> to <Domain> # 'pvusb -i' initializes <Domain> for PVUSB # <Device_Id> <Domain> <Comment> # 1234:5678 server USB-Printer on an USB-Port # <USB-Port> <Domain> <Comment> # 3-2 server Any Device on Port 3-2 # <Device_Id> <Domain> <Comment> # 0000:0000 server initialize 'server' for PVUSB EOT CONFIG=$(cat $CONFIG_FILE | sed -e 's/#.*$//' -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' -e '/^$/ d' -e '/^[[:graph:]]*$/ d') } # # paravirtualized USB Support # --------------------------- pvusb_init_xenstore () { # Setup and initialize the XenStore for PVUSB # based on init_xs.sh by Noboru Iwamatsu dom_id=$1 vusb_bus=$2 # vusb_bus (typical 0) # check if xenstore is already initialized [ -e /sys/bus/xen-backend/drivers/vusb/vusb-$dom_id-$vusb_bus ] && return # check if PVUSB is already initialized [ -e /sys/bus/xen-backend/drivers/vusb ] || pvusb_init_backend # Write backend information into the location that frontend look for. $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id 0 $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/backend \ /local/domain/0/backend/vusb/$dom_id/$vusb_bus # Write frontend information into the location that backend look for. $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id $dom_id $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend \ /local/domain/$dom_id/device/vusb/$vusb_bus # Write USB Spec version field. $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/usb-ver 2 # Write virtual root hub field. $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports $NUM_PORTS for i in $(seq 1 $NUM_PORTS) do # Set all port to disconnected state $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$i "" done # Set permission $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus n$dom_id r0 $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend-id n$dom_id r0 $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/backend n$dom_id r0 $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend-id n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/frontend n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/usb-ver n0 r$dom_id $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/num-ports n0 r$dom_id for i in $(seq 1 $NUM_PORTS) do $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$i n0 r0 done # Set state to XenbusStateInitialising $XSWRITE /local/domain/$dom_id/device/vusb/$vusb_bus/state 1 $XSCHMOD /local/domain/$dom_id/device/vusb/$vusb_bus/state n$dom_id r0 $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state 1 $XSCHMOD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/state n0 r$dom_id } pvusb_init_backend () { # initialise backend only once [ "$PVUSB_INIT" = "1" ] && { PVUSB_INIT=1; return; } # 1) First, you have to start from the state that no device is connected. # remove all usb modules but usbbk usb_mods="$(lsmod | grep usb | grep -v usbcore | grep -v usbbk | awk '/^usb/ {print $1}')" [ -n "$usb_mods" ] && rmmod $usb_mods # load usb (backend) modules modprobe usbbk } pvusb_connect_backend () { # connect USB devices to backend (and restart udevd afterwards) # stop udevd fetching events before the backend driver udevadm control --stop_exec_queue # XXX: find better way to connect USB devices dynamically!!! rmmod uhci_hcd ehci_hcd ohci_hcd 2>/dev/null || true modprobe -a uhci_hcd ehci_hcd ohci_hcd || true # start udevd again after we have finished udevadm control --start_exec_queue } pvusb_write_rule () { domain=$1 # domain to connect PVUSB Port usb_port=$2 # where is usb device connected comment=$3 # a name for usb device if [ -z "$domain" ]; then echo "missing option -s <domain>" return fi if [ -z "$usb_port" ]; then echo "missing option -u <usb_port>" return fi if [ "$domain" = "$(echo $domain | tr -d [:alpha:])" ]; then dom_name=$(xm domname $domain) dom_id=$domain else dom_name=$domain dom_id=$(xm domid $domain) fi # 1) initialise paravirtualized USB Support for dom_id pvusb_init_xenstore $dom_id $VUSB_BUS # get the next free USB Port in <domain> vusb_port=$(get_free_vusb_port $dom_id) # 2) Write the hotplug-rule to xenstore $XSWRITE /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$vusb_port $usb_port # tell me what happened if [ -z "$QUIET" ]; then actual_vport=$($XSREAD /local/domain/0/backend/vusb/$dom_id/$vusb_bus/port/$vusb_port || true) echo "xen PVUSB Rule written: $actual_vport $comment on domain $dom_name ($dom_id)" fi } get_free_vusb_port () { # get new vusb_port between 2 .. $NUM_PORTS dom_id=$1 for ((i=1; $i<=$NUM_PORTS; i++)); do vport=$($XSREAD /local/domain/0/backend/vusb/$dom_id/0/port/$i || true) [ -z "$vport" ] && { echo "$i"; return; } done echo "Error: no virtual USB-Port free for $dom_id" >&2 exit 1 } get_usb_port () { # find out where USB-Device is connected usb_dev=$1 usb_port="" usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') for port in $(echo "$usb_ports" | grep $usb_bus || true); do adr=$(cat /sys/bus/usb/devices/$port/devnum) if [ "$adr" = "$usb_adr" ]; then echo "$port" return fi done echo "Error: no USB-Port found for $usb_dev" >&2 } pvusb_init_domains_from_configfile () { domains=$(echo "$CONFIG" | awk '/^0000:0000 / {print $2}') for domain in $domains; do dom_id=$(xm domid $domain || continue) [ -n "$dom_id" ] || continue pvusb_init_xenstore $dom_id $VUSB_BUS # remember domain to be triggered PVUSB_DOMAINS=$(cat <<-EOT | sort -u $PVUSB_DOMAINS $domain) done } pvusb_write_defined_ports_from_configfile () { # $CONFIG: where to connect the USB-Ports # --------------------------------------- ports=$(echo "$CONFIG" | awk '/^[0-9]-[0-9] / {print $1}') for usb_port in $ports; do # read static definitions from $CONFIG pvusb_conf=$(echo "$CONFIG" | grep $usb_port | tail -1) if [ -n "$pvusb_conf" ]; then domain=$(echo $pvusb_conf | awk '{print $2}') comment=$(echo $pvusb_conf | cut -d\ -f3-) [ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue else continue fi # PVUSB write xen backend rules for usb device # -------------------------------------------- pvusb_write_rule $domain $usb_port "$comment" # remember domain to be triggered PVUSB_DOMAINS=$(cat <<-EOT | sort -u $PVUSB_DOMAINS $domain) done } pvusb_write_connected_devices_from_configfile () { # $CONFIG: where to connect my USB-Devices # ---------------------------------------- # see dmesg (syslog) where device ist connected: # e.g.: usb 3-2: new full speed USB device using uhci_hcd and address 4 # get all connected USB devices usb_devices=$(echo "$LSUSB" | awk '{print $6}') # ready if no devices are found if [ -z "$usb_devices" ]; then echo "$(basename $0): No connected USB Devices found." return fi # XXX: only normal USB-Ports (1-1) are supported yet, no cascaded USB-Hubs (1-1.1) usb_ports="$(/bin/ls -1 /sys/bus/usb/devices/ | awk '/^[0-9]\-[0-9]$/ {print}')" for usb_dev in $usb_devices; do # where to connect my usb devices to? # read static definitions from $CONFIG pvusb_conf=$(echo "$CONFIG" | grep $usb_dev | tail -1) if [ -n "$pvusb_conf" ]; then domain=$(echo $pvusb_conf | awk '{print $2}') comment=$(echo $pvusb_conf | cut -d\ -f3-) # skip if <domain> isn't activ [ -z "$(xm domname $domain 2>/dev/null || true)" ] && continue else continue fi # find out where USB-Device is connected usb_bus=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$2}') usb_adr=$(echo "$LSUSB" | grep $usb_dev | awk '{printf "%d",$4}') for port in $(echo "$usb_ports" | grep $usb_bus || true); do adr=$(cat /sys/bus/usb/devices/$port/devnum) if [ "$adr" = "$usb_adr" ]; then usb_port=$port break fi done if [ -z "$usb_port" ]; then echo "Error: No USB Port found for $usb_dev" continue fi echo "found $comment ($usb_dev) on USB-Port $usb_port" # PVUSB write xen backend rules for usb device # -------------------------------------------- pvusb_write_rule $domain $usb_port "$comment" # remember domain to be triggered PVUSB_DOMAINS=$(cat <<-EOT | sort -u $PVUSB_DOMAINS $domain) done } pvusb_trigger () { # PVUSB trigger for all relevant domains # XXX: better way to trigger domU? for domain in $(echo "$1" | sort -u); do ssh $domain "[ -n \"\$(lsusb || true)\" ] && exit 0 ; rmmod xen_hcd usbcore || true ; modprobe xen_hcd " || true done } pvusb_list () { # tell me what happened # --------------------- for vusb_dev in /sys/bus/xen-backend/drivers/vusb/vusb-*-*; do dom_id=$(basename "$vusb_dev" | cut -d- -f2) [ "$dom_id" == '*' ] && break for ((i=1; $i<=$NUM_PORTS; i++)); do port=$($XSREAD /local/domain/0/backend/vusb/$dom_id/0/port/$i || true) if [ -n "$port" ]; then usb_port=$port dev_id="$(cat /sys/bus/usb/devices/$usb_port/idVendor)" dev_id="$dev_id:$(cat /sys/bus/usb/devices/$usb_port/idProduct)" product=$(echo "$LSUSB" | grep $dev_id | cut -d\ -f6-) echo "PVUSB: $usb_port on $(xm domname $dom_id 2>/dev/null || echo "<inactiv>") ($dom_id) $product" fi done done #pvusb_grabbed=$(ls /sys/bus/xen-backend/drivers/vusb/vusb-*-*) #for pvusb in $pvusb_grabbed; do # usb_port=$(basename $pvusb | awk -F: '{print $1}') # dom_id=$(cat /sys/bus/usb/drivers/usbback/port_ids | awk -F: "/$usb_port/"' {print $2}') # dev_id="$(cat /sys/bus/usb/devices/$usb_port/idVendor)" # dev_id="$dev_id:$(cat /sys/bus/usb/devices/$usb_port/idProduct)" # product=$(echo "$LSUSB" | grep $dev_id | cut -d\ -f6-) # echo "PVUSB: $usb_port on $(xm domname $dom_id 2>/dev/null || echo "<inactiv>") ($dom_id) $product" #done } # ------------------------ # Script starts here # ------------------------ init #while getopts ":abc:d:Dhilqrs:tu:wx:" opt; do while getopts ":abc:d:Dhilqs:tu:w:" opt; do case $opt in a) # activate PVUSB for <domain> without any rules dom_id=$(xm domid $domain) pvusb_init_xenstore $dom_id $VUSB_BUS ;; b) # boot host with setup rules in $CONFIG pvusb_init_backend pvusb_init_domains_from_configfile pvusb_write_connected_devices_from_configfile pvusb_write_defined_ports_from_configfile pvusb_connect_backend ;; i) # initialise PVUSB without any rules pvusb_init_backend pvusb_init_domains_from_configfile pvusb_connect_backend ;; c) # comment e.g. Canon IP4000 comment=$OPTARG ;; d) # USB DEVICE_ID (0912:1234) usb_port=$(get_usb_port $OPTARG) ;; h) usage ;; l) # list PVUSB Ports LIST_PVUSB=1 ;; q) # don't list PVUSB Ports QUIET=0 ;; r) # list rules #cat /sys/bus/usb/drivers/usbback/vports ;; s) # Server Domain-name or -id domain=$(xm domname $OPTARG) # check if domain exists ;; t) # try to trigger domain with ssh pvusb_trigger "$PVUSB_DOMAINS $domain" ;; u) # USB-PORT (3-2) usb_port=$OPTARG ;; w) # write rule PVUSB pvusb_write_rule $domain $usb_port "$comment" LIST_PVUSB=1 ;; x) # delete PVUSB rule #echo "$OPTARG" > /sys/bus/usb/drivers/usbback/remove_vport || true ;; D) set -x ;; \?) echo "Invalid option: -$OPTARG" >&2 ;; esac done [ "$LIST_PVUSB" = "1" ] && pvusb_list