diff options
Diffstat (limited to 'usblib/device/usbdaudio.c')
| -rw-r--r-- | usblib/device/usbdaudio.c | 1485 |
1 files changed, 1485 insertions, 0 deletions
diff --git a/usblib/device/usbdaudio.c b/usblib/device/usbdaudio.c new file mode 100644 index 0000000..a1ef199 --- /dev/null +++ b/usblib/device/usbdaudio.c @@ -0,0 +1,1485 @@ +//*****************************************************************************
+//
+// usbdaudio.c - USB audio device class driver.
+//
+// Copyright (c) 2009-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 "driverlib/udma.h"
+#include "usblib/usblib.h"
+#include "usblib/usbaudio.h"
+#include "usblib/device/usbdevice.h"
+#include "usblib/device/usbdaudio.h"
+
+//*****************************************************************************
+//
+//! \addtogroup audio_device_class_api
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// The following are the USB audio descriptor identifiers.
+//
+//*****************************************************************************
+#define AUDIO_IN_TERMINAL_ID 1
+#define AUDIO_OUT_TERMINAL_ID 2
+#define AUDIO_CONTROL_ID 3
+
+//*****************************************************************************
+//
+// The following are the USB interface numbers for this audio device.
+//
+//*****************************************************************************
+#define AUDIO_INTERFACE_CONTROL 0
+#define AUDIO_INTERFACE_OUTPUT 1
+
+//*****************************************************************************
+//
+// Endpoints to use for each of the required endpoints in the driver.
+//
+//*****************************************************************************
+#define ISOC_OUT_ENDPOINT USB_EP_1
+#define ISOC_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX
+
+//*****************************************************************************
+//
+// Max size is (48000 samples/sec * 4 bytes/sample) * 0.001 seconds/frame.
+//
+//*****************************************************************************
+#define ISOC_OUT_EP_MAX_SIZE ((48000*4)/1000)
+
+//*****************************************************************************
+//
+// Device Descriptor. This is stored in RAM to allow several fields to be
+// changed at runtime based on the client's requirements.
+//
+//*****************************************************************************
+static unsigned char g_pAudioDeviceDescriptor[] =
+{
+ 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)
+ 0, // USB Device Class (spec 5.1.1)
+ 0, // USB Device Sub-class (spec 5.1.1)
+ 0, // USB Device protocol (spec 5.1.1)
+ 64, // Maximum packet size for default pipe.
+ USBShort(0), // Vendor ID (filled in during USBDAudioInit).
+ USBShort(0), // Product ID (filled in during USBDAudioInit).
+ USBShort(0x100), // Device Version BCD.
+ 1, // Manufacturer string identifier.
+ 2, // Product string identifier.
+ 3, // Product serial number.
+ 1 // Number of configurations.
+};
+
+//*****************************************************************************
+//
+// Audio class device configuration descriptor.
+//
+// It is vital that the configuration descriptor bConfigurationValue field
+// (byte 6) is 1 for the first configuration and increments by 1 for each
+// additional configuration defined here. This relationship is assumed in the
+// device stack for simplicity even though the USB 2.0 specification imposes
+// no such restriction on the bConfigurationValue values.
+//
+// Note that this structure is deliberately located in RAM since we need to
+// be able to patch some values in it based on client requirements.
+//
+//*****************************************************************************
+static unsigned char g_pAudioDescriptor[] =
+{
+ //
+ // Configuration descriptor header.
+ //
+ 9, // Size of the configuration descriptor.
+ USB_DTYPE_CONFIGURATION, // Type of this descriptor.
+ USBShort(32), // The total size of this full structure.
+ 2, // The number of interfaces in this
+ // configuration.
+ 1, // The unique value for this configuration.
+ 0, // The string identifier that describes this
+ // configuration.
+ USB_CONF_ATTR_BUS_PWR, // Bus Powered, Self Powered, remote wake up.
+ 250, // The maximum power in 2mA increments.
+};
+
+//*****************************************************************************
+//
+// This is the Interface Association Descriptor for the serial device used in
+// composite devices.
+//
+//*****************************************************************************
+unsigned char g_pIADAudioDescriptor[] =
+{
+
+ 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_AUDIO, // The device class for this association.
+ USB_SUBCLASS_UNDEFINED, // The device subclass for this association.
+ USB_PROTOCOL_UNDEFINED, // The protocol for this association.
+ 0 // The string index for this association.
+};
+
+const tConfigSection g_sIADAudioConfigSection =
+{
+ sizeof(g_pIADAudioDescriptor),
+ g_pIADAudioDescriptor
+};
+
+//*****************************************************************************
+//
+// The remainder of the configuration descriptor is stored in flash since we
+// don't need to modify anything in it at runtime.
+//
+//*****************************************************************************
+const unsigned char g_pAudioControlInterface[] =
+{
+ //
+ // Vendor-specific Interface Descriptor.
+ //
+ 9, // Size of the interface descriptor.
+ USB_DTYPE_INTERFACE, // Type of this descriptor.
+ AUDIO_INTERFACE_CONTROL, // The index for this interface.
+ 0, // The alternate setting for this interface.
+ 0, // The number of endpoints used by this
+ // interface.
+ USB_CLASS_AUDIO, // The interface class
+ USB_ASC_AUDIO_CONTROL, // The interface sub-class.
+ 0, // The interface protocol for the sub-class
+ // specified above.
+ 0, // The string index for this interface.
+
+ //
+ // Audio Header Descriptor.
+ //
+ 9, // The size of this descriptor.
+ USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific.
+ USB_ACDSTYPE_HEADER, // Descriptor sub-type is HEADER.
+ USBShort(0x0100), // Audio Device Class Specification Release
+ // Number in Binary-Coded Decimal.
+ // Total number of bytes in
+ // g_pAudioControlInterface
+ USBShort((9 + 9 + 12 + 13 + 9)),
+ 1, // Number of streaming interfaces.
+ 1, // Index of the first and only streaming
+ // interface.
+
+ //
+ // Audio Input Terminal Descriptor.
+ //
+ 12, // The size of this descriptor.
+ USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific.
+ USB_ACDSTYPE_IN_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL.
+ AUDIO_IN_TERMINAL_ID, // Terminal ID for this interface.
+ // USB streaming interface.
+ USBShort(USB_TTYPE_STREAMING),
+ 0, // ID of the Output Terminal to which this
+ // Input Terminal is associated.
+ 2, // Number of logical output channels in the
+ // Terminal's output audio channel cluster.
+ USBShort((USB_CHANNEL_L | // Describes the spatial location of the
+ USB_CHANNEL_R)), // logical channels.
+ 0, // Channel Name string index.
+ 0, // Terminal Name string index.
+
+ //
+ // Audio Feature Unit Descriptor
+ //
+ 13, // The size of this descriptor.
+ USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific.
+ USB_ACDSTYPE_FEATURE_UNIT, // Descriptor sub-type is FEATURE_UNIT.
+ AUDIO_CONTROL_ID, // Unit ID for this interface.
+ AUDIO_IN_TERMINAL_ID, // ID of the Unit or Terminal to which this
+ // Feature Unit is connected.
+ 2, // Size in bytes of an element of the
+ // bmaControls() array that follows.
+ // Master Mute control.
+ USBShort(USB_ACONTROL_MUTE),
+ // Left channel volume control.
+ USBShort(USB_ACONTROL_VOLUME),
+ // Right channel volume control.
+ USBShort(USB_ACONTROL_VOLUME),
+ 0, // Feature unit string index.
+
+ //
+ // Audio Output Terminal Descriptor.
+ //
+ 9, // The size of this descriptor.
+ USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific.
+ USB_ACDSTYPE_OUT_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL.
+ AUDIO_OUT_TERMINAL_ID, // Terminal ID for this interface.
+ // Output type is a generic speaker.
+ USBShort(USB_ATTYPE_SPEAKER),
+ AUDIO_IN_TERMINAL_ID, // ID of the input terminal to which this
+ // output terminal is connected.
+ AUDIO_CONTROL_ID, // ID of the feature unit that this output
+ // terminal is connected to.
+ 0, // Output terminal string index.
+
+};
+
+//*****************************************************************************
+//
+// The audio streaming interface descriptor. This describes the two valid
+// interfaces for this class. The first interface has no endpoints and is used
+// by host operating systems to put the device in idle mode, while the second
+// is used when the audio device is active.
+//
+//*****************************************************************************
+const unsigned char g_pAudioStreamInterface[] =
+{
+ //
+ // Vendor-specific Interface Descriptor.
+ //
+ 9, // Size of the interface descriptor.
+ USB_DTYPE_INTERFACE, // Type of this descriptor.
+ AUDIO_INTERFACE_OUTPUT, // The index for this interface.
+ 0, // The alternate setting for this interface.
+ 0, // The number of endpoints used by this
+ // interface.
+ USB_CLASS_AUDIO, // The interface class
+ USB_ASC_AUDIO_STREAMING, // The interface sub-class.
+ 0, // Unused must be 0.
+ 0, // The string index for this interface.
+
+ //
+ // Vendor-specific Interface Descriptor.
+ //
+ 9, // Size of the interface descriptor.
+ USB_DTYPE_INTERFACE, // Type of this descriptor.
+ 1, // The index for this interface.
+ 1, // The alternate setting for this interface.
+ 1, // The number of endpoints used by this
+ // interface.
+ USB_CLASS_AUDIO, // The interface class
+ USB_ASC_AUDIO_STREAMING, // The interface sub-class.
+ 0, // Unused must be 0.
+ 0, // The string index for this interface.
+
+ //
+ // Class specific Audio Streaming Interface descriptor.
+ //
+ 7, // Size of the interface descriptor.
+ USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific.
+ USB_ASDSTYPE_GENERAL, // General information.
+ AUDIO_IN_TERMINAL_ID, // ID of the terminal to which this streaming
+ // interface is connected.
+ 1, // One frame delay.
+ USBShort(USB_ADF_PCM), //
+
+ //
+ // Format type Audio Streaming descriptor.
+ //
+ 11, // Size of the interface descriptor.
+ USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific.
+ USB_ASDSTYPE_FORMAT_TYPE, // Audio Streaming format type.
+ USB_AF_TYPE_TYPE_I, // Type I audio format type.
+ 2, // Two audio channels.
+ 2, // Two bytes per audio sub-frame.
+ 16, // 16 bits per sample.
+ 1, // One sample rate provided.
+ USB3Byte(48000), // Only 48000 sample rate supported.
+
+ //
+ // Endpoint Descriptor
+ //
+ 9, // The size of the endpoint descriptor.
+ USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint.
+ // OUT endpoint with address
+ // ISOC_OUT_ENDPOINT.
+ USB_EP_DESC_OUT | USB_EP_TO_INDEX(ISOC_OUT_ENDPOINT),
+ USB_EP_ATTR_ISOC | // Endpoint is an adaptive isochronous data
+ USB_EP_ATTR_ISOC_ADAPT | // endpoint.
+ USB_EP_ATTR_USAGE_DATA,
+ USBShort(ISOC_OUT_EP_MAX_SIZE), // The maximum packet size.
+ 1, // The polling interval for this endpoint.
+ 0, // Refresh is unused.
+ 0, // Synch endpoint address.
+
+ //
+ // Audio Streaming Isochronous Audio Data Endpoint Descriptor
+ //
+ 7, // The size of the descriptor.
+ USB_ACSDT_ENDPOINT, // Audio Class Specific Endpoint Descriptor.
+ USB_ASDSTYPE_GENERAL, // This is a general descriptor.
+ USB_EP_ATTR_ACG_SAMPLING, // Sampling frequency is supported.
+ USB_EP_LOCKDELAY_UNDEF, // Undefined lock delay units.
+ USBShort(0), // No lock delay.
+};
+
+//*****************************************************************************
+//
+// The audio device configuration descriptor is defined as three sections,
+// one containing just the 9 byte USB configuration descriptor. The second
+// holds the audio streaming interface and the third holds the audio control
+// interface.
+//
+//*****************************************************************************
+const tConfigSection g_sAudioConfigSection =
+{
+ sizeof(g_pAudioDescriptor),
+ g_pAudioDescriptor
+};
+
+const tConfigSection g_sAudioStreamInterfaceSection =
+{
+ sizeof(g_pAudioStreamInterface),
+ g_pAudioStreamInterface
+};
+
+const tConfigSection g_sAudioControlInterfaceSection =
+{
+ sizeof(g_pAudioControlInterface),
+ g_pAudioControlInterface
+};
+
+//*****************************************************************************
+//
+// This array lists all the sections that must be concatenated to make a
+// single, complete audio device configuration descriptor.
+//
+//*****************************************************************************
+const tConfigSection *g_psAudioSections[] =
+{
+ &g_sAudioConfigSection,
+ &g_sIADAudioConfigSection,
+ &g_sAudioControlInterfaceSection,
+ &g_sAudioStreamInterfaceSection
+};
+
+#define NUM_AUDIO_SECTIONS (sizeof(g_psAudioSections) / \
+ sizeof(tConfigSection *))
+
+//*****************************************************************************
+//
+// The header for the single configuration we support. This is the root of
+// the data structure that defines all the bits and pieces that are pulled
+// together to generate the configuration descriptor.
+//
+//*****************************************************************************
+const tConfigHeader g_sAudioConfigHeader =
+{
+ NUM_AUDIO_SECTIONS,
+ g_psAudioSections
+};
+
+//*****************************************************************************
+//
+// Configuration Descriptor.
+//
+//*****************************************************************************
+const tConfigHeader * const g_pAudioConfigDescriptors[] =
+{
+ &g_sAudioConfigHeader
+};
+
+//*****************************************************************************
+//
+// Various internal handlers needed by this class.
+//
+//*****************************************************************************
+static void HandleDisconnect(void *pvInstance);
+static void InterfaceChange(void *pvInstance, unsigned char ucInterface,
+ unsigned char ucAlternateSetting);
+static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue);
+static void DataReceived(void *pvInstance, unsigned long ulInfo);
+static void HandleEndpoints(void *pvInstance, unsigned long ulStatus);
+static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest);
+static void HandleDevice(void *pvInstance, unsigned long ulRequest,
+ void *pvRequestData);
+
+//*****************************************************************************
+//
+// The FIFO configuration for USB audio device class.
+//
+//*****************************************************************************
+const tFIFOConfig g_sUSBAudioFIFOConfig =
+{
+ //
+ // IN endpoints.
+ //
+ {
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN }
+ },
+
+ //
+ // OUT endpoints.
+ //
+ {
+ { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT }
+ },
+};
+
+//*****************************************************************************
+//
+// The device information structure for the USB Audio device.
+//
+//*****************************************************************************
+tDeviceInfo g_sAudioDeviceInfo =
+{
+ //
+ // Device event handler callbacks.
+ //
+ {
+ //
+ // GetDescriptor
+ //
+ 0,
+
+ //
+ // RequestHandler
+ //
+ HandleRequests,
+
+ //
+ // InterfaceChange
+ //
+ InterfaceChange,
+
+ //
+ // ConfigChange
+ //
+ ConfigChangeHandler,
+
+ //
+ // DataReceived
+ //
+ DataReceived,
+
+ //
+ // DataSentCallback
+ //
+ 0,
+
+ //
+ // ResetHandler
+ //
+ 0,
+
+ //
+ // SuspendHandler
+ //
+ 0,
+
+ //
+ // ResumeHandler
+ //
+ 0,
+
+ //
+ // DisconnectHandler
+ //
+ HandleDisconnect,
+
+ //
+ // EndpointHandler
+ //
+ HandleEndpoints,
+
+ //
+ // Device handler
+ //
+ HandleDevice
+ },
+ g_pAudioDeviceDescriptor,
+ g_pAudioConfigDescriptors,
+ 0,
+ 0,
+ &g_sUSBAudioFIFOConfig
+};
+
+//*****************************************************************************
+//
+// This function is called to handle data being received back from the host so
+// that the application callback can be called when the new data is ready.
+//
+//*****************************************************************************
+static void
+DataReceived(void *pvInstance, unsigned long ulInfo)
+{
+ tAudioInstance *psInst;
+ const tUSBDAudioDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // Make a copy of this pointer for ease of use in this function.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // If there is an update pending and the request was to set a current
+ // value then check which value was set.
+ //
+ if(psInst->usUpdate && (psInst->ucRequest == USB_AC_SET_CUR))
+ {
+ //
+ // Only handling interface requests.
+ //
+ if((psInst->usRequestType & USB_RTYPE_RECIPIENT_M) ==
+ USB_RTYPE_INTERFACE)
+ {
+ if(psInst->usUpdate == VOLUME_CONTROL)
+ {
+ //
+ // Inform the callback of the new volume.
+ //
+ psDevice->pfnCallback(0, USBD_AUDIO_EVENT_VOLUME,
+ psInst->sVolume, 0);
+ }
+ else if(psDevice->psPrivateData->usUpdate == MUTE_CONTROL)
+ {
+ //
+ // Inform the callback of the new data.
+ //
+ psDevice->pfnCallback(0, USBD_AUDIO_EVENT_MUTE, psInst->ucMute,
+ 0);
+ }
+ }
+ psInst->usUpdate = 0;
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called to handle the interrupts on the isochronous endpoint
+// for the audio device class.
+//
+//*****************************************************************************
+static void
+HandleEndpoints(void *pvInstance, unsigned long ulStatus)
+{
+ unsigned long ulEPStatus;
+ tAudioInstance *psInst;
+ unsigned char *pucData;
+ const tUSBDAudioDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // Make a copy of this pointer for ease of use later in this function.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Make sure this was for the isochronous out endpoint.
+ //
+ if((psInst->sBuffer.pvData != 0) &&
+ (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP))
+ {
+ //
+ // Save the pointer to the data buffer.
+ //
+ pucData = psInst->sBuffer.pvData;
+
+ //
+ // Clear out the buffer pointer to indicate it is no longer in use.
+ //
+ psInst->sBuffer.pvData = 0;
+
+ //
+ // Inform the callback of the new data.
+ //
+ psInst->sBuffer.pfnCallback(pucData, psInst->sBuffer.ulSize,
+ USBD_AUDIO_EVENT_DATAOUT);
+
+ //
+ // Read out the current endpoint status.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint);
+
+ //
+ // Acknowledge that the data was read, this will not cause a bus
+ // acknowledgment.
+ //
+ MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ucOUTEndpoint, 0);
+
+ //
+ // Clear the status bits.
+ //
+ MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint,
+ ulEPStatus);
+ }
+}
+
+//*****************************************************************************
+//
+// Device instance specific handler.
+//
+//*****************************************************************************
+static void
+HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData)
+{
+ tAudioInstance *psInst;
+ unsigned char *pucData;
+
+ //
+ // Create the serial instance data.
+ //
+ psInst = ((tUSBDAudioDevice *)pvInstance)->psPrivateData;
+
+ //
+ // 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] == AUDIO_INTERFACE_CONTROL)
+ {
+ psInst->ucInterfaceControl = pucData[1];
+ }
+ else if(pucData[0] == AUDIO_INTERFACE_OUTPUT)
+ {
+ psInst->ucInterfaceAudio = 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) == 0)
+ {
+ //
+ // Extract the new endpoint number without the DIR bit.
+ //
+ psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f);
+
+ //
+ // Extract the new DMA channel.
+ //
+ psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX +
+ (((pucData[1] & 0x7f) - 1) * 2);
+
+ //
+ // Basic configuration for DMA on the OUT endpoint.
+ //
+ MAP_uDMAChannelControlSet(psInst->ucOUTDMA,
+ (UDMA_SIZE_32 | UDMA_SRC_INC_NONE|
+ UDMA_DST_INC_32 | UDMA_ARB_16));
+
+ //
+ // Select this channel for this endpoint, this only affects
+ // devices that have this feature.
+ //
+ MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint,
+ psInst->ucOUTDMA);
+ }
+ 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;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device is
+// disconnected from the host.
+//
+//*****************************************************************************
+static void
+HandleDisconnect(void *pvInstance)
+{
+ const tUSBDAudioDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // Inform the application that the device has been disconnected.
+ //
+ psDevice->pfnCallback(0, USB_EVENT_DISCONNECTED, 0, 0);
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device
+// interface changes. This occurs when the audio device transitions between
+// being active and inactive. Interface AUDIO_INTERFACE_CONTROL is the
+// inactive interface that has no endpoints, while interface
+// AUDIO_INTERFACE_AUDIO has the single Isochronous OUT endpoint.
+//
+//*****************************************************************************
+static void
+InterfaceChange(void *pvInstance, unsigned char ucInterface,
+ unsigned char ucAlternateSetting)
+{
+ const tUSBDAudioDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // Check which interface to change into.
+ //
+ if(ucAlternateSetting == 0)
+ {
+ //
+ // Alternate setting 0 is an inactive state.
+ //
+ if(psDevice->pfnCallback)
+ {
+ psDevice->pfnCallback(0, USBD_AUDIO_EVENT_IDLE, 0, 0);
+ }
+ }
+ else
+ {
+ //
+ // Alternate setting 1 is the active state.
+ //
+ if(psDevice->pfnCallback)
+ {
+ psDevice->pfnCallback(0, USBD_AUDIO_EVENT_ACTIVE, 0, 0);
+ }
+
+ //
+ // Enable uDMA on the endpoint now that the active configuration
+ // has been selected.
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE,
+ psDevice->psPrivateData->ucOUTEndpoint,
+ USB_EP_DEV_OUT);
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device
+// configuration changes.
+//
+//*****************************************************************************
+static void
+ConfigChangeHandler(void *pvInstance, unsigned long ulValue)
+{
+ const tUSBDAudioDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // If we have a control callback, let the client know we are open for
+ // business.
+ //
+ if(psDevice->pfnCallback)
+ {
+ //
+ // Pass the connected event to the client.
+ //
+ psDevice->pfnCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0);
+ }
+}
+
+//*****************************************************************************
+//
+//! This function should be called once for the audio class device to
+//! initialized basic operation and prepare for enumeration.
+//!
+//! \param ulIndex is the index of the USB controller to initialize for
+//! audio class device operation.
+//! \param psDevice points to a structure containing parameters customizing
+//! the operation of the audio device.
+//!
+//! In order for an application to initialize the USB audio device class, it
+//! must first call this function with the a valid audio device class structure
+//! in the \e psDevice parameter. This allows this function to initialize the
+//! USB controller and device code to be prepared to enumerate and function as
+//! a USB audio device.
+//!
+//! This function returns a void pointer that must be passed in to all other
+//! APIs used by the audio class.
+//!
+//! See the documentation on the tUSBDAudioDevice structure for more
+//! information on how to properly fill the structure members.
+//!
+//! \return Returns 0 on failure or a non-zero void pointer on success.
+//
+//*****************************************************************************
+void *
+USBDAudioInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice)
+{
+ //
+ // Check parameter validity.
+ //
+ ASSERT(ulIndex == 0);
+ ASSERT(psDevice);
+ ASSERT(psDevice->ppStringDescriptors);
+ ASSERT(psDevice->psPrivateData);
+
+ USBDAudioCompositeInit(ulIndex, psDevice);
+
+ //
+ // All is well so now pass the descriptors to the lower layer and put
+ // the bulk device on the bus.
+ //
+ USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo);
+
+ //
+ // Basic configuration for DMA on the OUT endpoint.
+ //
+ MAP_uDMAChannelControlSet(psDevice->psPrivateData->ucOUTDMA,
+ (UDMA_SIZE_32 | UDMA_SRC_INC_NONE|
+ UDMA_DST_INC_32 | UDMA_ARB_16));
+
+ //
+ // Select this channel for this endpoint, this only affects devices that
+ // have this feature.
+ //
+ MAP_USBEndpointDMAChannel(USB0_BASE, psDevice->psPrivateData->ucOUTEndpoint,
+ psDevice->psPrivateData->ucOUTDMA);
+
+ //
+ // Return the pointer to the instance indicating that everything went well.
+ //
+ return((void *)psDevice);
+}
+
+//*****************************************************************************
+//
+//! This function should be called once for the audio class device to
+//! initialized basic operation and prepare for enumeration.
+//!
+//! \param ulIndex is the index of the USB controller to initialize for
+//! audio class device operation.
+//! \param psDevice points to a structure containing parameters customizing
+//! the operation of the audio device.
+//!
+//! In order for an application to initialize the USB audio device class, it
+//! must first call this function with the a valid audio device class structure
+//! in the \e psDevice parameter. This allows this function to initialize the
+//! USB controller and device code to be prepared to enumerate and function as
+//! a USB audio device.
+//!
+//! This function returns a void pointer that must be passed in to all other
+//! APIs used by the audio class.
+//!
+//! See the documentation on the tUSBDAudioDevice structure for more
+//! information on how to properly fill the structure members.
+//!
+//! \return Returns 0 on failure or a non-zero void pointer on success.
+//
+//*****************************************************************************
+void *
+USBDAudioCompositeInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice)
+{
+ tAudioInstance *psInst;
+ tDeviceDescriptor *psDevDesc;
+
+ //
+ // Check parameter validity.
+ //
+ ASSERT(ulIndex == 0);
+ ASSERT(psDevice);
+ ASSERT(psDevice->ppStringDescriptors);
+ ASSERT(psDevice->psPrivateData);
+
+ //
+ // Initialize the workspace in the passed instance structure.
+ //
+ psInst = psDevice->psPrivateData;
+ psInst->psConfDescriptor = (tConfigDescriptor *)g_pAudioDescriptor;
+ psInst->psDevInfo = &g_sAudioDeviceInfo;
+ psInst->ulUSBBase = USB0_BASE;
+
+ //
+ // The Control interface is at index 0.
+ //
+ psInst->ucInterfaceControl = AUDIO_INTERFACE_CONTROL;
+
+ //
+ // The Audio interface is at index 1.
+ //
+ psInst->ucInterfaceAudio = AUDIO_INTERFACE_OUTPUT;
+
+ //
+ // Set the default Isochronous OUT endpoint.
+ //
+ psInst->ucOUTEndpoint = ISOC_OUT_ENDPOINT;
+ psInst->ucOUTDMA = ISOC_OUT_DMA_CHANNEL;
+
+ //
+ // Set the initial buffer to null.
+ //
+ psInst->sBuffer.pvData = 0;
+
+ //
+ // Save the volume settings.
+ //
+ psInst->sVolumeMax = psDevice->sVolumeMax;
+ psInst->sVolumeMin = psDevice->sVolumeMin;
+ psInst->sVolumeStep = psDevice->sVolumeStep;
+
+ //
+ // No update pending to any command.
+ //
+ psInst->usUpdate = 0;
+
+ //
+ // Fix up the device descriptor with the client-supplied values.
+ //
+ psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor;
+ psDevDesc->idVendor = psDevice->usVID;
+ psDevDesc->idProduct = psDevice->usPID;
+ //
+ // Fix up the configuration descriptor with client-supplied values.
+ //
+ psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes;
+ psInst->psConfDescriptor->bMaxPower =
+ (unsigned char)(psDevice->usMaxPowermA / 2);
+
+ //
+ // Plug in the client's string stable to the device information
+ // structure.
+ //
+ psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors;
+ psInst->psDevInfo->ulNumStringDescriptors =
+ psDevice->ulNumStringDescriptors;
+ psInst->psDevInfo->pvInstance = (void *)psDevice;
+
+ //
+ // Return the pointer to the instance indicating that everything went well.
+ //
+ return((void *)psDevice);
+}
+
+//*****************************************************************************
+//
+//! Shuts down the audio device.
+//!
+//! \param pvInstance is the pointer to the device instance structure as
+//! returned by USBDAudioInit().
+//!
+//! This function terminates audio interface for the instance supplied. This
+//! function should not be called if the audio 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
+USBDAudioTerm(void *pvInstance)
+{
+ ASSERT(pvInstance != 0);
+
+ //
+ // Cleanly exit device mode.
+ //
+ USBDCDTerm(0);
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever a non-standard
+// request is received.
+//
+// \param pvInstance is the instance data for this request.
+// \param pUSBRequest points to the request received.
+//
+// This call parses the provided request structure to the type of request and
+// will respond to all commands that are understood by the class.
+//
+// \return None.
+//
+//*****************************************************************************
+static void
+HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest)
+{
+ unsigned long ulControl;
+ unsigned long ulRecipient;
+ unsigned long ulStall;
+ tAudioInstance *psInst;
+ const tUSBDAudioDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // Make a copy of this pointer for ease of use in this function.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Make sure to acknowledge that the data was read, this will not send and
+ // ACK that has already been done at this point. This just tells the
+ // hardware that the data was read.
+ //
+ MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false);
+
+ //
+ // Don't stall by default.
+ //
+ ulStall = 0;
+
+ //
+ // Get the request type.
+ //
+ ulRecipient = pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M;
+
+ //
+ // Save the request type and request value.
+ //
+ psInst->usRequestType = pUSBRequest->bmRequestType;
+ psInst->ucRequest = pUSBRequest->bRequest;
+
+ //
+ // Check if this is an endpoint request to the audio streaming endpoint.
+ //
+ if((ulRecipient == USB_RTYPE_ENDPOINT) &&
+ (pUSBRequest->wIndex == USB_EP_TO_INDEX(psInst->ucOUTEndpoint)))
+ {
+ //
+ // Determine the type of request.
+ //
+ switch(psInst->ucRequest)
+ {
+ case USB_AC_SET_CUR:
+ {
+ //
+ // Handle retrieving the sample rate.
+ //
+ if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL)
+ {
+ //
+ // Retrieve the requested sample rate.
+ //
+ USBDCDRequestDataEP0(0,
+ (unsigned char *)&psInst->ulSampleRate,
+ 3);
+
+ //
+ // Save what we are updating.
+ //
+ psInst->usUpdate = SAMPLING_FREQ_CONTROL;
+ }
+ break;
+ }
+ case USB_AC_GET_CUR:
+ {
+ //
+ // Handle retrieving the sample rate.
+ //
+ if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL)
+ {
+ //
+ // Send back the current sample rate.
+ //
+ USBDCDSendDataEP0(0,
+ (unsigned char *)&psInst->ulSampleRate,
+ 3);
+ }
+ break;
+ }
+ default:
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ break;
+ }
+ }
+ }
+ else if(ulRecipient == USB_RTYPE_INTERFACE)
+ {
+ //
+ // Make sure the request was for the control interface.
+ //
+ if((unsigned char)pUSBRequest->wIndex != psInst->ucInterfaceControl)
+ {
+ return;
+ }
+
+ //
+ // Extract the control value from the message.
+ //
+ ulControl = pUSBRequest->wValue & USB_CS_CONTROL_M;
+
+ //
+ // Handle an audio control request to the feature control unit.
+ //
+ if((AUDIO_CONTROL_ID << 8) == (pUSBRequest->wIndex & USB_CS_CONTROL_M))
+ {
+ //
+ // Determine the type of request.
+ //
+ switch(psInst->ucRequest)
+ {
+ case USB_AC_GET_MAX:
+ {
+ if(ulControl == VOLUME_CONTROL)
+ {
+ //
+ // Return the maximum volume setting.
+ //
+ USBDCDSendDataEP0(0,
+ (unsigned char *)&psInst->sVolumeMax,
+ 2);
+ }
+ else
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ }
+ break;
+ }
+ case USB_AC_GET_MIN:
+ {
+ if(ulControl == VOLUME_CONTROL)
+ {
+ //
+ // Return the minimum volume setting.
+ //
+ USBDCDSendDataEP0(0,
+ (unsigned char *)&psInst->sVolumeMin,
+ 2);
+ }
+ else
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ }
+ break;
+ }
+ case USB_AC_GET_RES:
+ {
+ if(ulControl == VOLUME_CONTROL)
+ {
+ //
+ // Return the volume step setting.
+ //
+ USBDCDSendDataEP0(0,
+ (unsigned char *)&psInst->sVolumeStep,
+ 2);
+ }
+ else
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ }
+ break;
+ }
+ case USB_AC_GET_CUR:
+ {
+ if(ulControl == VOLUME_CONTROL)
+ {
+ //
+ // Send back the current volume level.
+ //
+ USBDCDSendDataEP0(0,
+ (unsigned char *)&psInst->sVolume,
+ 2);
+ }
+ else if(ulControl == MUTE_CONTROL)
+ {
+ //
+ // Send back the current mute value.
+ //
+ USBDCDSendDataEP0(0,
+ (unsigned char *)&psInst->ucMute, 1);
+ }
+ else
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ }
+ break;
+ }
+ case USB_AC_SET_CUR:
+ {
+ if(ulControl == VOLUME_CONTROL)
+ {
+ //
+ // Read the new volume level.
+ //
+ USBDCDRequestDataEP0(0,
+ (unsigned char *)&psInst->sVolume,
+ 2);
+
+ //
+ // Save what we are updating.
+ //
+ psInst->usUpdate = VOLUME_CONTROL;
+ }
+ else if(ulControl == MUTE_CONTROL)
+ {
+ //
+ // Read the new mute setting.
+ //
+ USBDCDRequestDataEP0(0,
+ (unsigned char *)&psInst->ucMute,
+ 1);
+
+ //
+ // Save what we are updating.
+ //
+ psInst->usUpdate = MUTE_CONTROL;
+ }
+ else
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ }
+ break;
+ }
+ case USB_AC_SET_RES:
+ {
+ if(ulControl == VOLUME_CONTROL)
+ {
+ //
+ // Read the new volume step setting.
+ //
+ USBDCDRequestDataEP0(0,
+ (unsigned char *)&psInst->sVolumeStep, 2);
+
+ //
+ // Save what we are updating.
+ //
+ psInst->usUpdate = VOLUME_CONTROL;
+ }
+ else
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ }
+ break;
+ }
+ default:
+ {
+ //
+ // Stall on unknown commands.
+ //
+ ulStall = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // Stall on all unknown commands.
+ //
+ if(ulStall)
+ {
+ USBDCDStallEP0(0);
+ }
+}
+
+//*****************************************************************************
+//
+//! This function is used to supply buffers to the audio class to be filled
+//! from the USB host device.
+//!
+//! \param pvInstance is the pointer to the device instance structure as
+//! returned by USBDAudioInit() or USBDAudioInitComposite().
+//! \param pvBuffer is a pointer to the buffer to fill with audio data.
+//! \param ulSize is the size in bytes of the buffer pointed to by the pvBuffer
+//! parameter.
+//! \param pfnCallback is a callback that will provide notification when this
+//! buffer has valid data.
+//!
+//! This function fills the buffer pointed to by the \e pvBuffer parameter with
+//! at most \e ulSize one packet of data from the host controller. The ulSize
+//! has a minimum value of \b ISOC_OUT_EP_MAX_SIZE since each USB packet can be
+//! at most \b ISOC_OUT_EP_MAX_SIZE bytes in size. Since the audio data may
+//! not be received in amounts that evenly fit in the buffer provided, the
+//! buffer may not be completely filled. The \e pfnCallback function will
+//! provide the amount of valid data that was actually stored in the buffer
+//! provided. The function will return zero if the buffer could be scheduled
+//! to be filled, otherwise the function will return a non-zero value if there
+//! was some reason that the buffer could not be added.
+//!
+//! \return Returns 0 to indicate success any other value indicates that the
+//! buffer will not be filled.
+//
+//*****************************************************************************
+long
+USBAudioBufferOut(void *pvInstance, void *pvBuffer, unsigned long ulSize,
+ tUSBAudioBufferCallback pfnCallback)
+{
+ tAudioInstance *psInst;
+ const tUSBDAudioDevice *psDevice;
+
+ //
+ // Make sure we were not passed NULL pointers.
+ //
+ ASSERT(pvInstance != 0);
+ ASSERT(pvBuffer != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDAudioDevice *)pvInstance;
+
+ //
+ // Buffer must be at least one packet in size.
+ //
+ ASSERT(ulSize >= ISOC_OUT_EP_MAX_SIZE);
+ ASSERT(pfnCallback);
+
+ //
+ // Create a pointer of the correct type from the private pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Initialize the buffer instance.
+ //
+ psInst->sBuffer.pvData = pvBuffer;
+ psInst->sBuffer.ulSize = ulSize;
+ psInst->sBuffer.ulNumBytes = 0;
+ psInst->sBuffer.pfnCallback = pfnCallback;
+
+ //
+ // Configure and enable DMA for the OUT transfer.
+ //
+ MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, UDMA_MODE_BASIC,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ psInst->ucOUTEndpoint),
+ psInst->sBuffer.pvData, ulSize >> 2);
+
+ //
+ // Start the DMA transfer.
+ //
+ MAP_uDMAChannelEnable(psInst->ucOUTDMA);
+
+ return(0);
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************
+
|
