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/usbdcdc.c | 2972 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2972 insertions(+) create mode 100644 usblib/device/usbdcdc.c (limited to 'usblib/device/usbdcdc.c') diff --git a/usblib/device/usbdcdc.c b/usblib/device/usbdcdc.c new file mode 100644 index 0000000..f25b4bc --- /dev/null +++ b/usblib/device/usbdcdc.c @@ -0,0 +1,2972 @@ +//***************************************************************************** +// +// usbdcdc.c - USB CDC ACM (serial) 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/usbcdc.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdcomp.h" +#include "usblib/device/usbdcdc.h" + +//***************************************************************************** +// +//! \addtogroup cdc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Some assumptions and deviations from the CDC specification +// ---------------------------------------------------------- +// +// 1. Although the CDC specification indicates that the following requests +// should be supported by ACM CDC devices, these don't seem relevant to a +// virtual COM port implementation and are never seen when connecting to a +// Windows host and running either Hyperterminal or TeraTerm. As a result, +// this implementation does not support them and stalls endpoint 0 if they are +// received. +// - SEND_ENCAPSULATED_COMMAND +// - GET_ENCAPSULATED_RESPONSE +// - SET_COMM_FEATURE +// - GET_COMM_FEATURE +// - CLEAR_COMM_FEATURE +// +// 2. The CDC specification is very clear on the fact that an ACM device +// should offer two interfaces - a control interface offering an interrupt IN +// endpoint and a data interface offering bulk IN and OUT endpoints. Using +// this descriptor configuration, however, Windows insists on enumerating the +// device as two separate entities resulting in two virtual COM ports or one +// COM port and an Unknown Device (depending upon INF contents) appearing +// in Device Manager. This implementation, derived by experimentation and +// examination of other virtual COM and CDC solutions, uses only a single +// interface combining all three endpoints. This appears to satisfy +// Windows2000, XP and Vista and operates as intended using the Hyperterminal +// and TeraTerm terminal emulators. Your mileage may vary with other +// (untested) operating systems! +// +//***************************************************************************** + +//***************************************************************************** +// +// 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) + +//***************************************************************************** +// +// Size of the buffer to hold request-specific data read from the host. This +// must be sized to accommodate the largest request structure that we intend +// processing. +// +//***************************************************************************** +#define MAX_REQUEST_DATA_SIZE sizeof(tLineCoding) + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +#define CDC_DO_SERIAL_STATE_CHANGE 0 +#define CDC_DO_SEND_BREAK 1 +#define CDC_DO_CLEAR_BREAK 2 +#define CDC_DO_LINE_CODING_CHANGE 3 +#define CDC_DO_LINE_STATE_CHANGE 4 +#define CDC_DO_PACKET_RX 5 + +//***************************************************************************** +// +// The subset of deferred operations which result in the receive channel +// being blocked. +// +//***************************************************************************** +#define RX_BLOCK_OPS ((1 << CDC_DO_SEND_BREAK) | \ + (1 << CDC_DO_LINE_CODING_CHANGE) | \ + (1 << CDC_DO_LINE_STATE_CHANGE)) + +//***************************************************************************** +// +// 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 CONTROL_ENDPOINT USB_EP_1 +#define DATA_IN_ENDPOINT USB_EP_2 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// The following are the USB interface numbers for the CDC serial device. +// +//***************************************************************************** +#define SERIAL_INTERFACE_CONTROL 0 +#define SERIAL_INTERFACE_DATA 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 CTL_IN_EP_FIFO_SIZE USB_FIFO_SZ_16 + +#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) +#define CTL_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(CTL_IN_EP_FIFO_SIZE) + +//***************************************************************************** +// +// The collection of serial state flags indicating character errors. +// +//***************************************************************************** +#define USB_CDC_SERIAL_ERRORS (USB_CDC_SERIAL_STATE_OVERRUN | \ + USB_CDC_SERIAL_STATE_PARITY | \ + USB_CDC_SERIAL_STATE_FRAMING) + +//***************************************************************************** +// +// 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_pCDCSerDeviceDescriptor[] = +{ + 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_CDC, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + USB_CDC_PROTOCOL_NONE, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDCDCInit). + USBShort(0), // Product ID (filled in during USBDCDCInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// CDC Serial 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_pCDCSerDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(9), // The total size of this full structure, this + // will be patched so it is just set to the + // size of this structure. + 2, // 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. +}; + +const tConfigSection g_sCDCSerConfigSection = +{ + sizeof(g_pCDCSerDescriptor), + g_pCDCSerDescriptor +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +unsigned char g_pIADSerDescriptor[] = +{ + + 8, // Size of the interface descriptor. + USB_DTYPE_INTERFACE_ASC, // Interface Association Type. + 0x0, // Default starting interface is 0. + 0x2, // Number of interfaces in this association. + USB_CLASS_CDC, // The device class for this association. + USB_CDC_SUBCLASS_ABSTRACT_MODEL, // The device subclass for this + // association. + USB_CDC_PROTOCOL_V25TER, // The protocol for this association. + 0 // The string index for this association. +}; + +const tConfigSection g_sIADSerConfigSection = +{ + sizeof(g_pIADSerDescriptor), + g_pIADSerDescriptor +}; + +//***************************************************************************** +// +// This is the control interface for the serial device. +// +//***************************************************************************** +const unsigned char g_pCDCSerCommInterface[] = +{ + // + // Communication Class Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + SERIAL_INTERFACE_CONTROL, // The index for this interface. + 0, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_CDC, // The interface class constant defined by + // USB-IF (spec 5.1.3). + USB_CDC_SUBCLASS_ABSTRACT_MODEL, // The interface sub-class constant + // defined by USB-IF (spec 5.1.3). + USB_CDC_PROTOCOL_V25TER, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. + + // + // Communication Class Interface Functional Descriptor - Header + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_HEADER, // Header functional descriptor + USBShort(0x110), // Complies with CDC version 1.1 + + // + // Communication Class Interface Functional Descriptor - ACM + // + 4, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_ABSTRACT_CTL_MGMT, + USB_CDC_ACM_SUPPORTS_LINE_PARAMS | USB_CDC_ACM_SUPPORTS_SEND_BREAK, + + // + // Communication Class Interface Functional Descriptor - Unions + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_UNION, + SERIAL_INTERFACE_CONTROL, + SERIAL_INTERFACE_DATA, // Data interface number + + // + // Communication Class Interface Functional Descriptor - Call Management + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_CALL_MGMT, + USB_CDC_CALL_MGMT_HANDLED, + SERIAL_INTERFACE_DATA, // Data interface number + + // + // Endpoint Descriptor (interrupt, IN) + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(CONTROL_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(CTL_IN_EP_MAX_SIZE), // The maximum packet size. + 1 // The polling interval for this endpoint. +}; + +const tConfigSection g_sCDCSerCommInterfaceSection = +{ + sizeof(g_pCDCSerCommInterface), + g_pCDCSerCommInterface +}; + +//***************************************************************************** +// +// This is the Data interface for the serial device. +// +//***************************************************************************** +const unsigned char g_pCDCSerDataInterface[] = +{ + // + // Communication Class Data Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + SERIAL_INTERFACE_DATA, // The index for this interface. + 0, // The alternate setting for this interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_CDC_DATA, // The interface class constant defined by + // USB-IF (spec 5.1.3). + 0, // The interface sub-class constant + // defined by USB-IF (spec 5.1.3). + USB_CDC_PROTOCOL_NONE, // The interface protocol for the sub-class + // specified above. + 0, // 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. +}; + +const tConfigSection g_sCDCSerDataInterfaceSection = +{ + sizeof(g_pCDCSerDataInterface), + g_pCDCSerDataInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete CDC ACM configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psCDCSerSections[] = +{ + &g_sCDCSerConfigSection, + &g_sCDCSerCommInterfaceSection, + &g_sCDCSerDataInterfaceSection, +}; + +#define NUM_CDCSER_SECTIONS (sizeof(g_psCDCSerSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration. 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_sCDCSerConfigHeader = +{ + NUM_CDCSER_SECTIONS, + g_psCDCSerSections +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete CDC ACM configuration descriptor used in composite devices. +// The only addition is the g_sIADSerConfigSection. +// +//***************************************************************************** +const tConfigSection *g_psCDCCompSerSections[] = +{ + &g_sCDCSerConfigSection, + &g_sIADSerConfigSection, + &g_sCDCSerCommInterfaceSection, + &g_sCDCSerDataInterfaceSection, +}; + +#define NUM_COMP_CDCSER_SECTIONS (sizeof(g_psCDCCompSerSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the composite configuration. 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_sCDCCompSerConfigHeader = +{ + NUM_COMP_CDCSER_SECTIONS, + g_psCDCCompSerSections +}; + +//***************************************************************************** +// +// Configuration Descriptor for the CDC serial class device. +// +//***************************************************************************** +const tConfigHeader * const g_pCDCSerConfigDescriptors[] = +{ + &g_sCDCSerConfigHeader +}; + +//***************************************************************************** +// +// Configuration Descriptor for the CDC serial class device used in a composite +// device. +// +//***************************************************************************** +const tConfigHeader * const g_pCDCCompSerConfigDescriptors[] = +{ + &g_sCDCCompSerConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleEP0Data(void *pvInstance, unsigned long ulDataSize); +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_sCDCSerDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + HandleEP0Data, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler. + // + HandleDevice + }, + + // + // The common device descriptor. + // + g_pCDCSerDeviceDescriptor, + + // + // Default to no interrupt endpoint. + // + g_pCDCCompSerConfigDescriptors, + + // + // String descriptors will be passed in. + // + 0, + 0, + + // + // Use the default USB FIFO configuration. + // + &g_sUSBDefaultFIFOConfig, + + // + // Zero out the instance pointer by default. + // + 0 +}; + +//***************************************************************************** +// +// 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; +} + +//***************************************************************************** +// +// Determines whether or not a client has consumed all received data previously +// passed to it. +// +//! \param psDevice is the pointer to the device instance structure as returned +//! by USBDCDCInit(). +// +// This function is called to determine whether or not a device has consumed +// all data previously passed to it via its receive callback. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +DeviceConsumedAllData(const tUSBDCDCDevice *psDevice) +{ + unsigned long ulRemaining; + + // + // Send the device an event requesting that it tell us how many bytes + // of data it still has to process. + // + ulRemaining = psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_DATA_REMAINING, 0, (void *)0); + + // + // If any data remains to be processed, return false, else return true. + // + return(ulRemaining ? false : true); +} + +//***************************************************************************** +// +// Notifies the client that it should set or clear a break condition. +// +// \param psDevice is the pointer to the device instance structure as returned +// by USBDCDCInit(). +// \param bSend is \b true if a break condition is to be set or \b false if +// it is to be cleared. +// +// This function is called to instruct the client to start or stop sending a +// break condition on its serial transmit line. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static void +SendBreak(const tUSBDCDCDevice *psDevice, tBoolean bSend) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Set the break state flags as necessary. If we are turning the break on, + // set the flag to tell ourselves that we need to notify the client when + // it is time to turn it off again. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SEND_BREAK, false); + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_CLEAR_BREAK, bSend); + + // + // Tell the client to start or stop sending the break. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + (bSend ? USBD_CDC_EVENT_SEND_BREAK : + USBD_CDC_EVENT_CLEAR_BREAK), 0, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a host request to set the serial communication +// parameters. +// +// \param psDevice is the device instance whose communication parameters are to +// be set. +// +// This function is called to notify the client when the host requests a change +// in the serial communication parameters (baud rate, parity, number of bits +// per character and number of stop bits) to use. +// +// \return None. +// +//***************************************************************************** +static void +SendLineCodingChange(const tUSBDCDCDevice *psDevice) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_LINE_CODING_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_SET_LINE_CODING, 0, + &(psInst->sLineCoding)); +} + +//***************************************************************************** +// +// Notifies the client of a host request to set the RTS and DTR handshake line +// states. +// +// \param psDevice is the device instance whose break condition is to be set or +// cleared. +// +// This function is called to notify the client when the host requests a change +// in the state of one or other of the RTS and DTR handshake lines. +// +// \return None. +// +//***************************************************************************** +static void +SendLineStateChange(const tUSBDCDCDevice *psDevice) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_LINE_STATE_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_SET_CONTROL_LINE_STATE, + psInst->usControlLineState, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a break request if no data remains to be processed. +// +// \param psDevice is the device instance that is to be commanded to send a +// break condition. +// +// This function is called when the host requests that the device set a break +// condition on the serial transmit line. If no data received from the host +// remains to be processed, the break request is passed to the control +// callback. If data is outstanding, the call is ignored (with the operation +// being retried on the next timer tick). +// +// \return Returns \b true if the break notification was sent, \b false +// otherwise. +// +//***************************************************************************** +static tBoolean +CheckAndSendBreak(const tUSBDCDCDevice *psDevice, unsigned short usDuration) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the break request on to the client since no data remains to be + // consumed. + // + SendBreak(psDevice, (usDuration ? true : false)); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a request to change the serial line parameters if no +// data remains to be processed. +// +// \param psDevice is the device instance whose line coding parameters are to +// be changed. +// +// This function is called when the host requests that the device change the +// serial line coding parameters. If no data received from the host remains +// to be processed, the request is passed to the control callback. If data is +// outstanding, the call is ignored (with the operation being retried on the +// next timer tick). +// +// \return Returns \b true if the notification was sent, \b false otherwise. +// +//***************************************************************************** +static tBoolean +CheckAndSendLineCodingChange(const tUSBDCDCDevice *psDevice) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineCodingChange(psDevice); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a request to change the handshake line states if no +// data remains to be processed. +// +// \param psDevice is the device instance whose handshake line states are to +// be changed. +// +// This function is called when the host requests that the device change the +// state of one or other of the RTS or DTR handshake lines. If no data +// received from the host remains to be processed, the request is passed to +// the control callback. If data is outstanding, the call is ignored (with +// the operation being retried on the next timer tick). +// +// \return Returns \b true if the notification was sent, \b false otherwise. +// +//***************************************************************************** +static tBoolean +CheckAndSendLineStateChange(const tUSBDCDCDevice *psDevice) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineStateChange(psDevice); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a change in the serial line state. +// +// \param psInst is the instance whose serial state is to be reported. +// +// This function is called to send the current serial state information to +// the host via the the interrupt IN endpoint. This notification informs the +// host of problems or conditions such as parity errors, breaks received, +// framing errors, etc. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +SendSerialState(const tUSBDCDCDevice *psDevice) +{ + tUSBRequest sRequest; + unsigned short usSerialState; + tCDCSerInstance *psInst; + long lRetcode; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Remember that we are in the middle of sending a notification. + // + psInst->eCDCInterruptState = CDC_STATE_WAIT_DATA; + + // + // Clear the flag we use to indicate that a send is required. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + false); + // + // Take a snapshot of the serial state. + // + usSerialState = psInst->usSerialState; + + // + // Build the request we will use to send the notification. + // + sRequest.bmRequestType = (USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE); + sRequest.bRequest = USB_CDC_NOTIFY_SERIAL_STATE; + sRequest.wValue = 0; + sRequest.wIndex = 0; + sRequest.wLength = USB_CDC_NOTIFY_SERIAL_STATE_SIZE; + + // + // Write the request structure to the USB FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, + psInst->ucControlEndpoint, + (unsigned char *)&sRequest, + sizeof(tUSBRequest)); + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucControlEndpoint, + (unsigned char *)&usSerialState, + USB_CDC_NOTIFY_SERIAL_STATE_SIZE); + + // + // Did we correctly write the data to the endpoint FIFO? + // + if(lRetcode != -1) + { + // + // We put the data into the FIFO so now schedule it to be + // sent. + // + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucControlEndpoint, + USB_TRANS_IN); + } + + // + // If an error occurred, mark the endpoint as idle (to prevent possible + // lockup) and return an error. + // + if(lRetcode == -1) + { + psInst->eCDCInterruptState = CDC_STATE_IDLE; + return(false); + } + else + { + // + // Everything went fine. Clear the error bits that we just notified + // and return true. + // + psInst->usSerialState &= ~(usSerialState & USB_CDC_SERIAL_ERRORS); + return(true); + } +} + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +tBoolean +ProcessDataFromHost(const tUSBDCDCDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tCDCSerInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucBulkOUTEndpoint, + 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 notified later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_PACKET_RX, true); + + // + // Is the receive channel currently blocked? + // + if(!psInst->bControlBlocked && !psInst->bRxBlocked) + { + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // 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 interrupt messages 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 interrupt IN endpoint (in other words, whenever a notification has +// been transmitted to the USB host). +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +ProcessNotificationToHost(const tUSBDCDCDevice *psDevice, + unsigned long ulStatus) +{ + unsigned long ulEPStatus; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + // + // Assume all will go well until we have reason to believe otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucControlEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucControlEndpoint, ulEPStatus); + + // + // Did the state change while we were waiting for the previous notification + // to complete? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_SERIAL_STATE_CHANGE)) + { + // + // The state changed while we were waiting so we need to schedule + // another notification immediately. + // + bRetcode = SendSerialState(psDevice); + } + else + { + // + // Our last notification completed and we didn't have any new + // notifications to make so the interrupt channel is now idle again. + // + psInst->eCDCInterruptState = CDC_STATE_IDLE; + } + + // + // Tell the caller how things went. + // + return(bRetcode); +} + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +tBoolean +ProcessDataToHost(const tUSBDCDCDevice *psDevice, unsigned long ulStatus) +{ + tCDCSerInstance *psInst; + unsigned long ulEPStatus, ulSize; + tBoolean bSentFullPacket; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, ulEPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->eCDCTxState = CDC_STATE_IDLE; + + // + // If this notification isn't as a result of sending a zero-length packet, + // call back to the client to let it know we sent the last thing it passed + // us. + // + if(psInst->usLastTxSize) + { + // + // Have we just sent a 64 byte packet? + // + bSentFullPacket = (psInst->usLastTxSize == DATA_IN_EP_MAX_SIZE) ? + true : false; + + // + // Notify the client that the last transmission completed. + // + ulSize = (unsigned long)psInst->usLastTxSize; + psInst->usLastTxSize = 0; + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ulSize, (void *)0); + + // + // If we had previously sent a full packet and the callback didn't + // schedule a new transmission, send a zero length packet to indicate + // the end of the transfer. + // + if(bSentFullPacket && !psInst->usLastTxSize) + { + // + // We can expect another transmit complete notification after doing + // this. + // + psInst->eCDCTxState = CDC_STATE_WAIT_DATA; + + // + // Send the zero-length packet. + // + MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, + USB_TRANS_IN); + } + } + + 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 tUSBDCDCDevice *psDeviceInst; + tCDCSerInstance *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. + // + psDeviceInst = pvInstance; + psInst = psDeviceInst->psPrivateCDCSerData; + + // + // Handler for the interrupt IN notification endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucControlEndpoint))) + { + // + // We have sent an interrupt notification to the host. + // + ProcessNotificationToHost(psDeviceInst, ulStatus); + } + + // + // Handler for the bulk OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucBulkOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psDeviceInst, ulStatus); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucBulkINEndpoint))) + { + ProcessDataToHost(psDeviceInst, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tCDCSerInstance *psInst; + const tUSBDCDCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Set all our endpoints to idle state. + // + psInst->eCDCInterruptState = CDC_STATE_IDLE; + psInst->eCDCRequestState = CDC_STATE_IDLE; + psInst->eCDCRxState = CDC_STATE_IDLE; + psInst->eCDCTxState = CDC_STATE_IDLE; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(!psInst->bConnected) + { + // + // Pass the connected event to the client. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// USB data received callback. +// +// This function is called by the USB stack whenever any data requested from +// EP0 is received. +// +//***************************************************************************** +static void +HandleEP0Data(void *pvInstance, unsigned long ulDataSize) +{ + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // If we were not passed any data, just return. + // + if(ulDataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Make sure we are actually expecting something. + // + if(psInst->eCDCRequestState != CDC_STATE_WAIT_DATA) + { + return; + } + + // + // Process the data received. This will be a request-specific data + // block associated with the last request received. + // + switch (psInst->ucPendingRequest) + { + // + // We just got the line coding structure. Make sure the client has + // read all outstanding data then pass it back to initiate a change + // in the line state. + // + case USB_CDC_SET_LINE_CODING: + { + if(ulDataSize != sizeof(tLineCoding)) + { + USBDCDStallEP0(0); + } + else + { + // + // Set the flag telling us that we need to send a line coding + // notification to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_LINE_CODING_CHANGE, true); + + // + // See if we can send the notification immediately. + // + bRetcode = CheckAndSendLineCodingChange(psDevice); + + // + // If we couldn't send the line coding change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + } + break; + } + + // + // Oops - we seem to be waiting on a request which has not yet been + // coded here. Flag the error and stall EP0 anyway (even though + // this would indicate a coding error). + // + default: + { + USBDCDStallEP0(0); + ASSERT(0); + break; + } + } + + // + // All is well. Set the state back to IDLE. + // + psInst->eCDCRequestState = CDC_STATE_IDLE; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tCDCSerInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // 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: + { + // + // Save the change to the appropriate interface number. + // + if(pucData[0] == SERIAL_INTERFACE_CONTROL) + { + psInst->ucInterfaceControl = pucData[1]; + } + else if(pucData[0] == SERIAL_INTERFACE_DATA) + { + psInst->ucInterfaceData = 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) + { + // + // Determine which IN endpoint to modify. + // + if((pucData[0] & 0x7f) == USB_EP_TO_INDEX(CONTROL_ENDPOINT)) + { + psInst->ucControlEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + psInst->ucBulkINEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucBulkOUTEndpoint = + INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + + // + // Handle class specific reconfiguring of the configuration descriptor + // once the composite class has built the full descriptor. + // + case USB_EVENT_COMP_CONFIG: + { + // + // This sets the bFirstInterface of the Interface Association + // descriptor to the first interface which is the control + // interface used by this instance. + // + pucData[2] = psInst->ucInterfaceControl; + + // + // This sets the bMasterInterface of the Union descriptor to the + // Control interface and the bSlaveInterface of the Union + // Descriptor to the Data interface used by this instance. + // + pucData[29] = psInst->ucInterfaceControl; + pucData[30] = psInst->ucInterfaceData; + + // + // This sets the bDataInterface of the Union descriptor to the + // Data interface used by this instance. + pucData[35] = psInst->ucInterfaceData; + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +// USB non-standard request callback. +// +// This function is called by the USB stack whenever any non-standard request +// is made to the device. The handler should process any requests that it +// supports or stall EP0 in any unsupported cases. +// +//***************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Only handle requests meant for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterfaceControl) + { + return; + } + + // + // Handle each of the requests that we expect from the host. + // + switch(pUSBRequest->bRequest) + { + case USB_CDC_SEND_ENCAPSULATED_COMMAND: + { + // + // This implementation makes use of no communication protocol so + // this request is meaningless. We stall endpoint 0 if we receive + // it. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_GET_ENCAPSULATED_RESPONSE: + { + // + // This implementation makes use of no communication protocol so + // this request is meaningless. We stall endpoint 0 if we receive + // it. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_SET_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_GET_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_CLEAR_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + // + // Set the serial communication parameters. + // + case USB_CDC_SET_LINE_CODING: + { + // + // Remember the request we are processing. + // + psInst->ucPendingRequest = USB_CDC_SET_LINE_CODING; + + // + // Set the state to indicate we are waiting for data. + // + psInst->eCDCRequestState = CDC_STATE_WAIT_DATA; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, (unsigned char *)&psInst->sLineCoding, + sizeof(tLineCoding)); + + // + // ACK what we have already received. We must do this after + // requesting the data or we get into a race condition where the + // data may return before we have set the stack state appropriately + // to receive it. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + break; + } + + // + // Return the serial communication parameters. + // + case USB_CDC_GET_LINE_CODING: + { + tLineCoding sLineCoding; + + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Ask the client for the current line coding. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_GET_LINE_CODING, 0, + &sLineCoding); + + // + // Send the line coding information back to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&sLineCoding, + sizeof(tLineCoding)); + + break; + } + + case USB_CDC_SET_CONTROL_LINE_STATE: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Set the handshake lines as required. + // + psInst->usControlLineState = pUSBRequest->wValue; + + // + // Remember that we are due to notify the client of a line + // state change. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_LINE_STATE_CHANGE, true); + + // + // See if we can notify now. + // + bRetcode = CheckAndSendLineStateChange(psDevice); + + // + // If we couldn't send the line state change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + + break; + } + + case USB_CDC_SEND_BREAK: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Keep a copy of the requested break duration. + // + psInst->usBreakDuration = pUSBRequest->wValue; + + // + // Remember that we need to send a break request. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_SEND_BREAK, true); + + // + // Send the break request if all outstanding receive data has been + // processed. + // + bRetcode = CheckAndSendBreak(psDevice, pUSBRequest->wValue); + + // + // If we couldn't send the line coding change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + + break; + } + + // + // These are valid CDC requests but not ones that an ACM device should + // receive. + // + case USB_CDC_SET_AUX_LINE_STATE: + case USB_CDC_SET_HOOK_STATE: + case USB_CDC_PULSE_SETUP: + case USB_CDC_SEND_PULSE: + case USB_CDC_SET_PULSE_TIME: + case USB_CDC_RING_AUX_JACK: + case USB_CDC_SET_RINGER_PARMS: + case USB_CDC_GET_RINGER_PARMS: + case USB_CDC_SET_OPERATION_PARMS: + case USB_CDC_GET_OPERATION_PARMS: + case USB_CDC_SET_LINE_PARMS: + case USB_CDC_GET_LINE_PARMS: + case USB_CDC_DIAL_DIGITS: + case USB_CDC_SET_UNIT_PARAMETER: + case USB_CDC_GET_UNIT_PARAMETER: + case USB_CDC_CLEAR_UNIT_PARAMETER: + case USB_CDC_GET_PROFILE: + case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + case USB_CDC_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + case USB_CDC_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + case USB_CDC_SET_ETHERNET_PACKET_FILTER: + case USB_CDC_GET_ETHERNET_STATISTIC: + case USB_CDC_SET_ATM_DATA_FORMAT: + case USB_CDC_GET_ATM_DEVICE_STATISTICS: + case USB_CDC_SET_ATM_DEFAULT_VC: + case USB_CDC_GET_ATM_VC_STATISTICS: + { + USBDCDStallEP0(0); + break; + } + + default: + { + // + // This request is not part of the CDC specification. + // + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // If we are not currently connected and we have a control callback, + // let the client know we are open for business. + // + if(psInst->bConnected) + { + // + // Pass the disconnected event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + 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 tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + 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) +{ + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + 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 +CDCTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tBoolean bCanSend; + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Is there any outstanding operation that we should try to perform? + // + if(psInst->usDeferredOpFlags) + { + // + // Yes - we have at least one deferred operation pending. First check + // to see if it is time to turn off a break condition. + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_CLEAR_BREAK)) + { + // + // Will our break timer expire this time? + // + if(psInst->usBreakDuration <= ulTimemS) + { + // + // Yes - turn off the break condition. + // + SendBreak(psDevice, false); + } + else + { + // + // We have not timed out yet. Decrement the break timer. + // + psInst->usBreakDuration -= (unsigned short)ulTimemS; + } + } + + // Now check to see if the client has any data remaining to be + // processed. This information is needed by the remaining deferred + // operations which are waiting for the receive pipe to be emptied + // before they can be carried out. + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Has all outstanding data been consumed? + // + if(bCanSend) + { + // + // Yes - go ahead and notify the client of the various things + // it has been asked to do while we waited for data to be + // consumed. + // + + // + // Do we need to start sending a break condition? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_SEND_BREAK)) + { + SendBreak(psDevice, true); + } + + // + // Do we need to set the RTS/DTR states? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_LINE_STATE_CHANGE)) + { + SendLineStateChange(psDevice); + } + + // + // Do we need to change the line coding parameters? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_LINE_CODING_CHANGE)) + { + SendLineCodingChange(psDevice); + } + + // + // NOTE: We do not need to handle CDC_DO_SERIAL_STATE_CHANGE here + // since this is handled in the transmission complete notification + // for the control IN endpoint (ProcessNotificationToHost()). + // + + // + // If all the deferred operations which caused the receive channel + // to be blocked are now handled, we can unblock receive and handle + // any packet that is currently waiting to be received. + // + if(!(psInst->usDeferredOpFlags & RX_BLOCK_OPS)) + { + // + // We can remove the receive block. + // + psInst->bRxBlocked = false; + } + } + + // + // Is the receive channel unblocked? + // + if(!psInst->bRxBlocked) + { + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + } + } + + return; +} + +//***************************************************************************** +// +//! Initializes CDC device operation when used with a composite device. +//! +//! \param ulIndex is the index of the USB controller in use. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! +//! This call is very similar to USBDCDCInit() except that it is used for +//! initializing an instance of the serial device for use in a composite device. +//! +//! \return Returns NULL on failure or the psCDCDevice pointer on success. +// +//***************************************************************************** +void * +USBDCDCCompositeInit(unsigned long ulIndex, const tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psCDCDevice); + ASSERT(psCDCDevice->psPrivateCDCSerData); + ASSERT(psCDCDevice->pfnControlCallback); + ASSERT(psCDCDevice->pfnRxCallback); + ASSERT(psCDCDevice->pfnTxCallback); + + // + // Create an instance pointer to the private data area. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucBulkINEndpoint = DATA_IN_ENDPOINT; + psInst->ucBulkOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucInterfaceControl = SERIAL_INTERFACE_CONTROL; + psInst->ucInterfaceData = SERIAL_INTERFACE_DATA; + + // + // By default do not use the interrupt control endpoint. The single + // instance CDC serial device will turn this on in USBDCDCInit(); + // + psInst->ucControlEndpoint = CONTROL_ENDPOINT; + + // + // Initialize the workspace in the passed instance structure. + // + psInst->psConfDescriptor = (tConfigDescriptor *)g_pCDCSerDescriptor; + psInst->psDevInfo = &g_sCDCSerDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eCDCRxState = CDC_STATE_UNCONFIGURED; + psInst->eCDCTxState = CDC_STATE_UNCONFIGURED; + psInst->eCDCInterruptState = CDC_STATE_UNCONFIGURED; + psInst->eCDCRequestState = CDC_STATE_UNCONFIGURED; + psInst->ucPendingRequest = 0; + psInst->usBreakDuration = 0; + psInst->usSerialState = 0; + psInst->usDeferredOpFlags = 0; + psInst->usControlLineState = 0; + psInst->bRxBlocked = false; + psInst->bControlBlocked = false; + psInst->bConnected = false; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psCDCDevice->usVID; + psDevDesc->idProduct = psCDCDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psCDCDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psCDCDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psCDCDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psCDCDevice->ulNumStringDescriptors; + + // + // 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(CDCTickHandler, + (void *)psCDCDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psCDCDevice); +} + +//***************************************************************************** +// +//! Initializes CDC device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for CDC device operation. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! +//! An application wishing to make use of a USB CDC communication channel and +//! appear as a virtual serial port on the host system must call this function +//! to initialize the USB controller and attach the device to the USB bus. +//! This function performs all required USB initialization. +//! +//! The value returned by this function is the \e psCDCDevice pointer passed +//! to it if successful. This pointer must be passed to all later calls to the +//! CDC class driver to identify the device instance. +//! +//! The USB CDC device class driver offers packet-based transmit and receive +//! operation. If the application would rather use block based communication +//! with transmit and receive buffers, USB buffers on the transmit and receive +//! channels may be used to offer this functionality. +//! +//! Transmit Operation: +//! +//! Calls to USBDCDCPacketWrite() 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 +//! \b 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 \b USB_EVENT_RX_AVAILABLE. The application must then +//! call USBDCDCPacketRead(), passing a buffer capable of holding the received +//! packet to retrieve the data and acknowledge reception to the USB host. The +//! size of the received packet may be queried by calling +//! USBDCDCRxPacketAvailable(). +//! +//! \note The application must not make any calls to the low level USB Device +//! API if interacting with USB via the CDC device class API. Doing so +//! will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psCDCDevice pointer on success. +// +//***************************************************************************** +void * +USBDCDCInit(unsigned long ulIndex, const tUSBDCDCDevice *psCDCDevice) +{ + void *pvRet; + tCDCSerInstance *psInst; + + // + // Initialize the internal state for this class. + // + pvRet = USBDCDCCompositeInit(ulIndex, psCDCDevice); + + if(pvRet) + { + // + // Create an instance pointer to the private data area. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // Set the instance data for this device so that USBDCDInit() call can + // have the instance data. + // + psInst->psDevInfo->pvInstance = (void *)psCDCDevice; + + // + // Enable the default interrupt control endpoint if this class is not + // being used in a composite device. + // + psInst->ucControlEndpoint = CONTROL_ENDPOINT; + + // + // Use the configuration descriptor with the interrupt control endpoint. + // + psInst->psDevInfo->ppConfigDescriptors = g_pCDCSerConfigDescriptors; + + // + // All is well so now pass the descriptors to the lower layer and put + // the CDC device on the bus. + // + USBDCDInit(ulIndex, psInst->psDevInfo); + } + + return(pvRet); +} + +//***************************************************************************** +// +//! Shuts down the CDC device instance. +//! +//! \param pvInstance is the pointer to the device instance structure as returned +//! by USBDCDCInit(). +//! +//! This function terminates CDC operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the CDC 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 +USBDCDCTerm(void *pvInstance) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // 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 for the control callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the control channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnControlCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() 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 control callback. +// +//***************************************************************************** +void * +USBDCDCSetControlCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback pointer for the control channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvControlCBData; + ((tUSBDCDCDevice *)pvInstance)->pvControlCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the receive channel callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \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 USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDCDCSetRxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvRxCBData; + ((tUSBDCDCDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the transmit callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \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 USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDCDCSetTxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvTxCBData; + ((tUSBDCDCDevice *)pvInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the CDC data interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \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. If the +//! \e bLast parameter is \b true, the newly written packet is then scheduled +//! for transmission. Whenever a USB packet is acknowledged by the host, a +//! USB_EVENT_TX_COMPLETE event will be sent to the application transmit +//! callback indicating that more data can now be transmitted. +//! +//! The maximum value for ulLength is 64 bytes (the maximum USB packet size +//! for the bulk endpoints in use by CDC). Attempts to send more data than +//! this will result in a return code of 0 indicating that the data cannot be +//! sent. +//! +//! \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 +USBDCDCPacketWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tCDCSerInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Can we send the data provided? + // + if((ulLength > DATA_IN_EP_MAX_SIZE) || + (psInst->eCDCTxState != CDC_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->ucBulkINEndpoint, 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->eCDCTxState = CDC_STATE_WAIT_DATA; + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, + 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 CDC data +//! interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \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 ulLength bytes of data received from the USB +//! host into the supplied application buffer. +//! +//! \note 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 +USBDCDCPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tCDCSerInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // If receive is currently blocked or the buffer we were passed is + // (potentially) too small, set the flag telling us that we have a + // packet waiting but return 0. + // + if(psInst->bRxBlocked || psInst->bControlBlocked) + { + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_PACKET_RX, true); + return(0); + } + else + { + // + // It is OK to receive the new packet. How many bytes are + // available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + 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->ucBulkOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_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 USBDCDCInit(). +//! +//! This function returns the maximum number of bytes that can be passed on a +//! call to USBDCDCPacketWrite 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 +USBDCDCTxPacketAvailable(void *pvInstance) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eCDCTxState != CDC_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 USBDCDCInit(). +//! +//! 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 +USBDCDCRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // If receive is currently blocked, return 0. + // + if(psInst->bRxBlocked || psInst->bControlBlocked) + { + return(0); + } + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Informs the CDC module of changes in the serial control line states or +//! receive error conditions. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param usState indicates the states of the various control lines and +//! any receive errors detected. Bit definitions are as for the USB CDC +//! SerialState asynchronous notification and are defined in header file +//! usbcdc.h. +//! +//! The application should call this function whenever the state of any of +//! the incoming RS232 handshake signals changes or in response to a receive +//! error or break condition. The usState parameter is the ORed combination +//! of the following flags with each flag indicating the presence of that +//! condition. +//! +//! - USB_CDC_SERIAL_STATE_OVERRUN +//! - USB_CDC_SERIAL_STATE_PARITY +//! - USB_CDC_SERIAL_STATE_FRAMING +//! - USB_CDC_SERIAL_STATE_RING_SIGNAL +//! - USB_CDC_SERIAL_STATE_BREAK +//! - USB_CDC_SERIAL_STATE_TXCARRIER +//! - USB_CDC_SERIAL_STATE_RXCARRIER +//! +//! This function should be called only when the state of any flag changes. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCSerialStateChange(void *pvInstance, unsigned short usState) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Add the newly reported state bits to the current collection. We do this + // in case two state changes occur back-to-back before the first has been + // notified. There are two distinct types of signals that we report here + // and we deal with them differently: + // + // 1. Errors (overrun, parity, framing error) are ORed together so that + // any reported error is sent on the next notification. + // 2. Signal line states (RI, break, TX carrier, RX carrier) always + // report the last state notified to us. The implementation here will + // send an interrupt showing the last state but, if two state changes + // occur very quickly, the host may receive a notification containing + // the same state that was last reported (in other words, a short pulse + // will be lost). It would be possible to reduce the likelihood of + // this happening by building a queue of state changes and sending + // these in order but you are left with exactly the same problem if the + // queue fills up. For now, therefore, we run the risk of missing very + // short pulses on the "steady-state" signal lines. + // + psInst->usSerialState |= (usState & USB_CDC_SERIAL_ERRORS); + psInst->usSerialState &= ~USB_CDC_SERIAL_ERRORS; + psInst->usSerialState |= (usState & ~USB_CDC_SERIAL_ERRORS); + + // + // Set the flag indicating that a serial state change is to be sent. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + true); + + // + // Can we send the state change immediately? + // + if(psInst->eCDCInterruptState == CDC_STATE_IDLE) + { + // + // The interrupt channel is free so send the notification immediately. + // If we can't do this, the tick timer will catch this next time + // round. + // + psInst->eCDCInterruptState = CDC_STATE_WAIT_DATA; + SendSerialState(pvInstance); + } + + return; +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the CDC 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 +USBDCDCPowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wakeup to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the CDC device instance structure. +//! +//! When the bus is suspended, an application which supports remote wakeup +//! (advertised to the host via the config descriptor) may call this function +//! to initiate remote wakeup signaling to the host. If the remote wakeup +//! feature has not been disabled by the host, this will cause the bus to +//! resume operation within 20mS. If the host has disabled remote wakeup, +//! \b false will be returned to indicate that the wakeup request was not +//! successful. +//! +//! \return Returns \b true if the remote wakeup is not disabled and the +//! signaling was started or \b false if remote wakeup is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDCDCRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1