aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/usbip/userspace/libsrc/vhci_driver.c')
-rw-r--r--drivers/staging/usbip/userspace/libsrc/vhci_driver.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
new file mode 100644
index 000000000000..db43f8d2eb80
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+
+#include "usbip.h"
+
+
+static const char vhci_driver_name[] = "vhci_hcd";
+
+struct usbip_vhci_driver *vhci_driver;
+
+static struct usbip_imported_device *imported_device_init(struct usbip_imported_device *idev, char *busid)
+{
+ struct sysfs_device *sudev;
+
+ sudev = sysfs_open_device("usb", busid);
+ if (!sudev) {
+ err("sysfs_open_device %s", busid);
+ goto err;
+ }
+ read_usb_device(sudev, &idev->udev);
+ sysfs_close_device(sudev);
+
+ /* add class devices of this imported device */
+ struct class_device *cdev;
+ dlist_for_each_data(vhci_driver->cdev_list, cdev, struct class_device) {
+ if (!strncmp(cdev->devpath, idev->udev.path, strlen(idev->udev.path))) {
+ struct class_device *new_cdev;
+
+ /* alloc and copy because dlist is linked from only one list */
+ new_cdev = calloc(1, sizeof(*new_cdev));
+ if (!new_cdev)
+ goto err;
+
+ memcpy(new_cdev, cdev, sizeof(*new_cdev));
+ dlist_unshift(idev->cdev_list, (void*) new_cdev);
+ }
+ }
+
+ return idev;
+
+err:
+ return NULL;
+}
+
+
+
+static int parse_status(char *value)
+{
+ int ret = 0;
+ char *c;
+
+
+ for (int i = 0; i < vhci_driver->nports; i++)
+ bzero(&vhci_driver->idev[i], sizeof(struct usbip_imported_device));
+
+
+ /* skip a header line */
+ c = strchr(value, '\n') + 1;
+
+ while (*c != '\0') {
+ int port, status, speed, devid;
+ unsigned long socket;
+ char lbusid[SYSFS_BUS_ID_SIZE];
+
+ ret = sscanf(c, "%d %d %d %x %lx %s\n",
+ &port, &status, &speed,
+ &devid, &socket, lbusid);
+
+ if (ret < 5) {
+ err("scanf %d", ret);
+ BUG();
+ }
+
+ dbg("port %d status %d speed %d devid %x",
+ port, status, speed, devid);
+ dbg("socket %lx lbusid %s", socket, lbusid);
+
+
+ /* if a device is connected, look at it */
+ {
+ struct usbip_imported_device *idev = &vhci_driver->idev[port];
+
+ idev->port = port;
+ idev->status = status;
+
+ idev->devid = devid;
+
+ idev->busnum = (devid >> 16);
+ idev->devnum = (devid & 0x0000ffff);
+
+ idev->cdev_list = dlist_new(sizeof(struct class_device));
+ if (!idev->cdev_list) {
+ err("init new device");
+ return -1;
+ }
+
+ if (idev->status != VDEV_ST_NULL && idev->status != VDEV_ST_NOTASSIGNED) {
+ idev = imported_device_init(idev, lbusid);
+ if (!idev) {
+ err("init new device");
+ return -1;
+ }
+ }
+ }
+
+
+ /* go to the next line */
+ c = strchr(c, '\n') + 1;
+ }
+
+ dbg("exit");
+
+ return 0;
+}
+
+
+static int check_usbip_device(struct sysfs_class_device *cdev)
+{
+ char clspath[SYSFS_PATH_MAX]; /* /sys/class/video4linux/video0/device */
+ char devpath[SYSFS_PATH_MAX]; /* /sys/devices/platform/vhci_hcd/usb6/6-1:1.1 */
+
+ int ret;
+
+ snprintf(clspath, sizeof(clspath), "%s/device", cdev->path);
+
+ ret = sysfs_get_link(clspath, devpath, SYSFS_PATH_MAX);
+ if (!ret) {
+ if (!strncmp(devpath, vhci_driver->hc_device->path,
+ strlen(vhci_driver->hc_device->path))) {
+ /* found usbip device */
+ struct class_device *cdev;
+
+ cdev = calloc(1, sizeof(*cdev));
+ if (!cdev) {
+ err("calloc cdev");
+ return -1;
+ }
+ dlist_unshift(vhci_driver->cdev_list, (void*) cdev);
+ strncpy(cdev->clspath, clspath, sizeof(cdev->clspath));
+ strncpy(cdev->devpath, devpath, sizeof(cdev->clspath));
+ dbg(" found %s %s", clspath, devpath);
+ }
+ }
+
+ return 0;
+}
+
+
+static int search_class_for_usbip_device(char *cname)
+{
+ struct sysfs_class *class;
+ struct dlist *cdev_list;
+ struct sysfs_class_device *cdev;
+ int ret = 0;
+
+ class = sysfs_open_class(cname);
+ if (!class) {
+ err("open class");
+ return -1;
+ }
+
+ dbg("class %s", class->name);
+
+ cdev_list = sysfs_get_class_devices(class);
+ if (!cdev_list)
+ /* nothing */
+ goto out;
+
+ dlist_for_each_data(cdev_list, cdev, struct sysfs_class_device) {
+ dbg(" cdev %s", cdev->name);
+ ret = check_usbip_device(cdev);
+ if (ret < 0)
+ goto out;
+ }
+
+out:
+ sysfs_close_class(class);
+
+ return ret;
+}
+
+
+static int refresh_class_device_list(void)
+{
+ int ret;
+ struct dlist *cname_list;
+ char *cname;
+
+ /* search under /sys/class */
+ cname_list = sysfs_open_directory_list("/sys/class");
+ if (!cname_list) {
+ err("open class directory");
+ return -1;
+ }
+
+ dlist_for_each_data(cname_list, cname, char) {
+ ret = search_class_for_usbip_device(cname);
+ if (ret < 0) {
+ sysfs_close_list(cname_list);
+ return -1;
+ }
+ }
+
+ sysfs_close_list(cname_list);
+
+ /* seach under /sys/block */
+ ret = search_class_for_usbip_device(SYSFS_BLOCK_NAME);
+ if (ret < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int refresh_imported_device_list(void)
+{
+ struct sysfs_attribute *attr_status;
+
+
+ attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status");
+ if (!attr_status) {
+ err("get attr %s of %s", "status", vhci_driver->hc_device->name);
+ return -1;
+ }
+
+ dbg("name %s, path %s, len %d, method %d\n", attr_status->name,
+ attr_status->path, attr_status->len, attr_status->method);
+
+ dbg("%s", attr_status->value);
+
+ return parse_status(attr_status->value);
+}
+
+static int get_nports(void)
+{
+ int nports = 0;
+ struct sysfs_attribute *attr_status;
+
+ attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status");
+ if (!attr_status) {
+ err("get attr %s of %s", "status", vhci_driver->hc_device->name);
+ return -1;
+ }
+
+ dbg("name %s, path %s, len %d, method %d\n", attr_status->name,
+ attr_status->path, attr_status->len, attr_status->method);
+
+ dbg("%s", attr_status->value);
+
+ {
+ char *c;
+
+ /* skip a header line */
+ c = strchr(attr_status->value, '\n') + 1;
+
+ while (*c != '\0') {
+ /* go to the next line */
+ c = strchr(c, '\n') + 1;
+ nports += 1;
+ }
+ }
+
+ return nports;
+}
+
+static int get_hc_busid(char *sysfs_mntpath, char *hc_busid)
+{
+ struct sysfs_driver *sdriver;
+ char sdriver_path[SYSFS_PATH_MAX];
+
+ struct sysfs_device *hc_dev;
+ struct dlist *hc_devs;
+
+ int found = 0;
+
+ snprintf(sdriver_path, SYSFS_PATH_MAX, "%s/%s/platform/%s/%s",
+ sysfs_mntpath, SYSFS_BUS_NAME, SYSFS_DRIVERS_NAME,
+ vhci_driver_name);
+
+ sdriver = sysfs_open_driver_path(sdriver_path);
+ if (!sdriver) {
+ info("%s is not found", sdriver_path);
+ info("load usbip-core.ko and vhci-hcd.ko !");
+ return -1;
+ }
+
+ hc_devs = sysfs_get_driver_devices(sdriver);
+ if (!hc_devs) {
+ err("get hc list");
+ goto err;
+ }
+
+ /* assume only one vhci_hcd */
+ dlist_for_each_data(hc_devs, hc_dev, struct sysfs_device) {
+ strncpy(hc_busid, hc_dev->bus_id, SYSFS_BUS_ID_SIZE);
+ found = 1;
+ }
+
+err:
+ sysfs_close_driver(sdriver);
+
+ if (found)
+ return 0;
+
+ err("not found usbip hc");
+ return -1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+int usbip_vhci_driver_open(void)
+{
+ int ret;
+ char hc_busid[SYSFS_BUS_ID_SIZE];
+
+ vhci_driver = (struct usbip_vhci_driver *) calloc(1, sizeof(*vhci_driver));
+ if (!vhci_driver) {
+ err("alloc vhci_driver");
+ return -1;
+ }
+
+ ret = sysfs_get_mnt_path(vhci_driver->sysfs_mntpath, SYSFS_PATH_MAX);
+ if (ret < 0) {
+ err("sysfs must be mounted");
+ goto err;
+ }
+
+ ret = get_hc_busid(vhci_driver->sysfs_mntpath, hc_busid);
+ if (ret < 0)
+ goto err;
+
+ /* will be freed in usbip_driver_close() */
+ vhci_driver->hc_device = sysfs_open_device("platform", hc_busid);
+ if (!vhci_driver->hc_device) {
+ err("get sysfs vhci_driver");
+ goto err;
+ }
+
+ vhci_driver->nports = get_nports();
+
+ info("%d ports available\n", vhci_driver->nports);
+
+ vhci_driver->cdev_list = dlist_new(sizeof(struct class_device));
+ if (!vhci_driver->cdev_list)
+ goto err;
+
+ if (refresh_class_device_list())
+ goto err;
+
+ if (refresh_imported_device_list())
+ goto err;
+
+
+ return 0;
+
+
+err:
+ if (vhci_driver->cdev_list)
+ dlist_destroy(vhci_driver->cdev_list);
+ if (vhci_driver->hc_device)
+ sysfs_close_device(vhci_driver->hc_device);
+ if (vhci_driver)
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+ return -1;
+}
+
+
+void usbip_vhci_driver_close()
+{
+ if (!vhci_driver)
+ return;
+
+ if (vhci_driver->cdev_list)
+ dlist_destroy(vhci_driver->cdev_list);
+
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ if (vhci_driver->idev[i].cdev_list)
+ dlist_destroy(vhci_driver->idev[i].cdev_list);
+ }
+
+ if (vhci_driver->hc_device)
+ sysfs_close_device(vhci_driver->hc_device);
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+}
+
+
+int usbip_vhci_refresh_device_list(void)
+{
+ if (vhci_driver->cdev_list)
+ dlist_destroy(vhci_driver->cdev_list);
+
+
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ if (vhci_driver->idev[i].cdev_list)
+ dlist_destroy(vhci_driver->idev[i].cdev_list);
+ }
+
+ vhci_driver->cdev_list = dlist_new(sizeof(struct class_device));
+ if (!vhci_driver->cdev_list)
+ goto err;
+
+ if (refresh_class_device_list())
+ goto err;
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+err:
+ if (vhci_driver->cdev_list)
+ dlist_destroy(vhci_driver->cdev_list);
+
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ if (vhci_driver->idev[i].cdev_list)
+ dlist_destroy(vhci_driver->idev[i].cdev_list);
+ }
+
+ err("refresh device list");
+ return -1;
+}
+
+
+int usbip_vhci_get_free_port(void)
+{
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ if (vhci_driver->idev[i].status == VDEV_ST_NULL)
+ return i;
+ }
+
+ return -1;
+}
+
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed) {
+ struct sysfs_attribute *attr_attach;
+ char buff[200]; /* what size should be ? */
+ int ret;
+
+ attr_attach = sysfs_get_device_attr(vhci_driver->hc_device, "attach");
+ if (!attr_attach) {
+ err("get attach");
+ return -1;
+ }
+
+ snprintf(buff, sizeof(buff), "%u %u %u %u",
+ port, sockfd, devid, speed);
+ dbg("writing: %s", buff);
+
+ ret = sysfs_write_attribute(attr_attach, buff, strlen(buff));
+ if (ret < 0) {
+ err("write to attach failed");
+ return -1;
+ }
+
+ info("port %d attached", port);
+
+ return 0;
+}
+
+static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
+{
+ return (busnum << 16) | devnum;
+}
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed)
+{
+ int devid = get_devid(busnum, devnum);
+
+ return usbip_vhci_attach_device2(port, sockfd, devid, speed);
+}
+
+int usbip_vhci_detach_device(uint8_t port)
+{
+ struct sysfs_attribute *attr_detach;
+ char buff[200]; /* what size should be ? */
+ int ret;
+
+ attr_detach = sysfs_get_device_attr(vhci_driver->hc_device, "detach");
+ if (!attr_detach) {
+ err("get detach");
+ return -1;
+ }
+
+ snprintf(buff, sizeof(buff), "%u", port);
+ dbg("writing to detach");
+ dbg("writing: %s", buff);
+
+ ret = sysfs_write_attribute(attr_detach, buff, strlen(buff));
+ if (ret < 0) {
+ err("write to detach failed");
+ return -1;
+ }
+
+ info("port %d detached", port);
+
+ return 0;
+}