blob: a4e181251971d5dde92eaee1eda374f5bc4e5472 [file] [log] [blame]
/*
* Copyright (c) 2018-2019, NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <string.h>
#include <usb/usb_dc.h>
#include <soc.h>
#include <device.h>
#include "usb_dc_mcux.h"
#include "usb_device_ehci.h"
#ifdef CONFIG_HAS_MCUX_CACHE
#include <fsl_cache.h>
#endif
#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(usb_dc_mcux_ehci);
#define CONTROLLER_ID kUSB_ControllerEhci0
static void usb_isr_handler(void);
extern void USB_DeviceEhciIsrFunction(void *deviceHandle);
/* the setup transfer state */
#define SETUP_DATA_STAGE_DONE (0)
#define SETUP_DATA_STAGE_IN (1)
#define SETUP_DATA_STAGE_OUT (2)
/* Then endpoint number/index calculation */
#define EP_ADDR2IDX(ep) ((ep) & ~USB_EP_DIR_MASK)
#define EP_ADDR2DIR(ep) ((ep) & USB_EP_DIR_MASK)
#define EP_ABS_IDX(ep) (((ep) & ~USB_EP_DIR_MASK) * 2 + (((ep) & USB_EP_DIR_MASK) >> 7))
#define NUM_OF_EP_MAX DT_USBD_MCUX_EHCI_NUM_BIDIR_EP
/* The minimum value is 1 */
#define EP_BUF_NUMOF_BLOCKS ((NUM_OF_EP_MAX + 3) / 4)
/* The max MPS is 1023 for FS, 1024 for HS. */
#if defined(CONFIG_NOCACHE_MEMORY)
#define EP_BUF_NONCACHED
__nocache K_MEM_POOL_DEFINE(ep_buf_pool, 16, 1024, EP_BUF_NUMOF_BLOCKS, 4);
#else
K_MEM_POOL_DEFINE(ep_buf_pool, 16, 1024, EP_BUF_NUMOF_BLOCKS, 4);
#endif
static usb_ep_ctrl_data_t s_ep_ctrl[NUM_OF_EP_MAX];
static usb_device_struct_t s_Device;
#if ((defined(USB_DEVICE_CONFIG_EHCI)) && (USB_DEVICE_CONFIG_EHCI > 0U))
/* EHCI device driver interface */
static const usb_device_controller_interface_struct_t s_UsbDeviceEhciInterface = {
USB_DeviceEhciInit, USB_DeviceEhciDeinit, USB_DeviceEhciSend,
USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl
};
#endif
int usb_dc_reset(void)
{
if (s_Device.controllerHandle != NULL) {
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlStop, NULL);
s_Device.interface->deviceDeinit(s_Device.controllerHandle);
s_Device.controllerHandle = NULL;
}
return 0;
}
int usb_dc_attach(void)
{
s_Device.eps = &s_ep_ctrl[0];
if (s_Device.attached) {
LOG_WRN("already attached");
return 0;
}
s_Device.interface = (const usb_device_controller_interface_struct_t *)(&s_UsbDeviceEhciInterface);
if (kStatus_USB_Success != s_Device.interface->deviceInit(CONTROLLER_ID, &s_Device, &s_Device.controllerHandle)) {
return -EINVAL;
}
/* Connect and enable USB interrupt */
IRQ_CONNECT(DT_USBD_MCUX_EHCI_IRQ, DT_USBD_MCUX_EHCI_IRQ_PRI,
usb_isr_handler, 0, 0);
irq_enable(DT_USBD_MCUX_EHCI_IRQ);
s_Device.attached = 1;
LOG_DBG("attached");
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlRun, NULL);
return 0;
}
int usb_dc_detach(void)
{
LOG_DBG("detached.");
if (s_Device.controllerHandle != NULL) {
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlStop, NULL);
s_Device.interface->deviceDeinit(s_Device.controllerHandle);
s_Device.controllerHandle = NULL;
}
s_Device.attached = 0;
return 0;
}
int usb_dc_set_address(const u8_t addr)
{
LOG_DBG("");
s_Device.address = addr;
return 0;
}
int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data *const cfg)
{
u8_t ep_idx = EP_ADDR2IDX(cfg->ep_addr);
if ((cfg->ep_type == USB_DC_EP_CONTROL) && ep_idx) {
LOG_ERR("invalid endpoint configuration");
return -1;
}
if (ep_idx >= NUM_OF_EP_MAX) {
LOG_ERR("endpoint index/address out of range");
return -1;
}
if (ep_idx & BIT(0)) {
if (EP_ADDR2DIR(cfg->ep_addr) != USB_EP_DIR_IN) {
LOG_INF("pre-selected as IN endpoint");
return -1;
}
} else {
if (EP_ADDR2DIR(cfg->ep_addr) != USB_EP_DIR_OUT) {
LOG_INF("pre-selected as OUT endpoint");
return -1;
}
}
return 0;
}
int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const cfg)
{
u8_t ep_abs_idx = EP_ABS_IDX(cfg->ep_addr);
usb_device_endpoint_init_struct_t epInit;
struct k_mem_block *block;
struct usb_ep_ctrl_data *eps = &s_Device.eps[ep_abs_idx];
epInit.zlt = 0U;
epInit.endpointAddress = cfg->ep_addr;
epInit.maxPacketSize = cfg->ep_mps;
epInit.transferType = cfg->ep_type;
s_Device.eps[ep_abs_idx].ep_type = cfg->ep_type;
if (ep_abs_idx >= NUM_OF_EP_MAX) {
LOG_ERR("Wrong endpoint index/address");
return -EINVAL;
}
block = &(eps->block);
if (block->data) {
k_mem_pool_free(block);
block->data = NULL;
}
if (k_mem_pool_alloc(&ep_buf_pool, block, cfg->ep_mps, K_MSEC(10)) == 0) {
memset(block->data, 0, cfg->ep_mps);
} else {
LOG_ERR("Memory allocation time-out");
return -ENOMEM;
}
s_Device.eps[ep_abs_idx].ep_mps = cfg->ep_mps;
if (s_Device.eps[ep_abs_idx].ep_enabled) {
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlEndpointDeinit, (void *)(&cfg->ep_addr));
}
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlEndpointInit, &epInit);
/* if it is controlendpoint, controller will prime setup
* here set the occupied flag.
*/
if ((EP_ADDR2IDX(cfg->ep_addr) == USB_CONTROL_ENDPOINT) && (EP_ADDR2DIR(cfg->ep_addr) == USB_EP_DIR_OUT)) {
s_Device.eps[ep_abs_idx].ep_occupied = true;
}
s_Device.eps[ep_abs_idx].ep_enabled = true;
return 0;
}
int usb_dc_ep_set_stall(const u8_t ep)
{
u8_t endpoint = ep;
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlEndpointStall, &endpoint);
return 0;
}
int usb_dc_ep_clear_stall(const u8_t ep)
{
u8_t endpoint = ep;
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlEndpointUnstall, &endpoint);
return 0;
}
int usb_dc_ep_is_stalled(const u8_t ep, u8_t *const stalled)
{
if (!stalled) {
return -EINVAL;
}
*stalled = 0;
/* Get the endpoint status */
usb_device_endpoint_status_struct_t endpointStatus;
endpointStatus.endpointAddress = ep;
endpointStatus.endpointStatus = kUSB_DeviceEndpointStateIdle;
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlGetEndpointStatus, &endpointStatus);
*stalled = endpointStatus.endpointStatus;
return 0;
}
int usb_dc_ep_halt(const u8_t ep)
{
return usb_dc_ep_set_stall(ep);
}
int usb_dc_ep_enable(const u8_t ep)
{
u8_t ep_abs_idx = EP_ABS_IDX(ep);
/* endpoint0 OUT is primed by controller driver when configure this
* endpoint.
*/
if (!ep_abs_idx) {
return 0;
}
if (s_Device.eps[ep_abs_idx].ep_occupied) {
LOG_WRN("endpoint 0x%x already enabled", ep);
return -EBUSY;
}
if ((EP_ADDR2IDX(ep) != USB_CONTROL_ENDPOINT) && (EP_ADDR2DIR(ep) == USB_EP_DIR_OUT)) {
s_Device.interface->deviceRecv(s_Device.controllerHandle,
ep,
(u8_t *)s_Device.eps[ep_abs_idx].block.data,
(uint32_t)s_Device.eps[ep_abs_idx].ep_mps);
s_Device.eps[ep_abs_idx].ep_occupied = true;
} else {
/* control enpoint just be enabled before enumeration,
* when running here, setup has been primed.
*/
s_Device.eps[ep_abs_idx].ep_occupied = true;
}
return 0;
}
int usb_dc_ep_disable(const u8_t ep)
{
u8_t ep_abs_idx = EP_ABS_IDX(ep);
s_Device.interface->deviceCancel(s_Device.controllerHandle, ep);
s_Device.eps[ep_abs_idx].ep_enabled = false;
return 0;
}
int usb_dc_ep_flush(const u8_t ep)
{
u8_t ep_idx = EP_ADDR2IDX(ep);
if (ep_idx >= NUM_OF_EP_MAX) {
LOG_ERR("Wrong endpoint index/address");
return -EINVAL;
}
LOG_DBG("ep %x, idx %d", ep_idx, ep);
return 0;
}
/* send data to the host */
int usb_dc_ep_write(const u8_t ep, const u8_t *const data,
const u32_t data_len, u32_t *const ret_bytes)
{
u8_t ep_abs_idx = EP_ABS_IDX(ep);
u8_t *buffer = (u8_t *)s_Device.eps[ep_abs_idx].block.data;
u32_t len_to_send;
if (data_len > s_Device.eps[ep_abs_idx].ep_mps) {
len_to_send = s_Device.eps[ep_abs_idx].ep_mps;
} else {
len_to_send = data_len;
}
for (u32_t n = 0; n < len_to_send; n++) {
buffer[n] = data[n];
}
#if defined(CONFIG_HAS_MCUX_CACHE) && !defined(EP_BUF_NONCACHED)
DCACHE_CleanByRange((uint32_t)buffer, len_to_send);
#endif
s_Device.interface->deviceSend(s_Device.controllerHandle,
ep,
buffer,
len_to_send);
if (ret_bytes) {
*ret_bytes = len_to_send;
}
return 0;
}
int usb_dc_ep_read_wait(u8_t ep, u8_t *data, u32_t max_data_len,
u32_t *read_bytes)
{
u8_t ep_idx = EP_ADDR2IDX(ep);
u8_t ep_abs_idx = EP_ABS_IDX(ep);
u32_t data_len;
u8_t *bufp = NULL;
while (s_Device.eps[ep_abs_idx].ep_occupied) {
LOG_ERR("Endpoint is occupied by the controller");
return -EBUSY;
}
if ((ep_idx >= NUM_OF_EP_MAX) || (EP_ADDR2DIR(ep) != USB_EP_DIR_OUT)) {
LOG_ERR("Wrong endpoint index/address/direction");
return -EINVAL;
}
/* Allow to read 0 bytes */
if (!data && max_data_len) {
LOG_ERR("Wrong arguments");
return -EINVAL;
}
/* it is control setup, we should use message.buffer,
* this buffer is from internal setup array.
*/
bufp = s_Device.eps[ep_abs_idx].transfer_message.buffer;
data_len = s_Device.eps[ep_abs_idx].transfer_message.length;
if (data_len == USB_UNINITIALIZED_VAL_32) {
if (read_bytes) {
*read_bytes = 0;
}
return -EINVAL;
}
if (!data && !max_data_len) {
/* When both buffer and max data to read are zero return the
* available data in buffer.
*/
if (read_bytes) {
*read_bytes = data_len;
}
return 0;
}
if (data_len > max_data_len) {
LOG_WRN("Not enough room to copy all the data!");
data_len = max_data_len;
}
if (data != NULL) {
for (u32_t i = 0; i < data_len; i++) {
data[i] = bufp[i];
}
}
if (read_bytes) {
*read_bytes = data_len;
}
if (EP_ADDR2IDX(ep) == USB_ENDPOINT_CONTROL) {
u8_t isSetup = s_Device.eps[0].transfer_message.isSetup;
u8_t *buffer = s_Device.eps[0].transfer_message.buffer;
if (isSetup) {
if (((usb_setup_struct_t *)buffer)->wLength == 0) {
s_Device.setupDataStage = SETUP_DATA_STAGE_DONE;
} else if (((usb_setup_struct_t *)buffer)->bmRequestType & USB_REQUEST_TYPE_DIR_MASK) {
s_Device.setupDataStage = SETUP_DATA_STAGE_IN;
} else {
s_Device.setupDataStage = SETUP_DATA_STAGE_OUT;
}
} else {
if (s_Device.setupDataStage != SETUP_DATA_STAGE_DONE) {
if ((data_len >= max_data_len) || (data_len < s_Device.eps[0].ep_mps)) {
s_Device.setupDataStage = SETUP_DATA_STAGE_DONE;
}
}
}
}
return 0;
}
int usb_dc_ep_read_continue(u8_t ep)
{
/* select the index of the next endpoint buffer */
u8_t ep_abs_idx = EP_ABS_IDX(ep);
if (s_Device.eps[ep_abs_idx].ep_occupied) {
LOG_WRN("endpoint 0x%x already occupied", ep);
return -EBUSY;
}
if (EP_ADDR2IDX(ep) == USB_ENDPOINT_CONTROL) {
if (s_Device.setupDataStage == SETUP_DATA_STAGE_DONE) {
return 0;
}
if (s_Device.setupDataStage == SETUP_DATA_STAGE_IN) {
s_Device.setupDataStage = SETUP_DATA_STAGE_DONE;
}
}
s_Device.interface->deviceRecv(s_Device.controllerHandle,
ep,
(u8_t *)s_Device.eps[ep_abs_idx].block.data,
s_Device.eps[ep_abs_idx].ep_mps);
s_Device.eps[ep_abs_idx].ep_occupied = true;
return 0;
}
int usb_dc_ep_read(const u8_t ep, u8_t *const data,
const u32_t max_data_len, u32_t *const read_bytes)
{
int retval = usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes);
if (retval) {
return retval;
}
if (!data && !max_data_len) {
/* When both buffer and max data to read are zero the above
* call would fetch the data len and we simply return.
*/
return 0;
}
if (usb_dc_ep_read_continue(ep) != 0) {
return -EINVAL;
}
return 0;
}
int usb_dc_ep_set_callback(const u8_t ep, const usb_dc_ep_callback cb)
{
u8_t ep_abs_idx = EP_ABS_IDX(ep);
if (!s_Device.attached) {
return -EINVAL;
}
s_Device.eps[ep_abs_idx].callback = cb;
return 0;
}
void usb_dc_set_status_callback(const usb_dc_status_callback cb)
{
s_Device.status_callback = cb;
}
int usb_dc_ep_mps(const u8_t ep)
{
u8_t ep_abs_idx = EP_ABS_IDX(ep);
return s_Device.eps[ep_abs_idx].ep_mps;
}
static void control_endpoint_enable(void)
{
usb_device_endpoint_init_struct_t epInit;
u8_t ep_abs_idx = 0;
epInit.zlt = 0U;
epInit.transferType = USB_ENDPOINT_CONTROL;
epInit.maxPacketSize = EP0_MAX_PACKET_SIZE;
epInit.endpointAddress = EP0_OUT;
ep_abs_idx = EP_ABS_IDX(epInit.endpointAddress);
s_Device.eps[ep_abs_idx].ep_mps = EP0_MAX_PACKET_SIZE;
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlEndpointInit, &epInit);
s_Device.eps[ep_abs_idx].ep_occupied = false;
s_Device.eps[ep_abs_idx].ep_enabled = true;
epInit.endpointAddress = EP0_IN;
ep_abs_idx = EP_ABS_IDX(epInit.endpointAddress);
s_Device.eps[ep_abs_idx].ep_mps = EP0_MAX_PACKET_SIZE;
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlEndpointInit, &epInit);
s_Device.eps[ep_abs_idx].ep_occupied = false;
s_Device.eps[ep_abs_idx].ep_enabled = true;
}
/* Notify the up layer the KHCI status changed. */
void USB_DeviceNotificationTrigger(void *handle, void *msg)
{
usb_device_callback_message_struct_t *message = (usb_device_callback_message_struct_t *)msg;
switch (message->code) {
case kUSB_DeviceNotifyBusReset:
s_Device.address = 0;
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlSetDefaultStatus, NULL);
for (int i = 0; i < NUM_OF_EP_MAX; i++) {
s_Device.eps[i].ep_occupied = false;
s_Device.eps[i].ep_enabled = false;
}
control_endpoint_enable();
s_Device.status_callback(USB_DC_RESET, NULL);
break;
case kUSB_DeviceNotifyError:
s_Device.status_callback(USB_DC_ERROR, NULL);
break;
case kUSB_DeviceNotifySuspend:
s_Device.status_callback(USB_DC_SUSPEND, NULL);
break;
case kUSB_DeviceNotifyResume:
s_Device.status_callback(USB_DC_RESUME, NULL);
break;
default:
{
u8_t ep_packet_type = 0;
u8_t ep_abs_idx = EP_ABS_IDX(message->code);
s_Device.eps[ep_abs_idx].transfer_message.length = message->length;
s_Device.eps[ep_abs_idx].transfer_message.isSetup = message->isSetup;
s_Device.eps[ep_abs_idx].transfer_message.code = message->code;
s_Device.eps[ep_abs_idx].transfer_message.buffer = message->buffer;
s_Device.eps[ep_abs_idx].ep_occupied = false;
if (message->isSetup) {
ep_packet_type = USB_DC_EP_SETUP;
} else {
/* IN TOKEN */
if ((message->code & USB_REQUEST_TYPE_DIR_MASK) == USB_REQUEST_TYPE_DIR_IN) {
/* control endpoint 0 and status stage for setAddr transfer */
if ((s_Device.address != 0) && (ep_abs_idx == 1)) {
/* SET ADDRESS in the status stage in the IN transfer*/
s_Device.interface->deviceControl(s_Device.controllerHandle, kUSB_DeviceControlSetDeviceAddress, &s_Device.address);
s_Device.address = 0;
}
ep_packet_type = USB_DC_EP_DATA_IN;
}
/* OUT TOKEN */
else {
ep_packet_type = USB_DC_EP_DATA_OUT;
}
}
if (s_Device.eps[ep_abs_idx].callback) {
#if defined(CONFIG_HAS_MCUX_CACHE) && !defined(EP_BUF_NONCACHED)
if (message->length) {
DCACHE_InvalidateByRange((uint32_t)message->buffer,
message->length);
}
#endif
s_Device.eps[ep_abs_idx].callback(message->code,
ep_packet_type);
}
}
}
}
static void usb_isr_handler(void)
{
USB_DeviceEhciIsrFunction(&s_Device);
}