Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usb: usbip: add initial support for USBIP server #74141

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions doc/connectivity/usb/host/usbip.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.. _usbip:

USB/IP protocol support
#######################

Overview
********

New USB support includes initial support for the USB/IP protocol. It is still
under development and is currently limited to supporting only one device
connected to the host controller being exported.

USB/IP uses TCP/IP. Both of the underlying connectivity stacks, USB and
networking, require significant memory resources, which must be considered when
choosing a platform.

In the USB/IP protocol, a server exports the USB devices and a client imports
them. USB/IP support in the Zephyr RTOS implements server functionality and
exports a device connected to a host controller on a device running the Zephyr
RTOS. A client, typically running the Linux kernel, imports this device. The
USB/IP protocol is described in `USB/IP protocol documentation`_.

To use USB/IP support, make sure the required modules are loaded on the client side.

.. code-block:: console

modprobe vhci_hcd
modprobe usbip-core
modprobe usbip-host

On the client side, you will also need the **usbip** user tool. It can be installed
using your Linux distribution's package management system or built from Linux
kernel sources.

There are a few basic commands for everyday use. To list exported USB devices,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth mentioning packages to get for ubuntu here so users don't have to search for why the command is not found?

run the following command:

.. code-block:: console

$ usbip list -r 192.0.2.1
Exportable USB devices
======================
- 192.0.2.1
1-1: NordicSemiconductor : unknown product (2fe3:0001)
: /sys/bus/usb/devices/usb1/1-1
: Miscellaneous Device / ? / Interface Association (ef/02/01)
: 0 - Communications / Abstract (modem) / None (02/02/00)
: 1 - CDC Data / Unused / unknown protocol (0a/00/00)

To attach an exported device with busid 1-1:

.. code-block:: console

$ sudo usbip attach -r 192.0.2.1 -b 1-1

To detach an exported device on port 0:

.. code-block:: console

$ sudo usbip detach -p 0

USB/IP with native_sim
**********************

The preferred method to develop with USB/IP support enabled is to use
:ref:`native_sim <native_sim>`. Use on real hardware is not really tested yet.
USB/IP requires a network connection, see :ref:`networking_with_native_sim`
for how to set up the interface on the client side.

Building and running a sample with USB/IP requires extensive configuration,
you can use usbip-native-sim snippet to configure host and USB/IP support.

.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/cdc_acm
:board: native_sim/native/64
:gen-args: -DSNIPPET=usbip-native-sim -DEXTRA_DTC_OVERLAY_FILE=app.overlay
:goals: build

.. _USB/IP protocol documentation: https://www.kernel.org/doc/html/latest/usb/usbip_protocol.html
1 change: 1 addition & 0 deletions doc/connectivity/usb/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ USB
device_next/usb_device.rst
device_next/api/index.rst
host/api/index.rst
host/usbip.rst

**USB Power Delivery support**

Expand Down
50 changes: 32 additions & 18 deletions drivers/usb/uhc/uhc_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,39 +92,54 @@ void uhc_xfer_buf_free(const struct device *dev, struct net_buf *const buf)
}

struct uhc_transfer *uhc_xfer_alloc(const struct device *dev,
const uint8_t addr,
const uint8_t ep,
const uint8_t attrib,
const uint16_t mps,
const uint16_t timeout,
void *const udev,
void *const cb)
struct usb_device *const udev,
void *const cb,
void *const cb_priv)
{
uint8_t ep_idx = USB_EP_GET_IDX(ep) & 0xF;
const struct uhc_api *api = dev->api;
struct uhc_transfer *xfer = NULL;
uint16_t mps;

api->lock(dev);

if (!uhc_is_initialized(dev)) {
goto xfer_alloc_error;
}

LOG_DBG("Allocate xfer, ep 0x%02x attrib 0x%02x cb %p",
ep, attrib, cb);
if (ep_idx == 0) {
mps = udev->dev_desc.bMaxPacketSize0;
} else {
struct usb_ep_descriptor *ep_desc;

if (USB_EP_DIR_IS_IN(ep)) {
ep_desc = udev->ep_in[ep_idx].desc;
} else {
ep_desc = udev->ep_out[ep_idx].desc;
}

if (ep_desc == NULL) {
LOG_ERR("Endpoint 0x%02x is not configured", ep);
goto xfer_alloc_error;
}

mps = ep_desc->wMaxPacketSize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is ep_desc stored in memory? If it has bytes as-is in the descriptor, then this is missing endian conversion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Device descriptor is stored in CPU native endianness.

}

LOG_DBG("Allocate xfer, ep 0x%02x mps %u cb %p", ep, mps, cb);

if (k_mem_slab_alloc(&uhc_xfer_pool, (void **)&xfer, K_NO_WAIT)) {
LOG_ERR("Failed to allocate transfer");
goto xfer_alloc_error;
}

memset(xfer, 0, sizeof(struct uhc_transfer));
xfer->addr = addr;
xfer->ep = ep;
xfer->attrib = attrib;
xfer->mps = mps;
xfer->timeout = timeout;
xfer->udev = udev;
xfer->cb = cb;
xfer->priv = cb_priv;

xfer_alloc_error:
api->unlock(dev);
Expand All @@ -133,13 +148,10 @@ struct uhc_transfer *uhc_xfer_alloc(const struct device *dev,
}

struct uhc_transfer *uhc_xfer_alloc_with_buf(const struct device *dev,
const uint8_t addr,
const uint8_t ep,
const uint8_t attrib,
const uint16_t mps,
const uint16_t timeout,
void *const udev,
struct usb_device *const udev,
void *const cb,
void *const cb_priv,
size_t size)
{
struct uhc_transfer *xfer;
Expand All @@ -150,7 +162,7 @@ struct uhc_transfer *uhc_xfer_alloc_with_buf(const struct device *dev,
return NULL;
}

xfer = uhc_xfer_alloc(dev, addr, ep, attrib, mps, timeout, udev, cb);
xfer = uhc_xfer_alloc(dev, ep, udev, cb, cb_priv);
if (xfer == NULL) {
net_buf_unref(buf);
return NULL;
Expand Down Expand Up @@ -298,7 +310,8 @@ int uhc_disable(const struct device *dev)
return ret;
}

int uhc_init(const struct device *dev, uhc_event_cb_t event_cb)
int uhc_init(const struct device *dev,
uhc_event_cb_t event_cb, const void *const event_ctx)
{
const struct uhc_api *api = dev->api;
struct uhc_data *data = dev->data;
Expand All @@ -316,6 +329,7 @@ int uhc_init(const struct device *dev, uhc_event_cb_t event_cb)
}

data->event_cb = event_cb;
data->event_ctx = event_ctx;
sys_dlist_init(&data->ctrl_xfers);
sys_dlist_init(&data->bulk_xfers);

Expand Down
64 changes: 40 additions & 24 deletions drivers/usb/uhc/uhc_max3421e.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,33 +377,27 @@ static int max3421e_xfer_bulk(const struct device *dev,
static int max3421e_schedule_xfer(const struct device *dev)
{
struct max3421e_data *priv = uhc_get_private(dev);
const uint8_t hirq = priv->hirq;
const uint8_t hrsl = priv->hrsl;
uint8_t hrsl = priv->hrsl;

if (priv->last_xfer == NULL) {
int ret;

/* Do not restart last transfer */
hrsl = 0;

priv->last_xfer = uhc_xfer_get_next(dev);
if (priv->last_xfer == NULL) {
LOG_DBG("Nothing to transfer");
return 0;
}

LOG_DBG("Next transfer %p", priv->last_xfer);
ret = max3421e_peraddr(dev, priv->last_xfer->addr);
ret = max3421e_peraddr(dev, priv->last_xfer->udev->addr);
if (ret) {
return ret;
}
}

if (hirq & MAX3421E_FRAME) {
if (priv->last_xfer->timeout) {
priv->last_xfer->timeout--;
} else {
LOG_INF("Transfer timeout");
}
}

/*
* TODO: currently we only support control transfers and
* treat all others as bulk.
Expand All @@ -425,6 +419,23 @@ static void max3421e_xfer_drop_active(const struct device *dev, int err)
}
}

static void max3421e_xfer_cleanup_cancelled(const struct device *dev)
{
struct max3421e_data *priv = uhc_get_private(dev);
struct uhc_data *data = dev->data;
struct uhc_transfer *tmp;

if (priv->last_xfer != NULL && priv->last_xfer->err == -ECONNRESET) {
max3421e_xfer_drop_active(dev, -ECONNRESET);
}

SYS_DLIST_FOR_EACH_CONTAINER(&data->ctrl_xfers, tmp, node) {
if (tmp->err == -ECONNRESET) {
uhc_xfer_return(dev, tmp, -ECONNRESET);
}
}
}

static int max3421e_hrslt_success(const struct device *dev)
{
struct max3421e_data *priv = uhc_get_private(dev);
Expand Down Expand Up @@ -527,17 +538,6 @@ static int max3421e_handle_hxfrdn(const struct device *dev)

switch (MAX3421E_HRSLT(hrsl)) {
case MAX3421E_HR_NAK:
/*
* The transfer did not take place within
* the specified number of frames.
*
* TODO: Transfer cancel request (xfer->cancel)
* can be handled here as well.
*/
if (xfer->timeout == 0) {
max3421e_xfer_drop_active(dev, -ETIMEDOUT);
}

break;
case MAX3421E_HR_STALL:
max3421e_xfer_drop_active(dev, -EPIPE);
Expand Down Expand Up @@ -682,7 +682,9 @@ static void uhc_max3421e_thread(void *p1, void *p2, void *p3)
/* Host Transfer Done Interrupt */
if (priv->hirq & MAX3421E_HXFRDN) {
err = max3421e_handle_hxfrdn(dev);
schedule = true;
if (unlikely(err)) {
uhc_submit_event(dev, UHC_EVT_ERROR, err);
}
}

/* Frame Generator Interrupt */
Expand All @@ -704,6 +706,8 @@ static void uhc_max3421e_thread(void *p1, void *p2, void *p3)
uhc_submit_event(dev, UHC_EVT_ERROR, err);
}

max3421e_xfer_cleanup_cancelled(dev);

if (schedule) {
err = max3421e_schedule_xfer(dev);
if (unlikely(err)) {
Expand Down Expand Up @@ -797,7 +801,19 @@ static int max3421e_enqueue(const struct device *dev,
static int max3421e_dequeue(const struct device *dev,
struct uhc_transfer *const xfer)
{
/* TODO */
struct uhc_data *data = dev->data;
struct uhc_transfer *tmp;
unsigned int key;

key = irq_lock();
SYS_DLIST_FOR_EACH_CONTAINER(&data->ctrl_xfers, tmp, node) {
if (xfer == tmp) {
tmp->err = -ECONNRESET;
}
}

irq_unlock(key);

return 0;
}

Expand Down
Loading
Loading