| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (C) 2003-2008 Takahiro Hirofuchi | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | 
					
						
							|  |  |  |  * USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-11 22:33:43 -07:00
										 |  |  | #include <linux/kthread.h>
 | 
					
						
							| 
									
										
										
										
											2012-10-22 06:45:00 +11:00
										 |  |  | #include <linux/file.h>
 | 
					
						
							| 
									
										
										
										
											2011-05-11 22:33:43 -07:00
										 |  |  | #include <linux/net.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | #include "usbip_common.h"
 | 
					
						
							|  |  |  | #include "vhci.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* TODO: refine locking ?*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Sysfs entry to show port status */ | 
					
						
							| 
									
										
										
										
											2013-08-26 12:02:54 -07:00
										 |  |  | static ssize_t status_show(struct device *dev, struct device_attribute *attr, | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 			   char *out) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *s = out; | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-10 00:10:27 -05:00
										 |  |  | 	BUG_ON(!the_controller || !out); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&the_controller->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * 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 | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * 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. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2014-03-19 23:04:56 +01:00
										 |  |  | 	out += sprintf(out, | 
					
						
							|  |  |  | 		       "prt sta spd bus dev socket           local_busid\n"); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < VHCI_NPORTS; i++) { | 
					
						
							|  |  |  | 		struct vhci_device *vdev = port_to_vdev(i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spin_lock(&vdev->ud.lock); | 
					
						
							|  |  |  | 		out += sprintf(out, "%03u %03u ", i, vdev->ud.status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (vdev->ud.status == VDEV_ST_USED) { | 
					
						
							|  |  |  | 			out += sprintf(out, "%03u %08x ", | 
					
						
							| 
									
										
										
										
											2011-05-06 03:47:52 -07:00
										 |  |  | 				       vdev->speed, vdev->devid); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 			out += sprintf(out, "%16p ", vdev->ud.tcp_socket); | 
					
						
							| 
									
										
										
										
											2008-10-30 01:59:32 +01:00
										 |  |  | 			out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-06 03:47:52 -07:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 			out += sprintf(out, "000 000 000 0000000000000000 0-0"); | 
					
						
							| 
									
										
										
										
											2011-05-06 03:47:52 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		out += sprintf(out, "\n"); | 
					
						
							|  |  |  | 		spin_unlock(&vdev->ud.lock); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock(&the_controller->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return out - s; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-08-26 12:02:54 -07:00
										 |  |  | static DEVICE_ATTR_RO(status); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Sysfs entry to shutdown a virtual connection */ | 
					
						
							|  |  |  | static int vhci_port_disconnect(__u32 rhport) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct vhci_device *vdev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-21 00:46:13 -06:00
										 |  |  | 	usbip_dbg_vhci_sysfs("enter\n"); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* lock */ | 
					
						
							|  |  |  | 	spin_lock(&the_controller->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vdev = port_to_vdev(rhport); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&vdev->ud.lock); | 
					
						
							|  |  |  | 	if (vdev->ud.status == VDEV_ST_NULL) { | 
					
						
							| 
									
										
										
										
											2011-05-19 16:47:32 -07:00
										 |  |  | 		pr_err("not connected %d\n", vdev->ud.status); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* unlock */ | 
					
						
							|  |  |  | 		spin_unlock(&vdev->ud.lock); | 
					
						
							|  |  |  | 		spin_unlock(&the_controller->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* unlock */ | 
					
						
							|  |  |  | 	spin_unlock(&vdev->ud.lock); | 
					
						
							|  |  |  | 	spin_unlock(&the_controller->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t store_detach(struct device *dev, struct device_attribute *attr, | 
					
						
							|  |  |  | 			    const char *buf, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	__u32 rhport = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-06 10:36:34 -08:00
										 |  |  | 	if (sscanf(buf, "%u", &rhport) != 1) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* check rhport */ | 
					
						
							|  |  |  | 	if (rhport >= VHCI_NPORTS) { | 
					
						
							| 
									
										
										
										
											2011-05-19 16:47:32 -07:00
										 |  |  | 		dev_err(dev, "invalid port %u\n", rhport); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = vhci_port_disconnect(rhport); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-21 00:46:13 -06:00
										 |  |  | 	usbip_dbg_vhci_sysfs("Leave\n"); | 
					
						
							| 
									
										
										
										
											2011-05-06 03:47:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* check rhport */ | 
					
						
							| 
									
										
										
										
											2011-05-26 09:24:45 +02:00
										 |  |  | 	if (rhport >= VHCI_NPORTS) { | 
					
						
							| 
									
										
										
										
											2011-05-19 16:47:32 -07:00
										 |  |  | 		pr_err("port %u\n", rhport); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check speed */ | 
					
						
							|  |  |  | 	switch (speed) { | 
					
						
							|  |  |  | 	case USB_SPEED_LOW: | 
					
						
							|  |  |  | 	case USB_SPEED_FULL: | 
					
						
							|  |  |  | 	case USB_SPEED_HIGH: | 
					
						
							| 
									
										
										
										
											2010-01-14 11:08:04 -08:00
										 |  |  | 	case USB_SPEED_WIRELESS: | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2014-01-22 09:38:14 -07:00
										 |  |  | 		pr_err("Failed attach request for unsupported USB speed: %s\n", | 
					
						
							|  |  |  | 			usb_speed_string(speed)); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  |  * information into this sysfs file. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * A remote device is virtually attached to the root-hub port of @rhport with | 
					
						
							|  |  |  |  * @speed. @devid is embedded into a request to specify the remote device in a | 
					
						
							|  |  |  |  * server host. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * write() returns 0 on success, else negative errno. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 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; | 
					
						
							| 
									
										
										
										
											2014-03-05 20:33:08 -05:00
										 |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * @rhport: port number of vhci_hcd | 
					
						
							|  |  |  | 	 * @sockfd: socket descriptor of an established TCP connection | 
					
						
							|  |  |  | 	 * @devid: unique device identifier in a remote host | 
					
						
							|  |  |  | 	 * @speed: usb device speed in a remote host | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2014-03-25 06:47:13 -06:00
										 |  |  | 	if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) | 
					
						
							| 
									
										
										
										
											2014-03-06 10:36:34 -08:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-21 00:46:13 -06:00
										 |  |  | 	usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", | 
					
						
							| 
									
										
										
										
											2011-05-06 03:47:52 -07:00
										 |  |  | 			     rhport, sockfd, devid, speed); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* check received parameters */ | 
					
						
							|  |  |  | 	if (valid_args(rhport, speed) < 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-22 06:45:00 +11:00
										 |  |  | 	/* Extract socket from fd. */ | 
					
						
							| 
									
										
										
										
											2014-03-05 20:33:08 -05:00
										 |  |  | 	socket = sockfd_lookup(sockfd, &err); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 	if (!socket) | 
					
						
							| 
									
										
										
										
											2011-05-27 06:18:48 +02:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* now need lock until setting vdev status as used */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* begin a lock */ | 
					
						
							|  |  |  | 	spin_lock(&the_controller->lock); | 
					
						
							|  |  |  | 	vdev = port_to_vdev(rhport); | 
					
						
							|  |  |  | 	spin_lock(&vdev->ud.lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (vdev->ud.status != VDEV_ST_NULL) { | 
					
						
							|  |  |  | 		/* end of the lock */ | 
					
						
							|  |  |  | 		spin_unlock(&vdev->ud.lock); | 
					
						
							|  |  |  | 		spin_unlock(&the_controller->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-05 20:33:08 -05:00
										 |  |  | 		sockfd_put(socket); | 
					
						
							| 
									
										
										
										
											2012-10-22 06:45:00 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 16:47:32 -07:00
										 |  |  | 		dev_err(dev, "port %d already used\n", rhport); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-22 14:24:29 -07:00
										 |  |  | 	dev_info(dev, | 
					
						
							|  |  |  | 		 "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", | 
					
						
							|  |  |  | 		 rhport, sockfd, devid, speed, usb_speed_string(speed)); | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	vdev->devid         = devid; | 
					
						
							|  |  |  | 	vdev->speed         = speed; | 
					
						
							|  |  |  | 	vdev->ud.tcp_socket = socket; | 
					
						
							|  |  |  | 	vdev->ud.status     = VDEV_ST_NOTASSIGNED; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock(&vdev->ud.lock); | 
					
						
							|  |  |  | 	spin_unlock(&the_controller->lock); | 
					
						
							|  |  |  | 	/* end the lock */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-13 19:07:18 +01:00
										 |  |  | 	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"); | 
					
						
							| 
									
										
										
										
											2011-04-18 21:44:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 	rh_port_connect(rhport, 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, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-30 21:50:26 +02:00
										 |  |  | const struct attribute_group dev_attr_group = { | 
					
						
							| 
									
										
										
										
											2008-07-09 14:56:51 -06:00
										 |  |  | 	.attrs = dev_attrs, | 
					
						
							|  |  |  | }; |