Commit | Line | Data |
---|---|---|
04679b34 TH |
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 | ||
7aaacb43 | 20 | #include <linux/kthread.h> |
3d0a2a22 | 21 | #include <linux/file.h> |
7aaacb43 | 22 | #include <linux/net.h> |
23 | ||
04679b34 TH |
24 | #include "usbip_common.h" |
25 | #include "vhci.h" | |
26 | ||
04679b34 TH |
27 | /* TODO: refine locking ?*/ |
28 | ||
29 | /* Sysfs entry to show port status */ | |
b1f56aca | 30 | static ssize_t status_show(struct device *dev, struct device_attribute *attr, |
04679b34 TH |
31 | char *out) |
32 | { | |
33 | char *s = out; | |
34 | int i = 0; | |
21619792 | 35 | unsigned long flags; |
04679b34 | 36 | |
2961f24f | 37 | BUG_ON(!the_controller || !out); |
04679b34 | 38 | |
21619792 | 39 | spin_lock_irqsave(&the_controller->lock, flags); |
04679b34 TH |
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 | */ | |
6bb3ee69 CC |
51 | out += sprintf(out, |
52 | "prt sta spd bus dev socket local_busid\n"); | |
04679b34 TH |
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); | |
04679b34 TH |
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 ", | |
bd608f6c | 62 | vdev->speed, vdev->devid); |
04679b34 | 63 | out += sprintf(out, "%16p ", vdev->ud.tcp_socket); |
e9133972 | 64 | out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); |
04679b34 | 65 | |
bd608f6c | 66 | } else { |
04679b34 | 67 | out += sprintf(out, "000 000 000 0000000000000000 0-0"); |
bd608f6c | 68 | } |
04679b34 TH |
69 | |
70 | out += sprintf(out, "\n"); | |
04679b34 TH |
71 | spin_unlock(&vdev->ud.lock); |
72 | } | |
73 | ||
21619792 | 74 | spin_unlock_irqrestore(&the_controller->lock, flags); |
04679b34 TH |
75 | |
76 | return out - s; | |
77 | } | |
b1f56aca | 78 | static DEVICE_ATTR_RO(status); |
04679b34 TH |
79 | |
80 | /* Sysfs entry to shutdown a virtual connection */ | |
81 | static int vhci_port_disconnect(__u32 rhport) | |
82 | { | |
83 | struct vhci_device *vdev; | |
21619792 | 84 | unsigned long flags; |
04679b34 | 85 | |
b8868e45 | 86 | usbip_dbg_vhci_sysfs("enter\n"); |
04679b34 TH |
87 | |
88 | /* lock */ | |
21619792 | 89 | spin_lock_irqsave(&the_controller->lock, flags); |
04679b34 TH |
90 | |
91 | vdev = port_to_vdev(rhport); | |
92 | ||
93 | spin_lock(&vdev->ud.lock); | |
94 | if (vdev->ud.status == VDEV_ST_NULL) { | |
1a4b6f66 | 95 | pr_err("not connected %d\n", vdev->ud.status); |
04679b34 TH |
96 | |
97 | /* unlock */ | |
98 | spin_unlock(&vdev->ud.lock); | |
21619792 | 99 | spin_unlock_irqrestore(&the_controller->lock, flags); |
04679b34 TH |
100 | |
101 | return -EINVAL; | |
102 | } | |
103 | ||
104 | /* unlock */ | |
105 | spin_unlock(&vdev->ud.lock); | |
21619792 | 106 | spin_unlock_irqrestore(&the_controller->lock, flags); |
04679b34 TH |
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 | ||
88fa1ebf JG |
119 | if (sscanf(buf, "%u", &rhport) != 1) |
120 | return -EINVAL; | |
04679b34 TH |
121 | |
122 | /* check rhport */ | |
123 | if (rhport >= VHCI_NPORTS) { | |
1a4b6f66 | 124 | dev_err(dev, "invalid port %u\n", rhport); |
04679b34 TH |
125 | return -EINVAL; |
126 | } | |
127 | ||
128 | err = vhci_port_disconnect(rhport); | |
129 | if (err < 0) | |
130 | return -EINVAL; | |
131 | ||
b8868e45 | 132 | usbip_dbg_vhci_sysfs("Leave\n"); |
bd608f6c | 133 | |
04679b34 TH |
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 */ | |
988e7520 | 142 | if (rhport >= VHCI_NPORTS) { |
1a4b6f66 | 143 | pr_err("port %u\n", rhport); |
04679b34 TH |
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: | |
551cdbbe | 152 | case USB_SPEED_WIRELESS: |
04679b34 TH |
153 | break; |
154 | default: | |
8360fb0d SK |
155 | pr_err("Failed attach request for unsupported USB speed: %s\n", |
156 | usb_speed_string(speed)); | |
04679b34 TH |
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; | |
964ea96e | 181 | int err; |
21619792 | 182 | unsigned long flags; |
04679b34 TH |
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 | */ | |
de4734bc | 190 | if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) |
88fa1ebf | 191 | return -EINVAL; |
04679b34 | 192 | |
b8868e45 | 193 | usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", |
bd608f6c | 194 | rhport, sockfd, devid, speed); |
04679b34 TH |
195 | |
196 | /* check received parameters */ | |
197 | if (valid_args(rhport, speed) < 0) | |
198 | return -EINVAL; | |
199 | ||
3d0a2a22 | 200 | /* Extract socket from fd. */ |
964ea96e | 201 | socket = sockfd_lookup(sockfd, &err); |
04679b34 | 202 | if (!socket) |
a6d81814 | 203 | return -EINVAL; |
04679b34 TH |
204 | |
205 | /* now need lock until setting vdev status as used */ | |
206 | ||
207 | /* begin a lock */ | |
21619792 | 208 | spin_lock_irqsave(&the_controller->lock, flags); |
04679b34 | 209 | vdev = port_to_vdev(rhport); |
04679b34 TH |
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); | |
21619792 | 215 | spin_unlock_irqrestore(&the_controller->lock, flags); |
04679b34 | 216 | |
964ea96e | 217 | sockfd_put(socket); |
3d0a2a22 | 218 | |
1a4b6f66 | 219 | dev_err(dev, "port %d already used\n", rhport); |
04679b34 TH |
220 | return -EINVAL; |
221 | } | |
222 | ||
a6646ea6 SK |
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)); | |
04679b34 TH |
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); | |
21619792 | 233 | spin_unlock_irqrestore(&the_controller->lock, flags); |
04679b34 TH |
234 | /* end the lock */ |
235 | ||
ba46ce30 ON |
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"); | |
d1b2e95a | 238 | |
04679b34 TH |
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 | ||
cf77acfc | 253 | const struct attribute_group dev_attr_group = { |
04679b34 TH |
254 | .attrs = dev_attrs, |
255 | }; |