456 lines
9.7 KiB
C
456 lines
9.7 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/* For accept4() */
|
|
#define _GNU_SOURCE 1
|
|
|
|
#define __packed __attribute__((__packed__))
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
/* Zephyr headers */
|
|
#include <kernel.h>
|
|
#include <usb/usb_device.h>
|
|
|
|
#include <posix_board_if.h>
|
|
#include "usb_dc_native_posix_adapt.h"
|
|
|
|
#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
|
|
LOG_MODULE_REGISTER(native_posix_adapt);
|
|
|
|
#define USBIP_PORT 3240
|
|
#define USBIP_VERSION 273
|
|
|
|
#define VERBOSE_DEBUG
|
|
|
|
int connfd_global;
|
|
int seqnum_global;
|
|
int devid_global;
|
|
|
|
/* Helpers */
|
|
|
|
#ifdef VERBOSE_DEBUG
|
|
static void usbip_header_dump(struct usbip_header *hdr)
|
|
{
|
|
LOG_DBG("cmd %x seq %u dir %u ep %x", ntohl(hdr->common.command),
|
|
ntohl(hdr->common.seqnum), ntohl(hdr->common.direction),
|
|
ntohl(hdr->common.ep));
|
|
|
|
switch (ntohl(hdr->common.command)) {
|
|
case USBIP_CMD_SUBMIT:
|
|
LOG_DBG("flags %x np %u int %u buflen %u",
|
|
ntohl(hdr->u.submit.transfer_flags),
|
|
ntohl(hdr->u.submit.number_of_packets),
|
|
ntohl(hdr->u.submit.interval),
|
|
ntohl(hdr->u.submit.transfer_buffer_length));
|
|
break;
|
|
case USBIP_CMD_UNLINK:
|
|
LOG_DBG("seq %d", ntohl(hdr->u.unlink.seqnum));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
#define usbip_header_dump(x)
|
|
#endif
|
|
|
|
void get_interface(uint8_t *descriptors)
|
|
{
|
|
while (descriptors[0]) {
|
|
if (descriptors[1] == USB_DESC_INTERFACE) {
|
|
LOG_DBG("interface found");
|
|
}
|
|
|
|
/* skip to next descriptor */
|
|
descriptors += descriptors[0];
|
|
}
|
|
}
|
|
|
|
static int send_interfaces(const uint8_t *descriptors, int connfd)
|
|
{
|
|
struct devlist_interface {
|
|
uint8_t bInterfaceClass;
|
|
uint8_t bInterfaceSubClass;
|
|
uint8_t bInterfaceProtocol;
|
|
uint8_t padding; /* alignment */
|
|
} __packed iface;
|
|
|
|
while (descriptors[0]) {
|
|
if (descriptors[1] == USB_DESC_INTERFACE) {
|
|
struct usb_if_descriptor *desc = (void *)descriptors;
|
|
|
|
iface.bInterfaceClass = desc->bInterfaceClass;
|
|
iface.bInterfaceSubClass = desc->bInterfaceSubClass;
|
|
iface.bInterfaceProtocol = desc->bInterfaceProtocol;
|
|
iface.padding = 0U;
|
|
|
|
if (send(connfd, &iface, sizeof(iface), 0) !=
|
|
sizeof(iface)) {
|
|
LOG_ERR("send() failed: %s", strerror(errno));
|
|
return errno;
|
|
}
|
|
}
|
|
|
|
/* skip to next descriptor */
|
|
descriptors += descriptors[0];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fill_device(struct devlist_device *dev, const uint8_t *desc)
|
|
{
|
|
struct usb_device_descriptor *dev_dsc = (void *)desc;
|
|
struct usb_cfg_descriptor *cfg =
|
|
(void *)(desc + sizeof(struct usb_device_descriptor));
|
|
|
|
memset(dev->path, 0, 256);
|
|
strcpy(dev->path, "/sys/devices/pci0000:00/0000:00:01.2/usb1/1-1");
|
|
memset(dev->busid, 0, 32);
|
|
strcpy(dev->busid, "1-1");
|
|
|
|
dev->busnum = htonl(1);
|
|
dev->devnum = htonl(2);
|
|
dev->speed = htonl(2);
|
|
|
|
dev->idVendor = htons(dev_dsc->idVendor);
|
|
dev->idProduct = htons(dev_dsc->idProduct);
|
|
dev->bcdDevice = htons(dev_dsc->bcdDevice);
|
|
dev->bDeviceClass = dev_dsc->bDeviceClass;
|
|
dev->bDeviceSubClass = dev_dsc->bDeviceSubClass;
|
|
dev->bDeviceProtocol = dev_dsc->bDeviceProtocol;
|
|
|
|
dev->bConfigurationValue = cfg->bConfigurationValue;
|
|
dev->bNumConfigurations = dev_dsc->bNumConfigurations;
|
|
dev->bNumInterfaces = cfg->bNumInterfaces;
|
|
}
|
|
|
|
static int send_device(const uint8_t *desc, int connfd)
|
|
{
|
|
struct devlist_device dev;
|
|
|
|
fill_device(&dev, desc);
|
|
|
|
if (send(connfd, &dev, sizeof(dev), 0) != sizeof(dev)) {
|
|
LOG_ERR("send() device failed: %s", strerror(errno));
|
|
return errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handle_device_list(const uint8_t *desc, int connfd)
|
|
{
|
|
struct op_common header = {
|
|
.version = htons(USBIP_VERSION),
|
|
.code = htons(OP_REP_DEVLIST),
|
|
.status = 0,
|
|
};
|
|
|
|
LOG_DBG("desc %p", desc);
|
|
|
|
if (send(connfd, &header, sizeof(header), 0) != sizeof(header)) {
|
|
LOG_ERR("send() header failed: %s", strerror(errno));
|
|
return errno;
|
|
}
|
|
|
|
/* Send number of devices */
|
|
uint32_t ndev = htonl(1);
|
|
|
|
if (send(connfd, &ndev, sizeof(ndev), 0) != sizeof(ndev)) {
|
|
LOG_ERR("send() ndev failed: %s", strerror(errno));
|
|
return errno;
|
|
}
|
|
|
|
send_device(desc, connfd);
|
|
|
|
send_interfaces(desc, connfd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void handle_usbip_submit(int connfd, struct usbip_header *hdr)
|
|
{
|
|
struct usbip_submit *req = &hdr->u.submit;
|
|
int read;
|
|
|
|
LOG_DBG("");
|
|
|
|
read = recv(connfd, req, sizeof(*req), 0);
|
|
if (read != sizeof(*req)) {
|
|
LOG_ERR("recv() failed: %s", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
usbip_header_dump((void *)hdr);
|
|
|
|
if (ntohl(hdr->common.ep) == 0) {
|
|
handle_usb_control(hdr);
|
|
} else {
|
|
handle_usb_data(hdr);
|
|
}
|
|
}
|
|
|
|
static void handle_usbip_unlink(int connfd, struct usbip_header *hdr)
|
|
{
|
|
int read;
|
|
|
|
LOG_DBG("");
|
|
|
|
/* Need to read the whole structure */
|
|
read = recv(connfd, &hdr->u, sizeof(hdr->u), 0);
|
|
if (read != sizeof(hdr->u)) {
|
|
LOG_ERR("recv() failed: %s", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
usbip_header_dump((void *)hdr);
|
|
|
|
/* TODO: unlink */
|
|
}
|
|
|
|
static int handle_import(const uint8_t *desc, int connfd)
|
|
{
|
|
struct op_common header = {
|
|
.version = htons(USBIP_VERSION),
|
|
.code = htons(OP_REP_IMPORT),
|
|
.status = 0,
|
|
};
|
|
char busid[32];
|
|
|
|
LOG_DBG("attach device");
|
|
|
|
if (recv(connfd, busid, 32, 0) != sizeof(busid)) {
|
|
LOG_ERR("recv() failed: %s", strerror(errno));
|
|
return errno;
|
|
}
|
|
|
|
if (send(connfd, &header, sizeof(header), 0) != sizeof(header)) {
|
|
LOG_ERR("send() header failed: %s", strerror(errno));
|
|
return errno;
|
|
}
|
|
|
|
send_device(desc, connfd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern struct usb_desc_header __usb_descriptor_start[];
|
|
|
|
void usbip_start(void)
|
|
{
|
|
struct sockaddr_in srv;
|
|
unsigned char attached;
|
|
int listenfd, connfd;
|
|
const uint8_t *desc;
|
|
int reuse = 1;
|
|
|
|
LOG_DBG("Starting");
|
|
|
|
/*
|
|
* Do not use usb_get_device_descriptor();
|
|
* to prevent double string fixing
|
|
*/
|
|
desc = (const uint8_t *)__usb_descriptor_start;
|
|
if (!desc) {
|
|
LOG_ERR("Descriptors are not set");
|
|
posix_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
|
if (listenfd < 0) {
|
|
LOG_ERR("socket() failed: %s", strerror(errno));
|
|
posix_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
|
|
(const char *)&reuse, sizeof(reuse)) < 0) {
|
|
LOG_WRN("setsockopt() failed: %s", strerror(errno));
|
|
}
|
|
|
|
memset(&srv, 0, sizeof(srv));
|
|
srv.sin_family = AF_INET;
|
|
srv.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
srv.sin_port = htons(USBIP_PORT);
|
|
|
|
if (bind(listenfd, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
|
|
LOG_ERR("bind() failed: %s", strerror(errno));
|
|
posix_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (listen(listenfd, SOMAXCONN) < 0) {
|
|
LOG_ERR("listen() failed: %s", strerror(errno));
|
|
posix_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
while (true) {
|
|
struct sockaddr_in client_addr;
|
|
socklen_t client_addr_len = sizeof(client_addr);
|
|
|
|
connfd = accept4(listenfd, (struct sockaddr *)&client_addr,
|
|
&client_addr_len, SOCK_NONBLOCK);
|
|
if (connfd < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
/* Non-blocking accept */
|
|
k_sleep(K_MSEC(100));
|
|
|
|
continue;
|
|
}
|
|
|
|
LOG_ERR("accept() failed: %s", strerror(errno));
|
|
posix_exit(EXIT_FAILURE);
|
|
}
|
|
|
|
connfd_global = connfd;
|
|
|
|
LOG_DBG("Connection: %s", inet_ntoa(client_addr.sin_addr));
|
|
|
|
/* Set attached 0 */
|
|
attached = 0U;
|
|
|
|
while (true) {
|
|
struct usbip_header cmd;
|
|
struct usbip_header_common *hdr = &cmd.common;
|
|
int read;
|
|
|
|
if (!attached) {
|
|
struct op_common req;
|
|
|
|
read = recv(connfd, &req, sizeof(req), 0);
|
|
if (read < 0) {
|
|
if (errno == EAGAIN ||
|
|
errno == EWOULDBLOCK) {
|
|
/* Non-blocking accept */
|
|
k_sleep(K_MSEC(100));
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (read != sizeof(req)) {
|
|
LOG_WRN("wrong length, %d", read);
|
|
|
|
/* Closing connection */
|
|
break;
|
|
}
|
|
|
|
LOG_HEXDUMP_DBG((uint8_t *)&req, sizeof(req),
|
|
"Got request");
|
|
|
|
LOG_DBG("Code: 0x%x", ntohs(req.code));
|
|
|
|
switch (ntohs(req.code)) {
|
|
case OP_REQ_DEVLIST:
|
|
handle_device_list(desc, connfd);
|
|
break;
|
|
case OP_REQ_IMPORT:
|
|
if (!handle_import(desc, connfd)) {
|
|
attached = 1U;
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("Unhandled code: 0x%x",
|
|
ntohs(req.code));
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Handle attached case */
|
|
|
|
read = recv(connfd, hdr, sizeof(*hdr), 0);
|
|
if (read < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
/* Non-blocking accept */
|
|
k_sleep(K_MSEC(100));
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
LOG_HEXDUMP_DBG((uint8_t *)hdr, read, "Got cmd");
|
|
|
|
if (read != sizeof(*hdr)) {
|
|
LOG_ERR("recv wrong length: %d", read);
|
|
|
|
/* Closing connection */
|
|
break;
|
|
}
|
|
|
|
devid_global = ntohl(hdr->devid);
|
|
seqnum_global = ntohl(hdr->seqnum);
|
|
|
|
switch (ntohl(hdr->command)) {
|
|
case USBIP_CMD_SUBMIT:
|
|
handle_usbip_submit(connfd, &cmd);
|
|
break;
|
|
case USBIP_CMD_UNLINK:
|
|
handle_usbip_unlink(connfd, &cmd);
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown command: 0x%x",
|
|
ntohl(hdr->command));
|
|
close(connfd);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG_DBG("Closing connection");
|
|
close(connfd);
|
|
}
|
|
}
|
|
|
|
int usbip_recv(uint8_t *buf, size_t len)
|
|
{
|
|
return recv(connfd_global, buf, len, 0);
|
|
}
|
|
|
|
int usbip_send(uint8_t ep, const uint8_t *data, size_t len)
|
|
{
|
|
return send(connfd_global, data, len, 0);
|
|
}
|
|
|
|
bool usbip_send_common(uint8_t ep, uint32_t data_len)
|
|
{
|
|
struct usbip_submit_rsp rsp;
|
|
uint32_t ep_dir = USB_EP_DIR_IS_IN(ep) ? USBIP_DIR_IN : USBIP_DIR_OUT;
|
|
uint32_t ep_idx = USB_EP_GET_IDX(ep);
|
|
|
|
rsp.common.command = htonl(USBIP_RET_SUBMIT);
|
|
rsp.common.seqnum = htonl(seqnum_global);
|
|
rsp.common.devid = htonl(0);
|
|
rsp.common.direction = htonl(ep_dir);
|
|
rsp.common.ep = htonl(ep_idx);
|
|
|
|
rsp.status = htonl(0);
|
|
rsp.actual_length = htonl(data_len);
|
|
rsp.start_frame = htonl(0);
|
|
rsp.number_of_packets = htonl(0);
|
|
rsp.error_count = htonl(0);
|
|
|
|
rsp.setup = htonl(0);
|
|
|
|
if (usbip_send(ep, (uint8_t *)&rsp, sizeof(rsp)) == sizeof(rsp)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|