XEN PVUSB
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.
Das Skript /usr/local/sbin/pvusb für Xen 3.4.2 (XEN 3.4.3 siehe weiter unten!)
#!/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 # # 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