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