/** * "Unplug" a device from a VM. * vusb_unassign() will "unassign" it, then * xenstore_create_usb() will be called to "detach" the device. * * @param domid The domid of the VM to unplug the device from * @param bus The bus ID of the device * @param device The ID of the device on the bus * * @return 0 for success, 1 for failure */ int usbowls_unplug_device(int domid, int bus, int device) { dominfo_t di; usbinfo_t ui; int ret; ret = xenstore_get_dominfo(domid, &di); if (ret != 0) { xd_log(LOG_ERR, "Invalid domid %d", domid); return 1; } ret = get_usbinfo(bus, device, &ui); if (ret != 0) { xd_log(LOG_ERR, "Invalid device %d-%d", bus, device); return 1; } ret = vusb_assign(ui.usb_vendor, ui.usb_product, 0); if (ret != 0) { xd_log(LOG_ERR, "Failed to unassign device"); return 1; } ret = xenstore_destroy_usb(&di, &ui); if (ret != 0) { xd_log(LOG_ERR, "Failed to detach device"); return 1; } return 0; }
int get_usbinfo(int bus, int dev, usbinfo_t *ui) { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *udev_dev; char bus_str[16], dev_str[16]; int found = 0; memset(ui, 0, sizeof(usbinfo_t)); /* construct xenstore dev id */ if (dev > 0xFFF) { xd_log(LOG_ERR, "bad device id %d", dev); return -EINVAL; } ui->usb_virtid = bus << 12 | (dev & 0xFFF); ui->usb_bus = bus; ui->usb_device = dev; /* udev scan */ udev = udev_new(); if (!udev) { xd_log(LOG_ERR, "Can't create udev"); return -ENOMEM; } enumerate = udev_enumerate_new(udev); if (!enumerate) { xd_log(LOG_ERR, "Can't create enumeration"); return -ENOMEM; } snprintf(bus_str, sizeof(bus_str), "%d", bus); snprintf(dev_str, sizeof(dev_str), "%d", dev); udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_add_match_sysattr(enumerate, "busnum", bus_str); udev_enumerate_add_match_sysattr(enumerate, "devnum", dev_str); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path; path = udev_list_entry_get_name(dev_list_entry); udev_dev = udev_device_new_from_syspath(udev, path); sscanf(udev_device_get_sysattr_value(udev_dev, "idVendor"), "%x", &ui->usb_vendor); sscanf(udev_device_get_sysattr_value(udev_dev, "idProduct"), "%x", &ui->usb_product); udev_device_unref(udev_dev); udev_enumerate_unref(enumerate); udev_unref(udev); return 0; } udev_enumerate_unref(enumerate); udev_unref(udev); return -ENOENT; }
/* * Remove information about a usb device for this domain from Xenstore */ int xenstore_destroy_usb(dominfo_t *domp, usbinfo_t *usbp) { char value[32]; char *bepath; char *fepath; int i; xd_log(LOG_INFO, "Deleting VUSB node %d for %d.%d", usbp->usb_virtid, usbp->usb_bus, usbp->usb_device); bepath = xs_dev_bepath(domp, "vusb", usbp->usb_virtid); fepath = xs_dev_fepath(domp, "vusb", usbp->usb_virtid); /* Notify the backend that the device is being shut down */ xs_set_keyval(XBT_NULL, bepath, "online", "0"); xs_set_keyval(XBT_NULL, bepath, "physical-device", "0.0"); snprintf(value, sizeof (value), "%d", XB_CLOSING); xs_set_keyval(XBT_NULL, bepath, "state", value); for (i = 0; i < 30; ++i) { usleep(100000); if (test_offline(domp, usbp)) { xs_rm(xs_handle, XBT_NULL, fepath); xs_rm(xs_handle, XBT_NULL, bepath); break; } } free(bepath); free(fepath); return (0); }
/* * Write a single value into Xenstore. */ int xs_set_keyval(xs_transaction_t xt, char *path, char *key, char *val) { char tmppath[256]; if (key != NULL) { snprintf(tmppath, sizeof (tmppath), "%s/%s", path, key); path = tmppath; } xd_log(LOG_VERBOSE_DEBUG, "Writing to XenStore: %s = %s", path, val); if (xs_write(xs_handle, xt, path, val, strlen(val)) == false) { xd_log(LOG_ERR, "XenStore error writing %s", path); return (-1); } return (0); }
int get_dominfo(int domid, dominfo_t *di) { di->di_domid = domid; di->di_dompath = xs_get_domain_path(xs_handle, di->di_domid); if (!di->di_dompath) { xd_log(LOG_ERR, "Could not get domain %d path from xenstore", domid); return -ENOENT; } di->di_name = xasprintf("Domain-%d", domid); return 0; }
void * xmalloc(size_t size) { void *p; if ((p = malloc(size)) == NULL) { xd_log(LOG_CRIT, "Out of memory"); exit(2); } return (p); }
/** * "Plug" a device to a VM. * xenstore_create_usb() will be called to "attach" the device, then * vusb_assign() will "assign" it. * * @param domid The domid of the VM to plug the device to * @param bus The bus ID of the device * @param device The ID of the device on the bus * * @return 0 for success, 1 for failure */ int usbowls_plug_device(int domid, int bus, int device) { dominfo_t di; usbinfo_t ui; int ret; ret = xenstore_get_dominfo(domid, &di); if (ret != 0) { xd_log(LOG_ERR, "Invalid domid %d", domid); return 1; } ret = get_usbinfo(bus, device, &ui); if (ret != 0) { xd_log(LOG_ERR, "Invalid device %d-%d", bus, device); return 1; } /* FIXME: nicely unbind dom0 drivers on interfaces? * Or not, USB supports hot unplug doesn't it? :) */ ret = xenstore_create_usb(&di, &ui); if (ret != 0) { xd_log(LOG_ERR, "Failed to attach device"); return 1; } if (xenstore_wait_for_online(&di, &ui) < 0) xd_log(LOG_ERR, "The frontend or the backend didn't go online, continue anyway"); ret = vusb_assign(ui.usb_vendor, ui.usb_product, 1); if (ret != 0) { xd_log(LOG_ERR, "Failed to assign device"); xenstore_destroy_usb(&di, &ui); return 1; } return 0; }
/* * Create a new directory in Xenstore */ static int xs_add_dir(xs_transaction_t xt, char *path, int d0, int p0, int d1, int p1) { struct xs_permissions perms[2]; xd_log(LOG_VERBOSE_DEBUG, "Making %s in XenStore", path); if (xs_mkdir(xs_handle, xt, path) == false) { xd_log(LOG_ERR, "XenStore error mkdir()ing %s", path); return (-1); } perms[0].perms = p0; perms[0].id = d0; perms[1].perms = p1; perms[1].id = d1; if (xs_set_permissions(xs_handle, xt, path, perms, 2) == false) { xd_log(LOG_ERR, "XenStore error setting permissions on %s", path); xs_remove(xt, path); return (-1); } return (0); }
/** * Build a usbinfo_t from a udev device handle * * @param udev_dev Udev device handle * @param bus Bus ID * @param dev Device ID on the bus * @param ui The structure to fill (must be already allocated) * * @return 0 on success */ int usbowls_build_usbinfo(int bus, int dev, int vendor, int product, usbinfo_t *ui) { memset(ui, 0, sizeof(usbinfo_t)); /* construct xenstore dev id */ if (dev > 0xFFF) { xd_log(LOG_ERR, "bad device id %d", dev); return -EINVAL; } ui->usb_virtid = bus << 12 | (dev & 0xFFF); ui->usb_bus = bus; ui->usb_device = dev; ui->usb_vendor = vendor; ui->usb_product = product; return 0; }
static int vusb_assign(int vendor, int product, int add) { char command[64]; int fd; int ret = 0; char *path = add ? VUSB_ADD_DEV : VUSB_DEL_DEV; fd = open(path, O_WRONLY); if (fd == -1) { xd_log(LOG_ERR, "%s: failed to open %s", __func__, path); return (-1); } snprintf(command, sizeof (command), "%x %x\n", vendor, product); if (write(fd, command, strlen(command)) == -1) ret = -1; if (close(fd) == -1) ret = -1; return (ret); }
static int get_usbinfo(int bus, int dev, usbinfo_t *ui) { struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *udev_dev; char bus_str[16], dev_str[16]; int vendor, product; enumerate = udev_enumerate_new(udev_handle); if (!enumerate) { xd_log(LOG_ERR, "Can't create enumeration"); return -ENOMEM; } snprintf(bus_str, sizeof(bus_str), "%d", bus); snprintf(dev_str, sizeof(dev_str), "%d", dev); udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_add_match_sysattr(enumerate, "busnum", bus_str); udev_enumerate_add_match_sysattr(enumerate, "devnum", dev_str); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path; path = udev_list_entry_get_name(dev_list_entry); udev_dev = udev_device_new_from_syspath(udev_handle, path); sscanf(udev_device_get_sysattr_value(udev_dev, "idVendor"), "%x", &vendor); sscanf(udev_device_get_sysattr_value(udev_dev, "idProduct"), "%x", &product); udev_device_unref(udev_dev); udev_enumerate_unref(enumerate); return usbowls_build_usbinfo(bus, dev, vendor, product, ui); } udev_enumerate_unref(enumerate); return -ENOENT; }
static void dump_dev(usbinfo_t *ui) { xd_log(LOG_INFO, "bus %d device %d vendor %04x product %04x virtid %06x", ui->usb_bus, ui->usb_device, ui->usb_vendor, ui->usb_product, ui->usb_virtid); }
static void xs_remove(xs_transaction_t xt, char *path) { xd_log(LOG_VERBOSE_DEBUG, "XenStore removing %s", path); xs_rm(xs_handle, xt, path); }
int main(int argc, char **argv) { int option = 0; int domid = -1, bus = -1, device = -1; int vassign = 0, vunassign = 0, attach = 0, detach = 0, list = 0, dump = 0; char *action = NULL; dominfo_t di; usbinfo_t ui; int ret = 0; xs_handle = xs_daemon_open(); if (xs_handle == NULL) { xd_log(LOG_ERR, "Failed to connect to xenstore"); exit(1); } if ((xs_dom0path = xs_get_domain_path(xs_handle, 0)) == NULL) { xd_log(LOG_ERR, "Could not get domain 0 path from XenStore"); exit(1); } while ((option = getopt(argc, argv, "D:b:d:")) != -1) { switch (option) { case 'D': domid = atoi(optarg); if (get_dominfo(domid, &di)) exit(1); break; case 'b': bus = atoi(optarg); break; case 'd': device = atoi(optarg); break; } } if (optind != argc-1) { usage(); } action = argv[optind]; vassign = !strcmp(action, "assign"); vunassign = !strcmp(action, "unassign"); attach = !strcmp(action, "attach"); detach = !strcmp(action, "detach"); list = !strcmp(action, "list"); dump = !strcmp(action, "dump"); if (vassign || vunassign || attach || detach || dump) { if (bus < 0 || device < 0) usage(); if (get_usbinfo(bus, device, &ui)) { if (detach) { /* can proceed detaching without detailed device info, we only need bus & dev num */ } else if (vunassign) { /* no device so nothing to unassign */ exit(0); } else { xd_log(LOG_ERR, "Failed to find device %d:%d", bus, device); exit(1); } } } if (optind == argc) { dump_dev(&ui); return 0; } if (dump) dump_dev(&ui); else if (vassign) ret = vusb_assign(ui.usb_vendor, ui.usb_product, 1); else if (vunassign) ret = vusb_assign(ui.usb_vendor, ui.usb_product, 0); else if (attach) { if (domid < 0) usage(); ret = xenstore_create_usb(&di, &ui); } else if (detach) { if (domid < 0) usage(); ret = xenstore_destroy_usb(&di, &ui); } else if (list) { if (domid < 0) usage(); list_domain_devs(&di); } if (ret) xd_log(LOG_ERR, "Operation failed"); return ret == 0 ? 0 : 1; }
/* * Populate Xenstore with the information about a usb device for this domain */ int xenstore_create_usb(dominfo_t *domp, usbinfo_t *usbp) { char *bepath, *fepath; char value[32]; xs_transaction_t trans; xd_log(LOG_DEBUG, "Creating VUSB node for %d.%d", usbp->usb_bus, usbp->usb_device); /* * Construct Xenstore paths for both the front and back ends. */ fepath = xs_dev_fepath(domp, "vusb", usbp->usb_virtid); bepath = xs_dev_bepath(domp, "vusb", usbp->usb_virtid); for (;;) { trans = xs_transaction_start(xs_handle); /* * Make directories for both front and back ends */ if (xs_add_dir(trans, bepath, 0, XS_PERM_NONE, domp->di_domid, XS_PERM_READ)) break; if (xs_add_dir(trans, fepath, domp->di_domid, XS_PERM_NONE, 0, XS_PERM_READ)) break; /* * Populate frontend device info */ if (xs_set_keyval(trans, fepath, "backend-id", "0")) break; snprintf(value, sizeof (value), "%d", usbp->usb_virtid); if (xs_set_keyval(trans, fepath, "virtual-device", value)) break; if (xs_set_keyval(trans, fepath, "backend", bepath)) break; snprintf(value, sizeof (value), "%d", XB_INITTING); if (xs_set_keyval(trans, fepath, "state", value)) break; /* * Populate backend device info */ if (xs_set_keyval(trans, bepath, "domain", domp->di_name)) break; if (xs_set_keyval(trans, bepath, "frontend", fepath)) break; snprintf(value, sizeof (value), "%d", XB_INITTING); if (xs_set_keyval(trans, bepath, "state", value)) break; if (xs_set_keyval(trans, bepath, "online", "1")) break; snprintf(value, sizeof (value), "%d", domp->di_domid); if (xs_set_keyval(trans, bepath, "frontend-id", value)) break; snprintf(value, sizeof (value), "%d.%d", usbp->usb_bus, usbp->usb_device); if (xs_set_keyval(trans, bepath, "physical-device", value)) break; if (xs_transaction_end(xs_handle, trans, false) == false) { if (errno == EAGAIN) continue; break; } free(fepath); free(bepath); xd_log(LOG_DEBUG, "Finished creating VUSB node for %d.%d", usbp->usb_bus, usbp->usb_device); return (0); } xs_transaction_end(xs_handle, trans, true); xd_log(LOG_ERR, "Failed to write usb info to XenStore"); free(fepath); free(bepath); return (-1); }