From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/device/usbdbulk.c | 1447 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1447 insertions(+) create mode 100644 usblib/device/usbdbulk.c (limited to 'usblib/device/usbdbulk.c') diff --git a/usblib/device/usbdbulk.c b/usblib/device/usbdbulk.c new file mode 100644 index 0000000..f623516 --- /dev/null +++ b/usblib/device/usbdbulk.c @@ -0,0 +1,1447 @@ +//***************************************************************************** +// +// usbdbulk.c - USB bulk device class driver. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdbulk.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup bulk_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// Flags that may appear in usDeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// Each deferred operation is defined as the bit number that should be set in +// tBulkInstance->usDeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define BULK_DO_PACKET_RX 5 + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define DATA_IN_ENDPOINT USB_EP_1 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints used for serial data +// transmission and reception and the associated FIFO sizes to set aside +// for each endpoint. +// +//***************************************************************************** +#define DATA_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define DATA_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 + +#define DATA_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +unsigned char g_pBulkDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_VEND_SPECIFIC, // USB Device Class + 0, // USB Device Sub-class + 0, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Bulk device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +unsigned char g_pBulkDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const unsigned char g_pBulkInterface[] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_VEND_SPECIFIC, // The interface class + 0, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The serial config descriptor is defined as two sections, one containing +// just the 9 byte USB configuration descriptor and the other containing +// everything else that is sent to the host along with it. +// +//***************************************************************************** +const tConfigSection g_sBulkConfigSection = +{ + sizeof(g_pBulkDescriptor), + g_pBulkDescriptor +}; + +const tConfigSection g_sBulkInterfaceSection = +{ + sizeof(g_pBulkInterface), + g_pBulkInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete bulk device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psBulkSections[] = +{ + &g_sBulkConfigSection, + &g_sBulkInterfaceSection +}; + +#define NUM_BULK_SECTIONS (sizeof(g_psBulkSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sBulkConfigHeader = +{ + NUM_BULK_SECTIONS, + g_psBulkSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pBulkConfigDescriptors[] = +{ + &g_sBulkConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +tDeviceInfo g_sBulkDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + 0, // GetDescriptor + 0, // RequestHandler + 0, // InterfaceChange + HandleConfigChange, // ConfigChange + 0, // DataReceived + 0, // DataSentCallback + 0, // ResetHandler + HandleSuspend, // SuspendHandler + HandleResume, // ResumeHandler + HandleDisconnect, // DisconnectHandler + HandleEndpoints, // EndpointHandler + HandleDevice // Device handler. + }, + g_pBulkDeviceDescriptor, + g_pBulkConfigDescriptors, + 0, // Will be completed during USBDBulkInit(). + 0, // Will be completed during USBDBulkInit(). + &g_sUSBDefaultFIFOConfig +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile unsigned short *pusDeferredOp, unsigned short usBit, + tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the bulk OUT endpoint (in other words, whenever the +// host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +ProcessDataFromHost(const tUSBDBulkDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tBulkInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client doesn't read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, BULK_DO_PACKET_RX, true); + + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the bulk IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +ProcessDataToHost(const tUSBDBulkDevice *psDevice, unsigned long ulStatus) +{ + tBulkInstance *psInst; + unsigned long ulEPStatus; + unsigned long ulSize; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, psInst->ucINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucINEndpoint, + ulEPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->eBulkTxState = BULK_STATE_IDLE; + + // + // Notify the client that the last transmission completed. + // + ulSize = psInst->usLastTxSize; + psInst->usLastTxSize = 0; + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ulSize, (void *)0); + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDBulkDevice *psBulkInst; + tBulkInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psBulkInst = (const tUSBDBulkDevice *)pvInstance; + psInst = psBulkInst->psPrivateBulkData; + + // + // Handler for the bulk OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvInstance, ulStatus); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) + { + ProcessDataToHost(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tBulkInstance *psInst; + const tUSBDBulkDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Set all our endpoints to idle state. + // + psInst->eBulkRxState = BULK_STATE_IDLE; + psInst->eBulkTxState = BULK_STATE_IDLE; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnRxCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tBulkInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psBulkDevice->psPrivateBulkData; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psInst->bConnected) + { + // + // Pass the disconnected event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psInst->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_SUSPEND, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_RESUME, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param ulIndex is the index of the USB controller for which this tick +// is being generated. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +BulkTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tBulkInstance *psInst; + unsigned long ulSize; + const tUSBDBulkDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateBulkData; + + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << BULK_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_RX_AVAILABLE, + ulSize, (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! +//! An application wishing to make use of a USB bulk communication channel +//! must call this function to initialize the USB controller and attach the +//! device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psDevice pointer +//! passed to it. This must be passed on all future calls to the device driver +//! related to this device. +//! +//! The USBDBulk interface offers packet-based transmit and receive operation. +//! If the application would rather use block based communication with +//! transmit and receive buffers, USB buffers may be used above the bulk +//! transmit and receive channels to offer this functionality. +//! +//! Transmit Operation: +//! +//! Calls to USBDBulkPacketWrite must send no more than 64 bytes of data at a +//! time and may only be made when no other transmission is currently +//! outstanding. +//! +//! Once a packet of data has been acknowledged by the USB host, a +//! USB_EVENT_TX_COMPLETE event is sent to the application callback to inform +//! it that another packet may be transmitted. +//! +//! Receive Operation: +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event USBD_EVENT_RX_AVAILABLE. The application must then +//! call USBDBulkPacketRead(), passing a buffer capable of holding 64 bytes, to +//! retrieve the data and acknowledge reception to the USB host. +//! +//! \note The application must not make any calls to the low level USB Device +//! API if interacting with USB via the USB bulk device class API. Doing so +//! will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDBulkInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice) +{ + void *pvInstance; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + + pvInstance = USBDBulkCompositeInit(ulIndex, psDevice); + + if(pvInstance) + { + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateBulkData->psDevInfo); + } + + // + // Return the pointer to the instance indicating that everything went well. + // + return(pvInstance); +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! +//! This call is very similar to USBDBulkInit() except that it is used for +//! initializing an instance of the bulk device for use in a composite device. +//! +//! \return Returns zero on failure or a non-zero value that should be +//! used with the remaining USB HID Bulk APIs. +// +//***************************************************************************** +void * +USBDBulkCompositeInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice) +{ + tBulkInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateBulkData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateBulkData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pBulkDescriptor; + psInst->psDevInfo = &g_sBulkDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eBulkRxState = BULK_STATE_UNCONFIGURED; + psInst->eBulkTxState = BULK_STATE_UNCONFIGURED; + psInst->usDeferredOpFlags = 0; + psInst->bConnected = false; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucINEndpoint = DATA_IN_ENDPOINT; + psInst->ucOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucInterface = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + + // + // Set the device instance. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(BulkTickHandler, + (void *)psDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shut down the bulk device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function terminates device operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the bulk device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkTerm(void *pvInstance) +{ + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDBulkInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvInstance structure passed to USBDBulkInit() resides in +//! RAM. If this structure is in flash, callback pointer changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDBulkSetRxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvInstance)->pvRxCBData; + ((tUSBDBulkDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the transmit callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDBulkInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvInstance structure passed to USBDBulkInit() resides in +//! RAM. If this structure is in flash, callback pointer changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDBulkSetTxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback pointer for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvInstance)->pvTxCBData; + ((tUSBDBulkDevice *)pvInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the bulk data interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength is the number of bytes of data to transmit. +//! \param bLast indicates whether more data is to be written before a packet +//! should be scheduled for transmission. If \b true, the client will make +//! a further call to this function. If \b false, no further call will be +//! made and the driver should schedule transmission of a short packet. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB packet. If no transmission is currently ongoing, +//! the data is immediately copied to the relevant USB endpoint FIFO for +//! transmission. Whenever a USB packet is acknowledged by the host, a +//! USB_EVENT_TX_COMPLETE event will be sent to the transmit channel callback +//! indicating that more data can now be transmitted. +//! +//! The maximum value for \e ulLength is 64 bytes (the maximum USB packet size +//! for the bulk endpoints in use by the device). Attempts to send more data +//! than this will result in a return code of 0 indicating that the data cannot +//! be sent. +//! +//! The \e bLast parameter allows a client to make multiple calls to this +//! function before scheduling transmission of the packet to the host. This +//! can be helpful if, for example, constructing a packet on the fly or +//! writing a packet which spans the wrap point in a ring buffer. +//! +//! \return Returns the number of bytes actually sent. At this level, this +//! will either be the number of bytes passed (if less than or equal to the +//! maximum packet size for the USB endpoint in use and no outstanding +//! transmission ongoing) or 0 to indicate a failure. +// +//***************************************************************************** +unsigned long +USBDBulkPacketWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tBulkInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Can we send the data provided? + // + if((ulLength > DATA_IN_EP_MAX_SIZE) || + (psInst->eBulkTxState != BULK_STATE_IDLE)) + { + // + // Either the packet was too big or we are in the middle of sending + // another packet. Return 0 to indicate that we can't send this data. + // + return(0); + } + + // + // Copy the data into the USB endpoint FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucINEndpoint, + pcData, ulLength); + + // + // Did we copy the data successfully? + // + if(lRetcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->usLastTxSize += (unsigned short)ulLength; + + // + // If this is the last call for this packet, schedule transmission. + // + if(bLast) + { + // + // Send the packet to the host if we have received all the data we + // can expect for this packet. + // + psInst->eBulkTxState = BULK_STATE_WAIT_DATA; + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the bulk data +//! interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ulLength bytes of data received from the USB +//! host into the supplied application buffer. If the driver detects that the +//! entire packet has been read, it is acknowledged to the host. +//! +//! The \e bLast parameter is ignored in this implementation since the end of +//! a packet can be determined without relying upon the client to provide +//! this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +unsigned long +USBDBulkPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tBulkInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, psInst->ucOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, BULK_DO_PACKET_RX, + false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function returns the maximum number of bytes that can be passed on a +//! call to USBDBulkPacketWrite and accepted for transmission. The value +//! returned will be the maximum USB packet size (64) if no transmission is +//! currently outstanding or 0 if a transmission is in progress. +//! +//! \return Returns the number of bytes available in the transmit buffer. +// +//***************************************************************************** +unsigned long +USBDBulkTxPacketAvailable(void *pvInstance) +{ + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eBulkTxState != BULK_STATE_IDLE) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(DATA_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +unsigned long +USBDBulkRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the bulk device instance structure. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkPowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the bulk device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this function +//! to initiate remote wake up signaling to the host. If the remote wake up +//! feature has not been disabled by the host, this will cause the bus to +//! resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDBulkRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1