diff options
Diffstat (limited to 'drivers/usb/usbip/vhci_sysfs.c')
-rw-r--r-- | drivers/usb/usbip/vhci_sysfs.c | 296 |
1 files changed, 230 insertions, 66 deletions
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 5b5462eb1665..c404017c1b5a 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -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" @@ -27,106 +30,190 @@ /* 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); +} |