diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2012-10-29 23:08:53 +0200 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2012-10-29 23:08:53 +0200 |
| commit | c241dbd7e78c50327781a35d88d9f2db7ff2b271 (patch) | |
| tree | 2c84fe512c0cd3ee328244bca2f8ed4f9622074d /usblib/device/usbdcdc.c | |
| parent | 4ba8614c006f9828f0796c140bc3e13c9e67938c (diff) | |
Added usblib
Diffstat (limited to 'usblib/device/usbdcdc.c')
| -rw-r--r-- | usblib/device/usbdcdc.c | 2972 |
1 files changed, 2972 insertions, 0 deletions
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.
+//! @}
+//
+//*****************************************************************************
|
