1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
4 | * 2005-2007 Takahiro Hirofuchi |
5 | * Copyright (C) 2015-2016 Samsung Electronics |
6 | * Igor Kotrasinski <i.kotrasinsk@samsung.com> |
7 | * Krzysztof Opasiak <k.opasiak@samsung.com> |
8 | */ |
9 | |
10 | #include <sys/stat.h> |
11 | |
12 | #include <limits.h> |
13 | #include <stdint.h> |
14 | #include <stdio.h> |
15 | #include <string.h> |
16 | |
17 | #include <fcntl.h> |
18 | #include <getopt.h> |
19 | #include <unistd.h> |
20 | #include <errno.h> |
21 | |
22 | #include "vhci_driver.h" |
23 | #include "usbip_common.h" |
24 | #include "usbip_network.h" |
25 | #include "usbip.h" |
26 | |
27 | static const char usbip_attach_usage_string[] = |
28 | "usbip attach <args>\n" |
29 | " -r, --remote=<host> The machine with exported USB devices\n" |
30 | " -b, --busid=<busid> Busid of the device on <host>\n" |
31 | " -d, --device=<devid> Id of the virtual UDC on <host>\n" ; |
32 | |
33 | void usbip_attach_usage(void) |
34 | { |
35 | printf("usage: %s" , usbip_attach_usage_string); |
36 | } |
37 | |
38 | #define MAX_BUFF 100 |
39 | static int record_connection(char *host, char *port, char *busid, int rhport) |
40 | { |
41 | int fd; |
42 | char path[PATH_MAX+1]; |
43 | char buff[MAX_BUFF+1]; |
44 | int ret; |
45 | |
46 | ret = mkdir(VHCI_STATE_PATH, 0700); |
47 | if (ret < 0) { |
48 | /* if VHCI_STATE_PATH exists, then it better be a directory */ |
49 | if (errno == EEXIST) { |
50 | struct stat s; |
51 | |
52 | ret = stat(VHCI_STATE_PATH, &s); |
53 | if (ret < 0) |
54 | return -1; |
55 | if (!(s.st_mode & S_IFDIR)) |
56 | return -1; |
57 | } else |
58 | return -1; |
59 | } |
60 | |
61 | snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d" , rhport); |
62 | |
63 | fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); |
64 | if (fd < 0) |
65 | return -1; |
66 | |
67 | snprintf(buff, MAX_BUFF, "%s %s %s\n" , |
68 | host, port, busid); |
69 | |
70 | ret = write(fd, buff, strlen(buff)); |
71 | if (ret != (ssize_t) strlen(buff)) { |
72 | close(fd); |
73 | return -1; |
74 | } |
75 | |
76 | close(fd); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static int import_device(int sockfd, struct usbip_usb_device *udev) |
82 | { |
83 | int rc; |
84 | int port; |
85 | uint32_t speed = udev->speed; |
86 | |
87 | rc = usbip_vhci_driver_open(); |
88 | if (rc < 0) { |
89 | err("open vhci_driver (is vhci_hcd loaded?)" ); |
90 | goto err_out; |
91 | } |
92 | |
93 | do { |
94 | port = usbip_vhci_get_free_port(speed); |
95 | if (port < 0) { |
96 | err("no free port" ); |
97 | goto err_driver_close; |
98 | } |
99 | |
100 | dbg("got free port %d" , port); |
101 | |
102 | rc = usbip_vhci_attach_device(port, sockfd, udev->busnum, |
103 | udev->devnum, udev->speed); |
104 | if (rc < 0 && errno != EBUSY) { |
105 | err("import device" ); |
106 | goto err_driver_close; |
107 | } |
108 | } while (rc < 0); |
109 | |
110 | usbip_vhci_driver_close(); |
111 | |
112 | return port; |
113 | |
114 | err_driver_close: |
115 | usbip_vhci_driver_close(); |
116 | err_out: |
117 | return -1; |
118 | } |
119 | |
120 | static int query_import_device(int sockfd, char *busid) |
121 | { |
122 | int rc; |
123 | struct op_import_request request; |
124 | struct op_import_reply reply; |
125 | uint16_t code = OP_REP_IMPORT; |
126 | int status; |
127 | |
128 | memset(&request, 0, sizeof(request)); |
129 | memset(&reply, 0, sizeof(reply)); |
130 | |
131 | /* send a request */ |
132 | rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0); |
133 | if (rc < 0) { |
134 | err("send op_common" ); |
135 | return -1; |
136 | } |
137 | |
138 | strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1); |
139 | |
140 | PACK_OP_IMPORT_REQUEST(0, &request); |
141 | |
142 | rc = usbip_net_send(sockfd, (void *) &request, sizeof(request)); |
143 | if (rc < 0) { |
144 | err("send op_import_request" ); |
145 | return -1; |
146 | } |
147 | |
148 | /* receive a reply */ |
149 | rc = usbip_net_recv_op_common(sockfd, &code, &status); |
150 | if (rc < 0) { |
151 | err("Attach Request for %s failed - %s\n" , |
152 | busid, usbip_op_common_status_string(status)); |
153 | return -1; |
154 | } |
155 | |
156 | rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply)); |
157 | if (rc < 0) { |
158 | err("recv op_import_reply" ); |
159 | return -1; |
160 | } |
161 | |
162 | PACK_OP_IMPORT_REPLY(0, &reply); |
163 | |
164 | /* check the reply */ |
165 | if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) { |
166 | err("recv different busid %s" , reply.udev.busid); |
167 | return -1; |
168 | } |
169 | |
170 | /* import a device */ |
171 | return import_device(sockfd, udev: &reply.udev); |
172 | } |
173 | |
174 | static int attach_device(char *host, char *busid) |
175 | { |
176 | int sockfd; |
177 | int rc; |
178 | int rhport; |
179 | |
180 | sockfd = usbip_net_tcp_connect(hostname: host, port: usbip_port_string); |
181 | if (sockfd < 0) { |
182 | err("tcp connect" ); |
183 | return -1; |
184 | } |
185 | |
186 | rhport = query_import_device(sockfd, busid); |
187 | if (rhport < 0) |
188 | return -1; |
189 | |
190 | close(sockfd); |
191 | |
192 | rc = record_connection(host, port: usbip_port_string, busid, rhport); |
193 | if (rc < 0) { |
194 | err("record connection" ); |
195 | return -1; |
196 | } |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | int usbip_attach(int argc, char *argv[]) |
202 | { |
203 | static const struct option opts[] = { |
204 | { "remote" , required_argument, NULL, 'r' }, |
205 | { "busid" , required_argument, NULL, 'b' }, |
206 | { "device" , required_argument, NULL, 'd' }, |
207 | { NULL, 0, NULL, 0 } |
208 | }; |
209 | char *host = NULL; |
210 | char *busid = NULL; |
211 | int opt; |
212 | int ret = -1; |
213 | |
214 | for (;;) { |
215 | opt = getopt_long(argc, argv, "d:r:b:" , opts, NULL); |
216 | |
217 | if (opt == -1) |
218 | break; |
219 | |
220 | switch (opt) { |
221 | case 'r': |
222 | host = optarg; |
223 | break; |
224 | case 'd': |
225 | case 'b': |
226 | busid = optarg; |
227 | break; |
228 | default: |
229 | goto err_out; |
230 | } |
231 | } |
232 | |
233 | if (!host || !busid) |
234 | goto err_out; |
235 | |
236 | ret = attach_device(host, busid); |
237 | goto out; |
238 | |
239 | err_out: |
240 | usbip_attach_usage(); |
241 | out: |
242 | return ret; |
243 | } |
244 | |