Merge remote-tracking branch 'usb/usb-next'
[deliverable/linux.git] / drivers / usb / usbip / vhci_sysfs.c
index 5b5462eb16657b03d30b293caac2e6facfbb2226..c404017c1b5ae26aa1df115c0ac1652284ed21b4 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +21,8 @@
 #include <linux/kthread.h>
 #include <linux/file.h>
 #include <linux/net.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include "usbip_common.h"
 #include "vhci.h"
 /* TODO: refine locking ?*/
 
 /* Sysfs entry to show port status */
-static ssize_t status_show(struct device *dev, struct device_attribute *attr,
-                          char *out)
+static ssize_t status_show_vhci(int pdev_nr, char *out)
 {
+       struct platform_device *pdev = *(vhci_pdevs + pdev_nr);
+       struct vhci_hcd *vhci;
        char *s = out;
        int i = 0;
        unsigned long flags;
 
-       BUG_ON(!the_controller || !out);
+       if (!pdev || !out) {
+               usbip_dbg_vhci_sysfs("show status error\n");
+               return 0;
+       }
+
+       vhci = hcd_to_vhci(platform_get_drvdata(pdev));
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
 
        /*
         * output example:
-        * prt sta spd dev socket           local_busid
-        * 000 004 000 000         c5a7bb80 1-2.3
-        * 001 004 000 000         d8cee980 2-3.4
+        * port sta spd dev      socket           local_busid
+        * 0000 004 000 00000000         c5a7bb80 1-2.3
+        * 0001 004 000 00000000         d8cee980 2-3.4
         *
         * IP address can be retrieved from a socket pointer address by looking
         * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
         * port number and its peer IP address.
         */
-       out += sprintf(out,
-                      "prt sta spd bus dev socket           local_busid\n");
-
-       for (i = 0; i < VHCI_NPORTS; i++) {
-               struct vhci_device *vdev = port_to_vdev(i);
+       for (i = 0; i < VHCI_HC_PORTS; i++) {
+               struct vhci_device *vdev = &vhci->vdev[i];
 
                spin_lock(&vdev->ud.lock);
-               out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
+               out += sprintf(out, "%04u %03u ",
+                                   (pdev_nr * VHCI_HC_PORTS) + i,
+                                   vdev->ud.status);
 
                if (vdev->ud.status == VDEV_ST_USED) {
                        out += sprintf(out, "%03u %08x ",
-                                      vdev->speed, vdev->devid);
-                       out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
-                       out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
+                                           vdev->speed, vdev->devid);
+                       out += sprintf(out, "%16p %s",
+                                           vdev->ud.tcp_socket,
+                                           dev_name(&vdev->udev->dev));
 
                } else {
-                       out += sprintf(out, "000 000 000 0000000000000000 0-0");
+                       out += sprintf(out, "000 00000000 ");
+                       out += sprintf(out, "0000000000000000 0-0");
                }
 
                out += sprintf(out, "\n");
                spin_unlock(&vdev->ud.lock);
        }
 
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
+
+       return out - s;
+}
+
+static ssize_t status_show_not_ready(int pdev_nr, char *out)
+{
+       char *s = out;
+       int i = 0;
+
+       for (i = 0; i < VHCI_HC_PORTS; i++) {
+               out += sprintf(out, "%04u %03u ",
+                                   (pdev_nr * VHCI_HC_PORTS) + i,
+                                   VDEV_ST_NOTASSIGNED);
+               out += sprintf(out, "000 00000000 0000000000000000 0-0");
+               out += sprintf(out, "\n");
+       }
+       return out - s;
+}
+
+static int status_name_to_id(const char *name)
+{
+       char *c;
+       long val;
+       int ret;
+
+       c = strchr(name, '.');
+       if (c == NULL)
+               return 0;
 
+       ret = kstrtol(c+1, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+static ssize_t status_show(struct device *dev,
+                          struct device_attribute *attr, char *out)
+{
+       char *s = out;
+       int pdev_nr;
+
+       out += sprintf(out,
+                      "port sta spd dev      socket           local_busid\n");
+
+       pdev_nr = status_name_to_id(attr->attr.name);
+       if (pdev_nr < 0)
+               out += status_show_not_ready(pdev_nr, out);
+       else
+               out += status_show_vhci(pdev_nr, out);
+
+       return out - s;
+}
+
+static ssize_t nports_show(struct device *dev, struct device_attribute *attr,
+                          char *out)
+{
+       char *s = out;
+
+       out += sprintf(out, "%d\n", VHCI_HC_PORTS * vhci_num_controllers);
        return out - s;
 }
-static DEVICE_ATTR_RO(status);
+static DEVICE_ATTR_RO(nports);
 
 /* Sysfs entry to shutdown a virtual connection */
-static int vhci_port_disconnect(__u32 rhport)
+static int vhci_port_disconnect(struct vhci_hcd *vhci, __u32 rhport)
 {
-       struct vhci_device *vdev;
+       struct vhci_device *vdev = &vhci->vdev[rhport];
        unsigned long flags;
 
        usbip_dbg_vhci_sysfs("enter\n");
 
        /* lock */
-       spin_lock_irqsave(&the_controller->lock, flags);
-
-       vdev = port_to_vdev(rhport);
-
+       spin_lock_irqsave(&vhci->lock, flags);
        spin_lock(&vdev->ud.lock);
+
        if (vdev->ud.status == VDEV_ST_NULL) {
                pr_err("not connected %d\n", vdev->ud.status);
 
                /* unlock */
                spin_unlock(&vdev->ud.lock);
-               spin_unlock_irqrestore(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
 
                return -EINVAL;
        }
 
        /* unlock */
        spin_unlock(&vdev->ud.lock);
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
 
        usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
 
        return 0;
 }
 
+static int valid_port(__u32 pdev_nr, __u32 rhport)
+{
+       if (pdev_nr >= vhci_num_controllers) {
+               pr_err("pdev %u\n", pdev_nr);
+               return 0;
+       }
+       if (rhport >= VHCI_HC_PORTS) {
+               pr_err("rhport %u\n", rhport);
+               return 0;
+       }
+       return 1;
+}
+
 static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       int err;
-       __u32 rhport = 0;
+       __u32 port = 0, pdev_nr = 0, rhport = 0;
+       struct usb_hcd *hcd;
+       int ret;
 
-       if (sscanf(buf, "%u", &rhport) != 1)
+       if (kstrtoint(buf, 10, &port) < 0)
                return -EINVAL;
 
-       /* check rhport */
-       if (rhport >= VHCI_NPORTS) {
-               dev_err(dev, "invalid port %u\n", rhport);
+       pdev_nr = port_to_pdev_nr(port);
+       rhport = port_to_rhport(port);
+
+       if (!valid_port(pdev_nr, rhport))
                return -EINVAL;
+
+       hcd = platform_get_drvdata(*(vhci_pdevs + pdev_nr));
+       if (hcd == NULL) {
+               dev_err(dev, "port is not ready %u\n", port);
+               return -EAGAIN;
        }
 
-       err = vhci_port_disconnect(rhport);
-       if (err < 0)
+       ret = vhci_port_disconnect(hcd_to_vhci(hcd), rhport);
+       if (ret < 0)
                return -EINVAL;
 
        usbip_dbg_vhci_sysfs("Leave\n");
@@ -135,16 +222,12 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
 
-/* Sysfs entry to establish a virtual connection */
-static int valid_args(__u32 rhport, enum usb_device_speed speed)
+static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed speed)
 {
-       /* check rhport */
-       if (rhport >= VHCI_NPORTS) {
-               pr_err("port %u\n", rhport);
-               return -EINVAL;
+       if (!valid_port(pdev_nr, rhport)) {
+               return 0;
        }
 
-       /* check speed */
        switch (speed) {
        case USB_SPEED_LOW:
        case USB_SPEED_FULL:
@@ -154,12 +237,13 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
        default:
                pr_err("Failed attach request for unsupported USB speed: %s\n",
                        usb_speed_string(speed));
-               return -EINVAL;
+               return 0;
        }
 
-       return 0;
+       return 1;
 }
 
+/* Sysfs entry to establish a virtual connection */
 /*
  * To start a new USB/IP attachment, a userland program needs to setup a TCP
  * connection and then write its socket descriptor with remote device
@@ -174,10 +258,12 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
 static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct vhci_device *vdev;
        struct socket *socket;
        int sockfd = 0;
-       __u32 rhport = 0, devid = 0, speed = 0;
+       __u32 port = 0, pdev_nr = 0, rhport = 0, devid = 0, speed = 0;
+       struct usb_hcd *hcd;
+       struct vhci_hcd *vhci;
+       struct vhci_device *vdev;
        int err;
        unsigned long flags;
 
@@ -187,16 +273,28 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
         * @devid: unique device identifier in a remote host
         * @speed: usb device speed in a remote host
         */
-       if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4)
+       if (sscanf(buf, "%u %u %u %u", &port, &sockfd, &devid, &speed) != 4)
                return -EINVAL;
+       pdev_nr = port_to_pdev_nr(port);
+       rhport = port_to_rhport(port);
 
-       usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
-                            rhport, sockfd, devid, speed);
+       usbip_dbg_vhci_sysfs("port(%u) pdev(%d) rhport(%u)\n",
+                            port, pdev_nr, rhport);
+       usbip_dbg_vhci_sysfs("sockfd(%u) devid(%u) speed(%u)\n",
+                            sockfd, devid, speed);
 
        /* check received parameters */
-       if (valid_args(rhport, speed) < 0)
+       if (!valid_args(pdev_nr, rhport, speed))
                return -EINVAL;
 
+       hcd = platform_get_drvdata(*(vhci_pdevs + pdev_nr));
+       if (hcd == NULL) {
+               dev_err(dev, "port %d is not ready\n", port);
+               return -EAGAIN;
+       }
+       vhci = hcd_to_vhci(hcd);
+       vdev = &vhci->vdev[rhport];
+
        /* Extract socket from fd. */
        socket = sockfd_lookup(sockfd, &err);
        if (!socket)
@@ -205,14 +303,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
        /* now need lock until setting vdev status as used */
 
        /* begin a lock */
-       spin_lock_irqsave(&the_controller->lock, flags);
-       vdev = port_to_vdev(rhport);
+       spin_lock_irqsave(&vhci->lock, flags);
        spin_lock(&vdev->ud.lock);
 
        if (vdev->ud.status != VDEV_ST_NULL) {
                /* end of the lock */
                spin_unlock(&vdev->ud.lock);
-               spin_unlock_irqrestore(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
 
                sockfd_put(socket);
 
@@ -220,9 +317,10 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
                return -EINVAL;
        }
 
-       dev_info(dev,
-                "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
-                rhport, sockfd, devid, speed, usb_speed_string(speed));
+       dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n",
+                pdev_nr, rhport, sockfd);
+       dev_info(dev, "devid(%u) speed(%u) speed_str(%s)\n",
+                devid, speed, usb_speed_string(speed));
 
        vdev->devid         = devid;
        vdev->speed         = speed;
@@ -230,26 +328,92 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
        vdev->ud.status     = VDEV_ST_NOTASSIGNED;
 
        spin_unlock(&vdev->ud.lock);
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
        /* end the lock */
 
        vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
        vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
 
-       rh_port_connect(rhport, speed);
+       rh_port_connect(vdev, speed);
 
        return count;
 }
 static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
 
-static struct attribute *dev_attrs[] = {
-       &dev_attr_status.attr,
-       &dev_attr_detach.attr,
-       &dev_attr_attach.attr,
-       &dev_attr_usbip_debug.attr,
-       NULL,
+#define MAX_STATUS_NAME 16
+
+struct status_attr {
+       struct device_attribute attr;
+       char name[MAX_STATUS_NAME+1];
 };
 
-const struct attribute_group dev_attr_group = {
-       .attrs = dev_attrs,
+static struct status_attr *status_attrs;
+
+static void set_status_attr(int id)
+{
+       struct status_attr *status;
+
+       status = status_attrs + id;
+       if (id == 0)
+               strcpy(status->name, "status");
+       else
+               snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id);
+       status->attr.attr.name = status->name;
+       status->attr.attr.mode = S_IRUGO;
+       status->attr.show = status_show;
+}
+
+static int init_status_attrs(void)
+{
+       int id;
+
+       status_attrs = kcalloc(vhci_num_controllers, sizeof(struct status_attr),
+                              GFP_KERNEL);
+       if (status_attrs == NULL)
+               return -ENOMEM;
+
+       for (id = 0; id < vhci_num_controllers; id++)
+               set_status_attr(id);
+
+       return 0;
+}
+
+static void finish_status_attrs(void)
+{
+       kfree(status_attrs);
+}
+
+struct attribute_group vhci_attr_group = {
+       .attrs = NULL,
 };
+
+int vhci_init_attr_group(void)
+{
+       struct attribute **attrs;
+       int ret, i;
+
+       attrs = kcalloc((vhci_num_controllers + 5), sizeof(struct attribute *),
+                       GFP_KERNEL);
+       if (attrs == NULL)
+               return -ENOMEM;
+
+       ret = init_status_attrs();
+       if (ret) {
+               kfree(attrs);
+               return ret;
+       }
+       *attrs = &dev_attr_nports.attr;
+       *(attrs + 1) = &dev_attr_detach.attr;
+       *(attrs + 2) = &dev_attr_attach.attr;
+       *(attrs + 3) = &dev_attr_usbip_debug.attr;
+       for (i = 0; i < vhci_num_controllers; i++)
+               *(attrs + i + 4) = &((status_attrs + i)->attr.attr);
+       vhci_attr_group.attrs = attrs;
+       return 0;
+}
+
+void vhci_finish_attr_group(void)
+{
+       finish_status_attrs();
+       kfree(vhci_attr_group.attrs);
+}
This page took 0.028703 seconds and 5 git commands to generate.