embeddedsw/XilinxProcessorIPLib/drivers/usbps/examples/xusbps_intr_example.c

575 lines
18 KiB
C
Raw Normal View History

/******************************************************************************
*
* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xusbps_intr_example.c
*
* This file contains an example of how to use the USB driver with the USB
* controller in DEVICE mode.
*
*
*<pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ------ -------- ----------------------------------------------------
* 1.00a wgr/nm 10/09/10 First release
* 1.01a nm 03/05/10 Included xpseudo_asm.h instead of xpseudo_asm_gcc.h
* 1.04a nm 02/05/13 Fixed CR# 696550.
* Added template code for Vendor request.
* 1.06a kpc 11/11/13 Fixed CR#759458, cacheInvalidate size should be
* ailgned to ccahe line size.
* 2.1 kpc 04/28/14 Cleanup and removed unused functions
*</pre>
******************************************************************************/
/***************************** Include Files *********************************/
#include "xparameters.h" /* XPAR parameters */
#include "xusbps.h" /* USB controller driver */
#include "xscugic.h"
#include "xusbps_ch9.h" /* Generic Chapter 9 handling code */
#include "xusbps_class_storage.h" /* Storage class handling code */
#include "xil_exception.h"
#include "xpseudo_asm.h"
#include "xreg_cortexa9.h"
#include "xil_cache.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/************************** Constant Definitions *****************************/
#define MEMORY_SIZE (64 * 1024)
u8 Buffer[MEMORY_SIZE] ALIGNMENT_CACHELINE;
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
static int UsbIntrExample(XScuGic *IntcInstancePtr, XUsbPs *UsbInstancePtr,
u16 UsbDeviceId, u16 UsbIntrId);
static void UsbIntrHandler(void *CallBackRef, u32 Mask);
static void XUsbPs_Ep0EventHandler(void *CallBackRef, u8 EpNum,
u8 EventType, void *Data);
static void XUsbPs_Ep1EventHandler(void *CallBackRef, u8 EpNum,
u8 EventType, void *Data);
static int UsbSetupIntrSystem(XScuGic *IntcInstancePtr,
XUsbPs *UsbInstancePtr, u16 UsbIntrId);
static void UsbDisableIntrSystem(XScuGic *IntcInstancePtr, u16 UsbIntrId);
/************************** Variable Definitions *****************************/
/* The instances to support the device drivers are global such that the
* are initialized to zero each time the program runs.
*/
static XScuGic IntcInstance; /* The instance of the IRQ Controller */
static XUsbPs UsbInstance; /* The instance of the USB Controller */
static volatile int NumIrqs = 0;
static volatile int NumReceivedFrames = 0;
/*****************************************************************************/
/**
*
* Main function to call the USB interrupt example.
*
* @param None
*
* @return
* - XST_SUCCESS if successful
* - XST_FAILURE on error
*
******************************************************************************/
int main(void)
{
int Status;
/* Run the USB Interrupt example.*/
Status = UsbIntrExample(&IntcInstance, &UsbInstance,
XPAR_XUSBPS_0_DEVICE_ID, XPAR_XUSBPS_0_INTR);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function does a minimal DEVICE mode setup on the USB device and driver
* as a design example. The purpose of this function is to illustrate how to
* set up a USB flash disk emulation system.
*
*
* @param IntcInstancePtr is a pointer to the instance of the INTC driver.
* @param UsbInstancePtr is a pointer to the instance of USB driver.
* @param UsbDeviceId is the Device ID of the USB Controller and is the
* XPAR_<USB_instance>_DEVICE_ID value from xparameters.h.
* @param UsbIntrId is the Interrupt Id and is typically
* XPAR_<INTC_instance>_<USB_instance>_IP2INTC_IRPT_INTR value
* from xparameters.h.
*
* @return
* - XST_SUCCESS if successful
* - XST_FAILURE on error
*
******************************************************************************/
static int UsbIntrExample(XScuGic *IntcInstancePtr, XUsbPs *UsbInstancePtr,
u16 UsbDeviceId, u16 UsbIntrId)
{
int Status;
u8 *MemPtr = NULL;
int ReturnStatus = XST_FAILURE;
/* For this example we only configure 2 endpoints:
* Endpoint 0 (default control endpoint)
* Endpoint 1 (BULK data endpoint)
*/
const u8 NumEndpoints = 2;
XUsbPs_Config *UsbConfigPtr;
XUsbPs_DeviceConfig DeviceConfig;
/* Initialize the USB driver so that it's ready to use,
* specify the controller ID that is generated in xparameters.h
*/
UsbConfigPtr = XUsbPs_LookupConfig(UsbDeviceId);
if (NULL == UsbConfigPtr) {
goto out;
}
/* We are passing the physical base address as the third argument
* because the physical and virtual base address are the same in our
* example. For systems that support virtual memory, the third
* argument needs to be the virtual base address.
*/
Status = XUsbPs_CfgInitialize(UsbInstancePtr,
UsbConfigPtr,
UsbConfigPtr->BaseAddress);
if (XST_SUCCESS != Status) {
goto out;
}
/* Set up the interrupt subsystem.
*/
Status = UsbSetupIntrSystem(IntcInstancePtr,
UsbInstancePtr,
UsbIntrId);
if (XST_SUCCESS != Status)
{
goto out;
}
/* Configuration of the DEVICE side of the controller happens in
* multiple stages.
*
* 1) The user configures the desired endpoint configuration using the
* XUsbPs_DeviceConfig data structure. This includes the number of
* endpoints, the number of Transfer Descriptors for each endpoint
* (each endpoint can have a different number of Transfer Descriptors)
* and the buffer size for the OUT (receive) endpoints. Each endpoint
* can have different buffer sizes.
*
* 2) Request the required size of DMAable memory from the driver using
* the XUsbPs_DeviceMemRequired() call.
*
* 3) Allocate the DMAable memory and set up the DMAMemVirt and
* DMAMemPhys members in the XUsbPs_DeviceConfig data structure.
*
* 4) Configure the DEVICE side of the controller by calling the
* XUsbPs_ConfigureDevice() function.
*/
/*
* For this example we only configure Endpoint 0 and Endpoint 1.
*
* Bufsize = 0 indicates that there is no buffer allocated for OUT
* (receive) endpoint 0. Endpoint 0 is a control endpoint and we only
* receive control packets on that endpoint. Control packets are 8
* bytes in size and are received into the Queue Head's Setup Buffer.
* Therefore, no additional buffer space is needed.
*/
DeviceConfig.EpCfg[0].Out.Type = XUSBPS_EP_TYPE_CONTROL;
DeviceConfig.EpCfg[0].Out.NumBufs = 2;
DeviceConfig.EpCfg[0].Out.BufSize = 64;
DeviceConfig.EpCfg[0].Out.MaxPacketSize = 64;
DeviceConfig.EpCfg[0].In.Type = XUSBPS_EP_TYPE_CONTROL;
DeviceConfig.EpCfg[0].In.NumBufs = 2;
DeviceConfig.EpCfg[0].In.MaxPacketSize = 64;
DeviceConfig.EpCfg[1].Out.Type = XUSBPS_EP_TYPE_BULK;
DeviceConfig.EpCfg[1].Out.NumBufs = 16;
DeviceConfig.EpCfg[1].Out.BufSize = 512;
DeviceConfig.EpCfg[1].Out.MaxPacketSize = 512;
DeviceConfig.EpCfg[1].In.Type = XUSBPS_EP_TYPE_BULK;
DeviceConfig.EpCfg[1].In.NumBufs = 16;
DeviceConfig.EpCfg[1].In.MaxPacketSize = 512;
DeviceConfig.NumEndpoints = NumEndpoints;
MemPtr = (u8 *)&Buffer[0];
memset(MemPtr,0,MEMORY_SIZE);
Xil_DCacheFlushRange((unsigned int)MemPtr, MEMORY_SIZE);
/* Finish the configuration of the DeviceConfig structure and configure
* the DEVICE side of the controller.
*/
DeviceConfig.DMAMemPhys = (u32) MemPtr;
Status = XUsbPs_ConfigureDevice(UsbInstancePtr, &DeviceConfig);
if (XST_SUCCESS != Status) {
goto out;
}
/* Set the handler for receiving frames. */
Status = XUsbPs_IntrSetHandler(UsbInstancePtr, UsbIntrHandler, NULL,
XUSBPS_IXR_UE_MASK);
if (XST_SUCCESS != Status) {
goto out;
}
/* Set the handler for handling endpoint 0 events. This is where we
* will receive and handle the Setup packet from the host.
*/
Status = XUsbPs_EpSetHandler(UsbInstancePtr, 0,
XUSBPS_EP_DIRECTION_OUT,
XUsbPs_Ep0EventHandler, UsbInstancePtr);
/* Set the handler for handling endpoint 1 events.
*
* Note that for this example we do not need to register a handler for
* TX complete events as we only send data using static data buffers
* that do not need to be free()d or returned to the OS after they have
* been sent.
*/
Status = XUsbPs_EpSetHandler(UsbInstancePtr, 1,
XUSBPS_EP_DIRECTION_OUT,
XUsbPs_Ep1EventHandler, UsbInstancePtr);
/* Enable the interrupts. */
XUsbPs_IntrEnable(UsbInstancePtr, XUSBPS_IXR_UR_MASK |
XUSBPS_IXR_UI_MASK);
/* Start the USB engine */
XUsbPs_Start(UsbInstancePtr);
/* At this point we wait for the user to plug in the usb plug. This
* will cause the host to send USB packets. Once we received something,
* we clean up and stop the controller.
*
* This will not really work if we want to use the USB storage
* example. What can we do instead?
*/
while (NumReceivedFrames < 1) {
/* NOP */
}
/* Set return code to indicate success and fall through to clean-up
* code.
*/
ReturnStatus = XST_SUCCESS;
out:
/* Clean up. It's always safe to disable interrupts and clear the
* handlers, even if they have not been enabled/set. The same is true
* for disabling the interrupt subsystem.
*/
XUsbPs_Stop(UsbInstancePtr);
XUsbPs_IntrDisable(UsbInstancePtr, XUSBPS_IXR_ALL);
(int) XUsbPs_IntrSetHandler(UsbInstancePtr, NULL, NULL, 0);
UsbDisableIntrSystem(IntcInstancePtr, UsbIntrId);
/* Free allocated memory.
*/
if (NULL != UsbInstancePtr->UserDataPtr) {
free(UsbInstancePtr->UserDataPtr);
}
return ReturnStatus;
}
/*****************************************************************************/
/**
*
* This function is the handler which performs processing for the USB driver.
* It is called from an interrupt context such that the amount of processing
* performed should be minimized.
*
* This handler provides an example of how to handle USB interrupts and
* is application specific.
*
* @param CallBackRef is the Upper layer callback reference passed back
* when the callback function is invoked.
* @param Mask is the Interrupt Mask.
* @param CallBackRef is the User data reference.
*
* @return
* - XST_SUCCESS if successful
* - XST_FAILURE on error
*
* @note None.
*
******************************************************************************/
static void UsbIntrHandler(void *CallBackRef, u32 Mask)
{
NumIrqs++;
}
/*****************************************************************************/
/**
* This funtion is registered to handle callbacks for endpoint 0 (Control).
*
* It is called from an interrupt context such that the amount of processing
* performed should be minimized.
*
*
* @param CallBackRef is the reference passed in when the function
* was registered.
* @param EpNum is the Number of the endpoint on which the event occured.
* @param EventType is type of the event that occured.
*
* @return None.
*
******************************************************************************/
static void XUsbPs_Ep0EventHandler(void *CallBackRef, u8 EpNum,
u8 EventType, void *Data)
{
XUsbPs *InstancePtr;
int Status;
XUsbPs_SetupData SetupData;
u8 *BufferPtr;
u32 BufferLen;
u32 Handle;
Xil_AssertVoid(NULL != CallBackRef);
InstancePtr = (XUsbPs *) CallBackRef;
switch (EventType) {
/* Handle the Setup Packets received on Endpoint 0. */
case XUSBPS_EP_EVENT_SETUP_DATA_RECEIVED:
Status = XUsbPs_EpGetSetupData(InstancePtr, EpNum, &SetupData);
if (XST_SUCCESS == Status) {
/* Handle the setup packet. */
(int) XUsbPs_Ch9HandleSetupPacket(InstancePtr,
&SetupData);
}
break;
/* We get data RX events for 0 length packets on endpoint 0. We receive
* and immediately release them again here, but there's no action to be
* taken.
*/
case XUSBPS_EP_EVENT_DATA_RX:
/* Get the data buffer. */
Status = XUsbPs_EpBufferReceive(InstancePtr, EpNum,
&BufferPtr, &BufferLen, &Handle);
if (XST_SUCCESS == Status) {
/* Return the buffer. */
XUsbPs_EpBufferRelease(Handle);
}
break;
default:
/* Unhandled event. Ignore. */
break;
}
}
/*****************************************************************************/
/**
* This funtion is registered to handle callbacks for endpoint 1 (Bulk data).
*
* It is called from an interrupt context such that the amount of processing
* performed should be minimized.
*
*
* @param CallBackRef is the reference passed in when the function was
* registered.
* @param EpNum is the Number of the endpoint on which the event occured.
* @param EventType is type of the event that occured.
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void XUsbPs_Ep1EventHandler(void *CallBackRef, u8 EpNum,
u8 EventType, void *Data)
{
XUsbPs *InstancePtr;
int Status;
u8 *BufferPtr;
u32 BufferLen;
u32 InavalidateLen;
u32 Handle;
Xil_AssertVoid(NULL != CallBackRef);
InstancePtr = (XUsbPs *) CallBackRef;
switch (EventType) {
case XUSBPS_EP_EVENT_DATA_RX:
/* Get the data buffer.*/
Status = XUsbPs_EpBufferReceive(InstancePtr, EpNum,
&BufferPtr, &BufferLen, &Handle);
/* Invalidate the Buffer Pointer */
InavalidateLen = BufferLen;
if (BufferLen % 32) {
InavalidateLen = (BufferLen/32) * 32 + 32;
}
Xil_DCacheInvalidateRange((unsigned int)BufferPtr,
InavalidateLen);
if (XST_SUCCESS == Status) {
/* Handle the storage class request. */
XUsbPs_HandleStorageReq(InstancePtr, EpNum,
BufferPtr, BufferLen);
/* Release the buffer. */
XUsbPs_EpBufferRelease(Handle);
}
break;
default:
/* Unhandled event. Ignore. */
break;
}
}
/*****************************************************************************/
/**
*
* This function setups the interrupt system such that interrupts can occur for
* the USB controller. This function is application specific since the actual
* system may or may not have an interrupt controller. The USB controller could
* be directly connected to a processor without an interrupt controller. The
* user should modify this function to fit the application.
*
* @param IntcInstancePtr is a pointer to instance of the Intc controller.
* @param UsbInstancePtr is a pointer to instance of the USB controller.
* @param UsbIntrId is the Interrupt Id and is typically
* XPAR_<INTC_instance>_<USB_instance>_VEC_ID value
* from xparameters.h
*
* @return
* - XST_SUCCESS if successful
* - XST_FAILURE on error
*
******************************************************************************/
static int UsbSetupIntrSystem(XScuGic *IntcInstancePtr,
XUsbPs *UsbInstancePtr, u16 UsbIntrId)
{
int Status;
XScuGic_Config *IntcConfig;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_ExceptionInit();
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcInstancePtr);
/*
* Connect the device driver handler that will be called when an
* interrupt for the device occurs, the handler defined above performs
* the specific interrupt processing for the device.
*/
Status = XScuGic_Connect(IntcInstancePtr, UsbIntrId,
(Xil_ExceptionHandler)XUsbPs_IntrHandler,
(void *)UsbInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
/*
* Enable the interrupt for the device.
*/
XScuGic_Enable(IntcInstancePtr, UsbIntrId);
/*
* Enable interrupts in the Processor.
*/
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function disables the interrupts that occur for the USB controller.
*
* @param IntcInstancePtr is a pointer to instance of the INTC driver.
* @param UsbIntrId is the Interrupt Id and is typically
* XPAR_<INTC_instance>_<USB_instance>_VEC_ID value
* from xparameters.h
*
* @return None
*
* @note None.
*
******************************************************************************/
static void UsbDisableIntrSystem(XScuGic *IntcInstancePtr, u16 UsbIntrId)
{
/* Disconnect and disable the interrupt for the USB controller. */
XScuGic_Disconnect(IntcInstancePtr, UsbIntrId);
}