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 | ||
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 */ | |
28 | static ssize_t show_status(struct device *dev, struct device_attribute *attr, | |
29 | char *out) | |
30 | { | |
31 | char *s = out; | |
32 | int i = 0; | |
33 | ||
2961f24f | 34 | BUG_ON(!the_controller || !out); |
04679b34 TH |
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); | |
e9133972 | 62 | out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); |
04679b34 TH |
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 | } | |
76 | static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); | |
77 | ||
78 | /* Sysfs entry to shutdown a virtual connection */ | |
79 | static int vhci_port_disconnect(__u32 rhport) | |
80 | { | |
81 | struct vhci_device *vdev; | |
82 | ||
b8868e45 | 83 | usbip_dbg_vhci_sysfs("enter\n"); |
04679b34 TH |
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) { | |
b8868e45 | 92 | usbip_uerr("not connected %d\n", vdev->ud.status); |
04679b34 TH |
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 | ||
110 | static 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) { | |
b8868e45 | 120 | usbip_uerr("invalid port %u\n", rhport); |
04679b34 TH |
121 | return -EINVAL; |
122 | } | |
123 | ||
124 | err = vhci_port_disconnect(rhport); | |
125 | if (err < 0) | |
126 | return -EINVAL; | |
127 | ||
b8868e45 | 128 | usbip_dbg_vhci_sysfs("Leave\n"); |
04679b34 TH |
129 | return count; |
130 | } | |
131 | static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); | |
132 | ||
133 | /* Sysfs entry to establish a virtual connection */ | |
134 | static int valid_args(__u32 rhport, enum usb_device_speed speed) | |
135 | { | |
136 | /* check rhport */ | |
137 | if ((rhport < 0) || (rhport >= VHCI_NPORTS)) { | |
b8868e45 | 138 | usbip_uerr("port %u\n", rhport); |
04679b34 TH |
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: | |
551cdbbe | 147 | case USB_SPEED_WIRELESS: |
04679b34 TH |
148 | break; |
149 | default: | |
b8868e45 | 150 | usbip_uerr("speed %d\n", speed); |
04679b34 TH |
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 | */ | |
168 | static 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 | ||
b8868e45 BM |
184 | usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", |
185 | rhport, sockfd, devid, speed); | |
04679b34 TH |
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 | ||
b8868e45 | 211 | usbip_uerr("port %d already used\n", rhport); |
04679b34 TH |
212 | return -EINVAL; |
213 | } | |
214 | ||
b8868e45 BM |
215 | usbip_uinfo("rhport(%u) sockfd(%d) devid(%u) speed(%u)\n", |
216 | rhport, sockfd, devid, speed); | |
04679b34 TH |
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 | } | |
237 | static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); | |
238 | ||
239 | static 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 | ||
247 | struct attribute_group dev_attr_group = { | |
248 | .attrs = dev_attrs, | |
249 | }; |