179 lines
5.0 KiB
C++
179 lines
5.0 KiB
C++
#include "UsbIpUtils.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <log/log.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
constexpr uint16_t kIpVersion = 0x0111;
|
|
|
|
constexpr uint16_t kOpRequest = 0x8003;
|
|
constexpr uint16_t kOpResponse = 0x0003;
|
|
|
|
// Fields associated with all USBIP packets.
|
|
typedef struct op_req_common {
|
|
uint16_t ip_version;
|
|
uint16_t command_code;
|
|
uint32_t status;
|
|
} op_req_common;
|
|
|
|
typedef struct op_req_import {
|
|
op_req_common common;
|
|
uint32_t busid[8];
|
|
} op_req_import;
|
|
|
|
typedef struct op_rep_import {
|
|
op_req_common common;
|
|
} op_rep_import;
|
|
|
|
// Metadata fields only valid for successful status flag.
|
|
typedef struct op_rep_import_metadata {
|
|
uint32_t path[64];
|
|
uint32_t bus_id[8];
|
|
uint32_t bus_num;
|
|
uint32_t dev_num;
|
|
uint32_t speed;
|
|
uint16_t id_vendor;
|
|
uint16_t id_product;
|
|
uint16_t bcd_device;
|
|
uint8_t device_class;
|
|
uint8_t device_subclass;
|
|
uint8_t device_protocol;
|
|
uint8_t configuration_value;
|
|
uint8_t num_configurations;
|
|
uint8_t num_interfaces;
|
|
} op_rep_import_success;
|
|
|
|
bool get_usbip_connection(const char *server, const char *port,
|
|
const char *dev_id, usbip_conn_info *info) {
|
|
struct sockaddr_in address;
|
|
|
|
// Setup socket to the server.
|
|
if ((info->sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == 0) {
|
|
ALOGE("Opening socket failed: %s\n", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
int flags = 1;
|
|
if (setsockopt(info->sock_fd, SOL_SOCKET, SO_KEEPALIVE, &flags,
|
|
sizeof(flags)) == -1) {
|
|
ALOGE("Failed to enable keep alive: %s\n", strerror(errno));
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
if (setsockopt(info->sock_fd, SOL_TCP, TCP_NODELAY, &flags,
|
|
sizeof(flags)) == -1) {
|
|
ALOGE("Failed to enable no delay: %s\n", strerror(errno));
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
address.sin_family = AF_INET;
|
|
if (inet_aton(server, &address.sin_addr) == 0) {
|
|
ALOGE("Failure to convert ip address %s\n", server);
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
errno = 0;
|
|
char *error;
|
|
address.sin_port = strtol(port, &error, 10);
|
|
if (address.sin_port == 0) {
|
|
ALOGE("Port is invalid %s\n", port);
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
address.sin_port = htons(address.sin_port);
|
|
if (connect(info->sock_fd, (struct sockaddr *)&address, sizeof(address)) ==
|
|
-1) {
|
|
ALOGE("Connection failed: %s\n", strerror(errno));
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
// Fill in op request
|
|
op_req_import op_req;
|
|
op_req.common.ip_version = htons(kIpVersion);
|
|
op_req.common.command_code = htons(kOpRequest);
|
|
op_req.common.status = 0x0000;
|
|
strncpy((char *)op_req.busid, dev_id, sizeof(op_req.busid));
|
|
|
|
if (send(info->sock_fd, &op_req, sizeof(op_req), 0) == -1) {
|
|
ALOGE("Error sending op_req: %s\n", strerror(errno));
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
// Read in op response.
|
|
op_rep_import op_rep;
|
|
if (recv(info->sock_fd, &op_rep, sizeof(op_rep), MSG_WAITALL) == -1) {
|
|
ALOGE("Error receiving op_rep: %s\n", strerror(errno));
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
if (op_rep.common.status != 0) {
|
|
ALOGE("op_rep status is invalid.\n");
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
uint16_t command = ntohs(op_rep.common.command_code);
|
|
if (command != kOpResponse) {
|
|
ALOGE("Invalid command expected: %d received: %d", command, kOpResponse);
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
op_rep_import_success data;
|
|
if (recv(info->sock_fd, &data, sizeof(data), MSG_WAITALL) == -1) {
|
|
ALOGE("Error receiving op_rep_data: %s\n", strerror(errno));
|
|
close(info->sock_fd);
|
|
return false;
|
|
}
|
|
|
|
int temp_bus_num = ntohl(data.bus_num);
|
|
int temp_dev_num = ntohl(data.dev_num);
|
|
info->speed = ntohl(data.speed);
|
|
info->dev_id = (temp_bus_num << 16) | temp_dev_num;
|
|
return true;
|
|
}
|
|
|
|
int get_free_vhci_port(FILE *file, int speed) {
|
|
// Throw away the header line.
|
|
char *buf = NULL;
|
|
size_t length = 0;
|
|
if (getline(&buf, &length, file) == -1) {
|
|
ALOGE("Couldn't get the header line: %s\n", strerror(errno));
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
free(buf);
|
|
|
|
char busid[32];
|
|
char hub[3];
|
|
int port = 0;
|
|
int status = 0;
|
|
int spd = 0;
|
|
int dev = 0;
|
|
int sockfd = 0;
|
|
const char *expected_hub = (speed == USBIP_SPEED_SUPER) ? "ss" : "hs";
|
|
|
|
// Scan status lines for a free USB port.
|
|
while (fscanf(file, "%2s %d %d %d %x %u %31s\n", hub, &port, &status, &spd,
|
|
&dev, &sockfd, busid) != EOF) {
|
|
if (strcmp(expected_hub, hub) == 0 && status == USBIP_VDEV_NULL) {
|
|
return port;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|