/* * Copyright (c) 2018 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ /* For accept4() */ #define _GNU_SOURCE 1 #define __packed __attribute__((__packed__)) #include #include #include #include #include #include #include #include #include #include #include #include /* Zephyr headers */ #include #include #include #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; }