162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
462306a36Sopenharmony_ci *               2005-2007 Takahiro Hirofuchi
562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics
662306a36Sopenharmony_ci *               Igor Kotrasinski <i.kotrasinsk@samsung.com>
762306a36Sopenharmony_ci *               Krzysztof Opasiak <k.opasiak@samsung.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <sys/stat.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <limits.h>
1362306a36Sopenharmony_ci#include <stdint.h>
1462306a36Sopenharmony_ci#include <stdio.h>
1562306a36Sopenharmony_ci#include <string.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <fcntl.h>
1862306a36Sopenharmony_ci#include <getopt.h>
1962306a36Sopenharmony_ci#include <unistd.h>
2062306a36Sopenharmony_ci#include <errno.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "vhci_driver.h"
2362306a36Sopenharmony_ci#include "usbip_common.h"
2462306a36Sopenharmony_ci#include "usbip_network.h"
2562306a36Sopenharmony_ci#include "usbip.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const char usbip_attach_usage_string[] =
2862306a36Sopenharmony_ci	"usbip attach <args>\n"
2962306a36Sopenharmony_ci	"    -r, --remote=<host>      The machine with exported USB devices\n"
3062306a36Sopenharmony_ci	"    -b, --busid=<busid>    Busid of the device on <host>\n"
3162306a36Sopenharmony_ci	"    -d, --device=<devid>    Id of the virtual UDC on <host>\n";
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_civoid usbip_attach_usage(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	printf("usage: %s", usbip_attach_usage_string);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define MAX_BUFF 100
3962306a36Sopenharmony_cistatic int record_connection(char *host, char *port, char *busid, int rhport)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	int fd;
4262306a36Sopenharmony_ci	char path[PATH_MAX+1];
4362306a36Sopenharmony_ci	char buff[MAX_BUFF+1];
4462306a36Sopenharmony_ci	int ret;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	ret = mkdir(VHCI_STATE_PATH, 0700);
4762306a36Sopenharmony_ci	if (ret < 0) {
4862306a36Sopenharmony_ci		/* if VHCI_STATE_PATH exists, then it better be a directory */
4962306a36Sopenharmony_ci		if (errno == EEXIST) {
5062306a36Sopenharmony_ci			struct stat s;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci			ret = stat(VHCI_STATE_PATH, &s);
5362306a36Sopenharmony_ci			if (ret < 0)
5462306a36Sopenharmony_ci				return -1;
5562306a36Sopenharmony_ci			if (!(s.st_mode & S_IFDIR))
5662306a36Sopenharmony_ci				return -1;
5762306a36Sopenharmony_ci		} else
5862306a36Sopenharmony_ci			return -1;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
6462306a36Sopenharmony_ci	if (fd < 0)
6562306a36Sopenharmony_ci		return -1;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	snprintf(buff, MAX_BUFF, "%s %s %s\n",
6862306a36Sopenharmony_ci			host, port, busid);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ret = write(fd, buff, strlen(buff));
7162306a36Sopenharmony_ci	if (ret != (ssize_t) strlen(buff)) {
7262306a36Sopenharmony_ci		close(fd);
7362306a36Sopenharmony_ci		return -1;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	close(fd);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int import_device(int sockfd, struct usbip_usb_device *udev)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int rc;
8462306a36Sopenharmony_ci	int port;
8562306a36Sopenharmony_ci	uint32_t speed = udev->speed;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	rc = usbip_vhci_driver_open();
8862306a36Sopenharmony_ci	if (rc < 0) {
8962306a36Sopenharmony_ci		err("open vhci_driver (is vhci_hcd loaded?)");
9062306a36Sopenharmony_ci		goto err_out;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	do {
9462306a36Sopenharmony_ci		port = usbip_vhci_get_free_port(speed);
9562306a36Sopenharmony_ci		if (port < 0) {
9662306a36Sopenharmony_ci			err("no free port");
9762306a36Sopenharmony_ci			goto err_driver_close;
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		dbg("got free port %d", port);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
10362306a36Sopenharmony_ci					      udev->devnum, udev->speed);
10462306a36Sopenharmony_ci		if (rc < 0 && errno != EBUSY) {
10562306a36Sopenharmony_ci			err("import device");
10662306a36Sopenharmony_ci			goto err_driver_close;
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci	} while (rc < 0);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	usbip_vhci_driver_close();
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return port;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cierr_driver_close:
11562306a36Sopenharmony_ci	usbip_vhci_driver_close();
11662306a36Sopenharmony_cierr_out:
11762306a36Sopenharmony_ci	return -1;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int query_import_device(int sockfd, char *busid)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int rc;
12362306a36Sopenharmony_ci	struct op_import_request request;
12462306a36Sopenharmony_ci	struct op_import_reply   reply;
12562306a36Sopenharmony_ci	uint16_t code = OP_REP_IMPORT;
12662306a36Sopenharmony_ci	int status;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	memset(&request, 0, sizeof(request));
12962306a36Sopenharmony_ci	memset(&reply, 0, sizeof(reply));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* send a request */
13262306a36Sopenharmony_ci	rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
13362306a36Sopenharmony_ci	if (rc < 0) {
13462306a36Sopenharmony_ci		err("send op_common");
13562306a36Sopenharmony_ci		return -1;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	PACK_OP_IMPORT_REQUEST(0, &request);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
14362306a36Sopenharmony_ci	if (rc < 0) {
14462306a36Sopenharmony_ci		err("send op_import_request");
14562306a36Sopenharmony_ci		return -1;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* receive a reply */
14962306a36Sopenharmony_ci	rc = usbip_net_recv_op_common(sockfd, &code, &status);
15062306a36Sopenharmony_ci	if (rc < 0) {
15162306a36Sopenharmony_ci		err("Attach Request for %s failed - %s\n",
15262306a36Sopenharmony_ci		    busid, usbip_op_common_status_string(status));
15362306a36Sopenharmony_ci		return -1;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
15762306a36Sopenharmony_ci	if (rc < 0) {
15862306a36Sopenharmony_ci		err("recv op_import_reply");
15962306a36Sopenharmony_ci		return -1;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	PACK_OP_IMPORT_REPLY(0, &reply);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* check the reply */
16562306a36Sopenharmony_ci	if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
16662306a36Sopenharmony_ci		err("recv different busid %s", reply.udev.busid);
16762306a36Sopenharmony_ci		return -1;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* import a device */
17162306a36Sopenharmony_ci	return import_device(sockfd, &reply.udev);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int attach_device(char *host, char *busid)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int sockfd;
17762306a36Sopenharmony_ci	int rc;
17862306a36Sopenharmony_ci	int rhport;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
18162306a36Sopenharmony_ci	if (sockfd < 0) {
18262306a36Sopenharmony_ci		err("tcp connect");
18362306a36Sopenharmony_ci		return -1;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	rhport = query_import_device(sockfd, busid);
18762306a36Sopenharmony_ci	if (rhport < 0)
18862306a36Sopenharmony_ci		return -1;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	close(sockfd);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	rc = record_connection(host, usbip_port_string, busid, rhport);
19362306a36Sopenharmony_ci	if (rc < 0) {
19462306a36Sopenharmony_ci		err("record connection");
19562306a36Sopenharmony_ci		return -1;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciint usbip_attach(int argc, char *argv[])
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	static const struct option opts[] = {
20462306a36Sopenharmony_ci		{ "remote", required_argument, NULL, 'r' },
20562306a36Sopenharmony_ci		{ "busid",  required_argument, NULL, 'b' },
20662306a36Sopenharmony_ci		{ "device",  required_argument, NULL, 'd' },
20762306a36Sopenharmony_ci		{ NULL, 0,  NULL, 0 }
20862306a36Sopenharmony_ci	};
20962306a36Sopenharmony_ci	char *host = NULL;
21062306a36Sopenharmony_ci	char *busid = NULL;
21162306a36Sopenharmony_ci	int opt;
21262306a36Sopenharmony_ci	int ret = -1;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	for (;;) {
21562306a36Sopenharmony_ci		opt = getopt_long(argc, argv, "d:r:b:", opts, NULL);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		if (opt == -1)
21862306a36Sopenharmony_ci			break;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		switch (opt) {
22162306a36Sopenharmony_ci		case 'r':
22262306a36Sopenharmony_ci			host = optarg;
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci		case 'd':
22562306a36Sopenharmony_ci		case 'b':
22662306a36Sopenharmony_ci			busid = optarg;
22762306a36Sopenharmony_ci			break;
22862306a36Sopenharmony_ci		default:
22962306a36Sopenharmony_ci			goto err_out;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (!host || !busid)
23462306a36Sopenharmony_ci		goto err_out;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ret = attach_device(host, busid);
23762306a36Sopenharmony_ci	goto out;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cierr_out:
24062306a36Sopenharmony_ci	usbip_attach_usage();
24162306a36Sopenharmony_ciout:
24262306a36Sopenharmony_ci	return ret;
24362306a36Sopenharmony_ci}
244