Working Custom USB example using Bulk transfer incl code!

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Working Custom USB example using Bulk transfer incl code!

5,540 Views
carstengroen
Senior Contributor II

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

https://community.nxp.com/t5/i-MX-RT/Is-there-any-information-example-on-how-to-create-a-custom-usb/...

 

 

 

 

//-----------------------------------------------------------------------------
// 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;
}

 

 

 

11 Replies

4,147 Views
nickycruze2
Contributor I

Very nice work, this effort is much needed. 

 
 
0 Kudos

4,731 Views
yanz
Contributor III

Thank a lot for this great work!

I have two question about it:

1. main() missed, is code below enough :

void main(void)
{
initUSB();
}

2. Is there is a driver for this example?

0 Kudos

4,726 Views
carstengroen
Senior Contributor II

@yanz 

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 

carstengroen_0-1650890459029.png

 

0 Kudos

4,215 Views
kingchen2019
Contributor I

Could you recommend PC software to test it?

0 Kudos

4,210 Views
carstengroen
Senior Contributor II

I did the "PC end" myself using libusb and C#. works like a charm!

0 Kudos

4,198 Views
kingchen2019
Contributor I

It will be appreciated if you share the PC test software. I need to test this solution. Thank you!!

0 Kudos

4,195 Views
carstengroen
Senior Contributor II

Sorry, can't do that

You need to do some programming

0 Kudos

4,152 Views
kingchen2019
Contributor I

I can find a test SW in keil, which can test Bulk, Hid, CDC and so on.

0 Kudos

4,724 Views
yanz
Contributor III

Thank you.

 

0 Kudos

5,490 Views
MattInSeattle
Contributor III

Very nice work, this effort is much needed. 

5,529 Views
carstengroen
Senior Contributor II

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!