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 
usbip_attach_usage(void)33 void usbip_attach_usage(void)
34 {
35 	printf("usage: %s", usbip_attach_usage_string);
36 }
37 
38 #define MAX_BUFF 100
record_connection(char * host,char * port,char * busid,int rhport)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 
import_device(int sockfd,struct usbip_usb_device * udev)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");
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 
query_import_device(int sockfd,char * busid)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, &reply.udev);
172 }
173 
attach_device(char * host,char * busid)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(host, 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, usbip_port_string, busid, rhport);
193 	if (rc < 0) {
194 		err("record connection");
195 		return -1;
196 	}
197 
198 	return 0;
199 }
200 
usbip_attach(int argc,char * argv[])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