Comparar commits

...

3 Commits

Autor SHA1 Mensagem Data
Mike Voydanoff 129f0eadf2 xhci pdev wip
Change-Id: I2b489d8b50078407a12d6f028ad514bd566e447d
2017-09-08 07:24:12 -07:00
Mike Voydanoff 2cd57373c3 [dev][usb][dwc3] First cut at DWC3 USB controller driver
Implements basic peripheral mode support for DWC3

Still to do for peripheral mode:
- USB 3 support
- Isochronous endpoint support
- Scatter/gather support
- OTG support for better connect/disconnect detection

and beyond peripheral mode:
- USB host support (we will layer the XHCI driver on top of this driver for host support)
- role switching between host and peripheral mode
- power management

Change-Id: I028f7af18f4434e17e1fe5fe36f0d1a83a409e55
2017-09-08 07:24:02 -07:00
Mike Voydanoff 1ac867dc05 [dev][cdc-eth-function] Introduce CDC ethernet USB function driver
This driver implements CDC ethernet for USB peripheral mode

Change-Id: Ie12421d0dc24249fe014881b41c37ecab286d6e1
2017-09-08 07:23:53 -07:00
14 arquivos alterados com 2833 adições e 25 exclusões
@@ -0,0 +1,634 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/iotxn.h>
#include <ddk/protocol/ethernet.h>
#include <ddk/protocol/usb-function.h>
#include <magenta/listnode.h>
#include <magenta/process.h>
#include <magenta/syscalls.h>
#include <magenta/device/usb-device.h>
#include <magenta/hw/usb-cdc.h>
#define BULK_TXN_SIZE 2048
#define BULK_TX_COUNT 16
#define BULK_RX_COUNT 16
#define BULK_MAX_PACKET 512 // FIXME(voydanoff) USB 3.0 support
#define INTR_MAX_PACKET 16
#define CDC_BITRATE 1000000000 // say we are gigabit
typedef struct {
mx_device_t* mxdev;
usb_function_protocol_t function;
list_node_t bulk_out_txns;
list_node_t bulk_in_txns;
// Device attributes
uint8_t mac_addr[ETH_MAC_SIZE];
uint16_t mtu;
mtx_t ethmac_mutex;
ethmac_ifc_t* ethmac_ifc;
void* ethmac_cookie;
bool online;
mtx_t tx_mutex;
mtx_t rx_mutex;
uint8_t bulk_out_addr;
uint8_t bulk_in_addr;
uint8_t intr_addr;
uint16_t bulk_max_packet;
} usb_cdc_t;
static struct {
usb_interface_descriptor_t comm_intf;
usb_cs_header_interface_descriptor_t cdc_header;
usb_cs_union_interface_descriptor_1_t cdc_union;
usb_cs_ethernet_interface_descriptor_t cdc_eth;
usb_endpoint_descriptor_t intr_ep;
usb_interface_descriptor_t cdc_intf_0;
usb_interface_descriptor_t cdc_intf_1;
usb_endpoint_descriptor_t bulk_out_ep;
usb_endpoint_descriptor_t bulk_in_ep;
} descriptors = {
.comm_intf = {
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
// .bInterfaceNumber set later
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
.bInterfaceProtocol = 0,
.iInterface = 0,
},
.cdc_header = {
.bLength = sizeof(usb_cs_header_interface_descriptor_t),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_DST_HEADER,
.bcdCDC = 0x110,
},
.cdc_union = {
.bLength = sizeof(usb_cs_union_interface_descriptor_1_t),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_DST_UNION,
// .bControlInterface set later
// .bSubordinateInterface set later
},
.cdc_eth = {
.bLength = sizeof(usb_cs_ethernet_interface_descriptor_t),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_DST_ETHERNET,
// .iMACAddress filled in later
.bmEthernetStatistics = 0,
.wMaxSegmentSize = 1514,
.wNumberMCFilters = 0,
.bNumberPowerFilters = 0,
},
.intr_ep = {
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
// .bEndpointAddress set later
.bmAttributes = USB_ENDPOINT_INTERRUPT,
.wMaxPacketSize = htole16(INTR_MAX_PACKET),
.bInterval = 8,
},
.cdc_intf_0 = {
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
// .bInterfaceNumber set later
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
},
.cdc_intf_1 = {
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
// .bInterfaceNumber set later
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
},
.bulk_out_ep = {
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
// .bEndpointAddress set later
.bmAttributes = USB_ENDPOINT_BULK,
.wMaxPacketSize = htole16(BULK_MAX_PACKET),
.bInterval = 0,
},
.bulk_in_ep = {
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
// .bEndpointAddress set later
.bmAttributes = USB_ENDPOINT_BULK,
.wMaxPacketSize = htole16(BULK_MAX_PACKET),
.bInterval = 0,
},
};
static mx_status_t cdc_generate_mac_address(usb_cdc_t* cdc) {
size_t actual;
mx_status_t status = mx_cprng_draw(cdc->mac_addr, sizeof(cdc->mac_addr), &actual);
if (status != MX_OK) {
dprintf(ERROR, "cdc_generate_mac_address: mx_cprng_draw failed\n");
return status;
}
// set most significant byte so we are using a locally managed address
// TODO(voydanoff) add a way to configure a real MAC address here
cdc->mac_addr[0] = 0x02;
char buffer[sizeof(cdc->mac_addr) * 3];
snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X",
cdc->mac_addr[0], cdc->mac_addr[1], cdc->mac_addr[2],
cdc->mac_addr[3], cdc->mac_addr[4], cdc->mac_addr[5]);
return usb_function_alloc_string_desc(&cdc->function, buffer, &descriptors.cdc_eth.iMACAddress);
}
static mx_status_t cdc_ethmac_query(void* ctx, uint32_t options, ethmac_info_t* info) {
dprintf(TRACE, "%s: cdc_ethmac_query\n");
usb_cdc_t* cdc = ctx;
// No options are supported
if (options) {
dprintf(ERROR, "cdc_ethmac_query: unexpected options (0x%"PRIx32") to ethmac_query\n",
options);
return MX_ERR_INVALID_ARGS;
}
memset(info, 0, sizeof(*info));
info->mtu = cdc->mtu;
memcpy(info->mac, cdc->mac_addr, sizeof(cdc->mac_addr));
return MX_OK;
}
static void cdc_ethmac_stop(void* cookie) {
dprintf(TRACE, "%s: cdc_ethmac_stop\n");
usb_cdc_t* cdc = cookie;
mtx_lock(&cdc->ethmac_mutex);
cdc->ethmac_ifc = NULL;
mtx_unlock(&cdc->ethmac_mutex);
}
static mx_status_t cdc_ethmac_start(void* ctx_cookie, ethmac_ifc_t* ifc, void* ethmac_cookie) {
dprintf(TRACE, "%s: cdc_ethmac_start\n");
usb_cdc_t* cdc = ctx_cookie;
mx_status_t status = MX_OK;
mtx_lock(&cdc->ethmac_mutex);
if (cdc->ethmac_ifc) {
status = MX_ERR_ALREADY_BOUND;
} else {
cdc->ethmac_ifc = ifc;
cdc->ethmac_cookie = ethmac_cookie;
cdc->ethmac_ifc->status(ethmac_cookie, cdc->online ? ETH_STATUS_ONLINE : 0);
}
mtx_unlock(&cdc->ethmac_mutex);
return status;
}
static void cdc_ethmac_send(void* cookie, uint32_t options, void* data, size_t length) {
usb_cdc_t* cdc = cookie;
uint8_t* byte_data = data;
if (!cdc->online || length > cdc->mtu || length == 0) {
return;
}
dprintf(LTRACE, "ethmac_send: sending %d bytes\n", length);
mtx_lock(&cdc->tx_mutex);
// Make sure that we can get all of the tx buffers we need to use
iotxn_t* tx_req = list_remove_head_type(&cdc->bulk_in_txns, iotxn_t, node);
if (tx_req == NULL) {
dprintf(LINFO, "ethmac_send: no free write txns, dropping packet\n");
mtx_unlock(&cdc->tx_mutex);
return;
}
// As per the CDC-ECM spec, we need to send a zero-length packet to signify the end of
// transmission when the endpoint max packet size is a factor of the total transmission size.
iotxn_t* zlp_txn = NULL;
if (length % cdc->bulk_max_packet == 0) {
zlp_txn = list_remove_head_type(&cdc->bulk_in_txns, iotxn_t, node);
if (zlp_txn == NULL) {
dprintf(LINFO, "cdc_ethmac_send: no free write txns, dropping packet\n");
list_add_tail(&cdc->bulk_in_txns, &tx_req->node);
mtx_unlock(&cdc->tx_mutex);
return;
}
zlp_txn->length = 0;
}
// Send data
tx_req->length = length;
ssize_t bytes_copied = iotxn_copyto(tx_req, byte_data, tx_req->length, 0);
if (bytes_copied < 0) {
dprintf(LERROR, "cdc_ethmac_send: failed to copy data into send txn (error %zd)\n",
bytes_copied);
list_add_tail(&cdc->bulk_in_txns, &tx_req->node);
if (zlp_txn) {
list_add_tail(&cdc->bulk_in_txns, &zlp_txn->node);
}
mtx_unlock(&cdc->tx_mutex);
return;
}
// unlock before queueing txns to avoid potential deadlocks
mtx_unlock(&cdc->tx_mutex);
usb_function_queue(&cdc->function, tx_req, cdc->bulk_in_addr);
// Send zero-length terminal packet, if needed
if (zlp_txn) {
usb_function_queue(&cdc->function, zlp_txn, cdc->bulk_in_addr);
}
}
static ethmac_protocol_ops_t ethmac_ops = {
.query = cdc_ethmac_query,
.stop = cdc_ethmac_stop,
.start = cdc_ethmac_start,
.send = cdc_ethmac_send,
};
static void cdc_intr_complete(iotxn_t* txn, void* cookie) {
dprintf(TRACE, "cdc_intr_complete %d %ld\n", txn->status, txn->actual);
iotxn_release(txn);
}
static mx_status_t cdc_alloc_interrupt_txn(usb_cdc_t* cdc, iotxn_t** out_txn) {
iotxn_t* txn;
mx_status_t status = iotxn_alloc(&txn, 0, INTR_MAX_PACKET);
if (status != MX_OK) {
dprintf(ERROR, "cdc_alloc_interrupt_txn iotxn_alloc failed %d\n", status);
return status;
}
txn->complete_cb = cdc_intr_complete;
txn->cookie = cdc;
*out_txn = txn;
return MX_OK;
}
// sends network connection and speed change notifications on the interrupt endpoint
// we only do this once per USB connect, so instead of pooling iotxns we just allocate
// them here and release them when they complete.
static mx_status_t cdc_send_notifications(usb_cdc_t* cdc) {
iotxn_t* txn;
mx_status_t status;
usb_cdc_notification_t network_notification = {
.bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
.bNotification = USB_CDC_NC_NETWORK_CONNECTION,
.wValue = 1, // online
.wIndex = descriptors.cdc_intf_0.bInterfaceNumber,
.wLength = 0,
};
usb_cdc_speed_change_notification_t speed_notification = {
.notification = {
.bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
.bNotification = USB_CDC_NC_CONNECTION_SPEED_CHANGE,
.wValue = 0,
.wIndex = descriptors.cdc_intf_0.bInterfaceNumber,
.wLength = 0,
},
.downlink_br = CDC_BITRATE,
.uplink_br = CDC_BITRATE,
};
status = cdc_alloc_interrupt_txn(cdc, &txn);
if (status != MX_OK) return status;
iotxn_copyto(txn, &network_notification, sizeof(network_notification), 0);
txn->length = sizeof(network_notification);
usb_function_queue(&cdc->function, txn, cdc->intr_addr);
status = cdc_alloc_interrupt_txn(cdc, &txn);
if (status != MX_OK) return status;
iotxn_copyto(txn, &speed_notification, sizeof(speed_notification), 0);
txn->length = sizeof(speed_notification);
usb_function_queue(&cdc->function, txn, cdc->intr_addr);
return MX_OK;
}
static void cdc_rx_complete(iotxn_t* txn, void* cookie) {
usb_cdc_t* cdc = cookie;
dprintf(LTRACE, "cdc_rx_complete %d %ld\n", txn->status, txn->actual);
if (txn->status == MX_ERR_IO_NOT_PRESENT) {
mtx_lock(&cdc->rx_mutex);
list_add_head(&cdc->bulk_out_txns, &txn->node);
mtx_unlock(&cdc->rx_mutex);
return;
}
if (txn->status != MX_OK) {
dprintf(ERROR, "cdc_rx_complete: usb_read_complete called with status %d\n", txn->status);
}
if (txn->status == MX_OK) {
mtx_lock(&cdc->ethmac_mutex);
if (cdc->ethmac_ifc) {
uint8_t* data = NULL;
iotxn_mmap(txn, (void*)&data);
cdc->ethmac_ifc->recv(cdc->ethmac_cookie, data, txn->actual, 0);
}
mtx_unlock(&cdc->ethmac_mutex);
}
usb_function_queue(&cdc->function, txn, cdc->bulk_out_addr);
}
static void cdc_tx_complete(iotxn_t* txn, void* cookie) {
usb_cdc_t* cdc = cookie;
dprintf(LTRACE, "cdc_tx_complete %d %ld\n", txn->status, txn->actual);
mtx_lock(&cdc->tx_mutex);
list_add_tail(&cdc->bulk_in_txns, &txn->node);
mtx_unlock(&cdc->tx_mutex);
}
static const usb_descriptor_header_t* cdc_get_descriptors(void* ctx, size_t* out_length) {
*out_length = sizeof(descriptors);
return (const usb_descriptor_header_t *)&descriptors;
}
static mx_status_t cdc_control(void* ctx, const usb_setup_t* setup, void* buffer,
size_t length, size_t* out_actual) {
*out_actual = 0;
dprintf(TRACE, "cdc_control\n");
// USB_CDC_SET_ETHERNET_PACKET_FILTER is the only control request required by the spec
if (setup->bmRequestType == (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) &&
setup->bRequest == USB_CDC_SET_ETHERNET_PACKET_FILTER) {
dprintf(TRACE, "USB_CDC_SET_ETHERNET_PACKET_FILTER\n");
// TODO(voydanoff) implement the requested packet filtering
return MX_OK;
}
return MX_ERR_NOT_SUPPORTED;
}
static mx_status_t cdc_set_configured(void* ctx, bool configured, usb_speed_t speed) {
dprintf(TRACE, "cdc_set_configured %d %d\n", configured, speed);
usb_cdc_t* cdc = ctx;
mx_status_t status;
mtx_lock(&cdc->ethmac_mutex);
cdc->online = false;
if (cdc->ethmac_ifc) {
cdc->ethmac_ifc->status(cdc->ethmac_cookie, 0);
}
mtx_unlock(&cdc->ethmac_mutex);
if (configured) {
if ((status = usb_function_config_ep(&cdc->function, &descriptors.intr_ep, NULL)) != MX_OK) {
dprintf(ERROR, "cdc_set_configured: usb_function_config_ep failed\n");
return status;
}
} else {
usb_function_disable_ep(&cdc->function, cdc->bulk_out_addr);
usb_function_disable_ep(&cdc->function, cdc->bulk_in_addr);
usb_function_disable_ep(&cdc->function, cdc->intr_addr);
}
return MX_OK;
}
static mx_status_t cdc_set_interface(void* ctx, unsigned interface, unsigned alt_setting) {
dprintf(TRACE, "cdc_set_interface %d %d\n", interface, alt_setting);
usb_cdc_t* cdc = ctx;
mx_status_t status;
if (interface != descriptors.cdc_intf_0.bInterfaceNumber || alt_setting > 1) {
return MX_ERR_INVALID_ARGS;
}
// TODO(voydanoff) fullspeed and superspeed support
if (alt_setting) {
if ((status = usb_function_config_ep(&cdc->function, &descriptors.bulk_out_ep, NULL))
!= MX_OK ||
(status = usb_function_config_ep(&cdc->function, &descriptors.bulk_in_ep, NULL))
!= MX_OK) {
dprintf(ERROR, "cdc_set_interface: usb_function_config_ep failed\n");
}
} else {
if ((status = usb_function_disable_ep(&cdc->function, cdc->bulk_out_addr)) != MX_OK ||
(status = usb_function_disable_ep(&cdc->function, cdc->bulk_in_addr)) != MX_OK) {
dprintf(ERROR, "cdc_set_interface: usb_function_disable_ep failed\n");
}
}
bool online = false;
if (alt_setting && status == MX_OK) {
online = true;
// queue our OUT txns
mtx_lock(&cdc->rx_mutex);
iotxn_t* txn;
while ((txn = list_remove_head_type(&cdc->bulk_out_txns, iotxn_t, node)) != NULL) {
usb_function_queue(&cdc->function, txn, cdc->bulk_out_addr);
}
mtx_unlock(&cdc->rx_mutex);
// send status notifications on interrupt endpoint
status = cdc_send_notifications(cdc);
}
mtx_lock(&cdc->ethmac_mutex);
cdc->online = online;
if (cdc->ethmac_ifc) {
cdc->ethmac_ifc->status(cdc->ethmac_cookie, online ? ETH_STATUS_ONLINE : 0);
}
mtx_unlock(&cdc->ethmac_mutex);
return status;
}
usb_function_interface_ops_t device_ops = {
.get_descriptors = cdc_get_descriptors,
.control = cdc_control,
.set_configured = cdc_set_configured,
.set_interface = cdc_set_interface,
};
static void usb_cdc_unbind(void* ctx) {
dprintf(TRACE, "usb_cdc_unbind\n");
usb_cdc_t* cdc = ctx;
device_remove(cdc->mxdev);
}
static void usb_cdc_release(void* ctx) {
dprintf(TRACE, "usb_cdc_release\n");
usb_cdc_t* cdc = ctx;
iotxn_t* txn;
while ((txn = list_remove_head_type(&cdc->bulk_out_txns, iotxn_t, node)) != NULL) {
iotxn_release(txn);
}
while ((txn = list_remove_head_type(&cdc->bulk_in_txns, iotxn_t, node)) != NULL) {
iotxn_release(txn);
}
free(cdc);
}
static mx_protocol_device_t usb_cdc_proto = {
.version = DEVICE_OPS_VERSION,
.unbind = usb_cdc_unbind,
.release = usb_cdc_release,
};
mx_status_t usb_cdc_bind(void* ctx, mx_device_t* parent, void** cookie) {
dprintf(INFO, "usb_cdc_bind\n");
usb_cdc_t* cdc = calloc(1, sizeof(usb_cdc_t));
if (!cdc) {
return MX_ERR_NO_MEMORY;
}
mx_status_t status = device_get_protocol(parent, MX_PROTOCOL_USB_FUNCTION, &cdc->function);
if (status != MX_OK) {
goto fail;
}
list_initialize(&cdc->bulk_out_txns);
list_initialize(&cdc->bulk_in_txns);
mtx_init(&cdc->ethmac_mutex, mtx_plain);
mtx_init(&cdc->tx_mutex, mtx_plain);
cdc->bulk_max_packet = BULK_MAX_PACKET; // FIXME(voydanoff) USB 3.0 support
cdc->mtu = 1514;
status = usb_function_alloc_interface(&cdc->function, &descriptors.comm_intf.bInterfaceNumber);
if (status != MX_OK) {
dprintf(ERROR, "usb_cdc_bind: usb_function_alloc_interface failed\n");
goto fail;
}
status = usb_function_alloc_interface(&cdc->function, &descriptors.cdc_intf_0.bInterfaceNumber);
if (status != MX_OK) {
dprintf(ERROR, "usb_cdc_bind: usb_function_alloc_interface failed\n");
goto fail;
}
descriptors.cdc_intf_1.bInterfaceNumber = descriptors.cdc_intf_0.bInterfaceNumber;
descriptors.cdc_union.bControlInterface = descriptors.comm_intf.bInterfaceNumber;
descriptors.cdc_union.bSubordinateInterface = descriptors.cdc_intf_0.bInterfaceNumber;
status = usb_function_alloc_ep(&cdc->function, USB_DIR_OUT, &cdc->bulk_out_addr);
if (status != MX_OK) {
dprintf(ERROR, "usb_cdc_bind: usb_function_alloc_ep failed\n");
goto fail;
}
status = usb_function_alloc_ep(&cdc->function, USB_DIR_IN, &cdc->bulk_in_addr);
if (status != MX_OK) {
dprintf(ERROR, "usb_cdc_bind: usb_function_alloc_ep failed\n");
goto fail;
}
status = usb_function_alloc_ep(&cdc->function, USB_DIR_IN, &cdc->intr_addr);
if (status != MX_OK) {
dprintf(ERROR, "usb_cdc_bind: usb_function_alloc_ep failed\n");
goto fail;
}
descriptors.bulk_out_ep.bEndpointAddress = cdc->bulk_out_addr;
descriptors.bulk_in_ep.bEndpointAddress = cdc->bulk_in_addr;
descriptors.intr_ep.bEndpointAddress = cdc->intr_addr;
status = cdc_generate_mac_address(cdc);
if (status != MX_OK) {
goto fail;
}
// allocate bulk out iotxns
iotxn_t* txn;
for (int i = 0; i < BULK_TX_COUNT; i++) {
status = iotxn_alloc(&txn, 0, BULK_TXN_SIZE);
if (status != MX_OK) {
goto fail;
}
txn->length = BULK_TXN_SIZE;
txn->complete_cb = cdc_rx_complete;
txn->cookie = cdc;
list_add_head(&cdc->bulk_out_txns, &txn->node);
}
// allocate bulk in iotxns
for (int i = 0; i < BULK_RX_COUNT; i++) {
status = iotxn_alloc(&txn, 0, BULK_TXN_SIZE);
if (status != MX_OK) {
goto fail;
}
txn->complete_cb = cdc_tx_complete;
txn->cookie = cdc;
list_add_head(&cdc->bulk_in_txns, &txn->node);
}
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "cdc-eth-function",
.ctx = cdc,
.ops = &usb_cdc_proto,
.proto_id = MX_PROTOCOL_ETHERMAC,
.proto_ops = &ethmac_ops,
};
status = device_add(parent, &args, &cdc->mxdev);
if (status != MX_OK) {
dprintf(ERROR, "usb_device_bind add_device failed %d\n", status);
goto fail;
}
usb_function_interface_t intf = {
.ops = &device_ops,
.ctx = cdc,
};
usb_function_register(&cdc->function, &intf);
return MX_OK;
fail:
usb_cdc_release(cdc);
return status;
}
static mx_driver_ops_t usb_cdc_ops = {
.version = DRIVER_OPS_VERSION,
.bind = usb_cdc_bind,
};
// clang-format off
MAGENTA_DRIVER_BEGIN(usb_cdc, usb_cdc_ops, "magenta", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_USB_FUNCTION),
BI_ABORT_IF(NE, BIND_USB_CLASS, USB_CLASS_COMM),
BI_ABORT_IF(NE, BIND_USB_SUBCLASS, USB_CDC_SUBCLASS_ETHERNET),
BI_MATCH_IF(EQ, BIND_USB_PROTOCOL, 0),
MAGENTA_DRIVER_END(usb_cdc)
@@ -0,0 +1,18 @@
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_TYPE := driver
MODULE_SRCS := \
$(LOCAL_DIR)/cdc-eth-function.c \
MODULE_STATIC_LIBS := system/ulib/ddk system/ulib/sync
MODULE_LIBS := system/ulib/driver system/ulib/magenta system/ulib/c
include make/module.mk
+71
Ver Arquivo
@@ -0,0 +1,71 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "dwc3.h"
#include "dwc3-regs.h"
#include <stdio.h>
static void dwc3_ep_cmd(dwc3_t* dwc, unsigned ep_num, uint32_t command, uint32_t param0,
uint32_t param1, uint32_t param2, uint32_t flags) {
volatile void* mmio = dwc3_mmio(dwc);
mtx_lock(&dwc->lock);
DWC3_WRITE32(mmio + DEPCMDPAR0(ep_num), param0);
DWC3_WRITE32(mmio + DEPCMDPAR1(ep_num), param1);
DWC3_WRITE32(mmio + DEPCMDPAR2(ep_num), param2);
command |= (DEPCMD_CMDACT | flags);
volatile void* depcmd = mmio + DEPCMD(ep_num);
DWC3_WRITE32(depcmd, command | DEPCMD_CMDACT);
if ((flags & DEPCMD_CMDIOC) == 0) {
dwc3_wait_bits(depcmd, DEPCMD_CMDACT, 0);
}
mtx_unlock(&dwc->lock);
}
void dwc3_cmd_start_new_config(dwc3_t* dwc, unsigned ep_num, unsigned rsrc_id) {
dwc3_ep_cmd(dwc, ep_num, DEPSTARTCFG | DEPCMD_RESOURCE_INDEX(rsrc_id), 0, 0, 0, 0);
}
void dwc3_cmd_ep_set_config(dwc3_t* dwc, unsigned ep_num, unsigned ep_type,
unsigned max_packet_size, unsigned interval, bool modify) {
// fifo number is zero for OUT endpoints and EP0_IN
unsigned fifo_num = (EP_OUT(ep_num) || ep_num == EP0_IN ? 0 : ep_num >> 1);
uint32_t param0 = DEPCFG_FIFO_NUM(fifo_num) | DEPCFG_MAX_PACKET_SIZE(max_packet_size)
| DEPCFG_EP_TYPE(ep_type);
if (modify) {
param0 |= DEPCFG_ACTION_MODIFY;
} else {
param0 |= DEPCFG_ACTION_INITIALIZE;
}
uint32_t param1 = DEPCFG_EP_NUMBER(ep_num) | DEPCFG_INTERVAL(interval) |
DEPCFG_XFER_NOT_READY_EN | DEPCFG_XFER_COMPLETE_EN | DEPCFG_INTR_NUM(0);
dwc3_ep_cmd(dwc, ep_num, DEPCFG, param0, param1, 0, 0);
}
void dwc3_cmd_ep_transfer_config(dwc3_t* dwc, unsigned ep_num) {
dwc3_ep_cmd(dwc, ep_num, DEPXFERCFG, 1, 0, 0, 0);
}
void dwc3_cmd_ep_start_transfer(dwc3_t* dwc, unsigned ep_num, mx_paddr_t trb_phys) {
dwc3_ep_cmd(dwc, ep_num, DEPSTRTXFER, (uint32_t)(trb_phys >> 32),
(uint32_t)trb_phys, 0, DEPCMD_CMDIOC);
}
void dwc3_cmd_ep_end_transfer(dwc3_t* dwc, unsigned ep_num) {
unsigned rsrc_id = dwc->eps[ep_num].rsrc_id;
dwc3_ep_cmd(dwc, ep_num, DEPENDXFER, 0, 0, 0,
DEPCMD_RESOURCE_INDEX(rsrc_id) | DEPCMD_CMDIOC | DEPCMD_HIPRI_FORCERM);
}
void dwc3_cmd_ep_set_stall(dwc3_t* dwc, unsigned ep_num) {
dwc3_ep_cmd(dwc, ep_num, DEPSSTALL, 0, 0, 0, DEPCMD_CMDIOC);
}
void dwc3_cmd_ep_clear_stall(dwc3_t* dwc, unsigned ep_num) {
dwc3_ep_cmd(dwc, ep_num, DEPCSTALL, 0, 0, 0, DEPCMD_CMDIOC);
}
+350
Ver Arquivo
@@ -0,0 +1,350 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ddk/debug.h>
#include <magenta/assert.h>
#include "dwc3.h"
#include "dwc3-regs.h"
#include "dwc3-types.h"
#include <stdio.h>
#include <string.h>
#define EP_FIFO_SIZE PAGE_SIZE
static mx_paddr_t dwc3_ep_trb_phys(dwc3_endpoint_t* ep, dwc3_trb_t* trb) {
return io_buffer_phys(&ep->fifo.buffer) + ((void *)trb - (void *)ep->fifo.first);
}
static void dwc3_enable_ep(dwc3_t* dwc, unsigned ep_num, bool enable) {
volatile void* reg = dwc3_mmio(dwc) + DALEPENA;
mtx_lock(&dwc->lock);
uint32_t temp = DWC3_READ32(reg);
uint32_t bit = 1 << ep_num;
if (enable) {
temp |= bit;
} else {
temp &= ~bit;
}
DWC3_WRITE32(reg, temp);
mtx_unlock(&dwc->lock);
}
mx_status_t dwc3_ep_fifo_init(dwc3_t* dwc, unsigned ep_num) {
MX_DEBUG_ASSERT(ep_num < countof(dwc->eps));
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
dwc3_fifo_t* fifo = &ep->fifo;
mx_status_t status = io_buffer_init(&fifo->buffer, EP_FIFO_SIZE, IO_BUFFER_RW);
if (status != MX_OK) {
return status;
}
fifo->first = io_buffer_virt(&fifo->buffer);
fifo->next = fifo->first;
fifo->current = NULL;
fifo->last = (void *)fifo->first + EP_FIFO_SIZE - sizeof(dwc3_trb_t);
// set up link TRB pointing back to the start of the fifo
dwc3_trb_t* trb = fifo->last;
mx_paddr_t trb_phys = io_buffer_phys(&fifo->buffer);
trb->ptr_low = (uint32_t)trb_phys;
trb->ptr_high = (uint32_t)(trb_phys >> 32);
trb->status = 0;
trb->control = TRB_TRBCTL_LINK | TRB_HWO;
io_buffer_cache_op(&ep->fifo.buffer, MX_VMO_OP_CACHE_CLEAN,
(trb - ep->fifo.first) * sizeof(*trb), sizeof(*trb));
return MX_OK;
}
void dwc3_ep_fifo_release(dwc3_t* dwc, unsigned ep_num) {
MX_DEBUG_ASSERT(ep_num < countof(dwc->eps));
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
io_buffer_release(&ep->fifo.buffer);
}
void dwc3_ep_start_transfer(dwc3_t* dwc, unsigned ep_num, unsigned type, mx_paddr_t buffer,
size_t length) {
dprintf(LTRACE, "dwc3_ep_start_transfer ep %u type %u length %zu\n", ep_num, type, length);
// special case: EP0_OUT and EP0_IN use the same fifo
dwc3_endpoint_t* ep = (ep_num == EP0_IN ? &dwc->eps[EP0_OUT] : &dwc->eps[ep_num]);
dwc3_trb_t* trb = ep->fifo.next++;
if (ep->fifo.next == ep->fifo.last) {
ep->fifo.next = ep->fifo.first;
}
if (ep->fifo.current == NULL) {
ep->fifo.current = trb;
}
trb->ptr_low = (uint32_t)buffer;
trb->ptr_high = (uint32_t)(buffer >> 32);
trb->status = TRB_BUFSIZ(length);
trb->control = type | TRB_LST | TRB_IOC | TRB_HWO;
io_buffer_cache_op(&ep->fifo.buffer, MX_VMO_OP_CACHE_CLEAN,
(trb - ep->fifo.first) * sizeof(*trb), sizeof(*trb));
dwc3_cmd_ep_start_transfer(dwc, ep_num, dwc3_ep_trb_phys(ep, trb));
}
static void dwc3_ep_queue_next_locked(dwc3_t* dwc, dwc3_endpoint_t* ep) {
iotxn_t* txn;
if (ep->current_txn == NULL && ep->got_not_ready &&
(txn = list_remove_head_type(&ep->queued_txns, iotxn_t, node)) != NULL) {
ep->current_txn = txn;
ep->got_not_ready = false;
if (EP_IN(ep->ep_num)) {
iotxn_cacheop(txn, IOTXN_CACHE_CLEAN, 0, txn->length);
}
// TODO(voydanoff) scatter/gather support
iotxn_physmap(txn);
mx_paddr_t phys = iotxn_phys(txn);
dwc3_ep_start_transfer(dwc, ep->ep_num, TRB_TRBCTL_NORMAL, phys, txn->length);
}
}
mx_status_t dwc3_ep_config(dwc3_t* dwc, usb_endpoint_descriptor_t* ep_desc,
usb_ss_ep_comp_descriptor_t* ss_comp_desc) {
// convert address to index in range 0 - 31
// low bit is IN/OUT
unsigned ep_num = dwc3_ep_num(ep_desc->bEndpointAddress);
if (ep_num < 2) {
// index 0 and 1 are for endpoint zero
return MX_ERR_INVALID_ARGS;
}
unsigned ep_type = usb_ep_type(ep_desc);
if (ep_type == USB_ENDPOINT_ISOCHRONOUS) {
dprintf(ERROR, "dwc3_ep_config: isochronous endpoints are not supported\n");
return MX_ERR_NOT_SUPPORTED;
}
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
mx_status_t status = dwc3_ep_fifo_init(dwc, ep_num);
if (status != MX_OK) {
dprintf(ERROR, "dwc3_config_ep: dwc3_ep_fifo_init failed %d\n", status);
mtx_unlock(&ep->lock);
return status;
}
ep->max_packet_size = usb_ep_max_packet(ep_desc);
ep->type = ep_type;
ep->interval = ep_desc->bInterval;
// TODO(voydanoff) USB3 support
ep->enabled = true;
if (dwc->configured) {
dwc3_ep_queue_next_locked(dwc, ep);
}
mtx_unlock(&ep->lock);
return MX_OK;
}
mx_status_t dwc3_ep_disable(dwc3_t* dwc, uint8_t ep_addr) {
// convert address to index in range 0 - 31
// low bit is IN/OUT
unsigned ep_num = dwc3_ep_num(ep_addr);
if (ep_num < 2) {
// index 0 and 1 are for endpoint zero
return MX_ERR_INVALID_ARGS;
}
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
dwc3_ep_fifo_release(dwc, ep_num);
ep->enabled = false;
mtx_unlock(&ep->lock);
return MX_OK;
}
void dwc3_ep_queue(dwc3_t* dwc, unsigned ep_num, iotxn_t* txn) {
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
// OUT transactions must have length > 0 and multiple of max packet size
if (EP_OUT(ep_num)) {
if (txn->length == 0 || txn->length % ep->max_packet_size != 0) {
dprintf(ERROR, "dwc3_ep_queue: OUT transfers must be multiple of max packet size\n");
iotxn_complete(txn, MX_ERR_INVALID_ARGS, 0);
return;
}
}
mtx_lock(&ep->lock);
if (!ep->enabled) {
mtx_unlock(&ep->lock);
iotxn_complete(txn, MX_ERR_BAD_STATE, 0);
return;
}
list_add_tail(&ep->queued_txns, &txn->node);
if (dwc->configured) {
dwc3_ep_queue_next_locked(dwc, ep);
}
mtx_unlock(&ep->lock);
}
void dwc3_ep_set_config(dwc3_t* dwc, unsigned ep_num, bool enable) {
dprintf(TRACE, "dwc3_ep_set_config %u\n", ep_num);
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
if (enable) {
dwc3_cmd_ep_set_config(dwc, ep_num, ep->type, ep->max_packet_size, ep->interval, false);
dwc3_cmd_ep_transfer_config(dwc, ep_num);
dwc3_enable_ep(dwc, ep_num, true);
} else {
dwc3_enable_ep(dwc, ep_num, false);
}
}
void dwc3_start_eps(dwc3_t* dwc) {
dprintf(TRACE, "dwc3_start_eps\n");
dwc3_cmd_ep_set_config(dwc, EP0_IN, USB_ENDPOINT_CONTROL, dwc->eps[EP0_IN].max_packet_size, 0,
true);
dwc3_cmd_start_new_config(dwc, EP0_OUT, 2);
for (unsigned ep_num = 2; ep_num < countof(dwc->eps); ep_num++) {
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
if (ep->enabled) {
dwc3_ep_set_config(dwc, ep_num, true);
mtx_lock(&ep->lock);
dwc3_ep_queue_next_locked(dwc, ep);
mtx_unlock(&ep->lock);
}
}
}
static void dwc_ep_read_trb(dwc3_endpoint_t* ep, dwc3_trb_t* trb, dwc3_trb_t* out_trb) {
if (trb >= ep->fifo.first && trb < ep->fifo.last) {
io_buffer_cache_op(&ep->fifo.buffer, MX_VMO_OP_CACHE_INVALIDATE,
(trb - ep->fifo.first) * sizeof(*trb), sizeof(*trb));
memcpy((void *)out_trb, (void *)trb, sizeof(*trb));
} else {
dprintf(ERROR, "dwc_ep_read_trb: bad trb\n");
}
}
void dwc3_ep_xfer_started(dwc3_t* dwc, unsigned ep_num, unsigned rsrc_id) {
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
ep->rsrc_id = rsrc_id;
mtx_unlock(&ep->lock);
}
void dwc3_ep_xfer_not_ready(dwc3_t* dwc, unsigned ep_num, unsigned stage) {
dprintf(LTRACE, "dwc3_ep_xfer_not_ready ep %u state %d\n", ep_num, dwc->ep0_state);
if (ep_num == EP0_OUT || ep_num == EP0_IN) {
dwc3_ep0_xfer_not_ready(dwc, ep_num, stage);
} else {
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
ep->got_not_ready = true;
dwc3_ep_queue_next_locked(dwc, ep);
mtx_unlock(&ep->lock);
}
}
void dwc3_ep_xfer_complete(dwc3_t* dwc, unsigned ep_num) {
dprintf(LTRACE, "dwc3_ep_xfer_complete ep %u state %d\n", ep_num, dwc->ep0_state);
if (ep_num >= countof(dwc->eps)) {
dprintf(ERROR, "dwc3_ep_xfer_complete: bad ep_num %u\n", ep_num);
return;
}
if (ep_num == EP0_OUT || ep_num == EP0_IN) {
dwc3_ep0_xfer_complete(dwc, ep_num);
} else {
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
iotxn_t* txn = ep->current_txn;
ep->current_txn = NULL;
if (txn) {
dwc3_trb_t trb;
dwc_ep_read_trb(ep, ep->fifo.current, &trb);
ep->fifo.current = NULL;
if (trb.control & TRB_HWO) {
dprintf(ERROR, "TRB_HWO still set in dwc3_ep_xfer_complete\n");
}
mx_off_t actual = txn->length - TRB_BUFSIZ(trb.status);
// dwc3_ep_queue_next_locked(dwc, ep);
mtx_unlock(&ep->lock);
if (EP_OUT(ep_num)) {
iotxn_cacheop(txn, MX_VMO_OP_CACHE_INVALIDATE, 0, actual);
}
iotxn_complete(txn, MX_OK, actual);
} else {
mtx_unlock(&ep->lock);
dprintf(ERROR, "dwc3_ep_xfer_complete: no iotxn found to complete!\n");
}
}
}
mx_status_t dwc3_ep_set_stall(dwc3_t* dwc, unsigned ep_num, bool stall) {
if (ep_num >= countof(dwc->eps)) {
return MX_ERR_INVALID_ARGS;
}
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
if (!ep->enabled) {
mtx_unlock(&ep->lock);
return MX_ERR_BAD_STATE;
}
if (stall && !ep->stalled) {
dwc3_cmd_ep_set_stall(dwc, ep_num);
} else if (!stall && ep->stalled) {
dwc3_cmd_ep_clear_stall(dwc, ep_num);
}
ep->stalled = stall;
mtx_unlock(&ep->lock);
return MX_OK;
}
void dwc3_ep_end_transfers(dwc3_t* dwc, unsigned ep_num, mx_status_t reason) {
dwc3_endpoint_t* ep = &dwc->eps[ep_num];
mtx_lock(&ep->lock);
if (ep->current_txn) {
dwc3_cmd_ep_end_transfer(dwc, ep_num);
iotxn_complete(ep->current_txn, reason, 0);
ep->current_txn = NULL;
}
iotxn_t* txn;
while ((txn = list_remove_head_type(&ep->queued_txns, iotxn_t, node)) != NULL) {
iotxn_complete(txn, reason, 0);
}
mtx_unlock(&ep->lock);
}
+212
Ver Arquivo
@@ -0,0 +1,212 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ddk/debug.h>
#include "dwc3.h"
#include "dwc3-regs.h"
#include "dwc3-types.h"
#include <stdio.h>
#include <string.h>
#define EP0_LOCK(dwc) (&(dwc)->eps[EP0_OUT].lock)
static void dwc3_queue_setup_locked(dwc3_t* dwc) {
dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_SETUP, io_buffer_phys(&dwc->ep0_buffer),
sizeof(usb_setup_t));
dwc->ep0_state = EP0_STATE_SETUP;
}
mx_status_t dwc3_ep0_init(dwc3_t* dwc) {
// fifo only needed for physical endpoint 0
mx_status_t status = dwc3_ep_fifo_init(dwc, EP0_OUT);
if (status != MX_OK) {
return status;
}
for (unsigned i = EP0_OUT; i <= EP0_IN; i++) {
dwc3_endpoint_t* ep = &dwc->eps[i];
ep->enabled = false;
ep->max_packet_size = EP0_MAX_PACKET_SIZE;
ep->type = USB_ENDPOINT_CONTROL;
ep->interval = 0;
}
return MX_OK;
}
void dwc3_ep0_reset(dwc3_t* dwc) {
mtx_lock(EP0_LOCK(dwc));
dwc3_cmd_ep_end_transfer(dwc, EP0_OUT);
dwc->ep0_state = EP0_STATE_NONE;
mtx_unlock(EP0_LOCK(dwc));
}
void dwc3_ep0_start(dwc3_t* dwc) {
mtx_lock(EP0_LOCK(dwc));
dwc3_cmd_start_new_config(dwc, EP0_OUT, 0);
dwc3_ep_set_config(dwc, EP0_OUT, true);
dwc3_ep_set_config(dwc, EP0_IN, true);
dwc3_queue_setup_locked(dwc);
mtx_unlock(EP0_LOCK(dwc));
}
static mx_status_t dwc3_handle_setup(dwc3_t* dwc, usb_setup_t* setup, void* buffer, size_t length,
size_t* out_actual) {
mx_status_t status;
if (setup->bmRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
// handle some special setup requests in this driver
switch (setup->bRequest) {
case USB_REQ_SET_ADDRESS:
dprintf(TRACE, "SET_ADDRESS %d\n", setup->wValue);
dwc3_set_address(dwc, setup->wValue);
*out_actual = 0;
return MX_OK;
case USB_REQ_SET_CONFIGURATION:
dprintf(TRACE, "SET_CONFIGURATION %d\n", setup->wValue);
dwc3_reset_configuration(dwc);
dwc->configured = false;
status = usb_dci_control(&dwc->dci_intf, setup, buffer, length, out_actual);
if (status == MX_OK && setup->wValue) {
dwc->configured = true;
dwc3_start_eps(dwc);
}
return status;
default:
// fall through to usb_dci_control()
break;
}
} else if (setup->bmRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) &&
setup->bRequest == USB_REQ_SET_INTERFACE) {
dprintf(TRACE, "SET_INTERFACE %d\n", setup->wValue);
dwc3_reset_configuration(dwc);
dwc->configured = false;
status = usb_dci_control(&dwc->dci_intf, setup, buffer, length, out_actual);
if (status == MX_OK) {
dwc->configured = true;
dwc3_start_eps(dwc);
}
return status;
}
return usb_dci_control(&dwc->dci_intf, setup, buffer, length, out_actual);
}
void dwc3_ep0_xfer_not_ready(dwc3_t* dwc, unsigned ep_num, unsigned stage) {
mtx_lock(EP0_LOCK(dwc));
switch (dwc->ep0_state) {
case EP0_STATE_SETUP:
if (stage == DEPEVT_XFER_NOT_READY_STAGE_DATA ||
stage == DEPEVT_XFER_NOT_READY_STAGE_STATUS) {
// Stall if we receive xfer not ready data/status while waiting for setup to complete
dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
dwc3_queue_setup_locked(dwc);
}
break;
case EP0_STATE_DATA_OUT:
if (ep_num == EP0_IN && stage == DEPEVT_XFER_NOT_READY_STAGE_DATA) {
// end transfer and stall if we receive xfer not ready in the opposite direction
dwc3_cmd_ep_end_transfer(dwc, EP0_OUT);
dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
dwc3_queue_setup_locked(dwc);
}
break;
case EP0_STATE_DATA_IN:
if (ep_num == EP0_OUT && stage == DEPEVT_XFER_NOT_READY_STAGE_DATA) {
// end transfer and stall if we receive xfer not ready in the opposite direction
dwc3_cmd_ep_end_transfer(dwc, EP0_IN);
dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
dwc3_queue_setup_locked(dwc);
}
break;
case EP0_STATE_WAIT_NRDY_OUT:
if (ep_num == EP0_OUT) {
if (dwc->cur_setup.wLength > 0) {
dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_STATUS_3, 0, 0);
} else {
dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_STATUS_2, 0, 0);
}
dwc->ep0_state = EP0_STATE_STATUS;
}
break;
case EP0_STATE_WAIT_NRDY_IN:
if (ep_num == EP0_IN) {
if (dwc->cur_setup.wLength > 0) {
dwc3_ep_start_transfer(dwc, EP0_IN, TRB_TRBCTL_STATUS_3, 0, 0);
} else {
dwc3_ep_start_transfer(dwc, EP0_IN, TRB_TRBCTL_STATUS_2, 0, 0);
}
dwc->ep0_state = EP0_STATE_STATUS;
}
break;
default:
dprintf(ERROR, "dwc3_ep0_xfer_not_ready unhandled state %u\n", dwc->ep0_state);
break;
}
mtx_unlock(EP0_LOCK(dwc));
}
void dwc3_ep0_xfer_complete(dwc3_t* dwc, unsigned ep_num) {
mtx_lock(EP0_LOCK(dwc));
switch (dwc->ep0_state) {
case EP0_STATE_SETUP: {
usb_setup_t* setup = &dwc->cur_setup;
io_buffer_cache_op(&dwc->ep0_buffer, MX_VMO_OP_CACHE_INVALIDATE, 0, sizeof(*setup));
memcpy(setup, io_buffer_virt(&dwc->ep0_buffer), sizeof(*setup));
dprintf(TRACE, "got setup: type: 0x%02X req: %d value: %d index: %d length: %d\n",
setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex,
setup->wLength);
bool is_out = ((setup->bmRequestType & USB_DIR_MASK) == USB_DIR_OUT);
if (setup->wLength > 0 && is_out) {
// queue a read for the data phase
dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_CONTROL_DATA,
io_buffer_phys(&dwc->ep0_buffer), setup->wLength);
dwc->ep0_state = EP0_STATE_DATA_OUT;
} else {
size_t actual;
mx_status_t status = dwc3_handle_setup(dwc, setup, io_buffer_virt(&dwc->ep0_buffer),
dwc->ep0_buffer.size, &actual);
dprintf(TRACE, "dwc3_handle_setup returned %d actual %zu\n", status, actual);
if (status != MX_OK) {
dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
dwc3_queue_setup_locked(dwc);
break;
}
if (setup->wLength > 0) {
// queue a write for the data phase
io_buffer_cache_op(&dwc->ep0_buffer, MX_VMO_OP_CACHE_CLEAN, 0, actual);
dwc3_ep_start_transfer(dwc, EP0_IN, TRB_TRBCTL_CONTROL_DATA,
io_buffer_phys(&dwc->ep0_buffer), actual);
dwc->ep0_state = EP0_STATE_DATA_IN;
} else {
dwc->ep0_state = EP0_STATE_WAIT_NRDY_IN;
}
}
break;
}
case EP0_STATE_DATA_OUT:
dwc->ep0_state = EP0_STATE_WAIT_NRDY_IN;
break;
case EP0_STATE_DATA_IN:
dwc->ep0_state = EP0_STATE_WAIT_NRDY_OUT;
break;
case EP0_STATE_STATUS:
dwc3_queue_setup_locked(dwc);
break;
default:
break;
}
mtx_unlock(EP0_LOCK(dwc));
}
+236
Ver Arquivo
@@ -0,0 +1,236 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ddk/debug.h>
#include <hw/arch_ops.h>
#include "dwc3.h"
#include "dwc3-regs.h"
#include <stdio.h>
#include <unistd.h>
static void dwc_handle_ep_event(dwc3_t* dwc, uint32_t event) {
uint32_t type = DEPEVT_TYPE(event);
uint32_t ep_num = DEPEVT_PHYS_EP(event);
uint32_t status = DEPEVT_STATUS(event);
switch (type) {
case DEPEVT_XFER_COMPLETE:
dwc3_ep_xfer_complete(dwc, ep_num);
break;
case DEPEVT_XFER_IN_PROGRESS:
dprintf(TRACE, "DEPEVT_XFER_IN_PROGRESS ep_num: %u status %u\n", ep_num, status);
break;
case DEPEVT_XFER_NOT_READY:
dwc3_ep_xfer_not_ready(dwc, ep_num, DEPEVT_XFER_NOT_READY_STAGE(event));
break;
case DEPEVT_STREAM_EVT:
dprintf(TRACE, "DEPEVT_STREAM_EVT ep_num: %u status %u\n", ep_num, status);
break;
case DEPEVT_CMD_CMPLT: {
unsigned cmd_type = DEPEVT_CMD_CMPLT_CMD_TYPE(event);
unsigned rsrc_id = DEPEVT_CMD_CMPLT_RSRC_ID(event);
if (cmd_type == DEPSTRTXFER) {
dwc3_ep_xfer_started(dwc, ep_num, rsrc_id);
}
break;
}
default:
dprintf(ERROR, "dwc_handle_ep_event: unknown event type %u\n", type);
break;
}
}
static void dwc_handle_event(dwc3_t* dwc, uint32_t event) {
dprintf(LTRACE, "dwc_handle_event %08X\n", event);
if (!(event & DEPEVT_NON_EP)) {
dwc_handle_ep_event(dwc, event);
return;
}
uint32_t type = DEVT_TYPE(event);
uint32_t info = DEVT_INFO(event);
switch (type) {
case DEVT_DISCONNECT:
dprintf(TRACE, "DEVT_DISCONNECT\n");
break;
case DEVT_USB_RESET:
dprintf(TRACE, "DEVT_USB_RESET\n");
dwc3_usb_reset(dwc);
break;
case DEVT_CONNECTION_DONE:
dprintf(TRACE, "DEVT_CONNECTION_DONE\n");
dwc3_connection_done(dwc);
break;
case DEVT_LINK_STATE_CHANGE:
dprintf(TRACE, "DEVT_LINK_STATE_CHANGE: ");
switch (info) {
case DSTS_USBLNKST_U0 | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_U0\n");
break;
case DSTS_USBLNKST_U1 | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_U1\n");
break;
case DSTS_USBLNKST_U2 | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_U2\n");
break;
case DSTS_USBLNKST_U3 | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_U3\n");
break;
case DSTS_USBLNKST_ESS_DIS | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_ESS_DIS\n");
break;
case DSTS_USBLNKST_RX_DET | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_RX_DET\n");
break;
case DSTS_USBLNKST_ESS_INACT | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_ESS_INACT\n");
break;
case DSTS_USBLNKST_POLL | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_POLL\n");
break;
case DSTS_USBLNKST_RECOV | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_RECOV\n");
break;
case DSTS_USBLNKST_HRESET | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_HRESET\n");
break;
case DSTS_USBLNKST_CMPLY | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_CMPLY\n");
break;
case DSTS_USBLNKST_LPBK | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_LPBK\n");
break;
case DSTS_USBLNKST_RESUME_RESET | DEVT_LINK_STATE_CHANGE_SS:
dprintf(TRACE, "DSTS_USBLNKST_RESUME_RESET\n");
break;
case DSTS_USBLNKST_ON:
dprintf(TRACE, "DSTS_USBLNKST_ON\n");
break;
case DSTS_USBLNKST_SLEEP:
dprintf(TRACE, "DSTS_USBLNKST_SLEEP\n");
break;
case DSTS_USBLNKST_SUSPEND:
dprintf(TRACE, "DSTS_USBLNKST_SUSPEND\n");
break;
case DSTS_USBLNKST_DISCONNECTED:
dprintf(TRACE, "DSTS_USBLNKST_DISCONNECTED\n");
break;
case DSTS_USBLNKST_EARLY_SUSPEND:
dprintf(TRACE, "DSTS_USBLNKST_EARLY_SUSPEND\n");
break;
case DSTS_USBLNKST_RESET:
dprintf(TRACE, "DSTS_USBLNKST_RESET\n");
break;
case DSTS_USBLNKST_RESUME:
dprintf(TRACE, "DSTS_USBLNKST_RESUME\n");
break;
default:
dprintf(ERROR, "unknown state %d\n", info);
break;
}
break;
case DEVT_REMOTE_WAKEUP:
dprintf(TRACE, "DEVT_REMOTE_WAKEUP\n");
break;
case DEVT_HIBERNATE_REQUEST:
dprintf(TRACE, "DEVT_HIBERNATE_REQUEST\n");
break;
case DEVT_SUSPEND_ENTRY:
dprintf(TRACE, "DEVT_SUSPEND_ENTRY\n");
//TODO(voydanoff) is this the best way to detect disconnect?
dwc3_disconnected(dwc);
break;
case DEVT_SOF:
dprintf(TRACE, "DEVT_SOF\n");
break;
case DEVT_ERRATIC_ERROR:
dprintf(TRACE, "DEVT_ERRATIC_ERROR\n");
break;
case DEVT_COMMAND_COMPLETE:
dprintf(TRACE, "DEVT_COMMAND_COMPLETE\n");
break;
case DEVT_EVENT_BUF_OVERFLOW:
dprintf(TRACE, "DEVT_EVENT_BUF_OVERFLOW\n");
break;
case DEVT_VENDOR_TEST_LMP:
dprintf(TRACE, "DEVT_VENDOR_TEST_LMP\n");
break;
case DEVT_STOPPED_DISCONNECT:
dprintf(TRACE, "DEVT_STOPPED_DISCONNECT\n");
break;
case DEVT_L1_RESUME_DETECT:
dprintf(TRACE, "DEVT_L1_RESUME_DETECT\n");
break;
case DEVT_LDM_RESPONSE:
dprintf(TRACE, "DEVT_LDM_RESPONSE\n");
break;
default:
dprintf(ERROR, "dwc_handle_event: unknown event type %u\n", type);
break;
}
}
static int dwc_irq_thread(void* arg) {
dwc3_t* dwc = arg;
volatile void* mmio = dwc3_mmio(dwc);
dprintf(TRACE, "dwc_irq_thread start\n");
uint32_t* ring_start = io_buffer_virt(&dwc->event_buffer);
uint32_t* ring_end = (void *)ring_start + EVENT_BUFFER_SIZE;
volatile uint32_t* ring_cur = ring_start;
while (1) {
mx_status_t status = mx_interrupt_wait(dwc->irq_handle);
mx_interrupt_complete(dwc->irq_handle);
if (status != MX_OK) {
dprintf(ERROR, "mx_interrupt_wait returned %d\n", status);
break;
}
// read number of new bytes in the event buffer
uint32_t event_count;
while ((event_count = DWC3_READ32(mmio + GEVNTCOUNT(0)) & GEVNTCOUNT_EVNTCOUNT_MASK) > 0) {
// invalidate cache so we can read fresh events
io_buffer_cache_op(&dwc->event_buffer, MX_VMO_OP_CACHE_INVALIDATE, 0,
EVENT_BUFFER_SIZE);
for (unsigned i = 0; i < event_count; i += sizeof(uint32_t)) {
uint32_t event = *ring_cur++;
if (ring_cur == ring_end) {
ring_cur = ring_start;
}
dwc_handle_event(dwc, event);
}
// acknowledge the events we have processed
DWC3_WRITE32(mmio + GEVNTCOUNT(0), event_count);
}
}
dprintf(TRACE, "dwc_irq_thread done\n");
return 0;
}
void dwc3_events_start(dwc3_t* dwc) {
volatile void* mmio = dwc3_mmio(dwc);
// set event buffer pointer and size
// keep interrupts masked until we are ready
mx_paddr_t paddr = io_buffer_phys(&dwc->event_buffer);
DWC3_WRITE32(mmio + GEVNTADRLO(0), (uint32_t)paddr);
DWC3_WRITE32(mmio + GEVNTADRHI(0), (uint32_t)(paddr >> 32));
DWC3_WRITE32(mmio + GEVNTSIZ(0), EVENT_BUFFER_SIZE | GEVNTSIZ_EVNTINTRPTMASK);
DWC3_WRITE32(mmio + GEVNTCOUNT(0), 0);
// enable events
uint32_t event_mask = DEVTEN_USBRSTEVTEN | DEVTEN_CONNECTDONEEVTEN | DEVTEN_DISSCONNEVTEN |
DEVTEN_L1SUSPEN | DEVTEN_U3_L2_SUSP_EN;
DWC3_WRITE32(mmio + DEVTEN, event_mask);
thrd_create_with_name(&dwc->irq_thread, dwc_irq_thread, dwc, "dwc_irq_thread");
}
+488
Ver Arquivo
@@ -0,0 +1,488 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
// clang-format off
#include <hw/reg.h>
#define DWC3_READ32(a) readl(a)
#define DWC3_WRITE32(a, v) writel(v, a)
#define DWC3_READ64(a) readll(a)
#define DWC3_WRITE64(a, v) writell(v, a)
#define DWC3_MASK(start, count) (((1 << (count)) - 1) << (start))
#define DWC3_GET_BITS32(src, start, count) ((DWC3_READ32(src) & DWC3_MASK(start, count)) >> (start))
#define DWC3_SET_BITS32(dest, start, count, value) \
DWC3_WRITE32(dest, (DWC3_READ32(dest) & ~DWC3_MASK(start, count)) | \
(((value) << (start)) & DWC3_MASK(start, count)))
// XHCI register offsets
#define CAPLENGTH 0x0000 // Host Controller Operational Registers
#define CAPLENGTH_HCIVERSION_START 16
#define CAPLENGTH_HCIVERSION_BITS 16
#define CAPLENGTH_CAPLENGTH_START 0
#define CAPLENGTH_CAPLENGTH_BITS 8
#define HCSPARAMS1 0x0004 // Structural Parameters 1 Register
#define HCSPARAMS1_MAXPORTS_START 24
#define HCSPARAMS1_MAXPORTS_BITS 8
#define HCSPARAMS1_MAXINTRS_START 8
#define HCSPARAMS1_MAXINTRS_BITS 11
#define HCSPARAMS1_MAXSLOTS_START 0
#define HCSPARAMS1_MAXSLOTS_BITS 8
#define HCSPARAMS2 0x0008 // Structural Parameters 2 Register
#define HCSPARAMS2_MAXSCRATCHPADBUFS_START 27
#define HCSPARAMS2_MAXSCRATCHPADBUFS_BITS 5
#define HCSPARAMS2_SPR (1 << 26)
#define HCSPARAMS2_MAXSCRATCHPADBUFS_HI_START 21
#define HCSPARAMS2_MAXSCRATCHPADBUFS_HI_BITS 5
#define HCSPARAMS2_ERSTMAX_START 4
#define HCSPARAMS2_ERSTMAX_BITS 4
#define HCSPARAMS2_IST_START 0
#define HCSPARAMS2_IST_BITS 4
#define HCSPARAMS3 0x000c // Structural Parameters 3 Register
#define HCSPARAMS3_U2_DEVICE_EXIT_LAT_START 16
#define HCSPARAMS3_U2_DEVICE_EXIT_LAT_BITS 16
#define HCSPARAMS3_U1_DEVICE_EXIT_LAT_START 0
#define HCSPARAMS3_U1_DEVICE_EXIT_LAT_BITS 8
#define HCCPARAMS1 0x0010 // Capability Parameters 1 Register
#define HCCPARAMS1_XECP_START 16
#define HCCPARAMS1_XECP_BITS 16
#define HCCPARAMS1_MAXPSASIZE_START 12
#define HCCPARAMS1_MAXPSASIZE_BITS 4
#define HCCPARAMS1_CFC (1 << 11)
#define HCCPARAMS1_SEC (1 << 10)
#define HCCPARAMS1_SPC (1 << 9)
#define HCCPARAMS1_PAE (1 << 8)
#define HCCPARAMS1_NSS (1 << 7)
#define HCCPARAMS1_LTC (1 << 6)
#define HCCPARAMS1_LHRC (1 << 5)
#define HCCPARAMS1_PIND (1 << 4)
#define HCCPARAMS1_PPC (1 << 3)
#define HCCPARAMS1_CSZ (1 << 2)
#define HCCPARAMS1_BNC (1 << 1)
#define HCCPARAMS1_AC64 (1 << 0)
#define DBOFF 0x0014 // Doorbell Offset Register
#define RTSOFF 0x0018 // Runtime Register Space Offset Register
#define HCCPARAMS2 0x001c // Host Controller Capability Parameters 2
#define HCCPARAMS2_ETC (1 << 6)
#define HCCPARAMS2_CIC (1 << 5)
#define HCCPARAMS2_LEC (1 << 4)
#define HCCPARAMS2_CTC (1 << 3)
#define HCCPARAMS2_FSC (1 << 2)
#define HCCPARAMS2_CMC (1 << 1)
#define HCCPARAMS2_U3C (1 << 0)
// Global register offsets
#define GSBUSCFG0 0xc100 // Global SoC Bus Configuration Register 0
#define GSBUSCFG1 0xc104 // Global SoC Bus Configuration Register 1
#define GTXTHRCFG 0xc108 // Global Tx Threshold Control Register
#define GRXTHRCFG 0xc10c // Global Rx Threshold Control Register
#define GCTL 0xc110 // Global Core Control Register
#define GCTL_PWRDNSCALE(n) (((n) & 0x1fff) << 19)
#define GCTL_PWRDNSCALE_START 19
#define GCTL_PWRDNSCALE_BITS 13
#define GCTL_MASTERFILTBYPASS (1 << 18)
#define GCTL_BYPSSETADDR (1 << 17)
#define GCTL_U2RSTECN (1 << 16)
#define GCTL_FRMSCLDWN_START 14
#define GCTL_FRMSCLDWN_BITS 2
#define GCTL_PRTCAPDIR_START 12
#define GCTL_PRTCAPDIR_BITS 2
#define GCTL_PRTCAPDIR_HOST (1 << GCTL_PRTCAPDIR_START)
#define GCTL_PRTCAPDIR_DEVICE (2 << GCTL_PRTCAPDIR_START)
#define GCTL_PRTCAPDIR_OTG (3 << GCTL_PRTCAPDIR_START)
#define GCTL_PRTCAPDIR_MASK (3 << GCTL_PRTCAPDIR_START)
#define GCTL_CORESOFTRESET (1 << 11)
#define GCTL_U1_U2_TIMER_SCALE (1 << 9)
#define GCTL_DEBUGATTACH (1 << 8)
#define GCTL_SCALEDOWN_START 4
#define GCTL_SCALEDOWN_BITS 2
#define GCTL_DISSCRAMBLE (1 << 3)
#define GCTL_U2EXIT_LFPS (1 << 2)
#define GCTL_GBL_HIBERNATION_EN (1 << 1)
#define GCTL_DSBLCLKGTNG (1 << 0)
#define GPMSTS 0xc114 // Global Power Management Status Register
#define GSTS 0xc118 // Global Status Register
#define GSTS_CBELT_START 18
#define GSTS_CBELT_BITS 4
#define GSTS_CBELT(s) (((s) >> GSTS_CBELT_START) & ((1 << GSTS_CBELT_START) - 1))
#define GSTS_SSIC_IP (1 << 11)
#define GSTS_OTG_IP (1 << 10)
#define GSTS_BC_IP (1 << 9)
#define GSTS_ADP_IP (1 << 8)
#define GSTS_HOST_IP (1 << 7)
#define GSTS_DEVICE_IP (1 << 6)
#define GSTS_CSR_TIMEOUT (1 << 5)
#define GSTS_BUSERRADDRVLD (1 << 4)
#define GSTS_CURMOD_START 0
#define GSTS_CURMOD_BITS 2
#define GSTS_CURMOD(s) (((s) >> GSTS_CURMOD_START) & ((1 << GSTS_CURMOD_BITS) - 1))
#define GUCTL1 0xc11c // Global User Control Register 1
#define USB31_IP_NAME 0xc120 // IP NAME REGISTER
#define GGPIO 0xc124 // Global General Purpose Input/Output Register
#define GUID 0xc128 // Global User ID Register
#define GUCTL 0xc12c // Global User Control Register
#define GBUSERRADDR 0xc130 // Global Soc Bus Error Address Register
#define GBUSERRADDRLO 0xc130 // Global Soc Bus Error Address Register - Low
#define GBUSERRADDRHI 0xc134 // Global Soc Bus Error Address Register - High
#define GPRTBIMAP 0xc138 // Global SS Port to Bus Instance Mapping Register - Low
#define GPRTBIMAPHI 0xc13c // Global SS Port to Bus Instance Mapping Register - High
#define GHWPARAMS0 0xc140 // Global Hardware Parameters Register 0
#define GHWPARAMS0_AWIDTH_START 24
#define GHWPARAMS0_AWIDTH_BITS 8
#define GHWPARAMS0_AWIDTH(p) (((p) >> GHWPARAMS0_AWIDTH_START) & \
((1 << GHWPARAMS0_AWIDTH_BITS) - 1))
#define GHWPARAMS0_SDWIDTH_START 16
#define GHWPARAMS0_SDWIDTH_BITS 8
#define GHWPARAMS0_SDWIDTH(p) (((p) >> GHWPARAMS0_SDWIDTH_START) & \
((1 << GHWPARAMS0_SDWIDTH_BITS) - 1))
#define GHWPARAMS0_MDWIDTH_START 8
#define GHWPARAMS0_MDWIDTH_BITS 8
#define GHWPARAMS0_MDWIDTH(p) (((p) >> GHWPARAMS0_MDWIDTH_START) & \
((1 << GHWPARAMS0_MDWIDTH_BITS) - 1))
#define GHWPARAMS0_SBUS_TYPE_START 6
#define GHWPARAMS0_SBUS_TYPE_BITS 2
#define GHWPARAMS0_SBUS_TYPE(p) (((p) >> GHWPARAMS0_SBUS_TYPE_START) & \
((1 << GHWPARAMS0_SBUS_TYPE_BITS) - 1))
#define GHWPARAMS0_MBUS_TYPE_START 3
#define GHWPARAMS0_MBUS_TYPE_BITS 3
#define GHWPARAMS0_MBUS_TYPE(p) (((p) >> GHWPARAMS0_MBUS_TYPE_START) & \
((1 << GHWPARAMS0_MBUS_TYPE_BITS) - 1))
#define GHWPARAMS0_MODE_START 0
#define GHWPARAMS0_MODE_BITS 3
#define GHWPARAMS0_MODE(p) (((p) >> GHWPARAMS0_MODE_START) & \
((1 << GHWPARAMS0_MODE_BITS) - 1))
#define GHWPARAMS1 0xc144 // Global Hardware Parameters Register 1
#define GHWPARAMS2 0xc148 // Global Hardware Parameters Register 2
#define GHWPARAMS3 0xc14c // Global Hardware Parameters Register 3
#define GHWPARAMS4 0xc150 // Global Hardware Parameters Register 4
#define GHWPARAMS5 0xc154 // Global Hardware Parameters Register 5
#define GHWPARAMS6 0xc158 // Global Hardware Parameters Register 6
#define GHWPARAMS7 0xc15c // Global Hardware Parameters Register 7
#define GDBGFIFOSPACE 0xc160 // Global Debug Queue/FIFO Space Available Register
#define GBMUCTL 0xc164 // Global BMU Control Register
#define GDBGBMU 0xc16c // Global Debug BMU Register
#define GDBGLSPMUX_HST 0xc170 // Global Debug LSP MUX Register in host mode
#define GDBGLSPMUX_DEV 0xc170 // Global Debug LSP MUX Register
#define GDBGLSP 0xc174 // Global Debug LSP Register
#define GDBGEPINFO0 0xc178 // Global Debug Endpoint Information Register 0
#define GDBGEPINFO1 0xc17c // Global Debug Endpoint Information Register 1
#define GPRTBIMAP_HS 0xc180 // Global High-Speed Port to Bus Instance Mapping Register
#define GPRTBIMAP_HSLO 0xc180 // Global High-Speed Port to Bus Instance Mapping Register - Low
#define GPRTBIMAP_HSHI 0xc184 // Global High-Speed Port to Bus Instance Mapping Register - High
#define GPRTBIMAP_FS 0xc188 // Global Full/Low-Speed Port to Bus Instance Mapping Register
#define GPRTBIMAP_FSLO 0xc188 // Global Full/Low-Speed Port to Bus Instance Mapping Register - Low
#define GPRTBIMAP_FSHI 0xc18c // Global Full/Low-Speed Port to Bus Instance Mapping Register - High
#define GHMSOCBWOR 0xc190 // Global Host Mode SoC Bandwidth Override Register
#define GERRINJCTL_1 0xc194 // Global Error Injection 1 Control Register
#define GERRINJCTL_2 0xc194 // Global Error Injection 2 Control Register
#define USB31_VER_NUMBER 0xc1a0 // USB31 IP VERSION NUMBER
#define USB31_VER_TYPE 0xc1a4 // USB31 IP VERSION TYPE
#define GSYSBLKWINCTRL 0xc1b0 // System Bus Blocking Window Control
//#defineGUSB3RMMICTL(n) varies
#define GUSB2PHYCFG(n) (0xc200 + 4 * (n)) // Global USB2 PHY Configuration Register
#define GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define GUSB2PHYCFG_ULPI_LPM_WITH_OPMODE_CHK (1 << 29)
#define GUSB2PHYCFG_HSIC_CON_WIDTH_ADJ(n) (((n) & 0x3) << 27)
#define GUSB2PHYCFG_INV_SEL_HSIC (1 << 26)
#define GUSB2PHYCFG_LSTRD(n) (((n) & 0x7) << 22)
#define GUSB2PHYCFG_LSIPD(n) (((n) & 0x7) << 19)
#define GUSB2PHYCFG_ULPIEXTVBUSINDICATOR (1 << 18)
#define GUSB2PHYCFG_ULPIEXTVBUSDRV (1 << 17)
#define GUSB2PHYCFG_ULPIAUTORES (1 << 15)
#define GUSB2PHYCFG_USBTRDTIM(n) (((n) & 0xf) << 10)
#define GUSB2PHYCFG_USBTRDTIM_MASK (0xf << 10)
#define GUSB2PHYCFG_XCVRDLY (1 << 9)
#define GUSB2PHYCFG_ENBLSLPM (1 << 8)
#define GUSB2PHYCFG_PHYSEL (1 << 7)
#define GUSB2PHYCFG_SUSPENDUSB20 (1 << 6)
#define GUSB2PHYCFG_FSINTF (1 << 5)
#define GUSB2PHYCFG_ULPI_UTMI_SEL (1 << 4)
#define GUSB2PHYCFG_PHYIF (1 << 3)
#define GUSB2PHYCFG_TOUTCAL(n) (((n) & 0x7) << 0)
#define GUSB2I2CCTL(n) (0xc240 + 4 * (n)) // Reserved Register
#define GUSB2PHYACC_UTMI(n) (0xc280 + 4 * (n)) // Global USB 2.0 UTMI PHY Vendor Control Register
#define GUSB2PHYACC_ULPI(n) (0xc280 + 4 * (n)) // Global USB 2.0 UTMI PHY Vendor Control Register
#define GUSB3PIPECTL(n) (0xc2c0 + 4 * (n)) // Global USB 3.1 PIPE Control Register
#define GUSB3PIPECTL_PHY_SOFT_RST (1 << 31) // USB3 PHY Soft Reset
#define GUSB3PIPECTL_HST_PRT_CMPL (1 << 30)
#define GUSB3PIPECTL_DIS_RX_DET_P3 (1 << 28)
#define GUSB3PIPECTL_UX_EXIT_IN_PX (1 << 27)
#define GUSB3PIPECTL_PING_ENHANCE_EN (1 << 26)
#define GUSB3PIPECTL_U1U2_EXIT_FAIL_TO_RECOV (1 << 25)
#define GUSB3PIPECTL_REQUEST_P1P2P3 (1 << 24)
#define GUSB3PIPECTL_START_RX_DET_U3_RX_DET (1 << 23)
#define GUSB3PIPECTL_DIS_RX_DET_U3_RX_DET (1 << 22)
#define GUSB3PIPECTL_DELAY_P1P2P3(n) (((n) & 0x7) << 19)
#define GUSB3PIPECTL_DELAYP1TRANS (1 << 18)
#define GUSB3PIPECTL_SUSPENDENABLE (1 << 17)
#define GUSB3PIPECTL_DATWIDTH(n) (((n) & 0x3) << 15)
#define GUSB3PIPECTL_ABORT_RX_DET_IN_U2 (1 << 14)
#define GUSB3PIPECTL_SKIP_RX_DET (1 << 13)
#define GUSB3PIPECTL_LFPS_P0_ALGN (1 << 12)
#define GUSB3PIPECTL_P3P2_TRAN_OK (1 << 11)
#define GUSB3PIPECTL_P3_EX_SIG_P3 (1 << 10)
#define GUSB3PIPECTL_LFPSFILTER (1 << 9)
#define GUSB3PIPECTL_RX_DETECT_TO_POLLING_LFPS_CONTROL (1 << 8)
#define GUSB3PIPECTL_SSIC_EN (1 << 7)
#define GUSB3PIPECTL_TX_SWING (1 << 6)
#define GUSB3PIPECTL_TX_MARGIN(n) (((n) & 0x7) << 3)
#define GUSB3PIPECTL_SS_TX_DE_EMPHASIS(n) (((n) & 0x3) << 1)
#define GUSB3PIPECTL_ELASTIC_BUFFER_MODE (1 << 0)
#define GTXFIFOSIZ(n) (0xc300 + 0x7c * (n)) // Global Transmit FIFO Size Register
#define GRXFIFOSIZ(n) (0xc380 + 0x7c * (n)) // Global Receive FIFO Size Register
#define GEVNTADR(n) (0xc400 + 0x10 * (n)) // Global Event Buffer Address Register
#define GEVNTADRLO(n) (0xc400 + 0x10 * (n)) // Global Event Buffer Address Register - Low
#define GEVNTADRHI(n) (0xc404 + 0x10 * (n)) // Global Event Buffer Address Register - High
#define GEVNTSIZ(n) (0xc408 + 0x10 * (n)) // Global Event Buffer Size Register
#define GEVNTSIZ_EVNTINTRPTMASK (1 << 31) // Event Interrupt Mask
#define GEVNTCOUNT(n) (0xc40c + 0x10 * (n)) // Global Event Buffer Size Register
#define GEVNTCOUNT_EVNT_HANDLER_BUSY (1 << 31) // Event Handler Busy
#define GEVNTCOUNT_EVNTCOUNT_MASK 0xffff // Mask for Event Count
#define GHWPARAMS8 0xc600 // Global Hardware Parameters Register 8
#define GSMACCTL 0xc604 // Global SMAC CONTROL REGISTER
#define GUCTL2 0xc608 // Global User Control Register 2
#define GUCTL3 0xc60c // Global User Control Register 3
#define GTXFIFOPRIDEV 0xc610 // Global Device TXFIFO DMA Priority Register
#define GTXFIFOPRIHST 0xc618 // Global Host TXFIFO DMA Priority Register
#define GRXFIFOPRIHST 0xc61c // Global Host RXFIFO DMA Priority Register
#define GFIFOPRIDBC 0xc620 // Global Host Debug Capability DMA Priority Register
#define GDMAHLRATIO 0xc624 // Global Host FIFO DMA High-Low Priority Ratio Register
#define GOSTDDMA_ASYNC 0xc628 // Global Number of Async Outstanding DMA Register
#define GOSTDDMA_PRD 0xc62c // Global Number of Periodic Outstanding DMA Register
#define GFLADJ 0xc630 // Global Frame Length Adjustment Register
#define GUSB2RHBCTL(n) (0xc640 + 4 * (n)) // Global USB2 PHY Configuration Register
// Device mode register offsets
#define DCFG 0xc700 // Device Configuration Register
#define DCFG_STOP_ON_DISCONNECT (1 << 24)
#define DCFG_IGN_STRM_PP (1 << 23)
#define DCFG_LPMCAP (1 << 22)
#define DCFG_NUMP_START 17
#define DCFG_NUMP_BITS 5
#define DCFG_INTRNUM_START 12
#define DCFG_INTRNUM_BITS 5
#define DCFG_DEVADDR_START 3
#define DCFG_DEVADDR_BITS 7
#define DCFG_DEVSPD_START 0
#define DCFG_DEVSPD_BITS 3
#define DCFG_DEVSPD_HIGH 0
#define DCFG_DEVSPD_FULL 1
#define DCFG_DEVSPD_LOW 2
#define DCFG_DEVSPD_SUPER 4
#define DCTL 0xc704 // Device Control Register
#define DCTL_RUN_STOP (1 << 31)
#define DCTL_CSFTRST (1 << 30)
#define DCFG_HIRDTHRES_START 24
#define DCFG_HIRDTHRES_BITS 5
#define DCFG_LPM_NYET_THRES_START 20
#define DCFG_LPM_NYET_THRES_BITS 4
#define DCTL_KEEP_CONNECT (1 << 19)
#define DCTL_L1_HIBERNATION_EN (1 << 18)
#define DCTL_CRS (1 << 17)
#define DCTL_CSS (1 << 16)
#define DCTL_INITU2ENA (1 << 12)
#define DCTL_ACCEPTU2ENA (1 << 11)
#define DCTL_INITU1ENA (1 << 10)
#define DCTL_ACCEPTU1ENA (1 << 9)
#define DCTL_ACCEPTU1ENA (1 << 9)
#define DCFG_ULSTCHNGREQ_START 5
#define DCFG_ULSTCHNGREQ_BITS 4
#define DCFG_TSTCTL_START 1
#define DCFG_TSTCTL_BITS 4
#define DEVTEN 0xc708 // Device Event Enable Register
#define DEVTEN_LDMEVTEN (1 << 15)
#define DEVTEN_L1WKUPEVTEN (1 << 14)
#define DEVTEN_STOP_ON_DISCONNECT_EN (1 << 13)
#define DEVTEN_VENDEVTSTRCVDEN (1 << 12)
#define DEVTEN_ERRTICERREVTEN (1 << 9)
#define DEVTEN_L1SUSPEN (1 << 8)
#define DEVTEN_SOFTEVTEN (1 << 7)
#define DEVTEN_U3_L2_SUSP_EN (1 << 6)
#define DEVTEN_HIBERNATION_REQ_EVT_EN (1 << 5)
#define DEVTEN_WKUPEVTEN (1 << 4)
#define DEVTEN_ULSTCNGEN (1 << 3)
#define DEVTEN_CONNECTDONEEVTEN (1 << 2)
#define DEVTEN_USBRSTEVTEN (1 << 1)
#define DEVTEN_DISSCONNEVTEN (1 << 0)
#define DSTS 0xc70c // Device Status Register
#define DSTS_DCNRD (1 << 29)
#define DSTS_SRE (1 << 28)
#define DSTS_RSS (1 << 25)
#define DSTS_SSS (1 << 24)
#define DSTS_COREIDLE (1 << 23)
#define DSTS_DEVCTRLHLT (1 << 22)
#define DSTS_USBLNKST_START 18
#define DSTS_USBLNKST_BITS 4
#define DSTS_USBLNKST(s) (((s) >> DSTS_USBLNKST_START) & \
((1 << DSTS_USBLNKST_BITS) - 1))
#define DSTS_RXFIFOEMPTY (1 << 17)
#define DSTS_SOFFN_START 3
#define DSTS_SOFFN_BITS 14
#define DSTS_SOFFN(s) (((s) >> DSTS_SOFFN_START) & \
((1 << DSTS_SOFFN_BITS) - 1))
#define DSTS_CONNECTSPD_START 0
#define DSTS_CONNECTSPD_BITS 3
#define DSTS_CONNECTSPD(s) (((s) >> DSTS_CONNECTSPD_START) & \
((1 << DSTS_CONNECTSPD_BITS) - 1))
// DSTS link state in SS node
#define DSTS_USBLNKST_U0 0x0
#define DSTS_USBLNKST_U1 0x1
#define DSTS_USBLNKST_U2 0x2
#define DSTS_USBLNKST_U3 0x3
#define DSTS_USBLNKST_ESS_DIS 0x4
#define DSTS_USBLNKST_RX_DET 0x5
#define DSTS_USBLNKST_ESS_INACT 0x6
#define DSTS_USBLNKST_POLL 0x7
#define DSTS_USBLNKST_RECOV 0x8
#define DSTS_USBLNKST_HRESET 0x9
#define DSTS_USBLNKST_CMPLY 0xa
#define DSTS_USBLNKST_LPBK 0xb
#define DSTS_USBLNKST_RESUME_RESET 0xf
// DSTS link state in HS/FS/LS node
#define DSTS_USBLNKST_ON 0x0
#define DSTS_USBLNKST_SLEEP 0x2
#define DSTS_USBLNKST_SUSPEND 0x3
#define DSTS_USBLNKST_DISCONNECTED 0x4
#define DSTS_USBLNKST_EARLY_SUSPEND 0x5
#define DSTS_USBLNKST_RESET 0xe
#define DSTS_USBLNKST_RESUME 0xf
// DSTS connection speed
#define DSTS_CONNECTSPD_HIGH 0
#define DSTS_CONNECTSPD_FULL 1
#define DSTS_CONNECTSPD_SUPER 4
#define DSTS_CONNECTSPD_ENHANCED_SUPER 5
#define DGCMDPAR 0xc710 // Device Generic Command Parameter Register
#define DGCMD 0xc714 // Device Generic Command Register
#define DGCMD_CMDSTATUS_START 12
#define DGCMD_CMDSTATUS_BITS 4
#define DGCMD_CMDACT (1 << 10)
#define DGCMD_CMDIOC (1 << 8)
#define DGCMD_CMDTYP_START 0
#define DGCMD_CMDTYP_BITS 8
#define DALEPENA 0xc720 // Device Active USB Endpoint Enable Register
#define DLDMENA 0xc724 // Device LDM Request Control Register
#define DEPCMDPAR2(n) (0xc800 + 0x10 * (n)) // Endpoint-n Command Parameter 2 Register
#define DEPCMDPAR1(n) (0xc804 + 0x10 * (n)) // Endpoint-n Command Parameter 1 Register
#define DEPCMDPAR0(n) (0xc808 + 0x10 * (n)) // Endpoint-n Command Parameter 0 Register
#define DEPCMD(n) (0xc80c + 0x10 * (n)) // Endpoint-n Command Parameter 0 Register
#define DEPCMD_COMMANDPARAM_START 16 // Command Parameters
#define DEPCMD_COMMANDPARAM_BITS 16
#define DEPCMD_CMDSTATUS_START 12 // Command Completion Status
#define DEPCMD_CMDSTATUS_BITS 4
#define DEPCMD_HIPRI_FORCERM (1 << 11) // HighPriority/ForceRM
#define DEPCMD_CMDACT (1 << 10) // Command Active
#define DEPCMD_CMDIOC (1 << 8) // Command Interrupt on Complete
#define DEPCMD_CMDTYP(n) (((n) & 0xf) >> 0)
// Command Types for DEPCMD
#define DEPCFG 1 // Set Endpoint Configuration
#define DEPXFERCFG 2 // Set Endpoint Transfer Resource Configuration
#define DEPGETSTATE 3 // Get EndpointState
#define DEPSSTALL 4 // Set Stall
#define DEPCSTALL 5 // Clear Stall
#define DEPSTRTXFER 6 // Start Transfer
#define DEPUPDXFER 7 // Update Transfer
#define DEPENDXFER 8 // End Transfer
#define DEPSTARTCFG 9 // Start New Configuration
#define DEPCMD_RESOURCE_INDEX(n) (((n) & 0x7f) << 16)
// DEPCFG Params 0
#define DEPCFG_ACTION_INITIALIZE (0 << 30)
#define DEPCFG_ACTION_RESTORE (1 << 30)
#define DEPCFG_ACTION_MODIFY (2 << 30)
#define DEPCFG_BURST_SIZE(n) ((((n) - 1) & 0xf) << 22)
#define DEPCFG_FIFO_NUM(n) (((n) & 0x1f) << 17)
#define DEPCFG_INTERNAL_RETRY (1 << 15)
#define DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
#define DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
// DEPCFG Params 1
#define DEPCFG_FIFO_BASED (1 << 31)
#define DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DEPCFG_STREAM_CAPABLE (1 << 24)
#define DEPCFG_INTERVAL(n) (((n) & 0xff) << 16)
#define DEPCFG_EBC (1 << 15) // External Buffer Control
#define DEPCFG_EBC_NO_WRITE_BACK (1 << 14) // Don't write back HWO bit to the TRB descriptor
#define DEPCFG_STREAM_EVT_EN (1 << 13)
#define DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DEPCFG_INTR_NUM(n) (((n) & 0x1f) << 0)
// DEPXFERCFG Params 0
#define DEPXFERCFG_NUM_XFER_RES(n) (((n) & 0xff) << 0)
#define DEV_IMOD(n) (0xca00 + 4 * (n)) // Device Interrupt Moderation Register
// BC register offsets
#define BCFG 0xcc30 // BC Configuration Register
#define BCEVT 0xcc38 // BC Event Register
#define BCEVTEN 0xcc3c // BC Event Enable Register
// Link register offsets
#define LU1LFPSRXTIM(n) (0xd000 + 0x80 * (n)) // U1_LFPS_RX_TIMER_REG
#define LU1LFPSTXTIM(n) (0xd004 + 0x80 * (n)) // U1 LFPS TX TIMER REGISTER
#define LU2LFPSRXTIM(n) (0xd008 + 0x80 * (n)) // U1 LFPS RX TIMER REGISTER
#define LU2LFPSTXTIM(n) (0xd00c + 0x80 * (n)) // U2 LFPS TX TIMER REG REGISTER
#define LU3LFPSRXTIM(n) (0xd010 + 0x80 * (n)) // U3 LFPS RX TIMER REGS REGISTER
#define LU3LFPSTXTIM(n) (0xd014 + 0x80 * (n)) // U3 LFPS TX TIMER REGS REGISTER
#define LPINGLFPSTIM(n) (0xd018 + 0x80 * (n)) // PING LFPS TIMER REGISTER
#define LPOLLLFPSTXTIM(n) (0xd01c + 0x80 * (n)) // POLL LFPS TX TIMER REGISTER
#define LSKIPFREQ(n) (0xd020 + 0x80 * (n)) // SKIP FREQUENCY REGISTER
#define LLUCTL(n) (0xd024 + 0x80 * (n)) // TX TS1 COUNT REGISTER
#define LPTMDPDELAY(n) (0xd028 + 0x80 * (n)) // PTM DATAPATH DELAY REGISTER
#define LSCDTIM1(n) (0xd02c + 0x80 * (n)) // SCD TIMER 1 REGISTER
#define LSCDTIM2(n) (0xd030 + 0x80 * (n)) // SCD TIMER 2 REGISTER
#define LSCDTIM3(n) (0xd034 + 0x80 * (n)) // SCD TIMER 3 REGISTER
#define LSCDTIM4(n) (0xd038 + 0x80 * (n)) // SCD TIMER 4 REGISTER
#define LLPBMTIM1(n) (0xd03c + 0x80 * (n)) // LPBM TIMER 1 REGISTER
#define LLPBMTIM2(n) (0xd040 + 0x80 * (n)) // LPBM TIMER 2 REGISTER
#define LLPBMTXTIM(n) (0xd044 + 0x80 * (n)) // LPBM TX TIMER REGISTER
#define LLINKERRINJ(n) (0xd048 + 0x80 * (n)) // LINK ERROR TYPE INJECT REGISTER
#define LLINKERRINJEN(n) (0xd04c + 0x80 * (n)) // LINK ERROR INJECT ENABLE REGISTER
#define GDBGLTSSM(n) (0xd050 + 0x80 * (n)) // Global Debug LTSSM Register
#define GDBGLNMCC(n) (0xd054 + 0x80 * (n)) // Global Debug LNMCC Register
#define LLINKDBGCTRL(n) (0xd058 + 0x80 * (n)) // LINK DEBUG CONTROL REGISTER
#define LLINKDBGCNTTRIG(n) (0xd05c + 0x80 * (n)) // LINK DEBUG COUNT TRIGGER REGISTER
#define LCSR_TX_DEEMPH(n) (0xd060 + 0x80 * (n)) // LCSR_TX_DEEMPH REGISTER
#define LCSR_TX_DEEMPH_1(n) (0xd064 + 0x80 * (n)) // LCSR_TX_DEEMPH_1 REGISTER
#define LCSR_TX_DEEMPH_2(n) (0xd068 + 0x80 * (n)) // LCSR_TX_DEEMPH_2 REGISTER
#define LCSR_TX_DEEMPH_3(n) (0xd06c + 0x80 * (n)) // LCSR_TX_DEEMPH_3 REGISTER
#define LCSRPTMDEBUG1(n) (0xd070 + 0x80 * (n)) // LCSRPTMDEBUG1 REGISTER
#define LCSRPTMDEBUG2(n) (0xd074 + 0x80 * (n)) // LCSRPTMDELAY2 REGISTER
+122
Ver Arquivo
@@ -0,0 +1,122 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
// Transfer Request Block
typedef volatile struct {
uint32_t ptr_low;
uint32_t ptr_high;
uint32_t status;
uint32_t control;
} __PACKED dwc3_trb_t;
// TRB status fields
#define TRB_BUFSIZ_START 0 // Buffer Size
#define TRB_BUFSIZ_BITS 24
#define TRB_BUFSIZ(n) (((n) & 0xffffff) << 0)
#define TRB_PCM1_START 24 // Packet Count M1
#define TRB_PCM1_BITS 2
#define TRB_SPR (1 << 26) // Short Packet Received
#define TRB_TRBSTS_START 28 // TRB Status
#define TRB_TRBSTS_BITS 4
// TRB control fields
#define TRB_HWO (1 << 0) // Hardware Owner of Descriptor
#define TRB_LST (1 << 1) // Last TRB
#define TRB_CHN (1 << 2) // Chain Buffers
#define TRB_CSP (1 << 3) // Continue on Short Packet
#define TRB_TRBCTL_START 4
#define TRB_TRBCTL_BITS 5
#define TRB_TRBCTL(c) ((c) & (((1 << TRB_TRBCTL_BITS) - 1) << TRB_TRBCTL_START))
#define TRB_TRBCTL_NORMAL (1 << TRB_TRBCTL_START)
#define TRB_TRBCTL_SETUP (2 << TRB_TRBCTL_START)
#define TRB_TRBCTL_STATUS_2 (3 << TRB_TRBCTL_START)
#define TRB_TRBCTL_STATUS_3 (4 << TRB_TRBCTL_START)
#define TRB_TRBCTL_CONTROL_DATA (5 << TRB_TRBCTL_START)
#define TRB_TRBCTL_ISOCH_FIRST (6 << TRB_TRBCTL_START)
#define TRB_TRBCTL_ISOCH (7 << TRB_TRBCTL_START)
#define TRB_TRBCTL_LINK (8 << TRB_TRBCTL_START)
#define TRB_ISP (1 << 10) // Interrupt on Short Packet
#define TRB_IMI (1 << 10) // Interrupt on Missed ISOC
#define TRB_IOC (1 << 11) // Interrupt on Complete
#define TRB_STREAM_ID_START 14 // Stream ID
#define TRB_STREAM_ID_BITS 16
#define TRB_SOF_NUM_START 14 // SOF Number
#define TRB_SOF_NUM_BITS 16
// DEPEVT (endpoint specific)
#define DEPEVT_PARAMS_START 16 // Event Parameters
#define DEPEVT_PARAMS_BITS 16
#define DEPEVT_STATUS_START 12 // Event Status
#define DEPEVT_STATUS_BITS 4
#define DEPEVT_TYPE_START 6 // Event Type
#define DEPEVT_TYPE_BITS 4
#define DEPEVT_PHYS_EP_START 1
#define DEPEVT_PHYS_EP_BITS 5
#define DEPEVT_NON_EP (1 << 0) // Event is not endpoint specific
#define DEPEVT_PARAMS(e) (((e) >> DEPEVT_PARAMS_START) & ((1 << DEPEVT_PARAMS_BITS) - 1))
#define DEPEVT_STATUS(e) (((e) >> DEPEVT_STATUS_START) & ((1 << DEPEVT_STATUS_BITS) - 1))
#define DEPEVT_TYPE(e) (((e) >> DEPEVT_TYPE_START) & ((1 << DEPEVT_TYPE_BITS) - 1))
#define DEPEVT_PHYS_EP(e) (((e) >> DEPEVT_PHYS_EP_START) & \
((1 << DEPEVT_PHYS_EP_BITS) - 1))
// event parameters for DEPEVT_CMD_CMPLT
#define DEPEVT_CMD_CMPLT_CMD_TYPE_START 24
#define DEPEVT_CMD_CMPLT_CMD_TYPE_BITS 4
#define DEPEVT_CMD_CMPLT_CMD_TYPE(e) (((e) >> DEPEVT_CMD_CMPLT_CMD_TYPE_START) & \
((1 << DEPEVT_CMD_CMPLT_CMD_TYPE_BITS) - 1))
#define DEPEVT_CMD_CMPLT_RSRC_ID_START 16
#define DEPEVT_CMD_CMPLT_RSRC_ID_BITS 7
#define DEPEVT_CMD_CMPLT_RSRC_ID(e) (((e) >> DEPEVT_CMD_CMPLT_RSRC_ID_START) & \
((1 << DEPEVT_CMD_CMPLT_RSRC_ID_BITS) - 1))
// event parameters for DEPEVT_XFER_NOT_READY
#define DEPEVT_XFER_NOT_READY_REASON (1 << 15)
#define DEPEVT_XFER_NOT_READY_STAGE_START 12
#define DEPEVT_XFER_NOT_READY_STAGE_BITS 2
#define DEPEVT_XFER_NOT_READY_STAGE(e) (((e) >> DEPEVT_XFER_NOT_READY_STAGE_START) & \
((1 << DEPEVT_XFER_NOT_READY_STAGE_BITS) - 1))
#define DEPEVT_XFER_NOT_READY_STAGE_DATA 1
#define DEPEVT_XFER_NOT_READY_STAGE_STATUS 2
// DEPEVT event types
#define DEPEVT_XFER_COMPLETE 1
#define DEPEVT_XFER_IN_PROGRESS 2
#define DEPEVT_XFER_NOT_READY 3
#define DEPEVT_STREAM_EVT 6
#define DEPEVT_CMD_CMPLT 7
// DEVT (device specific)
#define DEVT_INFO_START 16 // Event Information Bits
#define DEVT_INFO_BITS 16
#define DEVT_TYPE_START 8 // Event type
#define DEVT_TYPE_BITS 7
#define DEVT_NON_EP (1 << 0) // Event is not endpoint specific
#define DEVT_INFO(e) (((e) >> DEVT_INFO_START) & ((1 << DEVT_INFO_BITS) - 1))
#define DEVT_TYPE(e) (((e) >> DEVT_TYPE_START) & ((1 << DEVT_TYPE_BITS) - 1))
// DEVT event types
#define DEVT_DISCONNECT 0
#define DEVT_USB_RESET 1
#define DEVT_CONNECTION_DONE 2
#define DEVT_LINK_STATE_CHANGE 3
#define DEVT_REMOTE_WAKEUP 4
#define DEVT_HIBERNATE_REQUEST 5
#define DEVT_SUSPEND_ENTRY 6
#define DEVT_SOF 7
#define DEVT_ERRATIC_ERROR 9
#define DEVT_COMMAND_COMPLETE 10
#define DEVT_EVENT_BUF_OVERFLOW 11
#define DEVT_VENDOR_TEST_LMP 12
#define DEVT_STOPPED_DISCONNECT 13
#define DEVT_L1_RESUME_DETECT 14
#define DEVT_LDM_RESPONSE 15
// for DEVT_LINK_STATE_CHANGE
#define DEVT_LINK_STATE_CHANGE_SS (1 << 4) // Set if link is super speed
#define DEVT_LINK_STATE_CHANGE_STATE(s) ((s) & 0xf) // Same as DSTS state
+403
Ver Arquivo
@@ -0,0 +1,403 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/protocol/platform-devices.h>
#include <ddk/protocol/usb-function.h>
#include <hw/reg.h>
#include <pretty/hexdump.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "dwc3.h"
#include "dwc3-regs.h"
#include "dwc3-types.h"
// MMIO indices
enum {
MMIO_USB3OTG,
};
// IRQ indices
enum {
IRQ_USB3,
};
void dwc3_wait_bits(volatile uint32_t* ptr, uint32_t bits, uint32_t expected) {
uint32_t value = DWC3_READ32(ptr);
while ((value & bits) != expected) {
usleep(1000);
value = DWC3_READ32(ptr);
}
}
void dwc3_print_status(dwc3_t* dwc) {
volatile void* mmio = dwc3_mmio(dwc);
uint32_t status = DWC3_READ32(mmio + DSTS);
dprintf(TRACE, "DSTS: ");
dprintf(TRACE, "USBLNKST: %d ", DSTS_USBLNKST(status));
dprintf(TRACE, "SOFFN: %d ", DSTS_SOFFN(status));
dprintf(TRACE, "CONNECTSPD: %d ", DSTS_CONNECTSPD(status));
if (status & DSTS_DCNRD) dprintf(TRACE, "DCNRD ");
if (status & DSTS_SRE) dprintf(TRACE, "SRE ");
if (status & DSTS_RSS) dprintf(TRACE, "RSS ");
if (status & DSTS_SSS) dprintf(TRACE, "SSS ");
if (status & DSTS_COREIDLE) dprintf(TRACE, "COREIDLE ");
if (status & DSTS_DEVCTRLHLT) dprintf(TRACE, "DEVCTRLHLT ");
if (status & DSTS_RXFIFOEMPTY) dprintf(TRACE, "RXFIFOEMPTY ");
dprintf(TRACE, "\n");
status = DWC3_READ32(mmio + GSTS);
dprintf(TRACE, "GSTS: ");
dprintf(TRACE, "CBELT: %d ", GSTS_CBELT(status));
dprintf(TRACE, "CURMOD: %d ", GSTS_CURMOD(status));
if (status & GSTS_SSIC_IP) dprintf(TRACE, "SSIC_IP ");
if (status & GSTS_OTG_IP) dprintf(TRACE, "OTG_IP ");
if (status & GSTS_BC_IP) dprintf(TRACE, "BC_IP ");
if (status & GSTS_ADP_IP) dprintf(TRACE, "ADP_IP ");
if (status & GSTS_HOST_IP) dprintf(TRACE, "HOST_IP ");
if (status & GSTS_DEVICE_IP) dprintf(TRACE, "DEVICE_IP ");
if (status & GSTS_CSR_TIMEOUT) dprintf(TRACE, "CSR_TIMEOUT ");
if (status & GSTS_BUSERRADDRVLD) dprintf(TRACE, "BUSERRADDRVLD ");
dprintf(TRACE, "\n");
}
static mx_status_t dwc3_start(dwc3_t* dwc) {
volatile void* mmio = dwc3_mmio(dwc);
uint32_t temp;
mtx_lock(&dwc->lock);
temp = DWC3_READ32(mmio + DCTL);
temp &= ~DCTL_RUN_STOP;
temp |= DCTL_CSFTRST;
DWC3_WRITE32(mmio + DCTL, temp);
dwc3_wait_bits(mmio + DCTL, DCTL_CSFTRST, 0);
// configure and enable PHYs
temp = DWC3_READ32(mmio + GUSB2PHYCFG(0));
temp &= ~(GUSB2PHYCFG_USBTRDTIM_MASK | GUSB2PHYCFG_SUSPENDUSB20);
temp |= GUSB2PHYCFG_USBTRDTIM(9);
DWC3_WRITE32(mmio + GUSB2PHYCFG(0), temp);
temp = DWC3_READ32(mmio + GUSB3PIPECTL(0));
temp &= ~(GUSB3PIPECTL_DELAYP1TRANS | GUSB3PIPECTL_SUSPENDENABLE);
temp |= GUSB3PIPECTL_LFPSFILTER | GUSB3PIPECTL_SS_TX_DE_EMPHASIS(1);
DWC3_WRITE32(mmio + GUSB3PIPECTL(0), temp);
// configure for device mode
DWC3_WRITE32(mmio + GCTL, GCTL_U2EXIT_LFPS | GCTL_PRTCAPDIR_DEVICE | GCTL_U2RSTECN |
GCTL_PWRDNSCALE(2));
temp = DWC3_READ32(mmio + DCFG);
uint32_t nump = 16;
uint32_t max_speed = DCFG_DEVSPD_SUPER;
temp &= ~DWC3_MASK(DCFG_NUMP_START, DCFG_NUMP_BITS);
temp |= nump << DCFG_NUMP_START;
temp &= ~DWC3_MASK(DCFG_DEVSPD_START, DCFG_DEVSPD_BITS);
temp |= max_speed << DCFG_DEVSPD_START;
temp &= ~DWC3_MASK(DCFG_DEVADDR_START, DCFG_DEVADDR_BITS); // clear address
DWC3_WRITE32(mmio + DCFG, temp);
dwc3_events_start(dwc);
mtx_unlock(&dwc->lock);
dwc3_ep0_start(dwc);
mtx_lock(&dwc->lock);
// start the controller
DWC3_WRITE32(mmio + DCTL, DCTL_RUN_STOP);
temp = DWC3_READ32(mmio + DCTL);
temp |= DCTL_RUN_STOP;
DWC3_WRITE32(mmio + DCTL, temp);
mtx_unlock(&dwc->lock);
return MX_OK;
}
void dwc3_usb_reset(dwc3_t* dwc) {
dprintf(INFO, "dwc3_usb_reset\n");
dwc3_ep0_reset(dwc);
for (unsigned i = 2; i < countof(dwc->eps); i++) {
dwc3_ep_end_transfers(dwc, i, MX_ERR_IO_NOT_PRESENT);
dwc3_ep_set_stall(dwc, i, false);
}
dwc3_set_address(dwc, 0);
dwc3_ep0_start(dwc);
usb_dci_set_connected(&dwc->dci_intf, true);
}
void dwc3_disconnected(dwc3_t* dwc) {
dprintf(INFO, "dwc3_disconnected\n");
dwc3_cmd_ep_end_transfer(dwc, EP0_OUT);
dwc->ep0_state = EP0_STATE_NONE;
if (dwc->dci_intf.ops) {
usb_dci_set_connected(&dwc->dci_intf, false);
}
for (unsigned i = 2; i < countof(dwc->eps); i++) {
dwc3_ep_end_transfers(dwc, i, MX_ERR_IO_NOT_PRESENT);
dwc3_ep_set_stall(dwc, i, false);
}
}
void dwc3_connection_done(dwc3_t* dwc) {
volatile void* mmio = dwc3_mmio(dwc);
mtx_lock(&dwc->lock);
uint32_t status = DWC3_READ32(mmio + DSTS);
uint32_t speed = DSTS_CONNECTSPD(status);
unsigned ep0_max_packet = 0;
switch (speed) {
case DSTS_CONNECTSPD_HIGH:
dwc->speed = USB_SPEED_HIGH;
ep0_max_packet = 64;
break;
case DSTS_CONNECTSPD_FULL:
dwc->speed = USB_SPEED_FULL;
ep0_max_packet = 64;
break;
case DSTS_CONNECTSPD_SUPER:
case DSTS_CONNECTSPD_ENHANCED_SUPER:
dwc->speed = USB_SPEED_SUPER;
ep0_max_packet = 512;
break;
default:
dprintf(ERROR, "dwc3_connection_done: unsupported speed %u\n", speed);
dwc->speed = USB_SPEED_UNDEFINED;
break;
}
mtx_unlock(&dwc->lock);
if (ep0_max_packet) {
dwc->eps[EP0_OUT].max_packet_size = ep0_max_packet;
dwc->eps[EP0_IN].max_packet_size = ep0_max_packet;
dwc3_cmd_ep_set_config(dwc, EP0_OUT, USB_ENDPOINT_CONTROL, ep0_max_packet, 0, true);
dwc3_cmd_ep_set_config(dwc, EP0_IN, USB_ENDPOINT_CONTROL, ep0_max_packet, 0, true);
}
usb_dci_set_speed(&dwc->dci_intf, dwc->speed);
}
void dwc3_set_address(dwc3_t* dwc, unsigned address) {
volatile void* mmio = dwc3_mmio(dwc);
mtx_lock(&dwc->lock);
DWC3_SET_BITS32(mmio + DCFG, DCFG_DEVADDR_START, DCFG_DEVADDR_BITS, address);
mtx_unlock(&dwc->lock);
}
void dwc3_reset_configuration(dwc3_t* dwc) {
volatile void* mmio = dwc3_mmio(dwc);
mtx_lock(&dwc->lock);
// disable all endpoints except EP0_OUT and EP0_IN
DWC3_WRITE32(mmio + DALEPENA, (1 << EP0_OUT) | (1 << EP0_IN));
mtx_unlock(&dwc->lock);
for (unsigned i = 2; i < countof(dwc->eps); i++) {
dwc3_ep_end_transfers(dwc, i, MX_ERR_IO_NOT_PRESENT);
dwc3_ep_set_stall(dwc, i, false);
}
}
static mx_status_t dwc3_set_interface(void* ctx, usb_dci_interface_t* dci_intf) {
dwc3_t* dwc = ctx;
memcpy(&dwc->dci_intf, dci_intf, sizeof(dwc->dci_intf));
return MX_OK;
}
static mx_status_t dwc3_config_ep(void* ctx, usb_endpoint_descriptor_t* ep_desc,
usb_ss_ep_comp_descriptor_t* ss_comp_desc) {
dwc3_t* dwc = ctx;
return dwc3_ep_config(dwc, ep_desc, ss_comp_desc);
}
static mx_status_t dwc3_disable_ep(void* ctx, uint8_t ep_addr) {
dwc3_t* dwc = ctx;
return dwc3_ep_disable(dwc, ep_addr);
}
static mx_status_t dwc_set_enabled(void* ctx, bool enabled) {
dwc3_t* dwc = ctx;
if (enabled) {
return dwc3_start(dwc);
} else {
// TODO(voydanoff) more cleanup to do here?
dwc3_disconnected(dwc);
return MX_OK;
}
}
static mx_status_t dwc3_set_stall(void* ctx, uint8_t ep_address) {
dwc3_t* dwc = ctx;
return dwc3_ep_set_stall(dwc, dwc3_ep_num(ep_address), true);
}
static mx_status_t dwc3_clear_stall(void* ctx, uint8_t ep_address) {
dwc3_t* dwc = ctx;
return dwc3_ep_set_stall(dwc, dwc3_ep_num(ep_address), false);
}
usb_dci_protocol_ops_t dwc_dci_protocol = {
.set_interface = dwc3_set_interface,
.config_ep = dwc3_config_ep,
.disable_ep = dwc3_disable_ep,
.set_enabled = dwc_set_enabled,
.ep_set_stall = dwc3_set_stall,
.ep_clear_stall = dwc3_clear_stall,
};
static void dwc3_unbind(void* ctx) {
dwc3_t* dwc = ctx;
mx_interrupt_signal(dwc->irq_handle);
thrd_join(dwc->irq_thread, NULL);
device_remove(dwc->mxdev);
}
static void dwc3_iotxn_queue(void* ctx, iotxn_t* txn) {
dwc3_t* dwc = ctx;
if (txn->protocol != MX_PROTOCOL_USB_FUNCTION) {
iotxn_complete(txn, MX_ERR_NOT_SUPPORTED, 0);
return;
}
usb_function_protocol_data_t* data = iotxn_pdata(txn, usb_function_protocol_data_t);
dprintf(LTRACE, "dwc3_iotxn_queue ep: %u\n", data->ep_address);
unsigned ep_num = dwc3_ep_num(data->ep_address);
if (ep_num < 2 || ep_num >= countof(dwc->eps)) {
dprintf(ERROR, "dwc3_iotxn_queue: bad ep address 0x%02X\n", data->ep_address);
iotxn_complete(txn, MX_ERR_INVALID_ARGS, 0);
return;
}
dwc3_ep_queue(dwc, ep_num, txn);
}
static void dwc3_release(void* ctx) {
dwc3_t* dwc = ctx;
for (unsigned i = 0; i < countof(dwc->eps); i++) {
dwc3_ep_fifo_release(dwc, i);
}
io_buffer_release(&dwc->event_buffer);
io_buffer_release(&dwc->ep0_buffer);
pdev_mmio_buffer_release(&dwc->mmio);
mx_handle_close(dwc->irq_handle);
free(dwc);
}
static mx_protocol_device_t dwc3_device_proto = {
.version = DEVICE_OPS_VERSION,
.iotxn_queue = dwc3_iotxn_queue,
.release = dwc3_release,
};
static mx_status_t dwc3_bind(void* ctx, mx_device_t* dev, void** cookie) {
dprintf(INFO, "dwc3_bind\n");
dwc3_t* dwc = calloc(1, sizeof(dwc3_t));
if (!dwc) {
return MX_ERR_NO_MEMORY;
}
platform_device_protocol_t pdev;
mx_status_t status = device_get_protocol(dev, MX_PROTOCOL_PLATFORM_DEV, &pdev);
if (status != MX_OK) {
goto fail;
}
mtx_init(&dwc->lock, mtx_plain);
for (unsigned i = 0; i < countof(dwc->eps); i++) {
dwc3_endpoint_t* ep = &dwc->eps[i];
ep->ep_num = i;
mtx_init(&ep->lock, mtx_plain);
list_initialize(&ep->queued_txns);
}
status = pdev_map_mmio_buffer(&pdev, MMIO_USB3OTG, MX_CACHE_POLICY_UNCACHED_DEVICE,
&dwc->mmio);
if (status != MX_OK) {
dprintf(ERROR, "dwc3_bind: pdev_map_mmio_buffer failed\n");
goto fail;
}
status = pdev_map_interrupt(&pdev, IRQ_USB3, &dwc->irq_handle);
if (status != MX_OK) {
dprintf(ERROR, "dwc3_bind: pdev_map_interrupt failed\n");
goto fail;
}
status = io_buffer_init(&dwc->event_buffer, EVENT_BUFFER_SIZE, IO_BUFFER_RO);
if (status != MX_OK) {
dprintf(ERROR, "dwc3_bind: io_buffer_init failed\n");
goto fail;
}
io_buffer_cache_op(&dwc->event_buffer, MX_VMO_OP_CACHE_CLEAN, 0, EVENT_BUFFER_SIZE);
status = io_buffer_init(&dwc->ep0_buffer, 65536, IO_BUFFER_RW);
if (status != MX_OK) {
dprintf(ERROR, "dwc3_bind: io_buffer_init failed\n");
goto fail;
}
status = dwc3_ep0_init(dwc);
if (status != MX_OK) {
dprintf(ERROR, "dwc3_bind: dwc3_ep_init failed\n");
goto fail;
}
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "dwc3",
.ctx = dwc,
.ops = &dwc3_device_proto,
.proto_id = MX_PROTOCOL_USB_DCI,
.proto_ops = &dwc_dci_protocol,
};
status = device_add(dev, &args, &dwc->mxdev);
if (status != MX_OK) {
goto fail;
}
return MX_OK;
fail:
dprintf(ERROR, "dwc3_bind failed %d\n", status);
dwc3_release(dwc);
return status;
}
static mx_driver_ops_t dwc3_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = dwc3_bind,
};
// The formatter does not play nice with these macros.
// clang-format off
MAGENTA_DRIVER_BEGIN(dwc3, dwc3_driver_ops, "magenta", "0.1", 3)
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_USB_DWC3),
MAGENTA_DRIVER_END(dwc3)
// clang-format on
+153
Ver Arquivo
@@ -0,0 +1,153 @@
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <ddk/device.h>
#include <ddk/io-buffer.h>
#include <ddk/protocol/platform-device.h>
#include <ddk/protocol/usb-dci.h>
#include <magenta/listnode.h>
#include <magenta/types.h>
#include <magenta/hw/usb.h>
#include <threads.h>
#include "dwc3-types.h"
// physical endpoint numbers for ep0
#define EP0_OUT 0
#define EP0_IN 1
#define EP0_FIFO EP0_OUT
#define EP_OUT(ep_num) (((ep_num) & 1) == 0)
#define EP_IN(ep_num) (((ep_num) & 1) == 1)
#define EVENT_BUFFER_SIZE PAGE_SIZE
#define EP0_MAX_PACKET_SIZE 512
#define DWC3_MAX_EPS 32
// converts a USB endpoint address to 0 - 31 index
#define dwc3_ep_num(addr) ((((addr) & 0xF) << 1) | !!((addr) & USB_DIR_IN))
typedef enum {
EP0_STATE_NONE,
EP0_STATE_SETUP, // Queued setup phase
EP0_STATE_DATA_OUT, // Queued data on EP0_OUT
EP0_STATE_DATA_IN, // Queued data on EP0_IN
EP0_STATE_WAIT_NRDY_OUT, // Waiting for not-ready on EP0_OUT
EP0_STATE_WAIT_NRDY_IN, // Waiting for not-ready on EP0_IN
EP0_STATE_STATUS, // Waiting for status to complete
} dwc3_ep0_state;
typedef struct {
io_buffer_t buffer;
dwc3_trb_t* first; // first TRB in the fifo
dwc3_trb_t* next; // next free TRB in the fifo
dwc3_trb_t* current; // TRB for currently pending transaction
dwc3_trb_t* last; // last TRB in the fifo (link TRB)
} dwc3_fifo_t;
typedef struct {
dwc3_fifo_t fifo;
list_node_t queued_txns; // iotxns waiting to be processed
iotxn_t* current_txn; // iotxn currently being processed
unsigned rsrc_id; // resource ID for current_txn
// Used for synchronizing endpoint state
// and ep specific hardware registers
// This should be acquired before dwc3_t.lock
// if acquiring both locks.
mtx_t lock;
uint16_t max_packet_size;
uint8_t ep_num;
bool enabled;
uint8_t type; // control, bulk, interrupt or isochronous
uint8_t interval;
// TODO(voydanoff) USB 3 specific stuff here
bool got_not_ready;
bool stalled;
} dwc3_endpoint_t;
typedef struct {
mx_device_t* mxdev;
usb_dci_interface_t dci_intf;
pdev_mmio_buffer_t mmio;
// event stuff
io_buffer_t event_buffer;
mx_handle_t irq_handle;
thrd_t irq_thread;
dwc3_endpoint_t eps[DWC3_MAX_EPS];
// connection state
usb_speed_t speed;
// ep0 stuff
usb_setup_t cur_setup; // current setup request
io_buffer_t ep0_buffer;
dwc3_ep0_state ep0_state;
// Used for synchronizing global state
// and non ep specific hardware registers.
// dwc3_endpoint_t.lock should be acquired first
// if acquiring both locks.
mtx_t lock;
bool configured;
} dwc3_t;
static inline volatile void* dwc3_mmio(dwc3_t* dwc) {
return dwc->mmio.vaddr;
}
void dwc3_usb_reset(dwc3_t* dwc);
void dwc3_disconnected(dwc3_t* dwc);
void dwc3_connection_done(dwc3_t* dwc);
void dwc3_set_address(dwc3_t* dwc, unsigned address);
void dwc3_reset_configuration(dwc3_t* dwc);
// Commands
void dwc3_cmd_start_new_config(dwc3_t* dwc, unsigned ep_num, unsigned resource_index);
void dwc3_cmd_ep_set_config(dwc3_t* dwc, unsigned ep_num, unsigned ep_type,
unsigned max_packet_size, unsigned interval, bool modify);
void dwc3_cmd_ep_transfer_config(dwc3_t* dwc, unsigned ep_num);
void dwc3_cmd_ep_start_transfer(dwc3_t* dwc, unsigned ep_num, mx_paddr_t trb_phys);
void dwc3_cmd_ep_end_transfer(dwc3_t* dwc, unsigned ep_num);
void dwc3_cmd_ep_set_stall(dwc3_t* dwc, unsigned ep_num);
void dwc3_cmd_ep_clear_stall(dwc3_t* dwc, unsigned ep_num);
// Endpoints
mx_status_t dwc3_ep_fifo_init(dwc3_t* dwc, unsigned ep_num);
void dwc3_ep_fifo_release(dwc3_t* dwc, unsigned ep_num);
mx_status_t dwc3_ep_config(dwc3_t* dwc, usb_endpoint_descriptor_t* ep_desc,
usb_ss_ep_comp_descriptor_t* ss_comp_desc);
void dwc3_ep_set_config(dwc3_t* dwc, unsigned ep_num, bool enable);
mx_status_t dwc3_ep_disable(dwc3_t* dwc, uint8_t ep_addr);
void dwc3_start_eps(dwc3_t* dwc);
void dwc3_ep_queue(dwc3_t* dwc, unsigned ep_num, iotxn_t* txn);
void dwc3_ep_start_transfer(dwc3_t* dwc, unsigned ep_num, unsigned type, mx_paddr_t buffer,
size_t length);
void dwc3_ep_xfer_started(dwc3_t* dwc, unsigned ep_num, unsigned rsrc_id);
void dwc3_ep_xfer_complete(dwc3_t* dwc, unsigned ep_num);
void dwc3_ep_xfer_not_ready(dwc3_t* dwc, unsigned ep_num, unsigned stage);
mx_status_t dwc3_ep_set_stall(dwc3_t* dwc, unsigned ep_num, bool stall);
void dwc3_ep_end_transfers(dwc3_t* dwc, unsigned ep_num, mx_status_t reason);
// Endpoint 0
mx_status_t dwc3_ep0_init(dwc3_t* dwc);
void dwc3_ep0_reset(dwc3_t* dwc);
void dwc3_ep0_start(dwc3_t* dwc);
void dwc3_ep0_xfer_not_ready(dwc3_t* dwc, unsigned ep_num, unsigned stage);
void dwc3_ep0_xfer_complete(dwc3_t* dwc, unsigned ep_num);
// Events
void dwc3_events_start(dwc3_t* dwc);
// Utils
void dwc3_wait_bits(volatile uint32_t* ptr, uint32_t bits, uint32_t expected);
void dwc3_print_status(dwc3_t* dwc);
+22
Ver Arquivo
@@ -0,0 +1,22 @@
# Copyright 2016 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_TYPE := driver
MODULE_SRCS += \
$(LOCAL_DIR)/dwc3.c \
$(LOCAL_DIR)/dwc3-commands.c \
$(LOCAL_DIR)/dwc3-endpoints.c \
$(LOCAL_DIR)/dwc3-ep0.c \
$(LOCAL_DIR)/dwc3-events.c
MODULE_STATIC_LIBS := system/ulib/ddk system/ulib/sync system/ulib/pretty
MODULE_LIBS := system/ulib/driver system/ulib/magenta system/ulib/c
include make/module.mk
+94 -23
Ver Arquivo
@@ -5,6 +5,7 @@
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/driver.h>
#include <ddk/protocol/platform-devices.h>
#include <ddk/protocol/usb-hci.h>
#include <ddk/protocol/usb.h>
@@ -26,6 +27,9 @@
#define DEFAULT_PRIORITY 16
#define HIGH_PRIORITY 24
#define PDEV_MMIO_INDEX 0
#define PDEV_IRQ_INDEX 0
mx_status_t xhci_add_device(xhci_t* xhci, int slot_id, int hub_address, int speed) {
dprintf(TRACE, "xhci_add_new_device\n");
@@ -264,19 +268,17 @@ error_return:
return status;
}
static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
static mx_status_t usb_xhci_bind_common(mx_device_t* parent, pci_protocol_t* pci) {
return 0;
}
static mx_status_t usb_xhci_bind_pci(mx_device_t* parent, pci_protocol_t* pci) {
mx_handle_t mmio_handle = MX_HANDLE_INVALID;
mx_handle_t cfg_handle = MX_HANDLE_INVALID;
xhci_t* xhci = NULL;
uint32_t num_irq_handles_initialized = 0;
mx_status_t status;
pci_protocol_t pci;
if (device_get_protocol(dev, MX_PROTOCOL_PCI, &pci)) {
status = MX_ERR_NOT_SUPPORTED;
goto error_return;
}
xhci = calloc(1, sizeof(xhci_t));
if (!xhci) {
status = MX_ERR_NO_MEMORY;
@@ -289,7 +291,7 @@ static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
* eXtensible Host Controller Interface revision 1.1, section 5, xhci
* should only use BARs 0 and 1. 0 for 32 bit addressing, and 0+1 for 64 bit addressing.
*/
status = pci_map_resource(&pci, PCI_RESOURCE_BAR_0, MX_CACHE_POLICY_UNCACHED_DEVICE,
status = pci_map_resource(pci, PCI_RESOURCE_BAR_0, MX_CACHE_POLICY_UNCACHED_DEVICE,
&mmio, &mmio_len, &mmio_handle);
if (status != MX_OK) {
dprintf(ERROR, "usb_xhci_bind could not find bar\n");
@@ -298,7 +300,7 @@ static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
}
uint32_t irq_cnt = 0;
status = pci_query_irq_mode_caps(&pci, MX_PCIE_IRQ_MODE_MSI, &irq_cnt);
status = pci_query_irq_mode_caps(pci, MX_PCIE_IRQ_MODE_MSI, &irq_cnt);
if (status != MX_OK) {
dprintf(ERROR, "pci_query_irq_mode_caps failed %d\n", status);
goto error_return;
@@ -306,9 +308,9 @@ static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
xhci_num_interrupts_init(xhci, mmio, irq_cnt);
// select our IRQ mode
status = pci_set_irq_mode(&pci, MX_PCIE_IRQ_MODE_MSI, xhci->num_interrupts);
status = pci_set_irq_mode(pci, MX_PCIE_IRQ_MODE_MSI, xhci->num_interrupts);
if (status < 0) {
mx_status_t status_legacy = pci_set_irq_mode(&pci, MX_PCIE_IRQ_MODE_LEGACY, 1);
mx_status_t status_legacy = pci_set_irq_mode(pci, MX_PCIE_IRQ_MODE_LEGACY, 1);
if (status_legacy < 0) {
dprintf(ERROR, "usb_xhci_bind Failed to set IRQ mode to either MSI "
@@ -323,7 +325,7 @@ static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
for (uint32_t i = 0; i < xhci->num_interrupts; i++) {
// register for interrupts
status = pci_map_interrupt(&pci, i, &xhci->irq_handles[i]);
status = pci_map_interrupt(pci, i, &xhci->irq_handles[i]);
if (status != MX_OK) {
dprintf(ERROR, "usb_xhci_bind map_interrupt failed %d\n", status);
goto error_return;
@@ -334,9 +336,9 @@ static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
xhci->cfg_handle = cfg_handle;
// stash this here for the startup thread to call device_add() with
xhci->parent = dev;
xhci->parent = parent;
// used for enabling bus mastering
memcpy(&xhci->pci, &pci, sizeof(pci_protocol_t));
memcpy(&xhci->pci, pci, sizeof(pci_protocol_t));
status = xhci_init(xhci, mmio);
if (status != MX_OK) {
@@ -350,18 +352,80 @@ static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* dev, void** cookie) {
return MX_OK;
error_return:
if (xhci) {
free(xhci);
}
free(xhci);
for (uint32_t i = 0; i < num_irq_handles_initialized; i++) {
mx_handle_close(xhci->irq_handles[i]);
}
if (mmio_handle != MX_HANDLE_INVALID) {
mx_handle_close(mmio_handle);
mx_handle_close(mmio_handle);
mx_handle_close(cfg_handle);
return status;
}
static mx_status_t usb_xhci_bind_pdev(mx_device_t* parent, platform_device_protocol_t* pdev) {
mx_handle_t mmio_handle = MX_HANDLE_INVALID;
mx_handle_t irq_handle = MX_HANDLE_INVALID;
xhci_t* xhci = NULL;
mx_status_t status;
xhci = calloc(1, sizeof(xhci_t));
if (!xhci) {
status = MX_ERR_NO_MEMORY;
goto error_return;
}
if (cfg_handle != MX_HANDLE_INVALID) {
mx_handle_close(cfg_handle);
void* mmio;
uint64_t mmio_len;
status = pdev_map_mmio(pdev, PDEV_MMIO_INDEX, MX_CACHE_POLICY_UNCACHED_DEVICE,
&mmio, &mmio_len, &mmio_handle);
if (status != MX_OK) {
dprintf(ERROR, "usb_xhci_bind_pdev: pdev_map_mmio failed\n");
goto error_return;
}
status = pdev_map_interrupt(pdev, PDEV_IRQ_INDEX, &irq_handle);
if (status != MX_OK) {
dprintf(ERROR, "usb_xhci_bind_pdev: pdev_map_interrupt failed\n");
goto error_return;
}
xhci->mmio_handle = mmio_handle;
xhci->irq_handles[0] = irq_handle;
xhci->num_interrupts = 1;
// stash this here for the startup thread to call device_add() with
xhci->parent = parent;
status = xhci_init(xhci, mmio);
if (status != MX_OK) {
goto error_return;
}
thrd_t thread;
thrd_create_with_name(&thread, xhci_start_thread, xhci, "xhci_start_thread");
thrd_detach(thread);
return MX_OK;
error_return:
free(xhci);
mx_handle_close(mmio_handle);
mx_handle_close(irq_handle);
return status;
}
static mx_status_t usb_xhci_bind(void* ctx, mx_device_t* parent, void** cookie) {
pci_protocol_t pci;
platform_device_protocol_t pdev;
mx_status_t status;
if ((status = device_get_protocol(parent, MX_PROTOCOL_PCI, &pci)) == MX_OK) {
return usb_xhci_bind_pci(parent, &pci);
}
if ((status = device_get_protocol(parent, MX_PROTOCOL_PLATFORM_DEV, &pdev)) == MX_OK) {
return usb_xhci_bind_pdev(parent, &pdev);
}
return status;
}
@@ -371,9 +435,16 @@ static mx_driver_ops_t xhci_driver_ops = {
};
// clang-format off
MAGENTA_DRIVER_BEGIN(usb_xhci, xhci_driver_ops, "magenta", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_PCI),
MAGENTA_DRIVER_BEGIN(usb_xhci, xhci_driver_ops, "magenta", "0.1", 8)
// PCI binding support
BI_GOTO_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_PCI, 0),
BI_ABORT_IF(NE, BIND_PCI_CLASS, 0x0C),
BI_ABORT_IF(NE, BIND_PCI_SUBCLASS, 0x03),
BI_MATCH_IF(EQ, BIND_PCI_INTERFACE, 0x30),
// platform bus binding support
BI_LABEL(0),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GENERIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_USB_XHCI),
MAGENTA_DRIVER_END(usb_xhci)
+7 -1
Ver Arquivo
@@ -15,6 +15,7 @@
#include <ddk/device.h>
#include <ddk/protocol/pci.h>
#include <ddk/protocol/platform-device.h>
#include <ddk/protocol/usb-bus.h>
#include "xhci-hw.h"
@@ -99,12 +100,17 @@ struct xhci {
#define INTERRUPTER_COUNT 2
mx_handle_t irq_handles[INTERRUPTER_COUNT];
mx_handle_t mmio_handle;
mx_handle_t cfg_handle;
thrd_t irq_thread;
// used by the start thread
mx_device_t* parent;
// PCI support
pci_protocol_t pci;
mx_handle_t cfg_handle;
// platform device support
platform_device_protocol_t* pdev;
// MMIO data structures
xhci_cap_regs_t* cap_regs;
+23 -1
Ver Arquivo
@@ -52,10 +52,17 @@
#define USB_CDC_DST_OBEX_SERVICE_ID 0x19
#define USB_CDC_DST_NCM 0x1A
/* CDC Class-Specific Notificaiton Codes */
/* CDC Class-Specific Notification Codes */
#define USB_CDC_NC_NETWORK_CONNECTION 0x00
#define USB_CDC_NC_CONNECTION_SPEED_CHANGE 0x2A
/* CDC Ethernet Class-Specific Request Codes */
#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40
#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41
#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42
#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43
#define USB_CDC_GET_ETHERNET_STATISTIC 0x44
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType; // USB_DT_CS_INTERFACE
@@ -86,6 +93,15 @@ typedef struct {
uint8_t bSubordinateInterface[];
} __attribute__ ((packed)) usb_cs_union_interface_descriptor_t;
// fixed size version of usb_cs_union_interface_descriptor_t
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType; // USB_DT_CS_INTERFACE
uint8_t bDescriptorSubType; // USB_CDC_DST_UNION
uint8_t bControlInterface;
uint8_t bSubordinateInterface;
} __attribute__ ((packed)) usb_cs_union_interface_descriptor_1_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType; // USB_DT_CS_INTERFACE
@@ -105,4 +121,10 @@ typedef struct {
uint16_t wLength;
} __attribute__ ((packed)) usb_cdc_notification_t;
typedef struct {
usb_cdc_notification_t notification;
uint32_t downlink_br;
uint32_t uplink_br;
} __attribute__ ((packed)) usb_cdc_speed_change_notification_t;
__BEGIN_CDECLS;