666 lines
20 KiB
C
666 lines
20 KiB
C
#ifndef __USBH_MDW_H__
|
|
#define __USBH_MDW_H__
|
|
|
|
#ifndef KNERON_USBH_MDW
|
|
#error "Please define 'KNERON_USBH_MDW' in global scope, it is necessary for Kneron USBH middleware."
|
|
// stop compliation
|
|
#endif
|
|
|
|
//#define USBH_MDW_DBG // turn on this can help trace code, a lot of code-size needed
|
|
//#define USBH_MDW_ERR // turn on this can add some more error checking, slightly code-size needed
|
|
|
|
#include "kmdw_usbh.h"
|
|
#include "Driver_USBH.h"
|
|
#include "kmdw_memory.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#if defined(USBH_MDW_DBG) | defined(USBH_MDW_ERR)
|
|
#include "kdrv_uart.h"
|
|
#endif
|
|
|
|
#define USBH_EVENT_QUEUE_LEN 16 // number of Message Queue Objects
|
|
|
|
typedef enum
|
|
{
|
|
USBH_STM_INITED = 0,
|
|
USBH_STM_CONNECTED_FS,
|
|
USBH_STM_DISCONNECTED,
|
|
USBH_STM_RESET_HS_DONE, // reset and at high-speed
|
|
USBH_STM_RESET_HS_DONE_2, // reset and at high-speed, 2nd
|
|
USBH_STM_CUSTOM_CONFIGURE,
|
|
USBH_STM_CUSTOM_INITIALIZE,
|
|
USBH_STM_CUSTOM_INITIALIZE_DONE,
|
|
USBH_STM_FAILED = 0x100,
|
|
} USBH_STM_t;
|
|
|
|
typedef struct
|
|
{ // object data type
|
|
uint8_t type; // 0x11 = port, 0x22 = pipe
|
|
uint8_t port; // for port event
|
|
//uint32_t pipe; // for pipe event
|
|
uint32_t event; // for both port and pipe events
|
|
} USBH_Event_t;
|
|
|
|
// below record what thread is running what pipe
|
|
typedef struct
|
|
{
|
|
USBH_PIPE_HANDLE pipe;
|
|
osThreadId_t thread;
|
|
} USBH_PIPE_TID_t;
|
|
|
|
#define MAX_PIPE_NUM 6 // FIXME
|
|
#define USER_FLAG_XFER_COMPLETE 0x100U
|
|
#define MDW_FLAG_MESSAGE 0x1000U
|
|
#define MDW_FLAG_ITD_WORK 0x2000U
|
|
|
|
#define DEV_ADDR 0x3 // FIXME: constant value ?
|
|
|
|
static osThreadId_t usbh_mdw_tid;
|
|
static int usbh_state; // state machine control
|
|
|
|
static uint32_t cur_pipe_num = 0;
|
|
static USBH_PIPE_TID_t dev_pipetid[MAX_PIPE_NUM] = {0}; // if pipe is not 0, it is in use
|
|
static osMessageQueueId_t usbh_msgq = NULL;
|
|
static ARM_USBH_PORT_STATE port_st;
|
|
|
|
ARM_USBH_ISOCH_ITD_WORK_FUNC itd_work_func = 0; // this will point to a callback from USBH driver
|
|
|
|
static USB_DEVICE_DESCRIPTOR dev_descp;
|
|
static USB_CONFIGURATION_DESCRIPTOR config_descp;
|
|
|
|
__weak uint8_t USBH_CustomClass_Configure(uint8_t device, const USB_DEVICE_DESCRIPTOR *ptr_dev_desc, const USB_CONFIGURATION_DESCRIPTOR *ptr_cfg_desc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
__weak usbStatus USBH_CustomClass_Initialize(uint8_t instance)
|
|
{
|
|
return usbUnknownError;
|
|
}
|
|
|
|
__weak usbStatus USBH_CustomClass_Disconnected(uint8_t instance)
|
|
{
|
|
return usbUnknownError;
|
|
}
|
|
|
|
static void usbh_port_event_cb(uint8_t port, uint32_t event)
|
|
{
|
|
// dont take too much time here, because it is invoked from ISR
|
|
USBH_Event_t uevent;
|
|
uevent.type = 0x11;
|
|
uevent.port = port;
|
|
uevent.event = event;
|
|
osMessageQueuePut(usbh_msgq, &uevent, 0U, 0U);
|
|
osThreadFlagsSet(usbh_mdw_tid, MDW_FLAG_MESSAGE);
|
|
}
|
|
|
|
static void usbh_pipe_event_cb(ARM_USBH_PIPE_HANDLE pipe_hndl, uint32_t event)
|
|
{
|
|
// dont take too much time here, because it is invoked from ISR
|
|
|
|
#ifdef USBH_MDW_ERR
|
|
if (event != ARM_USBH_EVENT_TRANSFER_COMPLETE)
|
|
kmdw_printf("@@ %s() error: event is not transfer_complete !!\n", __FUNCTION__);
|
|
#endif
|
|
|
|
// there must be a pipe transfer waiting for this
|
|
|
|
for (int i = 0; i < MAX_PIPE_NUM; i++)
|
|
{
|
|
if (pipe_hndl == dev_pipetid[i].pipe)
|
|
{
|
|
osThreadFlagsSet(dev_pipetid[i].thread, USER_FLAG_XFER_COMPLETE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usbh_handle_port_evnet(uint8_t port, uint32_t event)
|
|
{
|
|
switch (event)
|
|
{
|
|
case ARM_USBH_EVENT_CONNECT:
|
|
{
|
|
// NOTE: we support only high-speed
|
|
port_st = Driver_USBH0.PortGetState(0);
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() Connect: isCnt 0x%x, speed 0x%x\n", __FUNCTION__, port_st.connected, port_st.speed);
|
|
#endif
|
|
|
|
if (port_st.connected && port_st.speed == ARM_USB_SPEED_FULL)
|
|
{
|
|
// here make sure state is connected and FULL speed (pre state for high-speed)
|
|
usbh_state = USBH_STM_CONNECTED_FS;
|
|
}
|
|
else if (port_st.connected && port_st.speed == ARM_USB_SPEED_HIGH && usbh_state == USBH_STM_DISCONNECTED)
|
|
{
|
|
// here make sure state is connected and FULL speed (pre state for high-speed)
|
|
usbh_state = USBH_STM_CONNECTED_FS;
|
|
}
|
|
else
|
|
{
|
|
#ifdef USBH_MDW_ERR
|
|
kmdw_printf("@@ %s() Connect: state is incorrect\n", __FUNCTION__);
|
|
#endif
|
|
usbh_state = USBH_STM_FAILED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ARM_USBH_EVENT_DISCONNECT:
|
|
{
|
|
port_st = Driver_USBH0.PortGetState(0);
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() Disconnect: isCnt 0x%x, speed 0x%x\n", __FUNCTION__, port_st.connected, port_st.speed);
|
|
#endif
|
|
|
|
if (port_st.speed == ARM_USB_SPEED_HIGH)
|
|
{
|
|
usbh_state = USBH_STM_DISCONNECTED;
|
|
}
|
|
else
|
|
{
|
|
#ifdef USBH_MDW_ERR
|
|
kmdw_printf("@@ %s() Reset: error connection or speed\n", __FUNCTION__);
|
|
#endif
|
|
usbh_state = USBH_STM_FAILED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case ARM_USBH_EVENT_RESET:
|
|
{
|
|
port_st = Driver_USBH0.PortGetState(0);
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() Reset: isCnt 0x%x, speed 0x%x\n", __FUNCTION__, port_st.connected, port_st.speed);
|
|
#endif
|
|
|
|
if (port_st.connected && port_st.speed == ARM_USB_SPEED_HIGH)
|
|
{
|
|
if (usbh_state == USBH_STM_RESET_HS_DONE)
|
|
usbh_state = USBH_STM_RESET_HS_DONE_2;
|
|
else
|
|
usbh_state = USBH_STM_RESET_HS_DONE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef USBH_MDW_ERR
|
|
kmdw_printf("@@ %s() Reset: error connection or speed\n", __FUNCTION__);
|
|
#endif
|
|
usbh_state = USBH_STM_FAILED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
#ifdef USBH_MDW_ERR
|
|
kmdw_printf("@@ %s() error: this event is not handled\n", __FUNCTION__);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void usbh_protocol_stm()
|
|
{
|
|
switch (usbh_state)
|
|
{
|
|
case USBH_STM_CONNECTED_FS: // connected at FS
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() doing USBH_STM_CONNECTED_FS\n", __FUNCTION__);
|
|
#endif
|
|
// reset it to see if it can be HS
|
|
osDelay(500); // FIXME
|
|
Driver_USBH0.PortReset(0);
|
|
}
|
|
break;
|
|
|
|
case USBH_STM_DISCONNECTED:
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() doing USBH_STM_DISCONNECTED_FS\n", __FUNCTION__);
|
|
#endif
|
|
USBH_CustomClass_Disconnected(0);
|
|
// osDelay(500); // FIXME
|
|
// usbh_state = USBH_STM_INITED;
|
|
}
|
|
break;
|
|
|
|
case USBH_STM_RESET_HS_DONE: // state is after reset, at HS
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() doing USBH_STM_RESET_HS_DONE\n", __FUNCTION__);
|
|
#endif
|
|
|
|
// suspend & resume port
|
|
Driver_USBH0.PortSuspend(0);
|
|
osDelay(200); // FIXME
|
|
|
|
Driver_USBH0.PortResume(0);
|
|
osDelay(200); // FIXME
|
|
|
|
Driver_USBH0.PortReset(0);
|
|
}
|
|
break;
|
|
|
|
case USBH_STM_RESET_HS_DONE_2: // state is after reset, at HS, 2nd
|
|
{
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() doing USBH_STM_RESET_HS_DONE_2\n", __FUNCTION__);
|
|
#endif
|
|
uint8_t *config_buf = 0;
|
|
// modify pipe to be HS pipe
|
|
Driver_USBH0.PipeModify(dev_pipetid[0].pipe, 0x0, ARM_USB_SPEED_HIGH, 0x0, 0x0, 64);
|
|
|
|
uint8_t data[64];
|
|
USB_SETUP_PACKET setup;
|
|
|
|
// GET_DESCRIPTOR - device, 64 bytes
|
|
{
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_DEVICE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_DEVICE_TO_HOST;
|
|
setup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
|
|
setup.wValue = USB_DEVICE_DESCRIPTOR_TYPE << 8;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 64;
|
|
|
|
USBH_ControlTransfer(0, &setup, data, 64);
|
|
}
|
|
|
|
// SET_ADDRESS (set adrees to DEV_ADDR)
|
|
{
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_DEVICE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_HOST_TO_DEVICE;
|
|
setup.bRequest = USB_REQUEST_SET_ADDRESS;
|
|
setup.wValue = DEV_ADDR;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 0;
|
|
|
|
USBH_ControlTransfer(0, &setup, NULL, 0);
|
|
}
|
|
|
|
// update dev address of the pipe
|
|
Driver_USBH0.PipeModify(dev_pipetid[0].pipe, DEV_ADDR, ARM_USB_SPEED_HIGH, 0x0, 0x0, 64);
|
|
|
|
// GET_DESCRIPTOR - device, actual bytes with new address
|
|
{
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_DEVICE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_DEVICE_TO_HOST;
|
|
setup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
|
|
setup.wValue = USB_DEVICE_DESCRIPTOR_TYPE << 8;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 18;
|
|
|
|
USBH_ControlTransfer(0, &setup, data, 64);
|
|
memcpy(&dev_descp, data, sizeof(USB_DEVICE_DESCRIPTOR));
|
|
}
|
|
|
|
// GET_DESCRIPTOR - configuration, first 9 bytes
|
|
{
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_DEVICE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_DEVICE_TO_HOST;
|
|
setup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
|
|
setup.wValue = USB_CONFIGURATION_DESCRIPTOR_TYPE << 8;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 9;
|
|
|
|
USBH_ControlTransfer(0, &setup, data, 64);
|
|
memcpy(&config_descp, data, sizeof(USB_CONFIGURATION_DESCRIPTOR));
|
|
}
|
|
|
|
// GET_DESCRIPTOR - configuration, full length
|
|
{
|
|
|
|
if (0 == (config_buf = malloc(config_descp.wTotalLength)))
|
|
while (1) {};
|
|
memset(config_buf, 0,config_descp.wTotalLength);
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_DEVICE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_DEVICE_TO_HOST;
|
|
setup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
|
|
setup.wValue = USB_CONFIGURATION_DESCRIPTOR_TYPE << 8;
|
|
setup.wIndex = 0;
|
|
setup.wLength = config_descp.wTotalLength;
|
|
|
|
USBH_ControlTransfer(0, &setup, config_buf, config_descp.wTotalLength);
|
|
|
|
}
|
|
|
|
// SET_CONFIGURATION - 0x1
|
|
{
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_DEVICE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_HOST_TO_DEVICE;
|
|
setup.bRequest = USB_REQUEST_SET_CONFIGURATION;
|
|
setup.wValue = 1;
|
|
setup.wIndex = 0;
|
|
setup.wLength = 0;
|
|
|
|
USBH_ControlTransfer(0, &setup, NULL, 0);
|
|
}
|
|
|
|
usbh_state = USBH_STM_CUSTOM_CONFIGURE; // FIXME
|
|
|
|
// next : callback user's USBH_CustomClass_Configure()
|
|
|
|
USBH_CustomClass_Configure(0, &dev_descp, (const USB_CONFIGURATION_DESCRIPTOR *)config_buf);
|
|
memset(config_buf, 0,config_descp.wTotalLength);
|
|
free(config_buf);
|
|
|
|
usbh_state = USBH_STM_CUSTOM_INITIALIZE; // FIXME
|
|
|
|
// next : callback user's USBH_CustomClass_Initialize()
|
|
USBH_CustomClass_Initialize(0);
|
|
|
|
usbh_state = USBH_STM_CUSTOM_INITIALIZE_DONE; // FIXME
|
|
}
|
|
break;
|
|
|
|
default:
|
|
#ifdef USBH_MDW_ERR
|
|
kmdw_printf("@@ %s() error: invalid state\n", __FUNCTION__);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void usbh_mdw_thread(void *argument)
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s(), start USBH middleware thread.....\n", __FUNCTION__);
|
|
#endif
|
|
|
|
while (1)
|
|
{
|
|
uint32_t flags = osThreadFlagsWait(MDW_FLAG_MESSAGE | MDW_FLAG_ITD_WORK, osFlagsWaitAny, osWaitForever);
|
|
|
|
if (flags & MDW_FLAG_MESSAGE)
|
|
{
|
|
USBH_Event_t uevent;
|
|
osStatus_t status = osMessageQueueGet(usbh_msgq, &uevent, NULL, osWaitForever); // wait for message
|
|
if (status == osOK)
|
|
{
|
|
if (uevent.type == 0x11)
|
|
usbh_handle_port_evnet(uevent.port, uevent.event);
|
|
|
|
// state machine
|
|
usbh_protocol_stm();
|
|
}
|
|
}
|
|
if (flags & MDW_FLAG_ITD_WORK)
|
|
{
|
|
// here do the bottom-half iTD work
|
|
itd_work_func();
|
|
}
|
|
}
|
|
}
|
|
|
|
usbStatus USBH_Initialize(uint8_t ctrl)
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s()\n", __FUNCTION__);
|
|
#endif
|
|
|
|
// need a message queue
|
|
usbh_msgq = osMessageQueueNew(USBH_EVENT_QUEUE_LEN, sizeof(USBH_Event_t), NULL);
|
|
|
|
#ifdef USBH_MDW_ERR
|
|
if (usbh_msgq == NULL)
|
|
{
|
|
kmdw_printf("@@ osMessageQueueNew() failed\n");
|
|
}
|
|
#endif
|
|
|
|
Driver_USBH0.Initialize(&usbh_port_event_cb, &usbh_pipe_event_cb);
|
|
Driver_USBH0.PowerControl(ARM_POWER_FULL);
|
|
dev_pipetid[cur_pipe_num++].pipe = Driver_USBH0.PipeCreate(0x0, ARM_USB_SPEED_LOW, 0x0, 0x0, 0x0, 0x0, 8, 0);
|
|
Driver_USBH0.PortVbusOnOff(0, true);
|
|
|
|
usbh_state = USBH_STM_INITED;
|
|
|
|
// first, we need a thread
|
|
usbh_mdw_tid = osThreadNew(usbh_mdw_thread, NULL, NULL);
|
|
osThreadSetPriority(usbh_mdw_tid, osPriorityHigh); // FIXME: what priority is proper ?
|
|
|
|
return usbOK;
|
|
}
|
|
|
|
USBH_PIPE_HANDLE USBH_PipeCreate(uint8_t device, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_max_packet_size, uint8_t ep_interval)
|
|
{
|
|
USBH_PIPE_HANDLE pipe_h;
|
|
|
|
pipe_h = Driver_USBH0.PipeCreate(DEV_ADDR, ARM_USB_SPEED_HIGH, 0x0, 0x0, ep_addr, ep_type, ep_max_packet_size, ep_interval);
|
|
|
|
dev_pipetid[cur_pipe_num++].pipe = pipe_h;
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() ep_addr 0x%x ep_type %d max_packet %d ep_interval %d, pipe 0x%p\n",
|
|
__FUNCTION__, ep_addr, ep_type, ep_max_packet_size, ep_interval, pipe_h);
|
|
#endif
|
|
|
|
return pipe_h;
|
|
}
|
|
|
|
static usbStatus _usbh_transfer_payload(USBH_PIPE_HANDLE pipe_hndl, uint8_t *buf, uint32_t len, bool isSend)
|
|
{
|
|
// look up the pipe index from the pipetid table
|
|
int i;
|
|
for (i = 0; i < MAX_PIPE_NUM; i++)
|
|
if (pipe_hndl == dev_pipetid[i].pipe)
|
|
{
|
|
dev_pipetid[i].thread = osThreadGetId(); // register thread id for thread flag notification
|
|
break;
|
|
}
|
|
|
|
#ifdef USBH_MDW_ERR
|
|
if (i >= MAX_PIPE_NUM)
|
|
{
|
|
kmdw_printf("-- pipe 0x%p is invalid\n", pipe_hndl);
|
|
return usbInvalidParameter;
|
|
}
|
|
#endif
|
|
|
|
int32_t sts = Driver_USBH0.PipeTransfer(pipe_hndl, isSend ? ARM_USBH_PACKET_OUT : ARM_USBH_PACKET_IN, buf, len);
|
|
|
|
uint32_t flags = osThreadFlagsWait(USER_FLAG_XFER_COMPLETE, osFlagsWaitAny, 5000); // 5 secs timeout, should be long enough
|
|
|
|
#ifdef USBH_MDW_ERR
|
|
if (flags == osFlagsErrorTimeout)
|
|
{
|
|
kmdw_printf("-- pipe 0x%p transfer timeout\n", pipe_hndl);
|
|
return usbTimeout;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USBH_MDW_ERR
|
|
if (sts != ARM_DRIVER_OK)
|
|
{
|
|
kmdw_printf("-- pipe 0x%p error, sts = %d\n", pipe_hndl, sts);
|
|
return usbTransferError;
|
|
}
|
|
#endif
|
|
|
|
return usbOK;
|
|
}
|
|
|
|
usbStatus USBH_PipeSend(USBH_PIPE_HANDLE pipe_hndl, const uint8_t *buf, uint32_t len)
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() pipe_hndl 0x%p buf 0x%p len %d\n", __FUNCTION__, pipe_hndl, buf, len);
|
|
#endif
|
|
|
|
return _usbh_transfer_payload(pipe_hndl, (uint8_t *)buf, len, true);
|
|
}
|
|
|
|
usbStatus USBH_PipeReceive(USBH_PIPE_HANDLE pipe_hndl, uint8_t *buf, uint32_t len)
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() pipe_hndl 0x%p buf 0x%p len %d\n", __FUNCTION__, pipe_hndl, buf, len);
|
|
#endif
|
|
|
|
return _usbh_transfer_payload(pipe_hndl, buf, len, false);
|
|
}
|
|
|
|
static uint32_t _usbh_get_txfer_bytes(USBH_PIPE_HANDLE pipe_hndl)
|
|
{
|
|
uint32_t txfer_bytes = Driver_USBH0.PipeTransferGetResult(pipe_hndl);
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ pipe_hndl txfered %d bytes\n", pipe_hndl, txfer_bytes);
|
|
#endif
|
|
|
|
return txfer_bytes;
|
|
}
|
|
|
|
uint32_t USBH_PipeSendGetResult(USBH_PIPE_HANDLE pipe_hndl)
|
|
{
|
|
return _usbh_get_txfer_bytes(pipe_hndl);
|
|
}
|
|
|
|
uint32_t USBH_PipeReceiveGetResult(USBH_PIPE_HANDLE pipe_hndl)
|
|
{
|
|
return _usbh_get_txfer_bytes(pipe_hndl);
|
|
}
|
|
|
|
usbStatus USBH_ControlTransfer(uint8_t device, const USB_SETUP_PACKET *setup_packet, uint8_t *data, uint32_t len)
|
|
{
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() device %d, setup_packet : 0x ", __FUNCTION__, device);
|
|
uint8_t *sp = (uint8_t *)setup_packet;
|
|
for (int i = 0; i < 8; i++)
|
|
kmdw_printf("%02x ", sp[i]);
|
|
kmdw_printf(", data ptr 0x%p, len %u\n", data, len);
|
|
#endif
|
|
|
|
ARM_USBH_PIPE_HANDLE ctrl_pipe = dev_pipetid[0].pipe;
|
|
dev_pipetid[0].thread = osThreadGetId();
|
|
uint32_t packet;
|
|
uint8_t *payload;
|
|
uint32_t txfer_len;
|
|
|
|
// three stages
|
|
for (int stage = 0; stage < 3; stage++)
|
|
{
|
|
switch (stage)
|
|
{
|
|
case 0: // Setup stage
|
|
|
|
packet = ARM_USBH_PACKET_DATA0 | ARM_USBH_PACKET_SETUP;
|
|
payload = (uint8_t *)setup_packet;
|
|
txfer_len = 8;
|
|
|
|
break;
|
|
case 1: // Data stage (optional)
|
|
|
|
// skeip Data stage if wLength is 0
|
|
if (setup_packet->wLength == 0)
|
|
continue;
|
|
|
|
// Contorl IN or OUT
|
|
packet = ARM_USBH_PACKET_DATA1 |
|
|
((setup_packet->bmRequestType.Dir) ? ARM_USBH_PACKET_IN : ARM_USBH_PACKET_OUT);
|
|
payload = data;
|
|
txfer_len = len;
|
|
|
|
break;
|
|
case 2: // Status stage
|
|
|
|
packet = ARM_USBH_PACKET_DATA1 |
|
|
((setup_packet->bmRequestType.Dir) ? ARM_USBH_PACKET_OUT : ARM_USBH_PACKET_IN);
|
|
payload = NULL;
|
|
txfer_len = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
Driver_USBH0.PipeTransfer(ctrl_pipe, packet, payload, txfer_len);
|
|
|
|
uint32_t flags = osThreadFlagsWait(USER_FLAG_XFER_COMPLETE, osFlagsWaitAny, 5000); // 5 secs timeout, should be long enough
|
|
#ifdef USBH_MDW_ERR
|
|
if (flags == osFlagsErrorTimeout)
|
|
{
|
|
kmdw_printf("@@ %s() control transfer timeout\n", __FUNCTION__);
|
|
return usbTimeout;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return usbOK;
|
|
}
|
|
|
|
USBH_PIPE_HANDLE USBH_Pipe_ISOCH_PipeDelete(USBH_PIPE_HANDLE pipe_hndl)
|
|
{
|
|
Driver_USBH0.PipeDelete(pipe_hndl);
|
|
return usbOK;
|
|
|
|
}
|
|
|
|
USBH_PIPE_HANDLE USBH_Pipe_ISOCH_PipeCreate(uint8_t device, uint8_t ep_addr, uint32_t wMaxPacketSize, uint8_t bInterval, uint8_t *buf, uint32_t buf_size)
|
|
{
|
|
USBH_PIPE_HANDLE pipe_h;
|
|
|
|
uint16_t max_packet_size = wMaxPacketSize & 0x7FF;
|
|
uint8_t mult = (wMaxPacketSize >> 11) + 1;
|
|
|
|
pipe_h = Driver_USBH0.PipeCreate_ISOCH(DEV_ADDR, ep_addr, max_packet_size, mult, bInterval, buf, buf_size);
|
|
|
|
dev_pipetid[cur_pipe_num++].pipe = pipe_h;
|
|
|
|
#ifdef USBH_MDW_DBG
|
|
kmdw_printf("@@ %s() ep_addr 0x%x ep_type 'isoch' wMaxPacketSize 0x%x bInterval %d buf 0x%p buf_size %u pipe 0x%p\n",
|
|
__FUNCTION__, ep_addr, wMaxPacketSize, bInterval, buf, buf_size, pipe_h);
|
|
#endif
|
|
|
|
return pipe_h;
|
|
}
|
|
|
|
static uint32_t handle_itd_cb()
|
|
{
|
|
// wake up bottom-half thread
|
|
osThreadFlagsSet(usbh_mdw_tid, MDW_FLAG_ITD_WORK);
|
|
return 1;
|
|
}
|
|
|
|
usbStatus USBH_Pipe_ISOCH_Start(USBH_PIPE_HANDLE pipe_hndl, USBH_CB_ISR_Isoch_transfer user_isoch_cb)
|
|
{
|
|
// enable bottom-half mechanism
|
|
itd_work_func = Driver_USBH0.PipeEnableBH_ISOCH(pipe_hndl, handle_itd_cb);
|
|
|
|
// start isoch transfer with user data callback
|
|
Driver_USBH0.PipeStart_ISOCH(pipe_hndl, user_isoch_cb);
|
|
|
|
return usbOK;
|
|
}
|
|
|
|
usbStatus USBH_Pipe_ISOCH_Stop(USBH_PIPE_HANDLE pipe_hndl)
|
|
{
|
|
Driver_USBH0.PipeStop_ISOCH(pipe_hndl);
|
|
return usbOK;
|
|
}
|
|
|
|
usbStatus USBH_DeviceRequest_SetInterface(uint8_t device, uint8_t index, uint8_t alternate)
|
|
{
|
|
USB_SETUP_PACKET setup;
|
|
|
|
setup.bmRequestType.Recipient = USB_REQUEST_TO_INTERFACE;
|
|
setup.bmRequestType.Type = USB_REQUEST_STANDARD;
|
|
setup.bmRequestType.Dir = USB_REQUEST_HOST_TO_DEVICE;
|
|
setup.bRequest = USB_REQUEST_SET_INTERFACE;
|
|
setup.wValue = alternate;
|
|
setup.wIndex = index;
|
|
setup.wLength = 0;
|
|
|
|
return USBH_ControlTransfer(device, &setup, NULL, 0);
|
|
}
|
|
|
|
#endif
|