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