I have for a long time been wanting to do a Custom USB class driver on the RT106x devices. I have been put down by the, seemingly, complex structure of the NXP USB example code. While I still don't think the samples in the SDK are "the best" out there, they do seem to work, and at least provide a starting point. Several people here have been looking for an implementation of a Custom USB class device, myself including. I have a Bulk device running on a number of NXP processors (LPC2148, LPC2458, LPC1788, LPC54628 etc) and I wanted to have the same running on a RT1064.
Finally that seems to be working. I got some pointers from the user @markdodd in the process, thanks a lot!
Now, there is ABSOLUTELY NO GUARANTEE that the code is correct, the only guarantee I can give is that there is probably something (or a lot) that could be made much better/smarter/faster. But, this at least works for me as it is. I will continue testing and fixing stuff, when I find something that can be improved, I will post it in this thread.
You are absolutely free to use the code in any way you see fit (as long as you follow the rules/guidelines from NXP). However, if you spot something bad/wrong etc, PLEASE let us know so we all benefit from this!
PLEASE remember that ALL data that you want to receive/transmit over USB MUST be in non-cached memory !
This device creates a Custom USB class using VID=0xFFFF, PID=0x4712 and Class=0xFF (Vendor specific)
The USB class has one interface (interface 0) and one configuration (configuration 1).
It has 4 endpoints, 1 OUT and 3 IN endpoints. In practice, it uses the OUT and one of the IN endpoints as a "channel" for a request/response type command interface. The two last IN endpoints are used for sending log messages and wireshark compatible frames (when the target device has a TCP/IP stack embedded that it wants to send debug traffic from).
Tested on USB0 on i.MXRT1064. I'm not sure how much needs to be done to have it work on USB1, I have tried to leave the code that was in the CDC sample from NXP, but no guarantee!
On the PC side I use libusbdotnet library (and C#) for communication.
The two most important modules is show below, all code is in a zip file attached.
Previous threads about this:
https://community.nxp.com/t5/i-MX-RT/USB-Custom-class-for-RT106x/m-p/842919
//-----------------------------------------------------------------------------
// BulkUSB.c 20210405 CHG
//
// Based on NXP's CDC sample from SDK 2.9.2 by Carsten Groen (chg@moonbounce.dk)
//-----------------------------------------------------------------------------
//
// This device creates a Custom USB class using VID=0xFFFF, PID=0x4712 and Class=0xFF (Vendor specific)
// The USB class has one interface (interface 0) and one configuration (configuration 1).
// It has 4 endpoints, 1 OUT and 3 IN endpoints. In practice, it uses the OUT and one of the IN endpoints as
// a "channel" for a request/response type command interface. The two last IN endpoints are used
// for sending log messages and wireshark compatible frames (when the target device has a TCP/IP stack
// embedded that it wants to send debug traffic from).
//
// Tested on USB0 on i.MXRT1064. I'm not sure how much needs to be done to have it work on USB1,
// I have tried to leave the code that was in the CDC sample from NXP, but no guarantee!
//
// On the PC side I use libusbdotnet library (and C#) for communication.
//
//
//-----------------------------------------------------------------------------
// Files added/modified from original SDK example:
//
// Large changes:
// BulkUSB.c/h (many changes, based on virtual.com.c/h)
// usb_device_my_bulk.c/h (many changes, based on usb_device_cdc_acm.c/h))
//
// Minor changes:
// usb_device_descriptor.c/h (many changes)
// usb_device_config.h (changes marked with "CHG")
// usb_device_class.c/h (changes marked with "CHG")
//
//-----------------------------------------------------------------------------
#include "System.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "usb_device_config.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_my_bulk.h"
#include "usb_device_descriptor.h"
#include "usb_device_dci.h"
#include "usb_phy.h"
#include "BulkUSB.h"
// USB PHY condfiguration
#define BOARD_USB_PHY_D_CAL (0x0CU)
#define BOARD_USB_PHY_TXCAL45DP (0x06U)
#define BOARD_USB_PHY_TXCAL45DM (0x06U)
#define CONTROLLER_ID kUSB_ControllerEhci0
#define USB_DEVICE_INTERRUPT_PRIORITY (3U)
// Coming from usb_device_descriptors.h file
extern usb_device_endpoint_struct_t g_UsbDeviceMyBulkEndpoints[];
extern usb_device_class_struct_t g_UsbDeviceMyBulkConfig;
// Need to define these as they are used in config structs before being implemented
usb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void *param);
usb_status_t USB_DeviceBulkCallback(class_handle_t handle, uint32_t event, void *param);
// Define buffer for receiving data from the bulk OUT event
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t s_RecvBufBulkOut[512];
// Define struct for our Bulk device
typedef struct {
usb_device_handle deviceHandle; // USB device handle.
class_handle_t bulkHandle; // USB CDC ACM class handle.
volatile uint8_t attach; // A flag to indicate whether a usb device is attached. 1: attached, 0: not attached
uint8_t speed; // Speed of USB device. USB_SPEED_FULL/USB_SPEED_LOW/USB_SPEED_HIGH.
uint8_t currentConfiguration; // Current configuration value.
uint8_t currentInterfaceAlternateSetting[1]; // Current alternate setting value for each interface.
} usb_bulk_struct_t;
static usb_bulk_struct_t s_bulkDevice; // Handle to the MyBulk device
//---------------------------------------------------------------------------------------
// USB device class information
//---------------------------------------------------------------------------------------
static usb_device_class_config_struct_t s_BulkConfig[1] = {
{
USB_DeviceBulkCallback, // Class callback function to handle the device status-related event for the specified type of class
0, // The class handle of the class, filled by the common driver
&g_UsbDeviceMyBulkConfig, // Detailed information of the class
}
};
//---------------------------------------------------------------------------------------
// USB device class configuration information
//---------------------------------------------------------------------------------------
static usb_device_class_config_list_struct_t s_MyBulkConfigList = {
s_BulkConfig, // Array of class configuration structures
USB_DeviceCallback, // Device callback function
1, // Number of class supported
};
//---------------------------------------------------------------------------------------
// IRQ handler for USB0
//---------------------------------------------------------------------------------------
void USB_OTG1_IRQHandler(void) {
USB_DeviceEhciIsrFunction(s_bulkDevice.deviceHandle);
}
//---------------------------------------------------------------------------------------
// IRQ handler for USB1
//---------------------------------------------------------------------------------------
void USB_OTG2_IRQHandler(void) {
USB_DeviceEhciIsrFunction(s_bulkDevice.deviceHandle);
}
//---------------------------------------------------------------------------------------
// Init clock to USB device
//---------------------------------------------------------------------------------------
static void USB_DeviceClockInit(void) {
usb_phy_config_struct_t phyConfig = {
BOARD_USB_PHY_D_CAL,
BOARD_USB_PHY_TXCAL45DP,
BOARD_USB_PHY_TXCAL45DM,
};
CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, 480000000U);
CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, 480000000U);
USB_EhciPhyInit(CONTROLLER_ID, BOARD_XTAL0_CLK_HZ, &phyConfig);
}
//---------------------------------------------------------------------------------------
// MyBulk class specific callback function.
// handle The MyBulk class handle.
// event The MyBulk class event type.
// param The parameter of the class specific request.
// return A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
usb_status_t USB_DeviceBulkCallback(class_handle_t handle, uint32_t event, void *param) {
uint32_t len;
usb_device_endpoint_callback_message_struct_t *epCbParam;
usb_status_t error = kStatus_USB_Error;
epCbParam = (usb_device_endpoint_callback_message_struct_t *)param;
switch (event) {
//---------------------------------------------------------------------------------------
// Bulk OUT request frame from PC
// We get this event when a request frame FROM the PC has been received
//---------------------------------------------------------------------------------------
case kUSB_DeviceMyBulkEventRequestReceiveDone:
if (s_bulkDevice.attach) {
// epCbParam->length has the length of received bytes, s_RecvBufBulkOut has the data
// Schedule buffer for next receive event
error = USB_DeviceMyBulkRecv(handle, BULK_OUT_EP, s_RecvBufBulkOut, g_UsbDeviceMyBulkEndpoints[0].maxPacketSize);
}
break;
//---------------------------------------------------------------------------------------
// Bulk IN response to PC
// We get this event when the response frame we sent TO the PC has been received by the PC (and ACK'ed)
// Here we can signal the "upper layers", f.ex with a semaphore that we are ready to send another frame
//---------------------------------------------------------------------------------------
case kUSB_DeviceMyBulkEventResponseTransmitDone:
if (s_bulkDevice.attach) {
// This is not needed in my example
if ((epCbParam->length != 0) && (!(epCbParam->length % g_UsbDeviceMyBulkEndpoints[0].maxPacketSize))) {
// If the last packet is the size of endpoint, then send also zero-ended packet, meaning that we want to inform the host that we do not have any additional
// data, so it can flush the output.
//error = USB_DeviceMyBulkSend(handle, BULK_IN_EP, NULL, 0);
} else {
}
}
break;
//---------------------------------------------------------------------------------------
// Bulk IN Log message to PC
// We get this event when the Log frame we sent TO the PC has been received by the PC (and ACK'ed)
//---------------------------------------------------------------------------------------
case kUSB_DeviceMyBulkEventLogTransmitDone:
if (s_bulkDevice.attach) {
// This is not needed in my example
if ((epCbParam->length != 0) && (!(epCbParam->length % g_UsbDeviceMyBulkEndpoints[0].maxPacketSize))) {
// If the last packet is the size of endpoint, then send also zero-ended packet, meaning that we want to inform the host that we do not have any additional
// data, so it can flush the output.
//error = USB_DeviceMyBulkSend(handle, BULK_IN_EP, NULL, 0);
} else {
}
}
break;
//---------------------------------------------------------------------------------------
// Bulk IN Wireshark message to PC
// We get this event when the Wireshark frame we sent TO the PC has been received by the PC (and ACK'ed)
//---------------------------------------------------------------------------------------
case kUSB_DeviceMyBulkEventWiresharkTransmitDone:
if (s_bulkDevice.attach) {
// This is not needed in my example
if ((epCbParam->length != 0) && (!(epCbParam->length % g_UsbDeviceMyBulkEndpoints[0].maxPacketSize))) {
// If the last packet is the size of endpoint, then send also zero-ended packet, meaning that we want to inform the host that we do not have any additional
// data, so it can flush the output.
//error = USB_DeviceMyBulkSend(handle, BULK_IN_EP, NULL, 0);
} else {
}
}
break;
default:
break;
}
return error;
}
//---------------------------------------------------------------------------------------
// USB device callback function.
// handle The USB device handle.
// event The USB device event type.
// param The parameter of the device specific request.
// return A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
usb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void *param) {
usb_status_t error = kStatus_USB_Error;
uint16_t *temp16 = (uint16_t *)param;
uint8_t *temp8 = (uint8_t *)param;
switch (event) {
// USB bus reset signal detected
case kUSB_DeviceEventBusReset:
s_bulkDevice.attach = 0;
s_bulkDevice.currentConfiguration = 0U;
#if (defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U)) || \
(defined(USB_DEVICE_CONFIG_LPCIP3511HS) && (USB_DEVICE_CONFIG_LPCIP3511HS > 0U))
/* Get USB speed to configure the device, including max packet size and interval of the endpoints. */
if (kStatus_USB_Success == USB_DeviceClassGetSpeed(CONTROLLER_ID, &s_bulkDevice.speed)) {
USB_DeviceSetSpeed(handle, s_bulkDevice.speed);
}
#endif
break;
// Set configuration
case kUSB_DeviceEventSetConfiguration:
if (0U == (*temp8)) {
s_bulkDevice.attach = 0;
s_bulkDevice.currentConfiguration = 0U;
} else if (USB_DEVICE_CONFIGURE_INDEX == (*temp8)) {
s_bulkDevice.attach = 1;
s_bulkDevice.currentConfiguration = *temp8;
// Schedule buffers for receive
USB_DeviceMyBulkRecv(s_bulkDevice.bulkHandle, BULK_OUT_EP, s_RecvBufBulkOut, g_UsbDeviceMyBulkEndpoints[0].maxPacketSize);
} else {
error = kStatus_USB_InvalidRequest;
}
break;
// Set interface
case kUSB_DeviceEventSetInterface:
if (s_bulkDevice.attach) {
uint8_t interface = (uint8_t)((*temp16 & 0xFF00U) >> 0x08U);
uint8_t alternateSetting = (uint8_t)(*temp16 & 0x00FFU);
if (interface < 1) {
s_bulkDevice.currentInterfaceAlternateSetting[interface] = alternateSetting;
}
}
break;
// Get configuration
case kUSB_DeviceEventGetConfiguration:
break;
// Get interface number
case kUSB_DeviceEventGetInterface:
break;
// Set Descriptor/Get Descriptor is used to return the specified descriptor in wValue.
// A request for the configuration descriptor will return the device descriptor and all interface and endpoint descriptors in the one request.
case kUSB_DeviceEventGetDeviceDescriptor:
if (param)
error = USB_DeviceGetDeviceDescriptor(handle, (usb_device_get_device_descriptor_struct_t *)param);
break;
// Get device configuration structure
case kUSB_DeviceEventGetConfigurationDescriptor:
if (param)
error = USB_DeviceGetConfigurationDescriptor(handle, (usb_device_get_configuration_descriptor_struct_t *)param);
break;
// Get a specific string
case kUSB_DeviceEventGetStringDescriptor:
if (param)
error = USB_DeviceGetStringDescriptor(handle, (usb_device_get_string_descriptor_struct_t *)param);
break;
default:
break;
}
return error;
}
//---------------------------------------------------------------------------------------
// Enable interrupt for USB device
//---------------------------------------------------------------------------------------
static void USB_DeviceIsrEnable(void) {
uint8_t irqNumber;
uint8_t usbDeviceEhciIrq[] = USBHS_IRQS;
irqNumber = usbDeviceEhciIrq[CONTROLLER_ID - kUSB_ControllerEhci0];
// Install isr, set priority, and enable IRQ
NVIC_SetPriority((IRQn_Type)irqNumber, USB_DEVICE_INTERRUPT_PRIORITY);
EnableIRQ((IRQn_Type)irqNumber);
}
//---------------------------------------------------------------------------------------
// Test stuff, called from main thread
//---------------------------------------------------------------------------------------
void xxx(void) {
static char buffer[512];
for (int i=0; i<sizeof(buffer); i++)
buffer[i]=i;
// Send message on all 3 IN endpoints
// Actually, we are allowed to specify lengths up to 16 KByte in a transmission, these will then be sent as several 512 byte packets!
USB_DeviceMyBulkSend(s_bulkDevice.bulkHandle, BULK_IN_EP, (unsigned char*)buffer, sizeof(buffer));
USB_DeviceMyBulkSend(s_bulkDevice.bulkHandle, BULK_IN_EP_LOG, (unsigned char*)buffer, sizeof(buffer));
USB_DeviceMyBulkSend(s_bulkDevice.bulkHandle, BULK_IN_EP_WSHARK, (unsigned char*)buffer, sizeof(buffer));
}
//---------------------------------------------------------------------------------------
// Ínitialize USB and our bulk class
//---------------------------------------------------------------------------------------
void initUSB(void) {
USB_DeviceClockInit();
s_bulkDevice.speed = USB_SPEED_FULL;
s_bulkDevice.attach = 0;
s_bulkDevice.bulkHandle = (class_handle_t)NULL;
s_bulkDevice.deviceHandle = NULL;
if (kStatus_USB_Success != USB_DeviceClassInit(CONTROLLER_ID, &s_MyBulkConfigList, &s_bulkDevice.deviceHandle)) {
//usb_echo("USB device init failed\r\n");
}
s_bulkDevice.bulkHandle = s_MyBulkConfigList.config->classHandle;
USB_DeviceIsrEnable();
SDK_DelayAtLeastUs(5000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
USB_DeviceRun(s_bulkDevice.deviceHandle);
}
//---------------------------------------------------------------------------------------
// usb_device_my_bulk.c 20210409 CHG
//
// Modified from NXP's CDC sample from SDK 2.9.2 by Carsten Groen
//---------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include "usb_device_config.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_descriptor.h"
#include "usb_device_my_bulk.h"
extern usb_device_endpoint_struct_t g_UsbDeviceMyBulkEndpoints[];
#define USB_MYBULK_ENTER_CRITICAL() \
OSA_SR_ALLOC(); \
OSA_ENTER_CRITICAL()
#define USB_MYBULK_EXIT_CRITICAL() OSA_EXIT_CRITICAL()
// MyBulk device instance
USB_GLOBAL USB_RAM_ADDRESS_ALIGNMENT(USB_DATA_ALIGN_SIZE) static usb_device_mybulk_struct_t g_myBulkHandle[USB_DEVICE_CONFIG_MYBULK];
//---------------------------------------------------------------------------------------
// Allocates the MyBulk device handle.
//
// This function allocates the MyBulk device handle.
//
// handle The class handle of the MyBulk class.
// returns A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
static usb_status_t USB_DeviceMyBulkAllocateHandle(usb_device_mybulk_struct_t **handle) {
uint32_t count;
for (count = 0; count < USB_DEVICE_CONFIG_MYBULK; count++) {
if (NULL == g_myBulkHandle[count].handle) {
*handle = &g_myBulkHandle[count];
return kStatus_USB_Success;
}
}
return kStatus_USB_Busy;
}
//---------------------------------------------------------------------------------------
// Frees the MyBulk device handle.
//
// handle The class handle of the MyBulk class.
// returns A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
static usb_status_t USB_DeviceMyBulkFreeHandle(usb_device_mybulk_struct_t *handle) {
handle->handle = NULL;
handle->configStruct = NULL;
handle->configuration = 0;
handle->alternate = 0;
return kStatus_USB_Success;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Handle bulk IN events. These functions gets called when a mnessage we sent to the PC has been ACK'ed by the PC
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//---------------------------------------------------------------------------------------
// Common helper function for all IN event.
//
// Responds to a bulk IN endpoint event on a specific endpoint.
// handle The device handle of the MyBulk device.
// message The pointer to the message of the endpoint callback.
// callbackParam The pointer to the parameter of the callback.
// ep The IN endpoint that should be handled
// event Hvis event to send to the BulkUSB.c module
// Return USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
static usb_status_t handleDeviceResponseBulkIn(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam, int ep, int event) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_status_t status = kStatus_USB_Error;
int i;
myBulkHandle = (usb_device_mybulk_struct_t *)callbackParam;
if (NULL == myBulkHandle) {
return kStatus_USB_InvalidHandle;
}
// Find our index in bulkIn[] based on the specific EP number we handle in this function
for (i=0; i<BULK_NUM_ENDPOINTS_IN; i++) {
if (myBulkHandle->bulkIn[i].ep == ep) {
break;
}
}
myBulkHandle->bulkIn[i].isBusy = 0;
if ((NULL != myBulkHandle->configStruct) && (NULL != myBulkHandle->configStruct->classCallback)) {
status = myBulkHandle->configStruct->classCallback((class_handle_t)myBulkHandle, event, message);
}
return status;
}
//---------------------------------------------------------------------------------------
// 3 handlers that responds to a bulk IN endpoint event on their specific endpoint, each
// calls a common helper function
// handle The device handle of the MyBulk device.
// message The pointer to the message of the endpoint callback.
// callbackParam The pointer to the parameter of the callback.
// A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
static usb_status_t USB_DeviceResponseBulkIn(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam) {
return handleDeviceResponseBulkIn(handle, message, callbackParam, BULK_IN_EP, kUSB_DeviceMyBulkEventResponseTransmitDone);
}
static usb_status_t USB_DeviceLogBulkIn(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam) {
return handleDeviceResponseBulkIn(handle, message, callbackParam, BULK_IN_EP_LOG, kUSB_DeviceMyBulkEventLogTransmitDone);
}
static usb_status_t USB_DeviceWiresharkBulkIn(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam) {
return handleDeviceResponseBulkIn(handle, message, callbackParam, BULK_IN_EP_WSHARK,kUSB_DeviceMyBulkEventWiresharkTransmitDone);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Handle bulk OUT events. This functions gets called when a message sent by the PC has been received
// We only have 1 single OUT endpoint in this device
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//---------------------------------------------------------------------------------------
// Responds to the bulk out endpoint event
// handle The device handle of the MyBulk device.
// message The pointer to the message of the endpoint callback.
// callbackParam The pointer to the parameter of the callback.
// return A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
static usb_status_t USB_DeviceCommandBulkOut(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_status_t status = kStatus_USB_Error;
myBulkHandle = (usb_device_mybulk_struct_t *)callbackParam;
if (NULL == myBulkHandle) {
return kStatus_USB_InvalidHandle;
}
myBulkHandle->bulkOut[0].isBusy = 0U;
if ((NULL != myBulkHandle->configStruct) && (NULL != myBulkHandle->configStruct->classCallback)) {
status = myBulkHandle->configStruct->classCallback((class_handle_t)myBulkHandle, kUSB_DeviceMyBulkEventRequestReceiveDone, message);
}
return status;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//---------------------------------------------------------------------------------------
// Initializes the endpoints in MyBulk class.
// myBulkHandle The class handle of the MyBulk class.
// return A USB error code or kStatus_USB_Success.
//---------------------------------------------------------------------------------------
static usb_status_t USB_DeviceMyBulkEndpointsInit(usb_device_mybulk_struct_t *myBulkHandle) {
usb_device_interface_list_t *interfaceList;
usb_device_interface_struct_t *interface = NULL;
usb_status_t error = kStatus_USB_Error;
uint32_t count;
uint32_t index;
if (NULL == myBulkHandle) {
return error;
}
/* return error when configuration is invalid (0 or more than the configuration number) */
if ((myBulkHandle->configuration == 0U) ||
(myBulkHandle->configuration > myBulkHandle->configStruct->classInfomation->configurations)) {
return error;
}
interfaceList = &myBulkHandle->configStruct->classInfomation->interfaceList[myBulkHandle->configuration - 1U];
for (count = 0; count < interfaceList->count; count++) {
if (USB_DEVICE_CLASS == interfaceList->interfaces[count].classCode) {
for (index = 0; index < interfaceList->interfaces[count].count; index++) {
if (interfaceList->interfaces[count].interface[index].alternateSetting == myBulkHandle->alternate) {
interface = &interfaceList->interfaces[count].interface[index];
break;
}
}
myBulkHandle->interfaceNumber = interfaceList->interfaces[count].interfaceNumber;
break;
}
}
if (NULL == interface) {
return error;
}
//---------------------------------------------------------------------------------------
// We need to populate the myBulkHandle->bulkIn[] and myBulkHandle->bulkOut[] arrays with
// the correct callback functions. We look at the specific endpoint and assign the correct
// callback handler based on that
//---------------------------------------------------------------------------------------
int outIndex=0; // Index into myBulkHandle->bulkOut[]
int inIndex=0; // Index into myBulkHandle->bulkIn[]
// Loop thru all the defined endpoints
for (count = 0; count < interface->endpointList.count; count++) {
usb_device_endpoint_init_struct_t epInitStruct;
usb_device_endpoint_callback_struct_t epCallback;
epInitStruct.zlt = 0;
epInitStruct.interval = interface->endpointList.endpoint[count].interval;
epInitStruct.endpointAddress = interface->endpointList.endpoint[count].endpointAddress;
epInitStruct.maxPacketSize = interface->endpointList.endpoint[count].maxPacketSize;
epInitStruct.transferType = interface->endpointList.endpoint[count].transferType;
// If the endpoint is a IN endpoint, assign callback function etc to the specific myBulkHandle->bulkIn[]
if ((USB_IN == ((epInitStruct.endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) >>
USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT)) &&
(USB_ENDPOINT_BULK == epInitStruct.transferType))
{
myBulkHandle->bulkIn[inIndex].ep = (epInitStruct.endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK);
myBulkHandle->bulkIn[inIndex].isBusy = 0;
myBulkHandle->bulkIn[inIndex].pipeDataBuffer= (uint8_t *)USB_UNINITIALIZED_VAL_32;
myBulkHandle->bulkIn[inIndex].pipeStall = 0;
myBulkHandle->bulkIn[inIndex].pipeDataLen = 0;
// Decide whích bulk handler to call depending on endpoint number
switch (myBulkHandle->bulkIn[inIndex].ep) {
case BULK_IN_EP: epCallback.callbackFn = USB_DeviceResponseBulkIn; break;
case BULK_IN_EP_LOG: epCallback.callbackFn = USB_DeviceLogBulkIn; break;
case BULK_IN_EP_WSHARK: epCallback.callbackFn = USB_DeviceWiresharkBulkIn; break;
default: epCallback.callbackFn=NULL;
}
inIndex++;
}
// If the endpoint is a OUT endpoint, assign callback function etc to the specific myBulkHandle->bulkOut[]
// (This is pretty simple as we only have 1 single OUT EP in this device :)
else if ((USB_OUT == ((epInitStruct.endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) >>
USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT)) &&
(USB_ENDPOINT_BULK == epInitStruct.transferType))
{
myBulkHandle->bulkOut[outIndex].ep = (epInitStruct.endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK);
myBulkHandle->bulkOut[outIndex].isBusy = 0;
myBulkHandle->bulkOut[outIndex].pipeDataBuffer = (uint8_t *)USB_UNINITIALIZED_VAL_32;
myBulkHandle->bulkOut[outIndex].pipeStall = 0;
myBulkHandle->bulkOut[outIndex].pipeDataLen = 0;
// Decide whích bulk handler to call depending on endpoint number
switch (myBulkHandle->bulkOut[outIndex].ep) {
case BULK_OUT_EP: epCallback.callbackFn = USB_DeviceCommandBulkOut; break;
default: epCallback.callbackFn=NULL;
}
outIndex++;
} else {
}
epCallback.callbackParam = myBulkHandle;
// Register callback handler etc for each endpoint
error = USB_DeviceInitEndpoint(myBulkHandle->handle, &epInitStruct, &epCallback);
}
return error;
}
//-----------------------------------------------------------------------------
// De-initializes the endpoints in MyBulk class.
//
// This function de-initializes the endpoints in MyBulk class.
//
// myBulkHandle The class handle of the MyBulk class.
// returns A USB error code or kStatus_USB_Success.
//-----------------------------------------------------------------------------
static usb_status_t USB_DeviceMyBulkEndpointsDeinit(usb_device_mybulk_struct_t *myBulkHandle) {
usb_status_t status = kStatus_USB_Error;
uint32_t count;
if (NULL == myBulkHandle->dataInterfaceHandle) {
return status;
}
for (count = 0; count < myBulkHandle->dataInterfaceHandle->endpointList.count; count++) {
status = USB_DeviceDeinitEndpoint(myBulkHandle->handle, myBulkHandle->dataInterfaceHandle->endpointList.endpoint[count].endpointAddress);
}
myBulkHandle->dataInterfaceHandle = NULL;
return status;
}
//-----------------------------------------------------------------------------
// Handles the MyBulk class event.
//
// This function responds to various events including the common device events and the class-specific events.
// For class-specific events, it calls the class callback defined in the application to deal with the class-specific event.
//
// handle The class handle of the MyBulk class.
// event The event type.
// param The class handle of the MyBulk class.
// returns:
// A USB error code or kStatus_USB_Success.
// kStatus_USB_Success The MyBulk class is de-initialized successfully.
// kStatus_USB_Error The configure structure of the MyBulk class handle is invalid.
// kStatus_USB_InvalidHandle The MyBulk device handle or the MyBulk class handle is invalid.
// kStatus_USB_InvalidParameter The endpoint number of the MyBulk class handle is invalid.
// Others The error code returned by class callback in application.
//-----------------------------------------------------------------------------
usb_status_t USB_DeviceMyBulkEvent(void *handle, uint32_t event, void *param) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_device_mybulk_request_param_struct_t reqParam;
usb_status_t error = kStatus_USB_Error;
uint32_t count;
uint16_t interfaceAlternate;
uint8_t *temp8;
uint8_t alternate;
usb_device_class_event_t eventCode = (usb_device_class_event_t)event;
if ((NULL == param) || (NULL == handle)) {
return kStatus_USB_InvalidHandle;
}
myBulkHandle = (usb_device_mybulk_struct_t *)handle;
// CHG: We only handle the common class events
switch (eventCode) {
case kUSB_DeviceClassEventDeviceReset:
// Bus reset, clear the configuration
myBulkHandle->configuration = 0;
break;
case kUSB_DeviceClassEventSetConfiguration:
temp8 = ((uint8_t *)param);
if (NULL == myBulkHandle->configStruct) {
break;
}
if (*temp8 == myBulkHandle->configuration) {
break;
}
error = USB_DeviceMyBulkEndpointsDeinit(myBulkHandle);
myBulkHandle->configuration = *temp8;
myBulkHandle->alternate = 0U;
error = USB_DeviceMyBulkEndpointsInit(myBulkHandle);
if (kStatus_USB_Success != error) {
#ifdef DEBUG
usb_echo("kUSB_DeviceClassEventSetConfiguration, USB_DeviceInitEndpoint fail\r\n");
#endif
}
break;
case kUSB_DeviceClassEventSetInterface:
if (NULL == myBulkHandle->configStruct) {
break;
}
interfaceAlternate = *((uint16_t *)param);
alternate = (uint8_t)(interfaceAlternate & 0xFFU);
if (myBulkHandle->interfaceNumber != ((uint8_t)(interfaceAlternate >> 8U))) {
break;
}
if (alternate == myBulkHandle->alternate) {
break;
}
error = USB_DeviceMyBulkEndpointsDeinit(myBulkHandle);
myBulkHandle->alternate = alternate;
error = USB_DeviceMyBulkEndpointsInit(myBulkHandle);
if (kStatus_USB_Success != error) {
#ifdef DEBUG
usb_echo("kUSB_DeviceClassEventSetInterface, USB_DeviceInitEndpoint fail\r\n");
#endif
}
break;
// CHG Not used
case kUSB_DeviceClassEventSetEndpointHalt:
break;
// CHG Not used
case kUSB_DeviceClassEventClearEndpointHalt:
break;
// CHG Not used
case kUSB_DeviceClassEventClassRequest:
break;
default:
break;
}
return error;
}
//-----------------------------------------------------------------------------
// USB MyBulk Class Driver
//
// Initializes the MyBulk class.
//
// This function obtains a USB device handle according to the controller ID, initializes the MyBulk class
// with the class configure parameters and creates the mutex for each pipe.
//
// controllerId The ID of the controller. The value can be chosen from the kUSB_ControllerKhci0,
// kUSB_ControllerKhci1, kUSB_ControllerEhci0, or kUSB_ControllerEhci1.
// config The user configuration structure of type usb_device_class_config_struct_t. The user
// populates the members of this structure and passes the pointer of this structure
// into this function.
// handle It is out parameter. The class handle of the MyBulk class.
// returns:
// A USB error code or kStatus_USB_Success.
// kStatus_USB_Success The MyBulk class is initialized successfully.
// kStatus_USB_Busy No MyBulk device handle available for allocation.
// kStatus_USB_InvalidHandle The MyBulk device handle allocation failure.
// kStatus_USB_InvalidParameter The USB device handle allocation failure.
//-----------------------------------------------------------------------------
usb_status_t USB_DeviceMyBulkInit(uint8_t controllerId, usb_device_class_config_struct_t *config, class_handle_t *handle) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_status_t error;
int i;
error = USB_DeviceMyBulkAllocateHandle(&myBulkHandle);
if (kStatus_USB_Success != error) {
return error;
}
error = USB_DeviceClassGetDeviceHandle(controllerId, &myBulkHandle->handle);
if (kStatus_USB_Success != error) {
return error;
}
if (NULL == myBulkHandle->handle) {
return kStatus_USB_InvalidHandle;
}
myBulkHandle->configStruct = config;
myBulkHandle->configuration = 0;
myBulkHandle->alternate = 0xFF;
for (i=0; i<BULK_NUM_ENDPOINTS_IN; i++) {
myBulkHandle->bulkIn[i].mutex = (osa_mutex_handle_t)&myBulkHandle->bulkIn[i].mutexBuffer[0];
if (KOSA_StatusSuccess != OSA_MutexCreate((myBulkHandle->bulkIn[i].mutex))) {
#ifdef DEBUG
usb_echo("mutex create error!");
#endif
}
}
for (i=0; i<BULK_NUM_ENDPOINTS_OUT; i++) {
myBulkHandle->bulkOut[i].mutex = (osa_mutex_handle_t)&myBulkHandle->bulkOut[i].mutexBuffer[0];
if (KOSA_StatusSuccess != OSA_MutexCreate((myBulkHandle->bulkOut[i].mutex))) {
#ifdef DEBUG
usb_echo("mutex create error!");
#endif
}
}
*handle = (class_handle_t)myBulkHandle;
return error;
}
//-----------------------------------------------------------------------------
// Deinitializes the USB MyBulk class.
//
// This function destroys the mutex for each pipe, deinitializes each endpoint of the MyBulk class and frees
// the MyBulk class handle.
//
// handle The class handle of the MyBulk class.
// returns:
// A USB error code or kStatus_USB_Success.
// kStatus_USB_Success The MyBulk class is de-initialized successfully.
// kStatus_USB_Error The endpoint deinitialization failure.
// kStatus_USB_InvalidHandle The MyBulk device handle or the MyBulk class handle is invalid.
// kStatus_USB_InvalidParameter The endpoint number of the MyBulk class handle is invalid.
//-----------------------------------------------------------------------------
usb_status_t USB_DeviceMyBulkDeinit(class_handle_t handle) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_status_t error;
int i;
myBulkHandle = (usb_device_mybulk_struct_t *)handle;
if (NULL == myBulkHandle) {
return kStatus_USB_InvalidHandle;
}
for (i=0; i<BULK_NUM_ENDPOINTS_IN; i++) {
if (KOSA_StatusSuccess != OSA_MutexDestroy((myBulkHandle->bulkIn[i].mutex))) {
#ifdef DEBUG
usb_echo("mutex destroy error!");
#endif
}
}
for (i=0; i<BULK_NUM_ENDPOINTS_OUT; i++) {
if (KOSA_StatusSuccess != OSA_MutexDestroy((myBulkHandle->bulkOut[i].mutex))) {
#ifdef DEBUG
usb_echo("mutex destroy error!");
#endif
}
}
error = USB_DeviceMyBulkEndpointsDeinit(myBulkHandle);
(void)USB_DeviceMyBulkFreeHandle(myBulkHandle);
return error;
}
//-----------------------------------------------------------------------------
// Primes the endpoint to send packet to host.
//
// This function checks whether the endpoint is sending packet, then it primes the endpoint
// with the buffer address and the buffer length if the pipe is not busy. Otherwise, it ignores this transfer by
// returning an error code.
//
// handle The class handle of the MyBulk class.
// ep The endpoint number of the transfer.
// buffer The pointer to the buffer to be transferred.
// length The length of the buffer to be transferred.
// returns:
// A USB error code or kStatus_USB_Success.
// kStatus_USB_Success Prime to send packet successfully.
// kStatus_USB_Busy The endpoint is busy in transferring.
// // kStatus_USB_InvalidHandle The MyBulk device handle or the MyBulk class handle is invalid.
// kStatus_USB_ControllerNotFound The controller interface is invalid.
//
// The function can only be called in the same context.
//-----------------------------------------------------------------------------
usb_status_t USB_DeviceMyBulkSend(class_handle_t handle, uint8_t ep, uint8_t *buffer, uint32_t length) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_status_t status = kStatus_USB_Error;
usb_device_mybulk_pipe_t *myBulkPipe = NULL;
int i;
if (NULL == handle) {
return kStatus_USB_InvalidHandle;
}
myBulkHandle = (usb_device_mybulk_struct_t *)handle;
// Find our index in bulkIn[] base on EP number we handle in this function
for (i=0; i<BULK_NUM_ENDPOINTS_IN; i++) {
if (myBulkHandle->bulkIn[i].ep == ep) {
break;
}
}
// Create a pointer to this specific pipe
myBulkPipe = &(myBulkHandle->bulkIn[i]);
if (NULL != myBulkPipe) {
if (1U == myBulkPipe->isBusy) {
return kStatus_USB_Busy;
}
myBulkPipe->isBusy = 1U;
if (0u != myBulkPipe->pipeStall) {
myBulkPipe->pipeDataBuffer = buffer;
myBulkPipe->pipeDataLen = length;
return kStatus_USB_Success;
}
status = USB_DeviceSendRequest(myBulkHandle->handle, ep, buffer, length);
if (kStatus_USB_Success != status) {
myBulkPipe->isBusy = 0U;
}
}
return status;
}
//-----------------------------------------------------------------------------
// Primes the endpoint to receive packet from host.
//
// This function checks whether the endpoint is receiving packet, then it primes the endpoint
// with the buffer address and the buffer length if the pipe is not busy. Otherwise, it ignores this transfer by
// returning an error code.
//
// handle The class handle of the MyBulk class.
// ep The endpoint number of the transfer.
// buffer The pointer to the buffer to be transferred.
// length The length of the buffer to be transferred.
// returns:
// A USB error code or kStatus_USB_Success.
// kStatus_USB_Success Prime to receive packet successfully.
// kStatus_USB_Busy The endpoint is busy in transferring.
// kStatus_USB_InvalidHandle The MyBulk device handle or the MyBulk class handle is invalid.
// kStatus_USB_ControllerNotFound The controller interface is invalid.
//
// The function can only be called in the same context.
//-----------------------------------------------------------------------------
usb_status_t USB_DeviceMyBulkRecv(class_handle_t handle, uint8_t ep, uint8_t *buffer, uint32_t length) {
usb_device_mybulk_struct_t *myBulkHandle;
usb_status_t status;
int i=0;
if (NULL == handle) {
return kStatus_USB_InvalidHandle;
}
myBulkHandle = (usb_device_mybulk_struct_t *)handle;
// Find our index in bulkOut[] base on EP number we handle in this function
for (i=0; i<BULK_NUM_ENDPOINTS_OUT; i++) {
if (myBulkHandle->bulkOut[i].ep == ep) {
break;
}
}
if (1U == myBulkHandle->bulkOut[i].isBusy) {
return kStatus_USB_Busy;
}
myBulkHandle->bulkOut[i].isBusy = 1U;
if (0U != myBulkHandle->bulkOut[i].pipeStall) {
myBulkHandle->bulkOut[i].pipeDataBuffer = buffer;
myBulkHandle->bulkOut[i].pipeDataLen = length;
return kStatus_USB_Success;
}
status = USB_DeviceRecvRequest(myBulkHandle->handle, ep, buffer, length);
if (kStatus_USB_Success != status) {
myBulkHandle->bulkOut[i].isBusy = 0U;
}
return status;
}
Very nice work, this effort is much needed.
Yes, you call the function initUSB() in the first file "BulkUSB.c", this is all that there is to it.
I have some code in C# for the PC side, however I can not share this. You will find samples on the net for the PC
Could you recommend PC software to test it?
I did the "PC end" myself using libusb and C#. works like a charm!
It will be appreciated if you share the PC test software. I need to test this solution. Thank you!!
Sorry, can't do that
You need to do some programming
I can find a test SW in keil, which can test Bulk, Hid, CDC and so on.
Forgot to mention that the code is based on SDK 2.9.1.
Also just a heads-up, I acquired a Beagle 480 USB analyser some time ago, this is highly recommended for stuff like this, makes it so much easier to see what's going on!