1/* 2 * Copyright (C) 2003-2008 Takahiro Hirofuchi 3 * 4 * This is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 17 * USA. 18 */ 19 20#include <linux/kthread.h> 21#include <linux/file.h> 22#include <linux/net.h> 23 24#include "usbip_common.h" 25#include "vhci.h" 26 27/* TODO: refine locking ?*/ 28 29/* Sysfs entry to show port status */ 30static ssize_t status_show(struct device *dev, struct device_attribute *attr, 31 char *out) 32{ 33 char *s = out; 34 int i = 0; 35 36 BUG_ON(!the_controller || !out); 37 38 spin_lock(&the_controller->lock); 39 40 /* 41 * output example: 42 * prt sta spd dev socket local_busid 43 * 000 004 000 000 c5a7bb80 1-2.3 44 * 001 004 000 000 d8cee980 2-3.4 45 * 46 * IP address can be retrieved from a socket pointer address by looking 47 * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a 48 * port number and its peer IP address. 49 */ 50 out += sprintf(out, 51 "prt sta spd bus dev socket local_busid\n"); 52 53 for (i = 0; i < VHCI_NPORTS; i++) { 54 struct vhci_device *vdev = port_to_vdev(i); 55 56 spin_lock(&vdev->ud.lock); 57 out += sprintf(out, "%03u %03u ", i, vdev->ud.status); 58 59 if (vdev->ud.status == VDEV_ST_USED) { 60 out += sprintf(out, "%03u %08x ", 61 vdev->speed, vdev->devid); 62 out += sprintf(out, "%16p ", vdev->ud.tcp_socket); 63 out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); 64 65 } else { 66 out += sprintf(out, "000 000 000 0000000000000000 0-0"); 67 } 68 69 out += sprintf(out, "\n"); 70 spin_unlock(&vdev->ud.lock); 71 } 72 73 spin_unlock(&the_controller->lock); 74 75 return out - s; 76} 77static DEVICE_ATTR_RO(status); 78 79/* Sysfs entry to shutdown a virtual connection */ 80static int vhci_port_disconnect(__u32 rhport) 81{ 82 struct vhci_device *vdev; 83 84 usbip_dbg_vhci_sysfs("enter\n"); 85 86 /* lock */ 87 spin_lock(&the_controller->lock); 88 89 vdev = port_to_vdev(rhport); 90 91 spin_lock(&vdev->ud.lock); 92 if (vdev->ud.status == VDEV_ST_NULL) { 93 pr_err("not connected %d\n", vdev->ud.status); 94 95 /* unlock */ 96 spin_unlock(&vdev->ud.lock); 97 spin_unlock(&the_controller->lock); 98 99 return -EINVAL; 100 } 101 102 /* unlock */ 103 spin_unlock(&vdev->ud.lock); 104 spin_unlock(&the_controller->lock); 105 106 usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); 107 108 return 0; 109} 110 111static ssize_t store_detach(struct device *dev, struct device_attribute *attr, 112 const char *buf, size_t count) 113{ 114 int err; 115 __u32 rhport = 0; 116 117 if (sscanf(buf, "%u", &rhport) != 1) 118 return -EINVAL; 119 120 /* check rhport */ 121 if (rhport >= VHCI_NPORTS) { 122 dev_err(dev, "invalid port %u\n", rhport); 123 return -EINVAL; 124 } 125 126 err = vhci_port_disconnect(rhport); 127 if (err < 0) 128 return -EINVAL; 129 130 usbip_dbg_vhci_sysfs("Leave\n"); 131 132 return count; 133} 134static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); 135 136/* Sysfs entry to establish a virtual connection */ 137static int valid_args(__u32 rhport, enum usb_device_speed speed) 138{ 139 /* check rhport */ 140 if (rhport >= VHCI_NPORTS) { 141 pr_err("port %u\n", rhport); 142 return -EINVAL; 143 } 144 145 /* check speed */ 146 switch (speed) { 147 case USB_SPEED_LOW: 148 case USB_SPEED_FULL: 149 case USB_SPEED_HIGH: 150 case USB_SPEED_WIRELESS: 151 break; 152 default: 153 pr_err("Failed attach request for unsupported USB speed: %s\n", 154 usb_speed_string(speed)); 155 return -EINVAL; 156 } 157 158 return 0; 159} 160 161/* 162 * To start a new USB/IP attachment, a userland program needs to setup a TCP 163 * connection and then write its socket descriptor with remote device 164 * information into this sysfs file. 165 * 166 * A remote device is virtually attached to the root-hub port of @rhport with 167 * @speed. @devid is embedded into a request to specify the remote device in a 168 * server host. 169 * 170 * write() returns 0 on success, else negative errno. 171 */ 172static ssize_t store_attach(struct device *dev, struct device_attribute *attr, 173 const char *buf, size_t count) 174{ 175 struct vhci_device *vdev; 176 struct socket *socket; 177 int sockfd = 0; 178 __u32 rhport = 0, devid = 0, speed = 0; 179 int err; 180 181 /* 182 * @rhport: port number of vhci_hcd 183 * @sockfd: socket descriptor of an established TCP connection 184 * @devid: unique device identifier in a remote host 185 * @speed: usb device speed in a remote host 186 */ 187 if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) 188 return -EINVAL; 189 190 usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", 191 rhport, sockfd, devid, speed); 192 193 /* check received parameters */ 194 if (valid_args(rhport, speed) < 0) 195 return -EINVAL; 196 197 /* Extract socket from fd. */ 198 socket = sockfd_lookup(sockfd, &err); 199 if (!socket) 200 return -EINVAL; 201 202 /* now need lock until setting vdev status as used */ 203 204 /* begin a lock */ 205 spin_lock(&the_controller->lock); 206 vdev = port_to_vdev(rhport); 207 spin_lock(&vdev->ud.lock); 208 209 if (vdev->ud.status != VDEV_ST_NULL) { 210 /* end of the lock */ 211 spin_unlock(&vdev->ud.lock); 212 spin_unlock(&the_controller->lock); 213 214 sockfd_put(socket); 215 216 dev_err(dev, "port %d already used\n", rhport); 217 return -EINVAL; 218 } 219 220 dev_info(dev, 221 "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", 222 rhport, sockfd, devid, speed, usb_speed_string(speed)); 223 224 vdev->devid = devid; 225 vdev->speed = speed; 226 vdev->ud.tcp_socket = socket; 227 vdev->ud.status = VDEV_ST_NOTASSIGNED; 228 229 spin_unlock(&vdev->ud.lock); 230 spin_unlock(&the_controller->lock); 231 /* end the lock */ 232 233 vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); 234 vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx"); 235 236 rh_port_connect(rhport, speed); 237 238 return count; 239} 240static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); 241 242static struct attribute *dev_attrs[] = { 243 &dev_attr_status.attr, 244 &dev_attr_detach.attr, 245 &dev_attr_attach.attr, 246 &dev_attr_usbip_debug.attr, 247 NULL, 248}; 249 250const struct attribute_group dev_attr_group = { 251 .attrs = dev_attrs, 252}; 253