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 "usbip_common.h" 21#include "vhci.h" 22 23#include <linux/in.h> 24 25/* TODO: refine locking ?*/ 26 27/* Sysfs entry to show port status */ 28static ssize_t show_status(struct device *dev, struct device_attribute *attr, 29 char *out) 30{ 31 char *s = out; 32 int i = 0; 33 34 BUG_ON(!the_controller || !out); 35 36 spin_lock(&the_controller->lock); 37 38 /* 39 * output example: 40 * prt sta spd dev socket local_busid 41 * 000 004 000 000 c5a7bb80 1-2.3 42 * 001 004 000 000 d8cee980 2-3.4 43 * 44 * IP address can be retrieved from a socket pointer address by looking 45 * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a 46 * port number and its peer IP address. 47 */ 48 out += sprintf(out, "prt sta spd bus dev socket " 49 "local_busid\n"); 50 51 for (i = 0; i < VHCI_NPORTS; i++) { 52 struct vhci_device *vdev = port_to_vdev(i); 53 54 spin_lock(&vdev->ud.lock); 55 56 out += sprintf(out, "%03u %03u ", i, vdev->ud.status); 57 58 if (vdev->ud.status == VDEV_ST_USED) { 59 out += sprintf(out, "%03u %08x ", 60 vdev->speed, vdev->devid); 61 out += sprintf(out, "%16p ", vdev->ud.tcp_socket); 62 out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); 63 64 } else 65 out += sprintf(out, "000 000 000 0000000000000000 0-0"); 66 67 out += sprintf(out, "\n"); 68 69 spin_unlock(&vdev->ud.lock); 70 } 71 72 spin_unlock(&the_controller->lock); 73 74 return out - s; 75} 76static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); 77 78/* Sysfs entry to shutdown a virtual connection */ 79static int vhci_port_disconnect(__u32 rhport) 80{ 81 struct vhci_device *vdev; 82 83 usbip_dbg_vhci_sysfs("enter\n"); 84 85 /* lock */ 86 spin_lock(&the_controller->lock); 87 88 vdev = port_to_vdev(rhport); 89 90 spin_lock(&vdev->ud.lock); 91 if (vdev->ud.status == VDEV_ST_NULL) { 92 usbip_uerr("not connected %d\n", vdev->ud.status); 93 94 /* unlock */ 95 spin_unlock(&vdev->ud.lock); 96 spin_unlock(&the_controller->lock); 97 98 return -EINVAL; 99 } 100 101 /* unlock */ 102 spin_unlock(&vdev->ud.lock); 103 spin_unlock(&the_controller->lock); 104 105 usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); 106 107 return 0; 108} 109 110static ssize_t store_detach(struct device *dev, struct device_attribute *attr, 111 const char *buf, size_t count) 112{ 113 int err; 114 __u32 rhport = 0; 115 116 sscanf(buf, "%u", &rhport); 117 118 /* check rhport */ 119 if (rhport >= VHCI_NPORTS) { 120 usbip_uerr("invalid port %u\n", rhport); 121 return -EINVAL; 122 } 123 124 err = vhci_port_disconnect(rhport); 125 if (err < 0) 126 return -EINVAL; 127 128 usbip_dbg_vhci_sysfs("Leave\n"); 129 return count; 130} 131static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); 132 133/* Sysfs entry to establish a virtual connection */ 134static int valid_args(__u32 rhport, enum usb_device_speed speed) 135{ 136 /* check rhport */ 137 if ((rhport < 0) || (rhport >= VHCI_NPORTS)) { 138 usbip_uerr("port %u\n", rhport); 139 return -EINVAL; 140 } 141 142 /* check speed */ 143 switch (speed) { 144 case USB_SPEED_LOW: 145 case USB_SPEED_FULL: 146 case USB_SPEED_HIGH: 147 case USB_SPEED_WIRELESS: 148 break; 149 default: 150 usbip_uerr("speed %d\n", speed); 151 return -EINVAL; 152 } 153 154 return 0; 155} 156 157/* 158 * To start a new USB/IP attachment, a userland program needs to setup a TCP 159 * connection and then write its socket descriptor with remote device 160 * information into this sysfs file. 161 * 162 * A remote device is virtually attached to the root-hub port of @rhport with 163 * @speed. @devid is embedded into a request to specify the remote device in a 164 * server host. 165 * 166 * write() returns 0 on success, else negative errno. 167 */ 168static ssize_t store_attach(struct device *dev, struct device_attribute *attr, 169 const char *buf, size_t count) 170{ 171 struct vhci_device *vdev; 172 struct socket *socket; 173 int sockfd = 0; 174 __u32 rhport = 0, devid = 0, speed = 0; 175 176 /* 177 * @rhport: port number of vhci_hcd 178 * @sockfd: socket descriptor of an established TCP connection 179 * @devid: unique device identifier in a remote host 180 * @speed: usb device speed in a remote host 181 */ 182 sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed); 183 184 usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", 185 rhport, sockfd, devid, speed); 186 187 188 /* check received parameters */ 189 if (valid_args(rhport, speed) < 0) 190 return -EINVAL; 191 192 /* check sockfd */ 193 socket = sockfd_to_socket(sockfd); 194 if (!socket) 195 return -EINVAL; 196 197 /* now need lock until setting vdev status as used */ 198 199 /* begin a lock */ 200 spin_lock(&the_controller->lock); 201 202 vdev = port_to_vdev(rhport); 203 204 spin_lock(&vdev->ud.lock); 205 206 if (vdev->ud.status != VDEV_ST_NULL) { 207 /* end of the lock */ 208 spin_unlock(&vdev->ud.lock); 209 spin_unlock(&the_controller->lock); 210 211 usbip_uerr("port %d already used\n", rhport); 212 return -EINVAL; 213 } 214 215 usbip_uinfo("rhport(%u) sockfd(%d) devid(%u) speed(%u)\n", 216 rhport, sockfd, devid, speed); 217 218 vdev->devid = devid; 219 vdev->speed = speed; 220 vdev->ud.tcp_socket = socket; 221 vdev->ud.status = VDEV_ST_NOTASSIGNED; 222 223 spin_unlock(&vdev->ud.lock); 224 spin_unlock(&the_controller->lock); 225 /* end the lock */ 226 227 /* 228 * this function will sleep, so should be out of the lock. but, it's ok 229 * because we already marked vdev as being used. really? 230 */ 231 usbip_start_threads(&vdev->ud); 232 233 rh_port_connect(rhport, speed); 234 235 return count; 236} 237static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); 238 239static struct attribute *dev_attrs[] = { 240 &dev_attr_status.attr, 241 &dev_attr_detach.attr, 242 &dev_attr_attach.attr, 243 &dev_attr_usbip_debug.attr, 244 NULL, 245}; 246 247struct attribute_group dev_attr_group = { 248 .attrs = dev_attrs, 249}; 250