Working Custom USB example using Bulk transfer incl code!

cancel
Showing results for 
Search instead for 
Did you mean: 

Working Custom USB example using Bulk transfer incl code!

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

 

 

 

0 Kudos
1 Reply

28 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!

0 Kudos