<alp/usb.h> — USB Device & Host
USB 2.0 / 3.0 device support — CDC ACM, mass storage, HID, vendor-class — plus an experimental host role.
Header
#include <alp/usb.h>
CDC ACM (virtual serial)
alp_usb_dev_t *dev = alp_usb_device_open(&(alp_usb_device_config_t){
.device_class = ALP_USB_DEVICE_CDC_ACM,
.vendor_id = 0xAB12,
.product_id = 0xCD34,
.product = "Alp Sensor",
});
// Attach to the bus — presents the descriptor to the host on enumerate.
alp_usb_device_enable(dev);
// Write through the device's primary endpoint (CDC-ACM TX here).
alp_usb_device_write(dev, (const uint8_t *)"hello\n", 6, /* timeout_ms */ 100);
alp_usb_device_close(dev);
CDC-ACM does not expose a separate alp_uart_t — bytes move through the device handle directly with alp_usb_device_write / alp_usb_device_read (both take a timeout_ms). The same two calls serve every wrapped class (CDC-ACM TX/RX, HID IN/OUT, MSC bulk-in/out).
| Call | Returns |
|---|---|
alp_usb_device_open(cfg) | Handle, or NULL with ALP_ERR_INVAL / ALP_ERR_NOSUPPORT. |
alp_usb_device_enable(dev) | ALP_OK / ALP_ERR_INVAL / ALP_ERR_NOT_READY / ALP_ERR_NOSUPPORT. |
alp_usb_device_disable(dev) | ALP_OK / ALP_ERR_INVAL / ALP_ERR_NOSUPPORT. |
alp_usb_device_write(dev, data, len, timeout_ms) | ALP_OK / ALP_ERR_INVAL / ALP_ERR_NOT_READY / ALP_ERR_TIMEOUT / ALP_ERR_IO / ALP_ERR_NOSUPPORT. |
alp_usb_device_read(dev, data, len, out_len, timeout_ms) | ALP_OK / ALP_ERR_INVAL / ALP_ERR_NOT_READY / ALP_ERR_TIMEOUT / ALP_ERR_IO / ALP_ERR_NOSUPPORT. |
alp_usb_device_close(dev) | void (idempotent on NULL). |
Wrapped device_class values: ALP_USB_DEVICE_CDC_ACM (virtual COM), ALP_USB_DEVICE_MSC (mass storage), ALP_USB_DEVICE_HID. The config struct also carries bcd_device, manufacturer, and serial string fields.
USB host role
[ABI-EXPERIMENTAL] · new in v0.9
Alongside the device role, v0.9 adds an experimental host surface for enumerating attached devices (mass storage, keyboards). It is backed by an xHCI host skeleton on the AEN401 (Alif Ensemble E4); the code is compile-verified but live bring-up is bench-gated — not yet silicon-proven.
alp_usb_host_t *host = alp_usb_host_open(); // NULL + ALP_ERR_NOSUPPORT if no host stack
// wired, or ALP_ERR_BUSY if already opened
alp_usb_host_enable(host); // start the controller — enables enumeration
// ... attached devices enumerate ...
alp_usb_host_disable(host); // stop the controller
alp_usb_host_close(host); // release; idempotent on NULL
| Call | Returns |
|---|---|
alp_usb_host_open(void) | Handle, or NULL with ALP_ERR_NOSUPPORT (no host stack) / ALP_ERR_BUSY (already opened). |
alp_usb_host_enable(host) | ALP_OK / ALP_ERR_INVAL / ALP_ERR_NOSUPPORT / ALP_ERR_IO. |
alp_usb_host_disable(host) | ALP_OK / ALP_ERR_INVAL / ALP_ERR_NOSUPPORT. |
alp_usb_host_close(host) | void (idempotent on NULL). |
V2N / V2M Linux backend
The public API is unchanged on this backend. alp_usb_host_open() now does a real presence check of the root hub via /sys/bus/usb/devices — an open succeeds only if a root hub is actually there.
alp_usb_host_enable() and alp_usb_host_disable() return ALP_ERR_NOSUPPORT permanently. This is a deliberate, documented limitation, not a gap waiting to be filled: no Linux userspace ABI can start or stop an already-bound USB host controller. The kernel owns the controller; a userspace library cannot take it back. Do not write code that waits for these to start working.
Default-initialiser macro
ALP_USB_DEVICE_CONFIG_DEFAULT(id) (v0.10) takes the device class as its identity argument:
alp_usb_device_config_t cfg = ALP_USB_DEVICE_CONFIG_DEFAULT(ALP_USB_DEVICE_CDC_ACM);
cfg.vendor_id = 0x1209; // you must set a real VID/PID
cfg.product_id = 0x0001;
It sets device_class from id plus: vendor_id = 0x0000 and product_id = 0x0000 (both placeholders you must replace), bcd_device = 0x0100 (device release 1.00), manufacturer = NULL, product = NULL, serial = NULL (no string descriptors).
See the shared macro contract for the C++ compound-literal caveat that applies to every ALP_*_CONFIG_DEFAULT.
See also
<alp/peripheral.h>—alp_uart_*