usb/hv: add second CDC ACM interface for virtualized UART

Signed-off-by: Janne Grunau <j@jannau.net>
This commit is contained in:
Janne Grunau 2021-05-14 22:47:43 +02:00 committed by Hector Martin
parent 4a893dc57a
commit a742f05532
10 changed files with 313 additions and 77 deletions

View file

@ -443,7 +443,7 @@ class HV:
self.u.msr(APVMKEYHI_EL2, 0x697665596F755570) self.u.msr(APVMKEYHI_EL2, 0x697665596F755570)
self.u.msr(APSTS_EL12, 1) self.u.msr(APSTS_EL12, 1)
#self.p.hv_map_vuart(0x2_35200000) #self.p.hv_map_vuart(0x2_35200000, getattr(IODEV, self.iodev.name + "_SEC"))
actlr = ACTLR(self.u.mrs(ACTLR_EL12)) actlr = ACTLR(self.u.mrs(ACTLR_EL12))
actlr.EnMDSB = 1 actlr.EnMDSB = 1

View file

@ -370,6 +370,8 @@ class IODEV(IntEnum):
FB = 1 FB = 1
USB0 = 2 USB0 = 2
USB1 = 3 USB1 = 3
USB0_SEC = 4
USB1_SEC = 5
class USAGE(IntFlag): class USAGE(IntFlag):
CONSOLE = (1 << 0) CONSOLE = (1 << 0)
@ -850,8 +852,8 @@ class M1N1Proxy:
return self.request(self.P_HV_TRANSLATE, addr, s1, w) return self.request(self.P_HV_TRANSLATE, addr, s1, w)
def hv_pt_walk(self, addr): def hv_pt_walk(self, addr):
return self.request(self.P_HV_PT_WALK, addr) return self.request(self.P_HV_PT_WALK, addr)
def hv_map_vuart(self, base): def hv_map_vuart(self, base, iodev):
return self.request(self.P_HV_MAP_VUART, base) return self.request(self.P_HV_MAP_VUART, base, iodev)
def fb_init(self): def fb_init(self):
return self.request(self.P_FB_INIT) return self.request(self.P_FB_INIT)

View file

@ -3,6 +3,7 @@
#ifndef HV_H #ifndef HV_H
#define HV_H #define HV_H
#include "iodev.h"
#include "types.h" #include "types.h"
typedef bool(hv_hook_t)(u64 addr, u64 *val, bool write, int width); typedef bool(hv_hook_t)(u64 addr, u64 *val, bool write, int width);
@ -19,7 +20,7 @@ u64 hv_pt_walk(u64 addr);
bool hv_handle_dabort(u64 *regs); bool hv_handle_dabort(u64 *regs);
/* Virtual peripherals */ /* Virtual peripherals */
void hv_map_vuart(u64 base); void hv_map_vuart(u64 base, iodev_id_t iodev);
/* HV main */ /* HV main */
void hv_init(void); void hv_init(void);

View file

@ -1,26 +1,21 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
#include "hv.h" #include "hv.h"
#include "iodev.h"
#include "uart_regs.h" #include "uart_regs.h"
static iodev_id_t vuart_iodev;
bool handle_vuart(u64 addr, u64 *val, bool write, int width) bool handle_vuart(u64 addr, u64 *val, bool write, int width)
{ {
UNUSED(width); UNUSED(width);
static bool newline = true;
addr &= 0xfff; addr &= 0xfff;
if (write) { if (write) {
switch (addr) { switch (addr) {
case UTXH: { case UTXH: {
uint8_t b = *val; uint8_t b = *val;
if (newline) { if (iodev_can_write(vuart_iodev))
iodev_console_write("EL1> ", 5); iodev_write(vuart_iodev, &b, 1);
newline = false;
}
if (b == '\n')
newline = true;
iodev_console_write(&b, 1);
break; break;
} }
} }
@ -40,7 +35,8 @@ bool handle_vuart(u64 addr, u64 *val, bool write, int width)
return true; return true;
} }
void hv_map_vuart(u64 base) void hv_map_vuart(u64 base, iodev_id_t iodev)
{ {
hv_map_hook(base, handle_vuart, 0x1000); hv_map_hook(base, handle_vuart, 0x1000);
vuart_iodev = iodev;
} }

View file

@ -18,12 +18,12 @@
extern struct iodev iodev_uart; extern struct iodev iodev_uart;
extern struct iodev iodev_fb; extern struct iodev iodev_fb;
extern struct iodev iodev_usb[]; extern struct iodev iodev_usb[];
extern struct iodev iodev_usb_sec[];
struct iodev *iodevs[IODEV_MAX] = { struct iodev *iodevs[IODEV_MAX] = {
&iodev_uart, [IODEV_UART] = &iodev_uart, [IODEV_FB] = &iodev_fb,
&iodev_fb, [IODEV_USB0] = &iodev_usb[0], [IODEV_USB1] = &iodev_usb[1],
&iodev_usb[0], [IODEV_USB0_SEC] = &iodev_usb_sec[0], [IODEV_USB1_SEC] = &iodev_usb_sec[1],
&iodev_usb[1],
}; };
char con_buf[CONSOLE_BUFFER_SIZE]; char con_buf[CONSOLE_BUFFER_SIZE];

View file

@ -6,7 +6,15 @@
#include "types.h" #include "types.h"
#include "utils.h" #include "utils.h"
typedef enum _iodev_id_t { IODEV_UART, IODEV_FB, IODEV_USB0, IODEV_USB1, IODEV_MAX } iodev_id_t; typedef enum _iodev_id_t {
IODEV_UART,
IODEV_FB,
IODEV_USB0,
IODEV_USB1,
IODEV_USB0_SEC,
IODEV_USB1_SEC,
IODEV_MAX,
} iodev_id_t;
typedef enum _iodev_usage_t { typedef enum _iodev_usage_t {
USAGE_CONSOLE = BIT(0), USAGE_CONSOLE = BIT(0),

View file

@ -416,7 +416,7 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
reply->retval = hv_pt_walk(request->args[0]); reply->retval = hv_pt_walk(request->args[0]);
break; break;
case P_HV_MAP_VUART: case P_HV_MAP_VUART:
hv_map_vuart(request->args[0]); hv_map_vuart(request->args[0], request->args[1]);
break; break;
case P_FB_INIT: case P_FB_INIT:

View file

@ -149,12 +149,49 @@ dwc3_dev_t *usb_bringup(u32 idx)
return usb_dwc3_init(usb_regs.drd_regs, usb_dart); return usb_dwc3_init(usb_regs.drd_regs, usb_dart);
} }
#define USB_IODEV_WRAPPER(name, pipe) \
static bool usb_##name##_can_read(void *dev) \
{ \
return usb_dwc3_can_read(dev, pipe); \
} \
\
static bool usb_##name##_can_write(void *dev) \
{ \
return usb_dwc3_can_write(dev, pipe); \
} \
\
static ssize_t usb_##name##_read(void *dev, void *buf, size_t count) \
{ \
return usb_dwc3_read(dev, pipe, buf, count); \
} \
\
static ssize_t usb_##name##_write(void *dev, const void *buf, size_t count) \
{ \
return usb_dwc3_write(dev, pipe, buf, count); \
} \
\
static void usb_##name##_handle_events(void *dev) \
{ \
usb_dwc3_handle_events(dev); \
}
USB_IODEV_WRAPPER(0, CDC_ACM_PIPE_0)
USB_IODEV_WRAPPER(1, CDC_ACM_PIPE_1)
static struct iodev_ops iodev_usb_ops = { static struct iodev_ops iodev_usb_ops = {
.can_read = (bool (*)(void *))usb_dwc3_can_read, .can_read = usb_0_can_read,
.can_write = (bool (*)(void *))usb_dwc3_can_write, .can_write = usb_0_can_write,
.read = (ssize_t(*)(void *, void *, size_t))usb_dwc3_read, .read = usb_0_read,
.write = (ssize_t(*)(void *, const void *, size_t))usb_dwc3_write, .write = usb_0_write,
.handle_events = (void (*)(void *))usb_dwc3_handle_events, .handle_events = usb_0_handle_events,
};
static struct iodev_ops iodev_usb_sec_ops = {
.can_read = usb_1_can_read,
.can_write = usb_1_can_write,
.read = usb_1_read,
.write = usb_1_write,
.handle_events = usb_1_handle_events,
}; };
struct iodev iodev_usb[USB_INSTANCES] = { struct iodev iodev_usb[USB_INSTANCES] = {
@ -168,6 +205,17 @@ struct iodev iodev_usb[USB_INSTANCES] = {
}, },
}; };
struct iodev iodev_usb_sec[USB_INSTANCES] = {
{
.ops = &iodev_usb_sec_ops,
.usage = 0,
},
{
.ops = &iodev_usb_sec_ops,
.usage = 0,
},
};
void usb_init(void) void usb_init(void)
{ {
for (int i = 0; i < USB_INSTANCES; i++) { for (int i = 0; i < USB_INSTANCES; i++) {
@ -175,6 +223,8 @@ void usb_init(void)
if (!iodev_usb[i].opaque) if (!iodev_usb[i].opaque)
continue; continue;
iodev_usb_sec[i].opaque = iodev_usb[i].opaque;
printf("USB%d: initialized at %p\n", i, iodev_usb[i].opaque); printf("USB%d: initialized at %p\n", i, iodev_usb[i].opaque);
} }
} }
@ -187,5 +237,8 @@ void usb_shutdown(void)
printf("USB%d: shutdown\n", i); printf("USB%d: shutdown\n", i);
usb_dwc3_shutdown(iodev_usb[i].opaque); usb_dwc3_shutdown(iodev_usb[i].opaque);
iodev_usb[i].opaque = NULL;
iodev_usb_sec[i].opaque = NULL;
} }
} }

View file

@ -65,6 +65,13 @@
#define USB_LEP_CDC_BULK_OUT 4 #define USB_LEP_CDC_BULK_OUT 4
#define USB_LEP_CDC_BULK_IN 5 #define USB_LEP_CDC_BULK_IN 5
/* maps to interrupt endpoint 0x83 */
#define USB_LEP_CDC_INTR_IN_2 7
/* these map to physical endpoints 0x04 and 0x84 */
#define USB_LEP_CDC_BULK_OUT_2 8
#define USB_LEP_CDC_BULK_IN_2 9
/* content doesn't matter at all, this is the setting linux writes by default */ /* content doesn't matter at all, this is the setting linux writes by default */
static const u8 cdc_default_line_coding[] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08}; static const u8 cdc_default_line_coding[] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08};
@ -112,8 +119,13 @@ typedef struct dwc3_dev {
/* USB ACM CDC serial */ /* USB ACM CDC serial */
u8 cdc_line_coding[7]; u8 cdc_line_coding[7];
ringbuffer_t *host2device; struct {
ringbuffer_t *device2host; ringbuffer_t *host2device;
ringbuffer_t *device2host;
u8 ep_intr;
u8 ep_in;
u8 ep_out;
} pipe[CDC_ACM_PIPE_MAX];
bool ready; bool ready;
} dwc3_dev_t; } dwc3_dev_t;
@ -138,6 +150,12 @@ struct cdc_dev_desc {
const struct usb_interface_descriptor interface_data; const struct usb_interface_descriptor interface_data;
const struct usb_endpoint_descriptor endpoint_data_in; const struct usb_endpoint_descriptor endpoint_data_in;
const struct usb_endpoint_descriptor endpoint_data_out; const struct usb_endpoint_descriptor endpoint_data_out;
const struct usb_interface_descriptor sec_interface_management;
const struct cdc_union_functional_descriptor sec_cdc_union_func;
const struct usb_endpoint_descriptor sec_endpoint_notification;
const struct usb_interface_descriptor sec_interface_data;
const struct usb_endpoint_descriptor sec_endpoint_data_in;
const struct usb_endpoint_descriptor sec_endpoint_data_out;
} PACKED; } PACKED;
static const struct usb_device_descriptor usb_cdc_device_descriptor = { static const struct usb_device_descriptor usb_cdc_device_descriptor = {
@ -163,7 +181,7 @@ static const struct cdc_dev_desc cdc_configuration_descriptor = {
.bLength = sizeof(cdc_configuration_descriptor.configuration), .bLength = sizeof(cdc_configuration_descriptor.configuration),
.bDescriptorType = USB_CONFIGURATION_DESCRIPTOR, .bDescriptorType = USB_CONFIGURATION_DESCRIPTOR,
.wTotalLength = sizeof(cdc_configuration_descriptor), .wTotalLength = sizeof(cdc_configuration_descriptor),
.bNumInterfaces = 2, .bNumInterfaces = 4,
.bConfigurationValue = 1, .bConfigurationValue = 1,
.iConfiguration = 0, .iConfiguration = 0,
.bmAttributes = USB_CONFIGURATION_ATTRIBUTE_RES1 | USB_CONFIGURATION_SELF_POWERED, .bmAttributes = USB_CONFIGURATION_ATTRIBUTE_RES1 | USB_CONFIGURATION_SELF_POWERED,
@ -236,6 +254,77 @@ static const struct cdc_dev_desc cdc_configuration_descriptor = {
.wMaxPacketSize = 512, .wMaxPacketSize = 512,
.bInterval = 10, .bInterval = 10,
}, },
/*
* CDC ACM interface for virtual uart
*/
.sec_interface_management =
{
.bLength = sizeof(cdc_configuration_descriptor.sec_interface_management),
.bDescriptorType = USB_INTERFACE_DESCRIPTOR,
.bInterfaceNumber = 2,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = CDC_INTERFACE_CLASS,
.bInterfaceSubClass = CDC_INTERFACE_SUBCLASS_ACM,
.bInterfaceProtocol = CDC_INTERFACE_PROTOCOL_NONE,
.iInterface = 0,
},
.sec_cdc_union_func =
{
.bFunctionLength = sizeof(cdc_configuration_descriptor.sec_cdc_union_func),
.bDescriptorType = USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR,
.bDescriptorSubtype = USB_CDC_UNION_SUBTYPE,
.bControlInterface = 2,
.bDataInterface = 3,
},
/*
* we never use this endpoint, but it should exist and always be idle.
* it needs to exist in the descriptor though to make hosts correctly recognize
* us as a ACM CDC device.
*/
.sec_endpoint_notification =
{
.bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_notification),
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
.bEndpointAddress = USB_ENDPOINT_ADDR_IN(3),
.bmAttributes = USB_ENDPOINT_ATTR_TYPE_INTERRUPT,
.wMaxPacketSize = 64,
.bInterval = 10,
},
.sec_interface_data =
{
.bLength = sizeof(cdc_configuration_descriptor.sec_interface_data),
.bDescriptorType = USB_INTERFACE_DESCRIPTOR,
.bInterfaceNumber = 3,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = CDC_INTERFACE_CLASS_DATA,
.bInterfaceSubClass = 0, // unused
.bInterfaceProtocol = 0, // unused
.iInterface = 0,
},
.sec_endpoint_data_in =
{
.bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_data_in),
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
.bEndpointAddress = USB_ENDPOINT_ADDR_OUT(4),
.bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK,
.wMaxPacketSize = 512,
.bInterval = 10,
},
.sec_endpoint_data_out =
{
.bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_data_out),
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
.bEndpointAddress = USB_ENDPOINT_ADDR_IN(4),
.bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK,
.wMaxPacketSize = 512,
.bInterval = 10,
},
}; };
static const struct usb_device_qualifier_descriptor usb_cdc_device_qualifier_descriptor = { static const struct usb_device_qualifier_descriptor usb_cdc_device_qualifier_descriptor = {
@ -508,6 +597,9 @@ static void usb_dwc3_ep0_handle_standard_device(dwc3_dev_t *dev,
clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT));
clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN));
clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN));
clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT_2));
clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN_2));
clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN_2));
dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS; dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS;
dev->ready = false; dev->ready = false;
break; break;
@ -517,6 +609,9 @@ static void usb_dwc3_ep0_handle_standard_device(dwc3_dev_t *dev,
set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT));
set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN));
set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN));
set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT_2));
set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN_2));
set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN_2));
dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS; dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS;
break; break;
default: default:
@ -733,6 +828,22 @@ static void usb_dwc3_ep0_handle_xfer_not_ready(dwc3_dev_t *dev,
} }
} }
ringbuffer_t *usb_dwc3_cdc_get_ringbuffer(dwc3_dev_t *dev, u8 endpoint_number)
{
switch (endpoint_number) {
case USB_LEP_CDC_BULK_IN:
return dev->pipe[CDC_ACM_PIPE_0].device2host;
case USB_LEP_CDC_BULK_OUT:
return dev->pipe[CDC_ACM_PIPE_0].host2device;
case USB_LEP_CDC_BULK_IN_2:
return dev->pipe[CDC_ACM_PIPE_1].device2host;
case USB_LEP_CDC_BULK_OUT_2:
return dev->pipe[CDC_ACM_PIPE_1].host2device;
default:
return NULL;
}
}
static void usb_dwc3_cdc_start_bulk_out_xfer(dwc3_dev_t *dev, u8 endpoint_number) static void usb_dwc3_cdc_start_bulk_out_xfer(dwc3_dev_t *dev, u8 endpoint_number)
{ {
struct dwc3_trb *trb; struct dwc3_trb *trb;
@ -741,7 +852,11 @@ static void usb_dwc3_cdc_start_bulk_out_xfer(dwc3_dev_t *dev, u8 endpoint_number
if (dev->endpoints[endpoint_number].xfer_in_progress) if (dev->endpoints[endpoint_number].xfer_in_progress)
return; return;
if (ringbuffer_get_free(dev->host2device) < 512) ringbuffer_t *host2device = usb_dwc3_cdc_get_ringbuffer(dev, endpoint_number);
if (!host2device)
return;
if (ringbuffer_get_free(host2device) < 512)
return; return;
memset(dev->endpoints[endpoint_number].xfer_buffer, 0xaa, 512); memset(dev->endpoints[endpoint_number].xfer_buffer, 0xaa, 512);
@ -761,8 +876,11 @@ static void usb_dwc3_cdc_start_bulk_in_xfer(dwc3_dev_t *dev, u8 endpoint_number)
if (dev->endpoints[endpoint_number].xfer_in_progress) if (dev->endpoints[endpoint_number].xfer_in_progress)
return; return;
size_t len = ringbuffer_t *device2host = usb_dwc3_cdc_get_ringbuffer(dev, endpoint_number);
ringbuffer_read(dev->endpoints[endpoint_number].xfer_buffer, 512, dev->device2host); if (!device2host)
return;
size_t len = ringbuffer_read(dev->endpoints[endpoint_number].xfer_buffer, 512, device2host);
if (!len) if (!len)
return; return;
@ -778,9 +896,12 @@ static void usb_dwc3_cdc_start_bulk_in_xfer(dwc3_dev_t *dev, u8 endpoint_number)
static void usb_dwc3_cdc_handle_bulk_out_xfer_done(dwc3_dev_t *dev, static void usb_dwc3_cdc_handle_bulk_out_xfer_done(dwc3_dev_t *dev,
const struct dwc3_event_depevt event) const struct dwc3_event_depevt event)
{ {
size_t len = min(512, ringbuffer_get_free(dev->host2device)); ringbuffer_t *host2device = usb_dwc3_cdc_get_ringbuffer(dev, event.endpoint_number);
if (!host2device)
return;
size_t len = min(512, ringbuffer_get_free(host2device));
ringbuffer_write(dev->endpoints[event.endpoint_number].xfer_buffer, ringbuffer_write(dev->endpoints[event.endpoint_number].xfer_buffer,
len - dev->endpoints[event.endpoint_number].trb->size, dev->host2device); len - dev->endpoints[event.endpoint_number].trb->size, host2device);
} }
static void usb_dwc3_handle_event_ep(dwc3_dev_t *dev, const struct dwc3_event_depevt event) static void usb_dwc3_handle_event_ep(dwc3_dev_t *dev, const struct dwc3_event_depevt event)
@ -792,11 +913,14 @@ static void usb_dwc3_handle_event_ep(dwc3_dev_t *dev, const struct dwc3_event_de
case USB_LEP_CTRL_IN: case USB_LEP_CTRL_IN:
case USB_LEP_CTRL_OUT: case USB_LEP_CTRL_OUT:
return usb_dwc3_ep0_handle_xfer_done(dev, event); return usb_dwc3_ep0_handle_xfer_done(dev, event);
case USB_LEP_CDC_INTR_IN: case USB_LEP_CDC_INTR_IN: // [[fallthrough]]
case USB_LEP_CDC_INTR_IN_2:
return; return;
case USB_LEP_CDC_BULK_IN: case USB_LEP_CDC_BULK_IN: // [[fallthrough]]
case USB_LEP_CDC_BULK_IN_2:
return; return;
case USB_LEP_CDC_BULK_OUT: case USB_LEP_CDC_BULK_OUT: // [[fallthrough]]
case USB_LEP_CDC_BULK_OUT_2:
return usb_dwc3_cdc_handle_bulk_out_xfer_done(dev, event); return usb_dwc3_cdc_handle_bulk_out_xfer_done(dev, event);
} }
} else if (event.endpoint_event == DWC3_DEPEVT_XFERNOTREADY) { } else if (event.endpoint_event == DWC3_DEPEVT_XFERNOTREADY) {
@ -811,11 +935,14 @@ static void usb_dwc3_handle_event_ep(dwc3_dev_t *dev, const struct dwc3_event_de
case USB_LEP_CTRL_IN: case USB_LEP_CTRL_IN:
case USB_LEP_CTRL_OUT: case USB_LEP_CTRL_OUT:
return usb_dwc3_ep0_handle_xfer_not_ready(dev, event); return usb_dwc3_ep0_handle_xfer_not_ready(dev, event);
case USB_LEP_CDC_INTR_IN: case USB_LEP_CDC_INTR_IN: // [[fallthrough]]
case USB_LEP_CDC_INTR_IN_2:
return; return;
case USB_LEP_CDC_BULK_IN: case USB_LEP_CDC_BULK_IN: // [[fallthrough]]
case USB_LEP_CDC_BULK_IN_2:
return usb_dwc3_cdc_start_bulk_in_xfer(dev, event.endpoint_number); return usb_dwc3_cdc_start_bulk_in_xfer(dev, event.endpoint_number);
case USB_LEP_CDC_BULK_OUT: case USB_LEP_CDC_BULK_OUT: // [[fallthrough]]
case USB_LEP_CDC_BULK_OUT_2:
return usb_dwc3_cdc_start_bulk_out_xfer(dev, event.endpoint_number); return usb_dwc3_cdc_start_bulk_out_xfer(dev, event.endpoint_number);
} }
} }
@ -924,13 +1051,6 @@ dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart)
dev->regs = regs; dev->regs = regs;
dev->dart = dart; dev->dart = dart;
dev->host2device = ringbuffer_alloc(CDC_BUFFER_SIZE);
if (!dev->host2device)
goto error;
dev->device2host = ringbuffer_alloc(CDC_BUFFER_SIZE);
if (!dev->device2host)
goto error;
/* allocate and map dma buffers */ /* allocate and map dma buffers */
dev->evtbuffer = memalign(SZ_16K, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K)); dev->evtbuffer = memalign(SZ_16K, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K));
if (!dev->evtbuffer) if (!dev->evtbuffer)
@ -1033,15 +1153,34 @@ dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart)
if (usb_dwc3_ep_configure(dev, USB_LEP_CTRL_IN, DWC3_DEPCMD_TYPE_CONTROL, 64)) if (usb_dwc3_ep_configure(dev, USB_LEP_CTRL_IN, DWC3_DEPCMD_TYPE_CONTROL, 64))
goto error; goto error;
/* prepare INTR endpoint so that we don't have to reconfigure this device later */ /* prepare CDC ACM interfaces */
if (usb_dwc3_ep_configure(dev, USB_LEP_CDC_INTR_IN, DWC3_DEPCMD_TYPE_INTR, 64))
goto error;
/* prepare BULK endpoints so that we don't have to reconfigure this device later */ dev->pipe[CDC_ACM_PIPE_0].ep_intr = USB_LEP_CDC_INTR_IN;
if (usb_dwc3_ep_configure(dev, USB_LEP_CDC_BULK_OUT, DWC3_DEPCMD_TYPE_BULK, 512)) dev->pipe[CDC_ACM_PIPE_0].ep_in = USB_LEP_CDC_BULK_IN;
goto error; dev->pipe[CDC_ACM_PIPE_0].ep_out = USB_LEP_CDC_BULK_OUT;
if (usb_dwc3_ep_configure(dev, USB_LEP_CDC_BULK_IN, DWC3_DEPCMD_TYPE_BULK, 512))
goto error; dev->pipe[CDC_ACM_PIPE_1].ep_intr = USB_LEP_CDC_INTR_IN_2;
dev->pipe[CDC_ACM_PIPE_1].ep_in = USB_LEP_CDC_BULK_IN_2;
dev->pipe[CDC_ACM_PIPE_1].ep_out = USB_LEP_CDC_BULK_OUT_2;
for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) {
dev->pipe[i].host2device = ringbuffer_alloc(CDC_BUFFER_SIZE);
if (!dev->pipe[i].host2device)
goto error;
dev->pipe[i].device2host = ringbuffer_alloc(CDC_BUFFER_SIZE);
if (!dev->pipe[i].device2host)
goto error;
/* prepare INTR endpoint so that we don't have to reconfigure this device later */
if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_intr, DWC3_DEPCMD_TYPE_INTR, 64))
goto error;
/* prepare BULK endpoints so that we don't have to reconfigure this device later */
if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_in, DWC3_DEPCMD_TYPE_BULK, 512))
goto error;
if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_out, DWC3_DEPCMD_TYPE_BULK, 512))
goto error;
}
/* prepare first control transfer */ /* prepare first control transfer */
dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
@ -1097,30 +1236,44 @@ void usb_dwc3_shutdown(dwc3_dev_t *dev)
free(dev->scratchpad); free(dev->scratchpad);
free(dev->xferbuffer); free(dev->xferbuffer);
free(dev->trbs); free(dev->trbs);
ringbuffer_free(dev->host2device); for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) {
ringbuffer_free(dev->device2host); ringbuffer_free(dev->pipe[i].device2host);
ringbuffer_free(dev->pipe[i].host2device);
}
free(dev); free(dev);
} }
u8 usb_dwc3_getbyte(dwc3_dev_t *dev) u8 usb_dwc3_getbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
{ {
ringbuffer_t *host2device = dev->pipe[pipe].host2device;
if (!host2device)
return 0;
u8 ep = dev->pipe[pipe].ep_out;
u8 c; u8 c;
while (ringbuffer_read(&c, 1, dev->host2device) < 1) { while (ringbuffer_read(&c, 1, host2device) < 1) {
usb_dwc3_handle_events(dev); usb_dwc3_handle_events(dev);
usb_dwc3_cdc_start_bulk_out_xfer(dev, USB_LEP_CDC_BULK_OUT); usb_dwc3_cdc_start_bulk_out_xfer(dev, ep);
} }
return c; return c;
} }
void usb_dwc3_putbyte(dwc3_dev_t *dev, u8 byte) void usb_dwc3_putbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, u8 byte)
{ {
while (ringbuffer_write(&byte, 1, dev->device2host) < 1) { ringbuffer_t *device2host = dev->pipe[pipe].device2host;
if (!device2host)
return;
u8 ep = dev->pipe[pipe].ep_in;
while (ringbuffer_write(&byte, 1, device2host) < 1) {
usb_dwc3_handle_events(dev); usb_dwc3_handle_events(dev);
usb_dwc3_cdc_start_bulk_in_xfer(dev, USB_LEP_CDC_BULK_IN); usb_dwc3_cdc_start_bulk_in_xfer(dev, ep);
} }
} }
size_t usb_dwc3_write(dwc3_dev_t *dev, const void *buf, size_t count) size_t usb_dwc3_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count)
{ {
const u8 *p = buf; const u8 *p = buf;
size_t wrote, sent = 0; size_t wrote, sent = 0;
@ -1128,19 +1281,25 @@ size_t usb_dwc3_write(dwc3_dev_t *dev, const void *buf, size_t count)
if (!dev || !dev->ready) if (!dev || !dev->ready)
return 0; return 0;
ringbuffer_t *device2host = dev->pipe[pipe].device2host;
if (!device2host)
return 0;
u8 ep = dev->pipe[pipe].ep_in;
while (count) { while (count) {
wrote = ringbuffer_write(p, count, dev->device2host); wrote = ringbuffer_write(p, count, device2host);
count -= wrote; count -= wrote;
p += wrote; p += wrote;
sent += wrote; sent += wrote;
usb_dwc3_handle_events(dev); usb_dwc3_handle_events(dev);
usb_dwc3_cdc_start_bulk_in_xfer(dev, USB_LEP_CDC_BULK_IN); usb_dwc3_cdc_start_bulk_in_xfer(dev, ep);
} }
return sent; return sent;
} }
size_t usb_dwc3_read(dwc3_dev_t *dev, void *buf, size_t count) size_t usb_dwc3_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, void *buf, size_t count)
{ {
u8 *p = buf; u8 *p = buf;
size_t read, recvd = 0; size_t read, recvd = 0;
@ -1148,28 +1307,39 @@ size_t usb_dwc3_read(dwc3_dev_t *dev, void *buf, size_t count)
if (!dev || !dev->ready) if (!dev || !dev->ready)
return 0; return 0;
ringbuffer_t *host2device = dev->pipe[pipe].host2device;
if (!host2device)
return 0;
u8 ep = dev->pipe[pipe].ep_out;
while (count) { while (count) {
read = ringbuffer_read(p, count, dev->host2device); read = ringbuffer_read(p, count, host2device);
count -= read; count -= read;
p += read; p += read;
recvd += read; recvd += read;
usb_dwc3_handle_events(dev); usb_dwc3_handle_events(dev);
usb_dwc3_cdc_start_bulk_out_xfer(dev, USB_LEP_CDC_BULK_OUT); usb_dwc3_cdc_start_bulk_out_xfer(dev, ep);
} }
return recvd; return recvd;
} }
bool usb_dwc3_can_read(dwc3_dev_t *dev) bool usb_dwc3_can_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
{ {
if (!dev || !dev->ready) if (!dev || !dev->ready)
return false; return false;
return ringbuffer_get_used(dev->host2device); ringbuffer_t *host2device = dev->pipe[pipe].host2device;
if (!host2device)
return false;
return ringbuffer_get_used(host2device);
} }
bool usb_dwc3_can_write(dwc3_dev_t *dev) bool usb_dwc3_can_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
{ {
(void)pipe;
if (!dev) if (!dev)
return false; return false;

View file

@ -8,18 +8,24 @@
typedef struct dwc3_dev dwc3_dev_t; typedef struct dwc3_dev dwc3_dev_t;
typedef enum _cdc_acm_pipe_id_t {
CDC_ACM_PIPE_0,
CDC_ACM_PIPE_1,
CDC_ACM_PIPE_MAX
} cdc_acm_pipe_id_t;
dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart); dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart);
void usb_dwc3_shutdown(dwc3_dev_t *dev); void usb_dwc3_shutdown(dwc3_dev_t *dev);
void usb_dwc3_handle_events(dwc3_dev_t *dev); void usb_dwc3_handle_events(dwc3_dev_t *dev);
bool usb_dwc3_can_read(dwc3_dev_t *dev); bool usb_dwc3_can_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
bool usb_dwc3_can_write(dwc3_dev_t *dev); bool usb_dwc3_can_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
u8 usb_dwc3_getbyte(dwc3_dev_t *dev); u8 usb_dwc3_getbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
void usb_dwc3_putbyte(dwc3_dev_t *dev, u8 byte); void usb_dwc3_putbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, u8 byte);
size_t usb_dwc3_write(dwc3_dev_t *dev, const void *buf, size_t count); size_t usb_dwc3_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count);
size_t usb_dwc3_read(dwc3_dev_t *dev, void *buf, size_t count); size_t usb_dwc3_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, void *buf, size_t count);
#endif #endif