/****************************************************************************** * * Copyright (C) 2006 Vreelin Engineering, Inc. All Rights Reserved. * (c) Copyright 2007-2013 Xilinx, Inc. All rights reserved. * * This file contains confidential and proprietary information of Xilinx, Inc. * and is protected under U.S. and international copyright and other * intellectual property laws. * * DISCLAIMER * This disclaimer is not a license and does not grant any rights to the * materials distributed herewith. Except as otherwise provided in a valid * license issued to you by Xilinx, and to the maximum extent permitted by * applicable law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL * FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS, * IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF * MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; * and (2) Xilinx shall not be liable (whether in contract or tort, including * negligence, or under any other theory of liability) for any loss or damage * of any kind or nature related to, arising under or in connection with these * materials, including for any direct, or any indirect, special, incidental, * or consequential loss or damage (including loss of data, profits, goodwill, * or any type of loss or damage suffered as a result of any action brought by * a third party) even if such damage or loss was reasonably foreseeable or * Xilinx had been advised of the possibility of the same. * * CRITICAL APPLICATIONS * Xilinx products are not designed or intended to be fail-safe, or for use in * any application requiring fail-safe performance, such as life-support or * safety devices or systems, Class III medical devices, nuclear facilities, * applications related to the deployment of airbags, or any other applications * that could lead to death, personal injury, or severe property or * environmental damage (individually and collectively, "Critical * Applications"). Customer assumes the sole risk and liability of any use of * Xilinx products in Critical Applications, subject only to applicable laws * and regulations governing limitations on product liability. * * THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE * AT ALL TIMES. * ******************************************************************************/ /*****************************************************************************/ /** * @file xusb_keyboard.c * * This file contains an example for USB keyboard. * * @note * - The example is tested on ML403 and ML507 evaluation boards. * The push buttons SW3,SW4,SW5 and SW7 on the ML403 evaluation * board are used in the example for the key action of the USB * keyboard.The push buttons SW10, SW11, SW12, SW13 and SW14 * are used on the ML507 board. * The example sends a character from a fixed sequence of * characters from the device as and when any of the SW3 or SW4 * or SW5 and SW7 push button is pressed. The fixed sequence of * characters is XILINX USB KEYBOARD DEMO. The SW6 push button * switch is used to complete the test. SW14 is used on ML507 * board to complete the test. * - The GPIO device has to be added to the hardware design so that * the push buttons on the evaluation board could be used. If we * enable the debug statements in the xusb_cp9.c file, we must * add the UARTLite core to the hardware design. Debug messages * can be enabled by defining the constant XUSB_DEBUG, * - To run this example, the evaluation board is to be connected * to a windows Host PC over the USB port. * - The example configures the USB device for endpoint 0 and * endpoint 1. Endpoint 0 is the control endpoint and is * configured for a maximum packet length of 64 bytes. End point * 1 is configured for INTERRUPT IN transactions and the maximum * packet size is configured as 16 bytes. * - The USB keyboard example code has to be compiled along with * the xusb_cp9.c file. The xusb_cp9.c file contains all the USB * enumeration related functions. To compile the code for USB * keyboard example, the constant definitions HID_DEVICES * and USB_KEYBOARD are to be defined and the definitions the * constants USB_MOUSE and MASS_STORAGE_DEVICE are to be * undefined. These definitions can be found in the xusb_types.h * file. * *
 * MODIFICATION HISTORY:
 *
 * Ver   Who  Date     Changes
 * ----- ---- -----------------------------------------------------------------
 * 1.00a hvm  5/31/07 First release
 * 3.00a hvm  11/18/09 Updated to use HAL processor APIs.
 *		       XUsb_mReadReg is renamed to XUsb_ReadReg and
 *		       XUsb_mWriteReg is renamed to XUsb_WriteReg.
 * 4.00a hvm  08/11/11 Updated the code in gpio isr to increment the index by 4 as
 *			a dummy byte is added in the Message variable in keyboard.h
 *			file to	handle the address alignment issue.
 * 4.02a bss  11/01/11 Modified UsbIfIntrHandler function to unconditionally
 *			reset when USB reset is asserted (CR 627574).
 *
 * 
*****************************************************************************/ /***************************** Include Files *********************************/ #include "xusb.h" #include "xintc.h" #include "xusb_keyboard.h" #include "stdio.h" #include "xgpio.h" #include "xil_exception.h" /************************** Constant Definitions *****************************/ #define USB_DEVICE_ID XPAR_USB_0_DEVICE_ID #define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID #define USB_INTR XPAR_INTC_0_USB_0_VEC_ID /* * The GPIO_DEVICE_ID is to be modified as per the name provided during the * system design. Default name for default system built for ML403 board is * XPAR_PUSH_BUTTONS_POSITION_DEVICE_ID and the one for ML507 board is * XPAR_PUSH_BUTTONS_5BIT_DEVICE_ID. * */ #define GPIO_DEVICE_ID XPAR_PUSH_BUTTONS_4BITS_DEVICE_ID #define INTC_GPIO_INTERRUPT_ID XPAR_INTC_0_GPIO_0_VEC_ID #define GPIO_ALL_BUTTONS 0x1F /* The GPIO bits 0 to 4. */ #define EXIT_BUTTON 0x0010 /* The GPIO_SW_C on the ML403 board */ #define BUTTON_CHANNEL 1 /* Channel 1 of the GPIO Device */ #define BUTTON_INTERRUPT XGPIO_IR_CH1_MASK /* Channel 1 Interrupt Mask */ /************************** Function Prototypes ******************************/ int UsbKbdExample(u16 UsbId, u16 GpioId); static int SetupInterruptSystem(XUsb *UsbPtr, XGpio *GpioPtr); /************************** Variable Definitions *****************************/ static XUsb UsbInstance; /* The instance of the USB device */ static XGpio Gpio; /* The Instance of the GPIO Driver */ XUsb_Config *UsbConfigPtr; /* Pointer to the USB config structure */ XGpio_Config *GpioConfigPtr; /* Pointer to the GPIO config structure */ XIntc InterruptController; /* Instance of the Interrupt Controller */ volatile int StopTest = FALSE; int MaxMsgLength; /****************************************************************************/ /** * This function is the main function of the USB Keyboard example. * * @param None. * * @return * - XST_SUCCESS if successful, * - XST_FAILURE if unsuccessful. * * @note None. * * *****************************************************************************/ int main(void) { int Status; Status = UsbKbdExample(USB_DEVICE_ID, GPIO_DEVICE_ID); if (Status != XST_SUCCESS) { return XST_FAILURE; } return XST_SUCCESS; } /*****************************************************************************/ /** * This function implements the USB Keyboard example. * The Key board action can be seen on the PC as and when any of the push * buttons SW3 or SW4 or SW5 or SW7 on the ML403 evaluation board is pressed. * * @param UsbId is the USB device id. * @param GpioId is the GPIO device id. * * @return * - XST_SUCCESS if successful. * - XST_FAILURE if test fails. * @note None. * *****************************************************************************/ int UsbKbdExample(u16 UsbId, u16 GpioId) { int Status; /* * Initialize the GPIO driver. */ GpioConfigPtr = XGpio_LookupConfig(GpioId); if (GpioConfigPtr == NULL) { return XST_FAILURE; } /* * 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 = XGpio_CfgInitialize(&Gpio, GpioConfigPtr, GpioConfigPtr->BaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Initialize the USB driver. */ UsbConfigPtr = XUsb_LookupConfig(UsbId); if (UsbConfigPtr == NULL) { return XST_FAILURE; } Status = XUsb_CfgInitialize(&UsbInstance, UsbConfigPtr, UsbConfigPtr->BaseAddress); if (XST_SUCCESS != Status) { return XST_FAILURE; } /* * Initialize the USB instance as required for the keyboard * example. */ InitUsbInterface(&UsbInstance); XGpio_SetDataDirection(&Gpio, BUTTON_CHANNEL, GPIO_ALL_BUTTONS); MaxMsgLength = sizeof(Message); /* * Set our function address to 0 which is the unenumerated state. */ Status = XUsb_SetDeviceAddress(&UsbInstance, 0); if (XST_SUCCESS != Status) { return XST_FAILURE; } /* * Setup the interrupt handlers. */ XUsb_IntrSetHandler(&UsbInstance, (void *) UsbIfIntrHandler, &UsbInstance); XUsb_EpSetHandler(&UsbInstance, 0, (XUsb_EpHandlerFunc *) Ep0IntrHandler, &UsbInstance); XUsb_EpSetHandler(&UsbInstance, 1, (XUsb_EpHandlerFunc *) Ep1IntrHandler, &UsbInstance); /* * Setup the interrupt system. */ Status = SetupInterruptSystem(&UsbInstance, &Gpio); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Enable the interrupts. */ XUsb_IntrEnable(&UsbInstance, XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_DISCONNECT_MASK | XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK | XUSB_STATUS_EP0_BUFF1_COMP_MASK | XUSB_STATUS_EP1_BUFF1_COMP_MASK | XUSB_STATUS_EP1_BUFF2_COMP_MASK ); XUsb_Start(&UsbInstance); /* * Set the device configuration to unenumerated state. */ UsbInstance.DeviceConfig.CurrentConfiguration = 0; while (StopTest == FALSE){ /* * Stop the test if the Stop key is pressed */ } return XST_SUCCESS; } /*****************************************************************************/ /** * This function initializes the USB device for Keyboard example. The following * is the configuration. * - EP0 : CONTROL end point, Bidirectional, Packet size 64 bytes. * - EP1 : NON_ISOCHRONOUS, INTERRUPT_IN, packet size of 16 bytes. * * @param InstancePtr is a pointer to the XUsb instance. * * @return None. * * @note None. * ******************************************************************************/ void InitUsbInterface(XUsb * InstancePtr) { XUsb_DeviceConfig DeviceConfig; /* * Setup Endpoint 0. */ DeviceConfig.Ep[0].RamBase = 0x22; DeviceConfig.Ep[0].Size = 0x40; DeviceConfig.Ep[0].EpType = 0; DeviceConfig.Ep[0].OutIn = XUSB_EP_DIRECTION_OUT; /* * Setup Endpoint 1. */ DeviceConfig.Ep[1].RamBase = 0x1000; DeviceConfig.Ep[1].Size = 0x10; DeviceConfig.Ep[1].EpType = 0; DeviceConfig.Ep[1].OutIn = XUSB_EP_DIRECTION_IN; DeviceConfig.NumEndpoints = 2; /* * Initialize the device configuration. */ XUsb_ConfigureDevice(InstancePtr, &DeviceConfig); XUsb_EpEnable(InstancePtr, 0); XUsb_EpEnable(InstancePtr, 1); MaxControlSize = 64; /* * Store the actual RAM address offset in the device structure, so as to * avoid the multiplication during processing. */ InstancePtr->DeviceConfig.Ep[1].RamBase <<= 2; } /*****************************************************************************/ /** * This function is the interrupt handler for the USB keyboard example * * @param CallBackRef is the callback reference passed from the interrupt * handler, which in our case is a pointer to the driver instance. * @param IntrStatus is a bit mask indicating pending interrupts. * * @return None. * * @note Each interrupt source is disabled upon reception. This is to * avoid the repetitive occurrence of the same event. This is done * because these event conditions exist for few milliseconds. * ******************************************************************************/ void UsbIfIntrHandler(void *CallBackRef, u32 IntrStatus) { XUsb *InstancePtr; u8 Index; InstancePtr = (XUsb *) CallBackRef; if (IntrStatus & XUSB_STATUS_RESET_MASK) { XUsb_Stop(InstancePtr); InstancePtr->DeviceConfig.CurrentConfiguration = 0; InstancePtr->DeviceConfig.Status = XUSB_RESET; for (Index = 0; Index < 3; Index++) { XUsb_WriteReg(InstancePtr->Config.BaseAddress, InstancePtr-> EndPointOffset[Index], 0); } /* * Re-initialize the device and set the device address * to 0 and re-start the device. */ InitUsbInterface(InstancePtr); XUsb_SetDeviceAddress(InstancePtr, 0); XUsb_Start(InstancePtr); XUsb_IntrDisable(InstancePtr, XUSB_STATUS_RESET_MASK); XUsb_IntrEnable(InstancePtr, (XUSB_STATUS_DISCONNECT_MASK | XUSB_STATUS_SUSPEND_MASK)); } if (IntrStatus & XUSB_STATUS_SUSPEND_MASK) { /* * Process the suspend event. */ XUsb_IntrDisable(InstancePtr, XUSB_STATUS_SUSPEND_MASK); XUsb_IntrEnable(InstancePtr, (XUSB_STATUS_RESET_MASK | XUSB_STATUS_DISCONNECT_MASK)); } } /*****************************************************************************/ /** * This function is the interrupt handler for the USB End point Zero events. * * * @param CallBackRef is the callback reference passed from the interrupt. * handler, which in our case is a pointer to the driver instance. * @param EpNum is the end point number. * @param IntrStatus is a bit mask indicating pending interrupts. * * @return None. * * @note EpNum is not used in this function as the handler is attached * specific to end point zero. This parameter is useful when a * single handler is used for processing all end point interrupts. * ******************************************************************************/ void Ep0IntrHandler(void *CallBackRef, u8 EpNum, u32 IntrStatus) { XUsb *InstancePtr; int SetupRequest; InstancePtr = (XUsb *) CallBackRef; /* * Process the end point zero buffer interrupt. */ if (IntrStatus & XUSB_BUFFREADY_EP0_BUFF_MASK) { if (IntrStatus & XUSB_STATUS_SETUP_PACKET_MASK) { /* * Received a setup packet. Execute the chapter 9 * command. */ XUsb_IntrEnable(InstancePtr, (XUSB_STATUS_DISCONNECT_MASK | XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESET_MASK)); SetupRequest = Chapter9(InstancePtr); if (SetupRequest != XST_SUCCESS) { switch(SetupRequest){ case 0x9: break; case 0x10: break; /* * Unsupported command. Stall * the end point. */ default: XUsb_EpStall(InstancePtr, 0); break; } } } else if (IntrStatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK) { EP0ProcessOutToken(InstancePtr); } else if (IntrStatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK) { EP0ProcessInToken(InstancePtr); } } } /*****************************************************************************/ /** * This function is the interrupt handler for the USB End point one events. * * @param CallBackRef is the callback reference passed from the interrupt * handler, which in our case is a pointer to the driver instance. * @param EpNum is the end point number. * @param IntrStatus is a bit mask indicating pending interrupts. * * @return None. * * @note EpNum is not used in this function as the handler is attached * specific to end point one. This parameter is useful when a * single handler is used for processing all end point interrupts. * ******************************************************************************/ void Ep1IntrHandler(void *CallBackRef, u8 EpNum, u32 IntrStatus) { XUsb *InstancePtr; InstancePtr = (XUsb *) CallBackRef; /* * Process the End point 1 interrupts. */ if (IntrStatus & XUSB_BUFFREADY_EP1_BUFF1_MASK) { InstancePtr->DeviceConfig.Ep[1].Buffer0Ready = 0; } if (IntrStatus & XUSB_BUFFREADY_EP1_BUFF2_MASK) { InstancePtr->DeviceConfig.Ep[1].Buffer1Ready = 0; } } /****************************************************************************/ /** * This function is the Interrupt Service Routine for the GPIO device. * * This function will detect the push button on the board has changed state * and then prepare data to be sent to the host. * * @param InstancePtr is the GPIO component to operate on. It is a void * pointer and in this case will be a pointer to the GPIO * instance. * * @return None. * * @note None. * *****************************************************************************/ void GpioIsr(void *InstancePtr) { XGpio *GpioPtr = (XGpio *)InstancePtr; u32 Buttons; u32 ButtonsChanged = 0; static u32 PreviousButtons; int Status; static int Index = 0; u8 NoKeyData[3]= {0,0,0}; /* * Disable the GPIO interrupt. */ XGpio_InterruptDisable(GpioPtr, BUTTON_INTERRUPT); /* * There should not be any other GPIO interrupts occurring other than * the the button changes. */ if ((XGpio_InterruptGetStatus(GpioPtr) & BUTTON_INTERRUPT) != BUTTON_INTERRUPT) { return; } /* * Read state of push buttons and determine which ones changed * states from the previous interrupt. Save a copy of the buttons * for the next interrupt. */ Buttons = (XGpio_DiscreteRead(GpioPtr, BUTTON_CHANNEL) & 0x1F) ; ButtonsChanged = Buttons ^ PreviousButtons; PreviousButtons = Buttons; /* * Handle all button state changes that occurred since the last * interrupt. */ while (ButtonsChanged != 0) { /* * Determine which button changed state and then get * the current state of the associated LED. */ if (ButtonsChanged & 0x1F){ if (ButtonsChanged & EXIT_BUTTON){ StopTest = TRUE; break; } if (Index >= MaxMsgLength){ Index = 0; } XUsb_EpDataSend(&UsbInstance, 1, (unsigned char *)&Message[Index], 3); /* * Send no key status to PC. */ Status = XUsb_EpDataSend(&UsbInstance, 1, (unsigned char *)&NoKeyData[0], 3); if (Status == XST_SUCCESS) { Index += 4; } } break; } /* * Clear the interrupt such that it is no longer pending in the GPIO. */ (void)XGpio_InterruptClear(GpioPtr, BUTTON_INTERRUPT); /* * Enable the GPIO interrupt. */ XGpio_InterruptEnable(GpioPtr, BUTTON_INTERRUPT); } /******************************************************************************/ /** * * This function sets up the interrupt system such that interrupts can occur * for the USB and GPIO. This function is application specific since the actual * system may or may not have an interrupt controller. The USB and GPIO could be * directly connected to a processor without an interrupt controller. The * user should modify this function to fit the application. * * @param UsbPtr is a pointer to the instance of the USB device. * @param GpioPtr is a pointer to the instance of the GPIO device. * * @return * - XST_SUCCESS if successful. * - XST_FAILURE. if it fails. * * @note None. * *******************************************************************************/ static int SetupInterruptSystem(XUsb *UsbPtr, XGpio *GpioPtr) { int Status; /* * Initialize the interrupt controller driver. */ Status = XIntc_Initialize(&InterruptController, INTC_DEVICE_ID); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Connect a device driver handler that will be called when an interrupt * for the USB device occurs. */ Status = XIntc_Connect(&InterruptController, USB_INTR, (XInterruptHandler) XUsb_IntrHandler, (void *) UsbPtr); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Connect a device driver handler that will be called when an interrupt * for the GPIO device occurs. */ XIntc_Connect(&InterruptController, INTC_GPIO_INTERRUPT_ID, (XInterruptHandler)GpioIsr,(void *) GpioPtr); /* * Start the interrupt controller such that interrupts are enabled for * all devices that cause interrupts, specific real mode so that * the USB and GPIO can cause interrupts through the interrupt * controller. */ Status = XIntc_Start(&InterruptController, XIN_REAL_MODE); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Enable the GPIO channel interrupts so that push button can be * detected and enable interrupts for the GPIO device. */ XGpio_InterruptEnable(&Gpio, BUTTON_INTERRUPT); XGpio_InterruptGlobalEnable(GpioPtr); /* * Enable the interrupt vector at the interrupt controller. */ XIntc_Enable(&InterruptController, INTC_GPIO_INTERRUPT_ID); /* * Enable the interrupt for the USB. */ XIntc_Enable(&InterruptController, USB_INTR); /* * Initialize the exception table. */ Xil_ExceptionInit(); /* * Register the interrupt controller handler with the exception table. */ Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XIntc_InterruptHandler, &InterruptController); /* * Enable non-critical exceptions. */ Xil_ExceptionEnable(); return XST_SUCCESS; }