aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-12-11 08:52:17 +0000
committerKeir Fraser <keir.fraser@citrix.com>2009-12-11 08:52:17 +0000
commitfc63162a0927e3054c497a70175765600a08a697 (patch)
tree2ed58c8564ccf3092ccb81855371992670b76eaf /tools
parent04656384a1b9714e43db850c51431008e23450d8 (diff)
downloadxen-fc63162a0927e3054c497a70175765600a08a697.tar.gz
xen-fc63162a0927e3054c497a70175765600a08a697.tar.bz2
xen-fc63162a0927e3054c497a70175765600a08a697.zip
PVUSB: xm/xend support
You can see the following slides to understand the usage. http://www.xen.org/files/xensummit_intel09/PVUSBStatusUpdate.pdf Limitations: "xm usb-hc-create" accepts up to 16 ports, but, current usbfront can work with up to 15 ports. This may be bug and I'm preparing to fix it. This xm/xend support requires linux-2.6.18-xen.hg c/s 939 or above. I recommend latest tip. Signed-off-by: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/hotplug/Linux/xend.rules1
-rw-r--r--tools/python/xen/util/vusb_util.py338
-rw-r--r--tools/python/xen/xend/XendConfig.py82
-rw-r--r--tools/python/xen/xend/XendDevices.py3
-rw-r--r--tools/python/xen/xend/XendDomainInfo.py31
-rw-r--r--tools/python/xen/xend/XendNode.py15
-rw-r--r--tools/python/xen/xend/server/udevevent.py12
-rw-r--r--tools/python/xen/xend/server/vusbif.py126
-rw-r--r--tools/python/xen/xm/create.py45
-rw-r--r--tools/python/xen/xm/main.py151
10 files changed, 803 insertions, 1 deletions
diff --git a/tools/hotplug/Linux/xend.rules b/tools/hotplug/Linux/xend.rules
index 8cb82956e9..4d79ac0c99 100644
--- a/tools/hotplug/Linux/xend.rules
+++ b/tools/hotplug/Linux/xend.rules
@@ -1,3 +1,4 @@
SUBSYSTEM=="pci", RUN+="socket:/org/xen/xend/udev_event"
SUBSYSTEM=="scsi", RUN+="socket:/org/xen/xend/udev_event"
+SUBSYSTEM=="usb", RUN+="socket:/org/xen/xend/udev_event"
#SUBSYSTEM=="net", KERNEL!="vif[0-9]*.[0-9]*|tap[0-9]*.[0-9]*", RUN+="socket:/org/xen/xend/udev_event"
diff --git a/tools/python/xen/util/vusb_util.py b/tools/python/xen/util/vusb_util.py
new file mode 100644
index 0000000000..9ca498cb46
--- /dev/null
+++ b/tools/python/xen/util/vusb_util.py
@@ -0,0 +1,338 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+# Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+#============================================================================
+
+
+"""Support for VUSB Devices.
+"""
+import os
+import os.path
+import sys
+import re
+import string
+from xen.util import utils
+
+SYSFS_USB_DEVS_PATH = '/bus/usb/devices'
+SYSFS_USB_DEV_BDEVICECLASS_PATH = '/bDeviceClass'
+SYSFS_USB_DEV_BDEVICESUBCLASS_PATH = '/bDeviceSubClass'
+SYSFS_USB_DEV_DEVNUM_PATH = '/devnum'
+SYSFS_USB_DEV_IDVENDOR_PATH = '/idVendor'
+SYSFS_USB_DEV_IDPRODUCT_PATH = '/idProduct'
+SYSFS_USB_DEV_MANUFACTURER_PATH = '/manufacturer'
+SYSFS_USB_DEV_PRODUCT_PATH = '/product'
+SYSFS_USB_DEV_SERIAL_PATH = '/serial'
+SYSFS_USB_DEV_DRIVER_PATH = '/driver'
+SYSFS_USB_DRIVER_BIND_PATH = '/bind'
+SYSFS_USB_DRIVER_UNBIND_PATH = '/unbind'
+SYSFS_USBBACK_PATH = '/bus/usb/drivers/usbback'
+SYSFS_PORTIDS_PATH = '/port_ids'
+USBHUB_CLASS_CODE = '09'
+
+def get_usb_bDeviceClass(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_BDEVICECLASS_PATH):
+ usb_deviceclass = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_BDEVICECLASS_PATH).readline()
+ return usb_deviceclass.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_bDeviceSubClass(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_BDEVICESUBCLASS_PATH):
+ usb_devicesubclass = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_BDEVICESUBCLASS_PATH).readline()
+ return usb_devicesubclass.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_devnum(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_DEVNUM_PATH):
+ usb_devicesubclass = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_DEVNUM_PATH).readline()
+ return usb_devicesubclass.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_idvendor(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_IDVENDOR_PATH):
+ usb_idvendor = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_IDVENDOR_PATH).readline()
+ return usb_idvendor.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_idproduct(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_IDPRODUCT_PATH):
+ usb_idproduct = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_IDPRODUCT_PATH).readline()
+ return usb_idproduct.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_manufacturer(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_MANUFACTURER_PATH):
+ usb_manufacturer = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_MANUFACTURER_PATH).readline()
+ return usb_manufacturer.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_product(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_PRODUCT_PATH):
+ usb_product = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_PRODUCT_PATH).readline()
+ return usb_product.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usb_serial(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_usb_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_usb_dev_path + SYSFS_USB_DEV_SERIAL_PATH):
+ usb_serial = \
+ os.popen('cat ' + sysfs_usb_dev_path + \
+ SYSFS_USB_DEV_SERIAL_PATH).readline()
+ return usb_serial.splitlines()[0]
+ else:
+ return ""
+ except:
+ return None
+
+def get_usbdevice_info_by_lsusb(dev):
+ try:
+ vend = get_usb_idvendor(dev)
+ prod = get_usb_idproduct(dev)
+ output = os.popen('lsusb -d ' + vend + ':' + prod).readline().split()
+ text = ""
+ if len(output) > 6:
+ for str in output[6:]:
+ if text != "":
+ text= text + ' '
+ text = text + str
+ return text
+ else:
+ return ""
+ except:
+ return None
+
+def get_usbdevice_info(dev):
+ try:
+ manuf = get_usb_manufacturer(dev)
+ prod = get_usb_product(dev)
+ if manuf == "" or prod == "":
+ return get_usbdevice_info_by_lsusb(dev)
+ else:
+ return manuf + ' ' + prod
+ except:
+ return None
+
+def usb_device_is_hub(dev):
+ usb_classcode = get_usb_bDeviceClass(dev)
+ if (usb_classcode == USBHUB_CLASS_CODE):
+ return True
+ else:
+ return False
+
+def get_all_usb_names():
+ usb_names = []
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ usb_names = os.popen('ls ' + sysfs_mnt + SYSFS_USB_DEVS_PATH).read().split()
+ except:
+ pass
+ return usb_names
+
+def get_usb_devices():
+ devs = []
+ for name in get_all_usb_names():
+ dev_match = re.match(r"(^(?P<bus>[0-9]{1,2})[-,])" + \
+ r"(?P<root_port>[0-9]{1,2})" + \
+ r"(?P<port>([\.,]{1}[0-9]{1,2}){0,5})$", name)
+ if dev_match is not None:
+ dev = dev_match.group('bus') + '-' \
+ + dev_match.group('root_port') \
+ + dev_match.group('port')
+ if (usb_device_is_hub(dev)):
+ continue
+ else:
+ devs.append(dev)
+ return devs
+
+def get_usb_intfs(dev):
+ intfs = []
+ try:
+ search = re.compile(r'^' + dev + ':')
+ except:
+ raise UsbDeviceParseError("Invalid expression.")
+ for name in get_all_usb_names():
+ if search.match(name):
+ intfs.append(name)
+ return intfs
+
+def get_assigned_buses():
+ buses = []
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ if os.path.exists(sysfs_mnt + SYSFS_USBBACK_PATH + SYSFS_PORTIDS_PATH):
+ portids = \
+ os.popen('cat ' + sysfs_mnt + SYSFS_USBBACK_PATH + SYSFS_PORTIDS_PATH).read().splitlines()
+ for portid in portids:
+ buses.append(portid.split(':')[0])
+ except:
+ raise UsbDeviceParseError("Can't get assigned buses from port_ids.")
+ return buses
+
+def get_assigned_bus(domid, dev, port):
+ bus = ""
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ if os.path.exists(sysfs_mnt + SYSFS_USBBACK_PATH + SYSFS_PORTIDS_PATH):
+ portids = \
+ os.popen('cat ' + sysfs_mnt + SYSFS_USBBACK_PATH + SYSFS_PORTIDS_PATH).read().splitlines()
+ for portid in portids:
+ if portid.split(':')[1] == str(domid) and portid.split(':')[2] == str(dev) and portid.split(':')[3] == str(port):
+ bus = portid.split(':')[0]
+ except:
+ raise UsbDeviceParseError("Can't get assigned bus (%d:%d:%d)." % (domid, dev, port))
+ return bus
+
+def bus_is_assigned(bus):
+ assigned = False
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ if os.path.exists(sysfs_mnt + SYSFS_USBBACK_PATH + SYSFS_PORTIDS_PATH):
+ portids = \
+ os.popen('cat ' + sysfs_mnt + SYSFS_USBBACK_PATH + SYSFS_PORTIDS_PATH).read().splitlines()
+ for portid in portids:
+ if portid.split(':')[0] == bus:
+ assigned = True
+ except:
+ raise UsbDeviceParseError("Can't get assignment status: (%s)." % bus)
+ return assigned
+
+def usb_intf_is_binded(intf):
+ if os.path.exists(SYSFS_USBBACK_PATH + '/' + intf):
+ return True
+ else:
+ return False
+
+def usb_device_is_connected(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ sysfs_dev_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, dev)
+ if os.path.exists(sysfs_dev_path):
+ return True
+ else:
+ return False
+ except:
+ raise UsbDeviceParseError("Can't get connection status (%s)." % dev)
+
+def unbind_usb_device(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ for intf in get_usb_intfs(dev):
+ sysfs_usb_intf_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, intf)
+ if os.path.exists(sysfs_usb_intf_path + SYSFS_USB_DEV_DRIVER_PATH):
+ fd = os.open(sysfs_usb_intf_path + \
+ SYSFS_USB_DEV_DRIVER_PATH + \
+ SYSFS_USB_DRIVER_UNBIND_PATH, os.O_WRONLY)
+ os.write(fd, intf)
+ os.close(fd)
+ except:
+ raise UsbDeviceBindingError("can't unbind intf (%s). " % intf)
+
+def bind_usb_device(dev):
+ try:
+ sysfs_mnt = utils.find_sysfs_mount()
+ for intf in get_usb_intfs(dev):
+ sysfs_usb_intf_path = \
+ os.path.join(sysfs_mnt + SYSFS_USB_DEVS_PATH, intf)
+ if os.path.exists(sysfs_usb_intf_path + SYSFS_USB_DEV_DRIVER_PATH):
+ unbind_usb_device(dev)
+
+ fd = os.open(sysfs_mnt + SYSFS_USBBACK_PATH + \
+ SYSFS_USB_DRIVER_BIND_PATH, os.O_WRONLY)
+ os.write(fd, intf)
+ os.close(fd)
+ except:
+ raise UsbDeviceBindingError("can't bind intf (%s). " % intf)
+
+class UsbDeviceParseError(Exception):
+ def __init__(self,msg):
+ self.message = msg
+ def __str__(self):
+ return 'vusb: Error parsing USB device info: '+self.message
+
+class UsbDeviceBindingError(Exception):
+ def __init__(self,msg):
+ self.message = msg
+ def __str__(self):
+ return 'vusb: Failed to bind/unbind USB device: ' + \
+ self.message
diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py
index baf552a639..a79dfe5864 100644
--- a/tools/python/xen/xend/XendConfig.py
+++ b/tools/python/xen/xend/XendConfig.py
@@ -1400,6 +1400,14 @@ class XendConfig(dict):
(vscsi_devs, vscsi_mode))
return vscsi_devs_uuid
+ if dev_type == 'vusb':
+ vusb_devs_uuid = sxp.child_value(config, 'uuid',
+ uuid.createString())
+ vusb_dict = self.vusb_convert_sxp_to_dict(config)
+ vusb_dict['uuid'] = vusb_devs_uuid
+ target['devices'][vusb_devs_uuid] = (dev_type, vusb_dict)
+ return vusb_devs_uuid
+
for opt_val in config[1:]:
try:
opt, val = opt_val
@@ -1776,6 +1784,68 @@ class XendConfig(dict):
return dev_config
+ def vusb_convert_sxp_to_dict(self, dev_sxp):
+ """Convert vusb device sxp to dict
+ @param dev_sxp: device configuration
+ @type dev_sxp: SXP object (parsed config)
+ @return: dev_config
+ @rtype: dictionary
+ """
+ # Parsing USB devices SXP.
+ #
+ # USB device's SXP looks like this:
+ #
+ # [device,
+ # [vusb,
+ # [usb-ver, 2],
+ # [num-ports, 8],
+ # [port,
+ # [1, 1-1],
+ # [2, 1-2],
+ # [3, ''],
+ # [4, ''],
+ # [5, ''],
+ # [6, ''],
+ # [7, 6-2.1],
+ # [8, '']
+ # ]
+ # ],
+ # [vusb,
+ # [usb-ver, 1],
+ # [num-ports, 2],
+ # [port,
+ # [1, 4-1],
+ # [2, 4-2]
+ # ]
+ # ]
+ # ]
+ #
+ # The dict looks like this
+ #
+ # { usb-ver: 2,
+ # num-ports: 8,
+ # port-1: 1-1,
+ # port-2: 1-2,
+ # port-3: "",
+ # port-4: "",
+ # port-5: "",
+ # port-6: "",
+ # port-7: "",
+ # port-8: "" }
+
+ dev_config = {}
+ dev_config['usb-ver'] = sxp.child(dev_sxp, 'usb-ver')[1]
+ dev_config['num-ports'] = sxp.child(dev_sxp, 'num-ports')[1]
+ ports = sxp.child(dev_sxp, 'port')
+ for port in ports[1:]:
+ try:
+ num, bus = port
+ dev_config['port-%i' % int(num)] = str(bus)
+ except TypeError:
+ pass
+
+ return dev_config
+
def console_add(self, protocol, location, other_config = {}):
dev_uuid = uuid.createString()
if protocol == 'vt100':
@@ -2002,6 +2072,18 @@ class XendConfig(dict):
for pci_dev_info in dev_info['devs']:
sxpr.append(dev_dict_to_sxp(pci_dev_info))
sxprs.append((dev_type, sxpr))
+ elif dev_type == 'vusb':
+ sxpr = ['vusb', ['uuid', dev_info['uuid']],
+ ['usb-ver', dev_info['usb-ver']],
+ ['num-ports', dev_info['num-ports']]]
+ port_sxpr = ['port']
+ for i in range(1, int(dev_info['num-ports']) + 1):
+ if dev_info.has_key('port-%i' % i):
+ port_sxpr.append([i, str(dev_info['port-%i' % i])])
+ else:
+ port_sxpr.append([i, ""])
+ sxpr.append(port_sxpr)
+ sxprs.append((dev_type, sxpr))
else:
sxpr = self.device_sxpr(dev_type = dev_type,
dev_info = dev_info,
diff --git a/tools/python/xen/xend/XendDevices.py b/tools/python/xen/xend/XendDevices.py
index 5cd3b65d0b..e4585e1836 100644
--- a/tools/python/xen/xend/XendDevices.py
+++ b/tools/python/xen/xend/XendDevices.py
@@ -19,7 +19,7 @@
# A collection of DevControllers
#
-from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, vfbif, vscsiif, netif2
+from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, vfbif, vscsiif, netif2, vusbif
from xen.xend.server.BlktapController import BlktapController, Blktap2Controller
from xen.xend.server.ConsoleController import ConsoleController
@@ -48,6 +48,7 @@ class XendDevices:
'vkbd': vfbif.VkbdifController,
'console': ConsoleController,
'vscsi': vscsiif.VSCSIController,
+ 'vusb': vusbif.VUSBController,
}
#@classmethod
diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
index db744d7e1d..25c90db7e2 100644
--- a/tools/python/xen/xend/XendDomainInfo.py
+++ b/tools/python/xen/xend/XendDomainInfo.py
@@ -1102,6 +1102,27 @@ class XendDomainInfo:
return True
+ def vusb_device_configure(self, dev_sxp, devid):
+ """Configure a virtual root port.
+ """
+ dev_class = sxp.name(dev_sxp)
+ if dev_class != 'vusb':
+ return False
+
+ dev_config = {}
+ ports = sxp.child(dev_sxp, 'port')
+ for port in ports[1:]:
+ try:
+ num, bus = port
+ dev_config['port-%i' % int(num)] = str(bus)
+ except TypeError:
+ pass
+
+ dev_control = self.getDeviceController(dev_class)
+ dev_control.reconfigureDevice(devid, dev_config)
+
+ return True
+
def device_configure(self, dev_sxp, devid = None):
"""Configure an existing device.
@@ -1123,6 +1144,9 @@ class XendDomainInfo:
if dev_class == 'vscsi':
return self.vscsi_device_configure(dev_sxp)
+ if dev_class == 'vusb':
+ return self.vusb_device_configure(dev_sxp, devid)
+
for opt_val in dev_sxp[1:]:
try:
dev_config[opt_val[0]] = opt_val[1]
@@ -1378,6 +1402,13 @@ class XendDomainInfo:
return dev_info
return None
+ def _getDeviceInfo_vusb(self, devid):
+ for dev_type, dev_info in self.info.all_devices_sxpr():
+ if dev_type != 'vusb':
+ continue
+ return dev_info
+ return None
+
def _get_assigned_pci_devices(self, devid = 0):
if self.domid is not None:
return get_assigned_pci_devices(self.domid)
diff --git a/tools/python/xen/xend/XendNode.py b/tools/python/xen/xend/XendNode.py
index f11cd79532..b6ef374483 100644
--- a/tools/python/xen/xend/XendNode.py
+++ b/tools/python/xen/xend/XendNode.py
@@ -24,6 +24,7 @@ import xen.lowlevel.xc
from xen.util import Brctl
from xen.util import pci as PciUtil
from xen.util import vscsi_util
+from xen.util import vusb_util
from xen.xend import XendAPIStore
from xen.xend import osdep
from xen.xend.XendConstants import *
@@ -478,6 +479,20 @@ class XendNode:
return
+ def add_usbdev(self, busid):
+ # if the adding usb device should be owned by usbback
+ # and is probed by other usb drivers, seize it!
+ bus, intf = busid.split(':')
+ buses = vusb_util.get_assigned_buses()
+ if str(bus) in buses:
+ if not vusb_util.usb_intf_is_binded(busid):
+ log.debug("add_usb(): %s is binded to other driver" % busid)
+ vusb_util.unbind_usb_device(bus)
+ vusb_util.bind_usb_device(bus)
+ return
+
+ def remove_usbdev(self, busid):
+ log.debug("remove_usbdev(): Not implemented.")
## def network_destroy(self, net_uuid):
## del self.networks[net_uuid]
diff --git a/tools/python/xen/xend/server/udevevent.py b/tools/python/xen/xend/server/udevevent.py
index b2b9f093f3..6d61b8ed17 100644
--- a/tools/python/xen/xend/server/udevevent.py
+++ b/tools/python/xen/xend/server/udevevent.py
@@ -60,6 +60,18 @@ class UdevEventProtocol(protocol.Protocol):
log.info("Removing scsi device %s", hctl)
XendNode.instance().remove_PSCSI(hctl)
+ elif (udev_event.get('SUBSYSTEM', None) == 'usb'):
+ busid = udev_event.get('KERNEL', None)
+ if busid:
+ if len(busid.split(':')) != 2:
+ return
+ if (udev_event['ACTION'] == 'add'):
+ log.info("Adding usb device %s", busid)
+ XendNode.instance().add_usbdev(busid)
+ elif (udev_event['ACTION'] == 'remove'):
+ log.info("Removing usb device %s", busid)
+ XendNode.instance().remove_usbdev(busid)
+
elif (udev_event.get('SUBSYSTEM', None) == 'net'):
interface = udev_event.get('INTERFACE', None)
if (udev_event['ACTION'] == 'add'):
diff --git a/tools/python/xen/xend/server/vusbif.py b/tools/python/xen/xend/server/vusbif.py
new file mode 100644
index 0000000000..7b37e6ef7d
--- /dev/null
+++ b/tools/python/xen/xend/server/vusbif.py
@@ -0,0 +1,126 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+# Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+# Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+#============================================================================
+
+"""Support for virtual USB host controllers.
+"""
+import re
+import string
+
+import types
+
+from xen.xend import sxp
+from xen.xend.XendError import VmError
+from xen.xend.XendLogging import log
+
+from xen.xend.server.DevController import DevController
+from xen.xend.server.DevConstants import xenbusState
+from xen.xend.xenstore.xstransact import xstransact
+
+from xen.util import vusb_util
+
+class VUSBController(DevController):
+ """VUSB Devices.
+ """
+ def __init__(self, vm):
+ """Create a VUSB Devices.
+ """
+ DevController.__init__(self, vm)
+
+ def sxprs(self):
+ """@see DevController.sxprs"""
+ devslist = []
+ for devid in self.deviceIDs():
+ vusb_config = []
+ backid = self.readFrontend(devid, 'backend-id')
+ vusb_config.append(['backend-id', backid])
+ state = self.readFrontend(devid, 'state')
+ vusb_config.append(['state', state])
+ backpath = self.readFrontend(devid, 'backend')
+ vusb_config.append(['backend', backpath])
+ usbver = self.readBackend(devid, 'usb-ver')
+ vusb_config.append(['usb-ver', usbver])
+ numports = self.readBackend(devid, 'num-ports')
+ vusb_config.append(['num-ports', numports])
+
+ portpath = "port/"
+ ports = ['port']
+ for i in range(1, int(numports) + 1):
+ bus = self.readBackend(devid, portpath + '%i' % i)
+ ports.append(['%i' % i, str(bus)])
+
+ vusb_config.append(ports)
+ devslist.append([devid, vusb_config])
+
+ return devslist
+
+ def getDeviceDetails(self, config):
+ """@see DevController.getDeviceDetails"""
+ back = {}
+ devid = self.allocateDeviceID()
+ usbver = config.get('usb-ver', '')
+ numports = config.get('num-ports', '')
+ back['usb-ver'] = str(usbver)
+ back['num-ports'] = str(numports)
+ for i in range(1, int(numports) + 1):
+ back['port/%i' % i] = config['port-%i' % i]
+ return (devid, back, {})
+
+ def getDeviceConfiguration(self, devid, transaction = None):
+ """@see DevController.configuration"""
+ config = DevController.getDeviceConfiguration(self, devid, transaction)
+ if transaction is None:
+ hcinfo = self.readBackend(devid, 'usb-ver', 'num-ports')
+ else:
+ hcinfo = self.readBackendTxn(transaction, devid,
+ 'usb-ver', 'num-ports')
+ (usbver, numports) = hcinfo
+ config['usb-ver'] = str(usbver)
+ config['num-ports'] = str(numports)
+ for i in range(1, int(numports) + 1):
+ if transaction is None:
+ config['port-%i' % i] = self.readBackend(devid, 'port/%i' % i)
+ else:
+ config['port-%i' % i] = self.readBackendTxn(transaction, devid,
+ 'port/%i' % i)
+ return config
+
+ def reconfigureDevice(self, devid, config):
+ """@see DevController.reconfigureDevice"""
+ cur_config = self.getDeviceConfiguration(devid)
+
+ numports = cur_config['num-ports']
+ for i in range(1, int(numports) + 1):
+ if config.has_key('port-%i' % i):
+ if not config['port-%i' % i] == cur_config['port-%i' % i]:
+ if not cur_config['port-%i' % i] == "":
+ vusb_util.unbind_usb_device(cur_config['port-%i' % i])
+ self.writeBackend(devid, 'port/%i' % i,
+ config['port-%i' % i])
+ if not config['port-%i' % i] == "":
+ vusb_util.bind_usb_device(config['port-%i' % i])
+
+ return self.readBackend(devid, 'uuid')
+
+ def waitForBackend(self, devid):
+ return (0, "ok - no hotplug")
+
+ def waitForBackend_destroy(self, backpath):
+ return 0
+
+ def migrate(self, deviceConfig, network, dst, step, domName):
+ raise VmError('Migration not permitted with assigned USB device.')
diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py
index df7e9bd93e..c252acf7fb 100644
--- a/tools/python/xen/xm/create.py
+++ b/tools/python/xen/xm/create.py
@@ -350,6 +350,18 @@ gopts.var('vscsi', val='PDEV,VDEV[,DOM]',
use="""Add a SCSI device to a domain. The physical device is PDEV,
which is exported to the domain as VDEV(X:X:X:X).""")
+gopts.var('vusb', val="usbver=USBVER,numports=NUMPORTS," + \
+ "port_1=PORT1,port_2=PORT2,port_3=PORT3,port_4=PORT4" + \
+ "port_5=PORT5,port_6=PORT6,port_7=PORT7,port_8=PORT8" + \
+ "port_9=PORT9,port_10=PORT10,port_11=PORT11,port_12=PORT12" + \
+ "port_13=PORT13,port_14=PORT14,port_15=PORT15,port_16=PORT16",
+ fn=append_value, default=[],
+ use="""Add a Virtual USB Host Controller to a domain.
+ The USB Spec Version is usbver (1|2, default: 2).
+ usbver=1 means USB1.1, usbver=2 mens USB2.0.
+ The number of root ports is numports (1 to 16, default: 8).
+ This option may be repeated to add more than one host controller.""")
+
gopts.var('ioports', val='FROM[-TO]',
fn=append_value, default=[],
use="""Add a legacy I/O range to a domain, using given params (in hex).
@@ -849,6 +861,38 @@ def configure_vscsis(config_devs, vals):
device.append(['backend', config['backend']])
config_devs.append(['device', device])
+def configure_vusbs(config_devs, vals):
+ """Create the config for virtual usb host controllers.
+ """
+ for f in vals.vusb:
+ d = comma_sep_kv_to_dict(f)
+ config = ['vusb']
+
+ usbver = 2
+ if d.has_key('usbver'):
+ usbver = int(d['usbver'])
+ if usbver == 1 or usbver == 2:
+ config.append(['usb-ver', str(usbver)])
+ else:
+ err('Invalid vusb option: ' + 'usbver')
+
+ numports = 8
+ if d.has_key('numports'):
+ numports = d['numports']
+ if int(numports) < 1 or int(numports) > 16:
+ err('Invalid vusb option: ' + 'numports')
+ config.append(['num-ports', str(numports)])
+
+ port_config = []
+ for i in range(1, int(numports) + 1):
+ if d.has_key('port_%i' % i):
+ port_config.append(['%i' % i, str(d['port_%i' % i])])
+ else:
+ port_config.append(['%i' % i, ""])
+ port_config.insert(0, 'port')
+ config.append(port_config)
+ config_devs.append(['device', config])
+
def configure_ioports(config_devs, vals):
"""Create the config for legacy i/o ranges.
"""
@@ -1103,6 +1147,7 @@ def make_config(vals):
configure_disks(config_devs, vals)
configure_pci(config_devs, vals)
configure_vscsis(config_devs, vals)
+ configure_vusbs(config_devs, vals)
configure_ioports(config_devs, vals)
configure_irq(config_devs, vals)
configure_vifs(config_devs, vals)
diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py
index a62c2d566d..0ca4f7a344 100644
--- a/tools/python/xen/xm/main.py
+++ b/tools/python/xen/xm/main.py
@@ -39,6 +39,7 @@ import xml.dom.minidom
from xen.util.blkif import blkdev_name_to_number
from xen.util import vscsi_util
from xen.util.pci import *
+from xen.util import vusb_util
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)
@@ -211,6 +212,17 @@ SUBCOMMAND_HELP = {
'Detach a specified SCSI device.'),
'scsi-list' : ('<Domain> [--long]',
'List all SCSI devices currently attached.'),
+ 'usb-attach' : ('<Domain> <DevId> <PortNumber> <BusId>',
+ 'Attach a new USB physical bus to domain\'s virtual port.'),
+ 'usb-detach' : ('<Domain> <DevId> <PortNumber>',
+ 'Detach a USB physical bus from domain\'s virtual port.'),
+ 'usb-list' : ('<Domain>',
+ 'List domain\'s attachment state of all virtual port .'),
+ 'usb-list-assignable-devices' : ('', 'List all the assignable usb devices'),
+ 'usb-hc-create' : ('<Domain> <USBSpecVer> <NumberOfPorts>',
+ 'Create a domain\'s new virtual USB host controller.'),
+ 'usb-hc-destroy' : ('<Domain> <DevId>',
+ 'Destroy a domain\'s virtual USB host controller.'),
# tmem
'tmem-list' : ('[-l|--long] [<Domain>|-a|--all]', 'List tmem pools.'),
@@ -429,6 +441,12 @@ device_commands = [
"scsi-attach",
"scsi-detach",
"scsi-list",
+ "usb-attach",
+ "usb-detach",
+ "usb-list",
+ "usb-list-assignable-devices",
+ "usb-hc-create",
+ "usb-hc-destroy",
]
vnet_commands = [
@@ -2409,6 +2427,58 @@ def xm_scsi_list(args):
print "%(idx)-3d %(backend-id)-3d %(state)-5d %(feature-host)-4d " % ni,
print "%(p-dev)-10s %(p-devname)-5s %(v-dev)-10s %(frontstate)-4s" % mi
+def xm_usb_list(args):
+ xenapi_unsupported()
+ arg_check(args, 'usb-list', 1)
+ dom = args[0]
+ devs = server.xend.domain.getDeviceSxprs(dom, 'vusb')
+ for x in devs:
+ print "%-3s %-3s %-5s %-7s %-30s" \
+ % ('Idx', 'BE', 'state', 'usb-ver', 'BE-path')
+ ni = parse_dev_info(x[1])
+ ni['idx'] = int(x[0])
+ usbver = sxp.child_value(x[1], 'usb-ver')
+ if int(usbver) == 1:
+ ni['usb-ver'] = 'USB1.1'
+ else:
+ ni['usb-ver'] = 'USB2.0'
+ print "%(idx)-3d %(backend-id)-3d %(state)-5d %(usb-ver)-7s %(be-path)-30s " % ni
+
+ ports = sxp.child(x[1], 'port')
+ for port in ports[1:]:
+ try:
+ num, bus = port
+ if bus != "" and vusb_util.usb_device_is_connected(bus):
+ idvendor = vusb_util.get_usb_idvendor(bus)
+ idproduct = vusb_util.get_usb_idproduct(bus)
+ prodinfo = vusb_util.get_usbdevice_info(bus)
+ print "port %i: %s [ID %-4s:%-4s %s]" \
+ % (int(num), str(bus), idvendor, idproduct, prodinfo)
+ else:
+ print "port %i: " % int(num) + str(bus)
+ except TypeError:
+ pass
+
+def xm_usb_list_assignable_devices(args):
+ xenapi_unsupported()
+ arg_check(args, 'usb-list-assignable-devices', 0)
+
+ usb_devs = vusb_util.get_usb_devices()
+ buses = vusb_util.get_assigned_buses()
+
+ for x in buses:
+ try:
+ usb_devs.remove(x)
+ except ValueError:
+ pass
+
+ for dev in usb_devs:
+ idvendor = vusb_util.get_usb_idvendor(dev)
+ idproduct = vusb_util.get_usb_idproduct(dev)
+ prodinfo = vusb_util.get_usbdevice_info(dev)
+ print "%-13s: ID %-4s:%-4s %s" \
+ % (dev, idvendor, idproduct, prodinfo)
+
def parse_block_configuration(args):
dom = args[0]
@@ -2757,6 +2827,64 @@ def xm_scsi_attach(args):
scsi.append(['backend', args[3]])
server.xend.domain.device_configure(dom, scsi)
+def xm_usb_attach(args):
+ xenapi_unsupported()
+ arg_check(args, 'usb-attach', 4)
+ dom = args[0]
+ dev = args[1]
+ port = args[2]
+ bus = args[3]
+
+ dev_exist = 0
+ num_ports = 0
+ devs = server.xend.domain.getDeviceSxprs(dom, 'vusb')
+ for x in devs:
+ if int(x[0]) == int(dev):
+ dev_exist = 1
+ num_ports = sxp.child_value(x[1], 'num-ports')
+
+ if dev_exist == 0:
+ print "Cannot find device '%s' in domain '%s'" % (dev,dom)
+ return False
+
+ if int(port) < 1 or int(port) > int(num_ports):
+ print "Invalid Port Number '%s'" % port
+ return False
+
+ bus_match = re.match(r"(^(?P<bus>[0-9]{1,2})[-,])" + \
+ r"(?P<root_port>[0-9]{1,2})" + \
+ r"(?P<port>([\.,]{1}[0-9]{1,2}){0,5})$", bus)
+ if bus_match is None:
+ print "Invalid Busid '%s'" % bus
+ return False
+
+ if vusb_util.bus_is_assigned(bus):
+ print "Cannot assign already attached bus '%s', detach first." % bus
+ return False
+
+ prev_bus = vusb_util.get_assigned_bus(domain_name_to_domid(dom), dev, port)
+ if not prev_bus == "":
+ print "Cannot override already attached port '%s', detach first." % port
+ return False
+
+ usb = ['vusb', ['port', [port, str(bus)]]]
+ server.xend.domain.device_configure(dom, usb, dev)
+
+def xm_usb_hc_create(args):
+ xenapi_unsupported()
+ arg_check(args, 'usb-hc-create', 3)
+ dom = args[0]
+ ver = args[1]
+ num = args[2]
+ vusb_config = ['vusb']
+ vusb_config.append(['usb-ver', str(ver)])
+ vusb_config.append(['num-ports', str(num)])
+ port_config = ['port']
+ for i in range(1, int(num) + 1):
+ port_config.append(['%i' % i, ""])
+ vusb_config.append(port_config)
+ server.xend.domain.device_create(dom, vusb_config)
+
def detach(args, deviceClass):
rm_cfg = True
dom = args[0]
@@ -2942,6 +3070,22 @@ def xm_scsi_detach(args):
else:
server.xend.domain.device_configure(dom, scsi)
+def xm_usb_detach(args):
+ xenapi_unsupported()
+ arg_check(args, 'usb-detach', 3)
+ dom = args[0]
+ dev = args[1]
+ port = args[2]
+ usb = ['vusb', ['port', [port, '']]]
+ server.xend.domain.device_configure(dom, usb, dev)
+
+def xm_usb_hc_destroy(args):
+ xenapi_unsupported()
+ arg_check(args, 'usb-hc-destroy', 2)
+ dom = args[0]
+ dev = args[1]
+ server.xend.domain.destroyDevice(dom, 'vusb', dev)
+
def xm_vnet_list(args):
xenapi_unsupported()
try:
@@ -3372,6 +3516,13 @@ commands = {
"scsi-attach": xm_scsi_attach,
"scsi-detach": xm_scsi_detach,
"scsi-list": xm_scsi_list,
+ # vusb
+ "usb-attach": xm_usb_attach,
+ "usb-detach": xm_usb_detach,
+ "usb-list": xm_usb_list,
+ "usb-list-assignable-devices": xm_usb_list_assignable_devices,
+ "usb-hc-create": xm_usb_hc_create,
+ "usb-hc-destroy": xm_usb_hc_destroy,
# tmem
"tmem-thaw": xm_tmem_thaw,
"tmem-freeze": xm_tmem_freeze,