From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- usb/device/USBDCallbackInvocationFlowchart.png | Bin 0 -> 11380 bytes usb/device/USBDeviceStateDiagram.png | Bin 0 -> 8827 bytes usb/device/audio-looprec/AUDDLoopRecChannel.c | 129 ++ usb/device/audio-looprec/AUDDLoopRecChannel.h | 92 + usb/device/audio-looprec/AUDDLoopRecDriver.c | 493 ++++ usb/device/audio-looprec/AUDDLoopRecDriver.h | 163 ++ .../audio-looprec/AUDDLoopRecDriverDescriptors.c | 1461 ++++++++++++ .../audio-looprec/AUDDLoopRecDriverDescriptors.h | 148 ++ usb/device/audio-looprec/audio-looprec.dir | 51 + usb/device/audio-speaker/AUDDSpeakerChannel.c | 129 ++ usb/device/audio-speaker/AUDDSpeakerChannel.h | 92 + usb/device/audio-speaker/AUDDSpeakerDriver.c | 378 ++++ usb/device/audio-speaker/AUDDSpeakerDriver.h | 201 ++ .../audio-speaker/AUDDSpeakerDriverDescriptors.c | 620 +++++ .../audio-speaker/AUDDSpeakerDriverDescriptors.h | 146 ++ usb/device/audio-speaker/USBAudioSpeaker.png | Bin 0 -> 3711 bytes .../audio-speaker/USBAudioSpeakerDescriptors.png | Bin 0 -> 8026 bytes .../audio-speaker/USBAudioSpeakerRecorder.png | Bin 0 -> 5053 bytes .../USBAudioSpeakerRecorderDescriptors.png | Bin 0 -> 10906 bytes usb/device/audio-speaker/audio-speaker.dir | 617 +++++ usb/device/ccid/cciddriver.c | 1503 ++++++++++++ usb/device/ccid/cciddriver.h | 378 ++++ usb/device/ccid/cciddriverdescriptors.h | 152 ++ usb/device/cdc-serial/CDCDSerialDriver.c | 300 +++ usb/device/cdc-serial/CDCDSerialDriver.h | 117 + .../cdc-serial/CDCDSerialDriverDescriptors.c | 661 ++++++ .../cdc-serial/CDCDSerialDriverDescriptors.h | 77 + usb/device/cdc-serial/CDCarchitecture.png | Bin 0 -> 28223 bytes usb/device/cdc-serial/USB-SerialConverter.png | Bin 0 -> 10177 bytes usb/device/cdc-serial/cdc-serial.dir | 646 ++++++ usb/device/cdc-serial/drv/6119.inf | 45 + usb/device/cdc-serial/drv/drv.dir | 41 + usb/device/composite/AUDDFunctionDriver.c | 282 +++ usb/device/composite/AUDDFunctionDriver.h | 105 + .../composite/AUDDFunctionDriverDescriptors.h | 69 + usb/device/composite/CDCDFunctionDriver.c | 337 +++ usb/device/composite/CDCDFunctionDriver.h | 128 ++ .../composite/CDCDFunctionDriverDescriptors.h | 65 + usb/device/composite/CDCHIDDDriver.c | 227 ++ usb/device/composite/CDCHIDDDriver.h | 78 + usb/device/composite/CDCHIDDDriverDescriptors.c | 592 +++++ usb/device/composite/CDCHIDDDriverDescriptors.h | 70 + usb/device/composite/CDCMSDDDriver.c | 213 ++ usb/device/composite/CDCMSDDDriver.h | 77 + usb/device/composite/CDCMSDDDriverDescriptors.c | 568 +++++ usb/device/composite/CDCMSDDDriverDescriptors.h | 71 + usb/device/composite/COMPOSITEDDriver.c | 292 +++ usb/device/composite/COMPOSITEDDriver.h | 93 + usb/device/composite/COMPOSITEDDriverDescriptors.c | 954 ++++++++ usb/device/composite/COMPOSITEDDriverDescriptors.h | 61 + usb/device/composite/DUALCDCDDriver.c | 189 ++ usb/device/composite/DUALCDCDDriver.h | 77 + usb/device/composite/DUALCDCDDriverDescriptors.c | 702 ++++++ usb/device/composite/DUALCDCDDriverDescriptors.h | 72 + usb/device/composite/HIDDFunctionDriver.c | 477 ++++ usb/device/composite/HIDDFunctionDriver.h | 64 + .../composite/HIDDFunctionDriverDescriptors.c | 118 + .../composite/HIDDFunctionDriverDescriptors.h | 86 + usb/device/composite/HIDMSDDDriver.c | 227 ++ usb/device/composite/HIDMSDDDriver.h | 79 + usb/device/composite/HIDMSDDDriverDescriptors.c | 449 ++++ usb/device/composite/HIDMSDDDriverDescriptors.h | 66 + usb/device/composite/MSDDFunctionDriver.c | 327 +++ usb/device/composite/MSDDFunctionDriver.h | 72 + .../composite/MSDDFunctionDriverDescriptors.h | 69 + usb/device/composite/drv/CompositeCDCSerial.inf | 57 + usb/device/composite/drv/drv.dir | 45 + usb/device/core/USBD.h | 276 +++ usb/device/core/USBDCallbacks.h | 65 + usb/device/core/USBDCallbacks_Initialized.c | 67 + usb/device/core/USBDCallbacks_RequestReceived.c | 49 + usb/device/core/USBDCallbacks_Reset.c | 47 + usb/device/core/USBDCallbacks_Resumed.c | 54 + usb/device/core/USBDCallbacks_Suspended.c | 51 + usb/device/core/USBDDriver.c | 753 +++++++ usb/device/core/USBDDriver.h | 101 + usb/device/core/USBDDriverCallbacks.h | 61 + usb/device/core/USBDDriverCb_CfgChanged.c | 49 + usb/device/core/USBDDriverCb_IfSettingChanged.c | 52 + usb/device/core/USBDDriverDescriptors.h | 86 + usb/device/core/USBD_OTGHS.c | 1697 ++++++++++++++ usb/device/core/USBD_UDP.c | 1692 ++++++++++++++ usb/device/core/USBD_UDPHS.c | 2384 ++++++++++++++++++++ usb/device/core/core.dir | 41 + usb/device/device.dir | 655 ++++++ usb/device/hid-keyboard/HIDClassArch.png | Bin 0 -> 3495 bytes usb/device/hid-keyboard/HIDDKeyboardCallbacks.h | 57 + .../HIDDKeyboardCallbacks_LedsChanged.c | 59 + usb/device/hid-keyboard/HIDDKeyboardDriver.c | 477 ++++ usb/device/hid-keyboard/HIDDKeyboardDriver.h | 75 + .../hid-keyboard/HIDDKeyboardDriverDescriptors.c | 420 ++++ .../hid-keyboard/HIDDKeyboardDriverDescriptors.h | 114 + usb/device/hid-keyboard/HIDDKeyboardInputReport.c | 164 ++ usb/device/hid-keyboard/HIDDKeyboardInputReport.h | 149 ++ usb/device/hid-keyboard/HIDDKeyboardOutputReport.c | 94 + usb/device/hid-keyboard/HIDDKeyboardOutputReport.h | 96 + usb/device/hid-keyboard/hid-keyboard.dir | 787 +++++++ usb/device/hid-mouse/HIDDMouseDriver.c | 392 ++++ usb/device/hid-mouse/HIDDMouseDriver.h | 107 + usb/device/hid-mouse/HIDDMouseDriverDescriptors.c | 385 ++++ usb/device/hid-mouse/HIDDMouseDriverDescriptors.h | 86 + usb/device/hid-mouse/HIDDMouseInputReport.c | 72 + usb/device/hid-mouse/HIDDMouseInputReport.h | 93 + usb/device/hid-mouse/hid-mouse.dir | 544 +++++ usb/device/hid-transfer/HIDDTransferDriver.c | 479 ++++ usb/device/hid-transfer/HIDDTransferDriver.h | 87 + usb/device/hid-transfer/HIDDTransferDriverDesc.c | 417 ++++ usb/device/hid-transfer/HIDDTransferDriverDesc.h | 87 + usb/device/hid-transfer/hid-transfer.dir | 473 ++++ usb/device/massstorage/MSD.h | 233 ++ usb/device/massstorage/MSDAppArch.png | Bin 0 -> 7248 bytes usb/device/massstorage/MSDDStateMachine.c | 607 +++++ usb/device/massstorage/MSDDStateMachine.h | 264 +++ usb/device/massstorage/MSDDriver.c | 343 +++ usb/device/massstorage/MSDDriver.h | 75 + usb/device/massstorage/MSDDriverArch.png | Bin 0 -> 4889 bytes usb/device/massstorage/MSDDriverClasses.png | Bin 0 -> 12031 bytes usb/device/massstorage/MSDDriverDescriptors.c | 475 ++++ usb/device/massstorage/MSDDriverDescriptors.h | 83 + usb/device/massstorage/MSDDriverStates.png | Bin 0 -> 7920 bytes usb/device/massstorage/MSDIOFifo.c | 68 + usb/device/massstorage/MSDIOFifo.h | 133 ++ usb/device/massstorage/MSDLun.c | 366 +++ usb/device/massstorage/MSDLun.h | 152 ++ usb/device/massstorage/MSDMediaArch.png | Bin 0 -> 3622 bytes usb/device/massstorage/SBC.h | 653 ++++++ usb/device/massstorage/SBCMethods.c | 1611 +++++++++++++ usb/device/massstorage/SBCMethods.h | 111 + usb/device/massstorage/massstorage.dir | 1005 +++++++++ 129 files changed, 36171 insertions(+) create mode 100644 usb/device/USBDCallbackInvocationFlowchart.png create mode 100644 usb/device/USBDeviceStateDiagram.png create mode 100644 usb/device/audio-looprec/AUDDLoopRecChannel.c create mode 100644 usb/device/audio-looprec/AUDDLoopRecChannel.h create mode 100644 usb/device/audio-looprec/AUDDLoopRecDriver.c create mode 100644 usb/device/audio-looprec/AUDDLoopRecDriver.h create mode 100644 usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.c create mode 100644 usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.h create mode 100644 usb/device/audio-looprec/audio-looprec.dir create mode 100644 usb/device/audio-speaker/AUDDSpeakerChannel.c create mode 100644 usb/device/audio-speaker/AUDDSpeakerChannel.h create mode 100644 usb/device/audio-speaker/AUDDSpeakerDriver.c create mode 100644 usb/device/audio-speaker/AUDDSpeakerDriver.h create mode 100644 usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.c create mode 100644 usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.h create mode 100644 usb/device/audio-speaker/USBAudioSpeaker.png create mode 100644 usb/device/audio-speaker/USBAudioSpeakerDescriptors.png create mode 100644 usb/device/audio-speaker/USBAudioSpeakerRecorder.png create mode 100644 usb/device/audio-speaker/USBAudioSpeakerRecorderDescriptors.png create mode 100644 usb/device/audio-speaker/audio-speaker.dir create mode 100644 usb/device/ccid/cciddriver.c create mode 100644 usb/device/ccid/cciddriver.h create mode 100644 usb/device/ccid/cciddriverdescriptors.h create mode 100644 usb/device/cdc-serial/CDCDSerialDriver.c create mode 100644 usb/device/cdc-serial/CDCDSerialDriver.h create mode 100644 usb/device/cdc-serial/CDCDSerialDriverDescriptors.c create mode 100644 usb/device/cdc-serial/CDCDSerialDriverDescriptors.h create mode 100644 usb/device/cdc-serial/CDCarchitecture.png create mode 100644 usb/device/cdc-serial/USB-SerialConverter.png create mode 100644 usb/device/cdc-serial/cdc-serial.dir create mode 100644 usb/device/cdc-serial/drv/6119.inf create mode 100644 usb/device/cdc-serial/drv/drv.dir create mode 100644 usb/device/composite/AUDDFunctionDriver.c create mode 100644 usb/device/composite/AUDDFunctionDriver.h create mode 100644 usb/device/composite/AUDDFunctionDriverDescriptors.h create mode 100644 usb/device/composite/CDCDFunctionDriver.c create mode 100644 usb/device/composite/CDCDFunctionDriver.h create mode 100644 usb/device/composite/CDCDFunctionDriverDescriptors.h create mode 100644 usb/device/composite/CDCHIDDDriver.c create mode 100644 usb/device/composite/CDCHIDDDriver.h create mode 100644 usb/device/composite/CDCHIDDDriverDescriptors.c create mode 100644 usb/device/composite/CDCHIDDDriverDescriptors.h create mode 100644 usb/device/composite/CDCMSDDDriver.c create mode 100644 usb/device/composite/CDCMSDDDriver.h create mode 100644 usb/device/composite/CDCMSDDDriverDescriptors.c create mode 100644 usb/device/composite/CDCMSDDDriverDescriptors.h create mode 100644 usb/device/composite/COMPOSITEDDriver.c create mode 100644 usb/device/composite/COMPOSITEDDriver.h create mode 100644 usb/device/composite/COMPOSITEDDriverDescriptors.c create mode 100644 usb/device/composite/COMPOSITEDDriverDescriptors.h create mode 100644 usb/device/composite/DUALCDCDDriver.c create mode 100644 usb/device/composite/DUALCDCDDriver.h create mode 100644 usb/device/composite/DUALCDCDDriverDescriptors.c create mode 100644 usb/device/composite/DUALCDCDDriverDescriptors.h create mode 100644 usb/device/composite/HIDDFunctionDriver.c create mode 100644 usb/device/composite/HIDDFunctionDriver.h create mode 100644 usb/device/composite/HIDDFunctionDriverDescriptors.c create mode 100644 usb/device/composite/HIDDFunctionDriverDescriptors.h create mode 100644 usb/device/composite/HIDMSDDDriver.c create mode 100644 usb/device/composite/HIDMSDDDriver.h create mode 100644 usb/device/composite/HIDMSDDDriverDescriptors.c create mode 100644 usb/device/composite/HIDMSDDDriverDescriptors.h create mode 100644 usb/device/composite/MSDDFunctionDriver.c create mode 100644 usb/device/composite/MSDDFunctionDriver.h create mode 100644 usb/device/composite/MSDDFunctionDriverDescriptors.h create mode 100644 usb/device/composite/drv/CompositeCDCSerial.inf create mode 100644 usb/device/composite/drv/drv.dir create mode 100644 usb/device/core/USBD.h create mode 100644 usb/device/core/USBDCallbacks.h create mode 100644 usb/device/core/USBDCallbacks_Initialized.c create mode 100644 usb/device/core/USBDCallbacks_RequestReceived.c create mode 100644 usb/device/core/USBDCallbacks_Reset.c create mode 100644 usb/device/core/USBDCallbacks_Resumed.c create mode 100644 usb/device/core/USBDCallbacks_Suspended.c create mode 100644 usb/device/core/USBDDriver.c create mode 100644 usb/device/core/USBDDriver.h create mode 100644 usb/device/core/USBDDriverCallbacks.h create mode 100644 usb/device/core/USBDDriverCb_CfgChanged.c create mode 100644 usb/device/core/USBDDriverCb_IfSettingChanged.c create mode 100644 usb/device/core/USBDDriverDescriptors.h create mode 100644 usb/device/core/USBD_OTGHS.c create mode 100644 usb/device/core/USBD_UDP.c create mode 100644 usb/device/core/USBD_UDPHS.c create mode 100644 usb/device/core/core.dir create mode 100644 usb/device/device.dir create mode 100644 usb/device/hid-keyboard/HIDClassArch.png create mode 100644 usb/device/hid-keyboard/HIDDKeyboardCallbacks.h create mode 100644 usb/device/hid-keyboard/HIDDKeyboardCallbacks_LedsChanged.c create mode 100644 usb/device/hid-keyboard/HIDDKeyboardDriver.c create mode 100644 usb/device/hid-keyboard/HIDDKeyboardDriver.h create mode 100644 usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.c create mode 100644 usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.h create mode 100644 usb/device/hid-keyboard/HIDDKeyboardInputReport.c create mode 100644 usb/device/hid-keyboard/HIDDKeyboardInputReport.h create mode 100644 usb/device/hid-keyboard/HIDDKeyboardOutputReport.c create mode 100644 usb/device/hid-keyboard/HIDDKeyboardOutputReport.h create mode 100644 usb/device/hid-keyboard/hid-keyboard.dir create mode 100644 usb/device/hid-mouse/HIDDMouseDriver.c create mode 100644 usb/device/hid-mouse/HIDDMouseDriver.h create mode 100644 usb/device/hid-mouse/HIDDMouseDriverDescriptors.c create mode 100644 usb/device/hid-mouse/HIDDMouseDriverDescriptors.h create mode 100644 usb/device/hid-mouse/HIDDMouseInputReport.c create mode 100644 usb/device/hid-mouse/HIDDMouseInputReport.h create mode 100644 usb/device/hid-mouse/hid-mouse.dir create mode 100644 usb/device/hid-transfer/HIDDTransferDriver.c create mode 100644 usb/device/hid-transfer/HIDDTransferDriver.h create mode 100644 usb/device/hid-transfer/HIDDTransferDriverDesc.c create mode 100644 usb/device/hid-transfer/HIDDTransferDriverDesc.h create mode 100644 usb/device/hid-transfer/hid-transfer.dir create mode 100644 usb/device/massstorage/MSD.h create mode 100644 usb/device/massstorage/MSDAppArch.png create mode 100644 usb/device/massstorage/MSDDStateMachine.c create mode 100644 usb/device/massstorage/MSDDStateMachine.h create mode 100644 usb/device/massstorage/MSDDriver.c create mode 100644 usb/device/massstorage/MSDDriver.h create mode 100644 usb/device/massstorage/MSDDriverArch.png create mode 100644 usb/device/massstorage/MSDDriverClasses.png create mode 100644 usb/device/massstorage/MSDDriverDescriptors.c create mode 100644 usb/device/massstorage/MSDDriverDescriptors.h create mode 100644 usb/device/massstorage/MSDDriverStates.png create mode 100644 usb/device/massstorage/MSDIOFifo.c create mode 100644 usb/device/massstorage/MSDIOFifo.h create mode 100644 usb/device/massstorage/MSDLun.c create mode 100644 usb/device/massstorage/MSDLun.h create mode 100644 usb/device/massstorage/MSDMediaArch.png create mode 100644 usb/device/massstorage/SBC.h create mode 100644 usb/device/massstorage/SBCMethods.c create mode 100644 usb/device/massstorage/SBCMethods.h create mode 100644 usb/device/massstorage/massstorage.dir (limited to 'usb/device') diff --git a/usb/device/USBDCallbackInvocationFlowchart.png b/usb/device/USBDCallbackInvocationFlowchart.png new file mode 100644 index 0000000..af8028e Binary files /dev/null and b/usb/device/USBDCallbackInvocationFlowchart.png differ diff --git a/usb/device/USBDeviceStateDiagram.png b/usb/device/USBDeviceStateDiagram.png new file mode 100644 index 0000000..eacdd41 Binary files /dev/null and b/usb/device/USBDeviceStateDiagram.png differ diff --git a/usb/device/audio-looprec/AUDDLoopRecChannel.c b/usb/device/audio-looprec/AUDDLoopRecChannel.c new file mode 100644 index 0000000..c6d9a08 --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecChannel.c @@ -0,0 +1,129 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: AUDDLoopRecChannel implementation + + About: Purpose + Implementation of the AUDDLoopRecChannel functions. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDLoopRecChannel.h" +#include + +//------------------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------------------ +/* + Function: AUDDLoopRecChannel_MuteChanged + Callback triggered when the mute status of a channel changes. This is + a default implementation which does nothing and must be overriden. + + Parameters: + channel - Pointer to a AUDDLoopRecChannel instance. + muted - New mute status. +*/ +//__attribute__ ((weak)) void AUDDLoopRecChannel_MuteChanged( +// AUDDLoopRecChannel *channel, +// unsigned char muted) +//{ +// TRACE_DEBUG("MuteChanged(%d, %d) ", channel->number, muted); +//} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the member variables of an AUDDLoopRecChannel object to the +/// given values. +/// \param channel Pointer to an AUDDLoopRecChannel instance. +/// \param number Channel number in the audio function. +/// \param muted Indicates if the channel is muted. +//------------------------------------------------------------------------------ +void AUDDLoopRecChannel_Initialize(AUDDLoopRecChannel *channel, + unsigned char number, + unsigned char muted) +{ + channel->number = number; + channel->muted = muted; +} + +//------------------------------------------------------------------------------ +/// Indicates the number of a channel. +/// \param channel Pointer to an AUDDLoopRecChannel instance. +/// \return Channel number. +//------------------------------------------------------------------------------ +unsigned char AUDDLoopRecChannel_GetNumber(const AUDDLoopRecChannel *channel) +{ + return channel->number; +} + +//------------------------------------------------------------------------------ +/// Mutes the given channel and triggers the MuteChanged callback if +/// necessary. +/// \param channel Pointer to an AUDDLoopRecChannelInstance. +//------------------------------------------------------------------------------ +void AUDDLoopRecChannel_Mute(AUDDLoopRecChannel *channel) +{ + if (!channel->muted) { + + channel->muted = 1; + AUDDLoopRecChannel_MuteChanged(channel, 1); + } +} + +//------------------------------------------------------------------------------ +/// Unmutes the given channel and triggers the MuteChanged callback if +/// necessary. +/// \param channel Pointer to an AUDDLoopRecChannelInstance. +//------------------------------------------------------------------------------ +void AUDDLoopRecChannel_Unmute(AUDDLoopRecChannel *channel) +{ + if (channel->muted) { + + channel->muted = 0; + AUDDLoopRecChannel_MuteChanged(channel, 0); + } +} + +//------------------------------------------------------------------------------ +/// Indicates if the given channel is currently muted or not. +/// \param channel Pointer an AUDDLoopRecChannel instance. +/// \return 1 if the channel is muted; otherwise 0. +//------------------------------------------------------------------------------ +unsigned char AUDDLoopRecChannel_IsMuted(const AUDDLoopRecChannel *channel) +{ + return channel->muted; +} + diff --git a/usb/device/audio-looprec/AUDDLoopRecChannel.h b/usb/device/audio-looprec/AUDDLoopRecChannel.h new file mode 100644 index 0000000..6cfad6b --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecChannel.h @@ -0,0 +1,92 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !Purpose + + Manipulation of the channels of an USB audio LoopRec device. + + !Usage + + -# Initialize a AUDDLoopRecChannel instance using + AUDDLoopRecChannel_Initialize. + -# Retrieves the current status of a channel with the + AUDDLoopRecChannel_IsMuted method. + -# Re-implement the AUDDLoopRecChannel_MuteChanged callback to get + notified when the status of a channel changes. +*/ + +#ifndef AUDDLOOPRECCHANNEL_H +#define AUDDLOOPRECCHANNEL_H + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Modelizes a channel of an USB audio LoopRec device. +//------------------------------------------------------------------------------ +typedef struct { + + /// Zero-based channel number in the audio function. + unsigned char number; + /// Indicates if the channel is currently muted. + unsigned char muted; + +} AUDDLoopRecChannel; + +//------------------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------------------ + +extern void AUDDLoopRecChannel_MuteChanged(AUDDLoopRecChannel *channel, + unsigned char muted); + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void AUDDLoopRecChannel_Initialize(AUDDLoopRecChannel *channel, + unsigned char number, + unsigned char muted); + +extern unsigned char AUDDLoopRecChannel_GetNumber( + const AUDDLoopRecChannel *channel); + +extern void AUDDLoopRecChannel_Mute(AUDDLoopRecChannel *channel); + +extern void AUDDLoopRecChannel_Unmute(AUDDLoopRecChannel *channel); + +extern unsigned char AUDDLoopRecChannel_IsMuted( + const AUDDLoopRecChannel *channel); + +#endif //#ifndef AUDDLOOPRECCHANNEL_H + diff --git a/usb/device/audio-looprec/AUDDLoopRecDriver.c b/usb/device/audio-looprec/AUDDLoopRecDriver.c new file mode 100644 index 0000000..45534b5 --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecDriver.c @@ -0,0 +1,493 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDLoopRecDriver.h" +#include "AUDDLoopRecDriverDescriptors.h" +#include "AUDDLoopRecChannel.h" +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Audio LoopRec driver internal state. +//------------------------------------------------------------------------------ +typedef struct { + + /// USB device driver instance. + USBDDriver usbdDriver; + #if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + /// Multi-Buffer-Transfer List + USBDTransferBuffer * pMbl; + /// List Size + unsigned short listSize; + /// Current buffer + unsigned short currBuffer; + #endif + /// Callback for ISO IN + AUDDFrameTransferCallback fWrCallback; + /// List of AUDDLoopRecChannel instances for playback. + AUDDLoopRecChannel channels[AUDDLoopRecDriver_NUMCHANNELS+1]; + /// List of AUDDLoopRecChannel instances for record. + AUDDLoopRecChannel recChannels[1]; + +} AUDDLoopRecDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Global USB audio LoopRec driver instance. +static AUDDLoopRecDriver auddLoopRecDriver; +/// Array for storing the current setting of each interface. +static unsigned char auddLoopRecDriverInterfaces[3]; +/// Intermediate storage variable for the mute status of a channel. +static unsigned char muted; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback triggered after the new mute status of a channel has been read +/// by AUDDLoopRecDriver_SetFeatureCurrentValue. Changes the mute status +/// of the given channel accordingly. +/// \param channel Number of the channel whose mute status has changed. +//------------------------------------------------------------------------------ +static void AUDDLoopRecDriver_MuteReceived(unsigned int channel) +{ + AUDDLoopRecChannel * pChannel; + if ((unsigned char)(channel >> 8) == + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC) { + pChannel = &auddLoopRecDriver.recChannels[0]; + } + else { + pChannel = &auddLoopRecDriver.channels[channel]; + } + if (muted) { + + AUDDLoopRecChannel_Mute(pChannel); + } + else { + + AUDDLoopRecChannel_Unmute(pChannel); + } + + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sets the current value of a particular Feature control of a channel. +/// \param entity Entity number. +/// \param channel Channel number. +/// \param control Control selector value (see TODO). +/// \param length Length of the data containing the new value. +//------------------------------------------------------------------------------ +static void AUDDLoopRecDriver_SetFeatureCurrentValue(unsigned char entity, + unsigned char channel, + unsigned char control, + unsigned short length) +{ + TRACE_INFO_WP("sFeature "); + TRACE_DEBUG("\b(E%d, CS%d, CN%d, L%d) ", + entity, control, channel, length); + + // Check the the requested control is supported + // Control/channel combination not supported + if ((control != AUDFeatureUnitRequest_MUTE) || + (length != 1) || + ((entity == AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL) && + (channel > AUDDLoopRecDriver_NUMCHANNELS)) || + ((entity == AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC) && + (channel > 0))) { + + USBD_Stall(0); + } + // Mute control on master channel / speakerphone + else { + + unsigned int argument = channel | (entity << 8); + USBD_Read(0, // Endpoint #0 + &muted, + sizeof(muted), + (TransferCallback) AUDDLoopRecDriver_MuteReceived, + (void *) argument); + } +} + +//------------------------------------------------------------------------------ +/// Sends the current value of a particular channel Feature to the USB host. +/// \param entity Entity number. +/// \param channel Channel number. +/// \param control Control selector value (see TODO). +/// \param length Length of the data to return. +//------------------------------------------------------------------------------ +static void AUDDLoopRecDriver_GetFeatureCurrentValue(unsigned char entity, + unsigned char channel, + unsigned char control, + unsigned char length) +{ + TRACE_INFO_WP("gFeature "); + TRACE_DEBUG("\b(CS%d, CN%d, L%d) ", control, channel, length); + + // Check that the requested control is supported + // Master channel mute control + if ((control == AUDFeatureUnitRequest_MUTE) + && (channel < (AUDDLoopRecDriver_NUMCHANNELS+1)) + && (length == 1)) { + + if (entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) { + muted = auddLoopRecDriver.channels[channel].muted; + } + else { + muted = auddLoopRecDriver.recChannels[0].muted; + } + USBD_Write(0, &muted, sizeof(muted), 0, 0); + } + else { + + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Callback used by MBL transfer functions (USBD_Read & USBD_Write) to notify +/// that a transaction is complete. +/// \param pArg Pointer to callback arguments. +/// \param status USBD status. +/// \param nbBuffer Number of buffer that is processed. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_MblWriteCallback(void *pArg, + unsigned char status, + unsigned int nbBuffer) +{ + char rc = 1; + if (auddLoopRecDriver.fWrCallback) { + + rc = auddLoopRecDriver.fWrCallback(pArg, status); + } + if (rc && status == USBD_STATUS_PARTIAL_DONE) { + + while(nbBuffer --) { + USBD_MblReuse(AUDDLoopRecDriverDescriptors_DATAIN, + 0, 0); + } + } +} + +//------------------------------------------------------------------------------ +// Callback re-implementation +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Triggered when the USB host emits a new SETUP request. The request is +/// forward to . +/// \param request Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + AUDDLoopRecDriver_RequestHandler(request); +} +#endif + +//------------------------------------------------------------------------------ +/// Invoked whenever the active setting of an interface is changed by the +/// host. Changes the status of the third LED accordingly. +/// \param interface Interface number. +/// \param setting Newly active setting. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting) +{ + if ((interface == AUDDLoopRecDriverDescriptors_STREAMING) + && (setting == 0)) { + + LED_Clear(USBD_LEDOTHER); + } + else { + + LED_Set(USBD_LEDOTHER); + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an USB audio LoopRec device driver, as well as the underlying +/// USB controller. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_Initialize() +{ + // Reset callback function + auddLoopRecDriver.fWrCallback = 0; + + // Initialize LoopRec channels + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.channels[0]), + AUDDLoopRecDriver_MASTERCHANNEL, + 0); + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.channels[1]), + AUDDLoopRecDriver_LEFTCHANNEL, + 0); + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.channels[2]), + AUDDLoopRecDriver_RIGHTCHANNEL, + 0); + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.recChannels[0]), + AUDDLoopRecDriver_RECCHANNEL, + 0); + + // Initialize the USB driver + USBDDriver_Initialize(&(auddLoopRecDriver.usbdDriver), + &auddLoopRecDriverDescriptors, + auddLoopRecDriverInterfaces); + USBD_Init(); + + // Initialize the third LED to indicate when the audio interface is active + LED_Configure(USBD_LEDOTHER); +} + +//------------------------------------------------------------------------------ +/// Handles audio-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_RequestHandler(const USBGenericRequest *request) +{ + unsigned char entity; + unsigned char interface; + + TRACE_INFO_WP("NewReq "); + + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + // Check if the request is supported + switch (USBGenericRequest_GetRequest(request)) { + + case AUDGenericRequest_SETCUR: + TRACE_INFO_WP( + "sCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if (((entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) + ||(entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC)) + && (interface == AUDDLoopRecDriverDescriptors_CONTROL)) { + + AUDDLoopRecDriver_SetFeatureCurrentValue( + entity, + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + case AUDGenericRequest_GETCUR: + TRACE_INFO_WP( + "gCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if (((entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) + ||(entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC)) + && (interface == AUDDLoopRecDriverDescriptors_CONTROL)) { + + AUDDLoopRecDriver_GetFeatureCurrentValue( + entity, + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + default: + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // Forward request to the standard handler + USBDDriver_RequestHandler(&(auddLoopRecDriver.usbdDriver), request); + } + // Unsupported request type + else { + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Reads incoming audio data sent by the USB host into the provided +/// buffer. When the transfer is complete, an optional callback function is +/// invoked. +/// \param buffer Pointer to the data storage buffer. +/// \param length Size of the buffer in bytes. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer is started successfully; +/// otherwise an error code. +//------------------------------------------------------------------------------ +unsigned char AUDDLoopRecDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument) +{ + return USBD_Read(AUDDLoopRecDriverDescriptors_DATAOUT, + buffer, + length, + callback, + argument); +} + +//------------------------------------------------------------------------------ +/// Initialize Frame List for sending audio data. +/// \param pMultiBufferList Pointer to the transfer list to initialize. +/// \param pBufferList Pointer list to the buffers used for frames sending. +/// \param listSize Number of frames. +/// \param frameSize Size in number of bytes of each frame. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_SetupWriteMbl(void * pMultiBufferList, + unsigned char **pBufferList, + unsigned short listSize, + unsigned short frameSize) +{ + int i; + + SANITY_CHECK(pMultiBufferList && pBufferList && listSize && frameSize); + +#if (defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)) && defined(DMA) + // Initialize for circled DMA LLI + { + USBDDmaDescriptor * pLl = (USBDDmaDescriptor*)pMultiBufferList; + for (i = 0; ; i ++) { + pLl[i].pDataAddr = pBufferList[i]; + pLl[i].bufferLength = frameSize; + if (i < listSize - 1) { + pLl[i].pNxtDesc = &pLl[i + 1]; + } + else { + pLl[i].pNxtDesc = &pLl[0]; + break; + } + } + } +#else + // Initialize for circled multi-buffer-list + { + USBDTransferBuffer * pMbl = (USBDTransferBuffer*)pMultiBufferList; + for (i = 0; i < listSize; i ++) { + pMbl[i].pBuffer = pBufferList[i]; + pMbl[i].size = frameSize; + pMbl[i].transferred = 0; + pMbl[i].buffered = 0; + pMbl[i].remaining = frameSize; + } + } +#endif +} + +//------------------------------------------------------------------------------ +/// Start sending audio data, through MBL window. +/// When any frame is complete, an optional callback function is invoked, return +/// 1 of the callback cause the frame buffer re-used. +/// If callback not assigned, frames always reused. +/// \param buffer Pointer to the data storage buffer. +/// \param length Size of the buffer in bytes. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer is started successfully; +/// otherwise an error code. +//------------------------------------------------------------------------------ +unsigned char AUDDLoopRecDriver_Write(void * pMbl, + unsigned short listSize, + AUDDFrameTransferCallback callback, + void * argument) +{ + auddLoopRecDriver.fWrCallback = callback; + return USBD_MblWrite(AUDDLoopRecDriverDescriptors_DATAIN, + pMbl, + listSize, + 1, + 0, + AUDDLoopRecDriver_MblWriteCallback, + argument); +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void AUDDLoopRecDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&auddLoopRecDriver.usbdDriver)) { + + USBD_RemoteWakeUp(); + } +} + + diff --git a/usb/device/audio-looprec/AUDDLoopRecDriver.h b/usb/device/audio-looprec/AUDDLoopRecDriver.h new file mode 100644 index 0000000..8db30a1 --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecDriver.h @@ -0,0 +1,163 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !Purpose + + Definition of a USB Audio Loop Record Driver with two playback channels + and one record channel. + + !Usage + + -# Enable and setup USB related pins (see pio & board.h). + -# Configure the USB Audio Loop Record driver using + AUDDLoopRecDriver_Initialize + -# To get %audio stream frames from host, use + AUDDLoopRecDriver_Read + -# To send %audio sampling stream to host, use + AUDDLoopRecDriver_Write + +*/ + +#ifndef AUDDLoopRecDriver_H +#define AUDDLoopRecDriver_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Loop Record stream information" +/// +/// This page lists codes for USB Audio Loop Record stream information. +/// +/// !Code +/// - AUDDLoopRecDriver_SAMPLERATE +/// - AUDDLoopRecDriver_NUMCHANNELS +/// - AUDDLoopRecDriver_BYTESPERSAMPLE +/// - AUDDLoopRecDriver_BITSPERSAMPLE +/// - AUDDLoopRecDriver_SAMPLESPERFRAME +/// - AUDDLoopRecDriver_BYTESPERFRAME + +#if defined(at91sam7s) || defined(at91sam9xe) + /// Sample rate in Hz. + #define AUDDLoopRecDriver_SAMPLERATE 32000 + /// Number of channels in audio stream. + #define AUDDLoopRecDriver_NUMCHANNELS 1 + /// Number of bytes in one sample. + #define AUDDLoopRecDriver_BYTESPERSAMPLE 2 +#else + /// Sample rate in Hz. + #define AUDDLoopRecDriver_SAMPLERATE 48000 + /// Number of channels in audio stream. + #define AUDDLoopRecDriver_NUMCHANNELS 2 + /// Number of bytes in one sample. + #define AUDDLoopRecDriver_BYTESPERSAMPLE 2 +#endif +/// Number of bits in one sample. +#define AUDDLoopRecDriver_BITSPERSAMPLE (AUDDLoopRecDriver_BYTESPERSAMPLE * 8) +/// Number of bytes in one USB subframe. +#define AUDDLoopRecDriver_BYTESPERSUBFRAME (AUDDLoopRecDriver_NUMCHANNELS * \ + AUDDLoopRecDriver_BYTESPERSAMPLE) +/// Number of samples in one USB frame. +#define AUDDLoopRecDriver_SAMPLESPERFRAME (AUDDLoopRecDriver_SAMPLERATE / 1000 \ + * AUDDLoopRecDriver_NUMCHANNELS) +/// Number of bytes in one USB frame. +#define AUDDLoopRecDriver_BYTESPERFRAME (AUDDLoopRecDriver_SAMPLESPERFRAME * \ + AUDDLoopRecDriver_BYTESPERSAMPLE) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Loop Record Channel Numbers" +/// +/// This page lists codes for USB Audio Loop Record channel numbers. +/// +/// !Playback channel numbers +/// - AUDDLoopRecDriver_MASTERCHANNEL +/// - AUDDLoopRecDriver_LEFTCHANNEL +/// - AUDDLoopRecDriver_RIGHTCHANNEL +/// +/// !Record channel number +/// - AUDDLoopRecDriver_RECCHANNEL + +/// Master channel of playback. +#define AUDDLoopRecDriver_MASTERCHANNEL 0 +/// Front left channel of playback. +#define AUDDLoopRecDriver_LEFTCHANNEL 1 +/// Front right channel of playback. +#define AUDDLoopRecDriver_RIGHTCHANNEL 2 +/// Channel of record. +#define AUDDLoopRecDriver_RECCHANNEL 0 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback function for audio frames. +/// \param pArg Pointer to transfer arguments. +/// \param status Frame transfer status. +/// \return 1 to continue, 0 to stop frames. +//------------------------------------------------------------------------------ +typedef char (*AUDDFrameTransferCallback)(void* pArg, char status); + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void AUDDLoopRecDriver_Initialize(); + +extern void AUDDLoopRecDriver_RequestHandler(const USBGenericRequest *request); + +extern unsigned char AUDDLoopRecDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument); + +extern void AUDDLoopRecDriver_SetupWriteMbl(void * pMultiBufferList, + unsigned char **pBufferList, + unsigned short listSize, + unsigned short frameSize); + +extern unsigned char AUDDLoopRecDriver_Write(void * pMbl, + unsigned short listSize, + AUDDFrameTransferCallback callback, + void *argument); + +extern void AUDDLoopRecDriver_RemoteWakeUp(void); + +#endif //#ifndef AUDDLoopRecDriver_H + diff --git a/usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.c b/usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.c new file mode 100644 index 0000000..9f961fd --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.c @@ -0,0 +1,1461 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDLoopRecDriverDescriptors.h" +#include "AUDDLoopRecDriver.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Loop Record Device Codes" +/// +/// This page lists the %device IDs and release number of the USB Audio Loop Record +/// %device. +/// +/// !Codes +/// - AUDDLoopRecDriverDescriptors_VENDORID +/// - AUDDLoopRecDriverDescriptors_PRODUCTID +/// - AUDDLoopRecDriverDescriptors_RELEASE + +/// Device vendor ID. +#define AUDDLoopRecDriverDescriptors_VENDORID 0x03EB +/// Device product ID. +#define AUDDLoopRecDriverDescriptors_PRODUCTID 0x6128 +/// Device release number in BCD format. +#define AUDDLoopRecDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Audio control header descriptor with one slave interface. +//------------------------------------------------------------------------------ +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// Header descriptor with 1 interface. +//------------------------------------------------------------------------------ +typedef struct { + + /// Header descriptor. + AUDHeaderDescriptor header; + /// Id of the first grouped interface - Speaker. + unsigned char bInterface0; + /// Id of the second grouped interface - Speakerphone. + unsigned char bInterface1; + +} __attribute__ ((packed)) AUDHeaderDescriptor2; // GCC + +//------------------------------------------------------------------------------ +/// Feature unit descriptor with 3 channel controls (master, right, left). +//------------------------------------------------------------------------------ +typedef struct { + + /// Feature unit descriptor. + AUDFeatureUnitDescriptor feature; + /// Available controls for each channel. + unsigned char bmaControls[3]; + /// Index of a string descriptor for the feature unit. + unsigned char iFeature; + +} __attribute__ ((packed)) AUDFeatureUnitDescriptor3; // GCC + +//------------------------------------------------------------------------------ +/// List of descriptors for detailling the audio control interface of a +/// device using a USB Audio Loop Recorder driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Header descriptor (with one slave interface). + AUDHeaderDescriptor2 header; + /// Input terminal descriptor. + AUDInputTerminalDescriptor inputLoopRec; + /// Output terminal descriptor. + AUDOutputTerminalDescriptor outputLoopRec; + /// Feature unit descriptor - LoopRec. + AUDFeatureUnitDescriptor3 featureLoopRec; + /// Input terminal descriptor. + AUDInputTerminalDescriptor inputRec; + /// Output terminal descriptor. + AUDOutputTerminalDescriptor outputRec; + /// Feature unit descriptor - LoopRecphone. + AUDFeatureUnitDescriptor3 featureRec; + +} __attribute__ ((packed)) AUDDLoopRecDriverAudioControlDescriptors; // GCC + +//------------------------------------------------------------------------------ +/// Format type I descriptor with one discrete sampling frequency. +//------------------------------------------------------------------------------ +typedef struct { + + /// Format type I descriptor. + AUDFormatTypeOneDescriptor formatType; + /// Sampling frequency in Hz. + unsigned char tSamFreq[3]; + +} __attribute__ ((packed)) AUDFormatTypeOneDescriptor1; // GCC + +//------------------------------------------------------------------------------ +/// Holds a list of descriptors returned as part of the configuration of +/// a USB Audio Loop Record device. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard configuration. + USBConfigurationDescriptor configuration; + /// Audio control interface. + USBInterfaceDescriptor control; + /// Descriptors for the audio control interface. + AUDDLoopRecDriverAudioControlDescriptors controlDescriptors; + //- AUDIO OUT + /// Streaming out interface descriptor (with no endpoint, required). + USBInterfaceDescriptor streamingOutNoIsochronous; + /// Streaming out interface descriptor. + USBInterfaceDescriptor streamingOut; + /// Audio class descriptor for the streaming out interface. + AUDStreamingInterfaceDescriptor streamingOutClass; + /// Stream format descriptor. + AUDFormatTypeOneDescriptor1 streamingOutFormatType; + /// Streaming out endpoint descriptor. + AUDEndpointDescriptor streamingOutEndpoint; + /// Audio class descriptor for the streaming out endpoint. + AUDDataEndpointDescriptor streamingOutDataEndpoint; + //- AUDIO IN + /// Streaming in interface descriptor (with no endpoint, required). + USBInterfaceDescriptor streamingInNoIsochronous; + /// Streaming in interface descriptor. + USBInterfaceDescriptor streamingIn; + /// Audio class descriptor for the streaming in interface. + AUDStreamingInterfaceDescriptor streamingInClass; + /// Stream format descriptor. + AUDFormatTypeOneDescriptor1 streamingInFormatType; + /// Streaming in endpoint descriptor. + AUDEndpointDescriptor streamingInEndpoint; + /// Audio class descriptor for the streaming in endpoint. + AUDDataEndpointDescriptor streamingInDataEndpoint; + +} __attribute__ ((packed)) AUDDLoopRecDriverConfigurationDescriptors; // GCC + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Device descriptor for a USB Audio Loop Record driver. +const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + AUDDeviceDescriptor_CLASS, + AUDDeviceDescriptor_SUBCLASS, + AUDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + AUDDLoopRecDriverDescriptors_VENDORID, + AUDDLoopRecDriverDescriptors_PRODUCTID, + AUDDLoopRecDriverDescriptors_RELEASE, + 1, // Manufacturer string descriptor index + 2, // Product string descriptor index + 3, // Index of serial number string descriptor + 1 // One possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + AUDDeviceDescriptor_CLASS, + AUDDeviceDescriptor_SUBCLASS, + AUDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +/// HS Configuration descriptors for a USB Audio Loop Record driver. +const AUDDLoopRecDriverConfigurationDescriptors hsConfigurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(AUDDLoopRecDriverConfigurationDescriptors), + 3, // This configuration has 3 interfaces + 1, // This is configuration #1 + 0, // No string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor2), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDLoopRecDriverAudioControlDescriptors), + 2 // Two streaming interface + }, + AUDDLoopRecDriverDescriptors_STREAMING, + AUDDLoopRecDriverDescriptors_STREAMINGIN + }, + // Input terminal descriptor ( speaker ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speaker ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor ( speaker ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + }, + // Input terminal descriptor ( speakerphone ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDInputTerminalDescriptor_SPEAKERPHONE, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speakerphone ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDOutputTerminalDescriptor_USBTREAMING, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + 0 // No string descriptor + }, + // Feature unit descriptor ( speakerphone ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + 1 + }, + { + AUDFeatureUnitDescriptor_MUTE, // Mic controls + 0, + 0 + }, + 0 // No string descriptor + } + }, + // - AUIDO OUT + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDDLoopRecDriverDescriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_HS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + }, + //- AUDIO IN + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + AUDDLoopRecDriverDescriptors_DATAIN), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_HS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + } +}; + +/// HS Other Speed Configuration descriptors for a USB Audio Loop Record driver. +const AUDDLoopRecDriverConfigurationDescriptors + fsOtherSpeedConfigurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(AUDDLoopRecDriverConfigurationDescriptors), + 3, // This configuration has 3 interfaces + 1, // This is configuration #1 + 0, // No string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor2), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDLoopRecDriverAudioControlDescriptors), + 2 // Two streaming interface + }, + AUDDLoopRecDriverDescriptors_STREAMING, + AUDDLoopRecDriverDescriptors_STREAMINGIN + }, + // Input terminal descriptor ( speaker ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speaker ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor ( speaker ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + }, + // Input terminal descriptor ( speakerphone ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDInputTerminalDescriptor_SPEAKERPHONE, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speakerphone ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDOutputTerminalDescriptor_USBTREAMING, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + 0 // No string descriptor + }, + // Feature unit descriptor ( speakerphone ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + 1 + }, + { + AUDFeatureUnitDescriptor_MUTE, // Mic controls + 0, + 0 + }, + 0 // No string descriptor + } + }, + // - AUIDO OUT + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDDLoopRecDriverDescriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_HS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + }, + //- AUDIO IN + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + AUDDLoopRecDriverDescriptors_DATAIN), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_HS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + } +}; + +/// HS Other Speed Configuration descriptors. +const AUDDLoopRecDriverConfigurationDescriptors + hsOtherSpeedConfigurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(AUDDLoopRecDriverConfigurationDescriptors), + 3, // This configuration has 3 interfaces + 1, // This is configuration #1 + 0, // No string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor2), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDLoopRecDriverAudioControlDescriptors), + 2 // Two streaming interface + }, + AUDDLoopRecDriverDescriptors_STREAMING, + AUDDLoopRecDriverDescriptors_STREAMINGIN + }, + // Input terminal descriptor ( speaker ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speaker ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor ( speaker ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + }, + // Input terminal descriptor ( speakerphone ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDInputTerminalDescriptor_SPEAKERPHONE, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speakerphone ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDOutputTerminalDescriptor_USBTREAMING, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + 0 // No string descriptor + }, + // Feature unit descriptor ( speakerphone ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + 1 + }, + { + AUDFeatureUnitDescriptor_MUTE, // Mic controls + 0, + 0 + }, + 0 // No string descriptor + } + }, + // - AUIDO OUT + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDDLoopRecDriverDescriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_FS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + }, + //- AUDIO IN + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + AUDDLoopRecDriverDescriptors_DATAIN), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_FS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + } +}; + +#endif // defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// FS Configuration descriptors for a USB Audio Loop Record driver. +const AUDDLoopRecDriverConfigurationDescriptors fsConfigurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(AUDDLoopRecDriverConfigurationDescriptors), + 3, // This configuration has 3 interfaces + 1, // This is configuration #1 + 0, // No string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor2), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDLoopRecDriverAudioControlDescriptors), + 2 // Two streaming interface + }, + AUDDLoopRecDriverDescriptors_STREAMING, + AUDDLoopRecDriverDescriptors_STREAMINGIN + }, + // Input terminal descriptor ( speaker ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speaker ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor ( speaker ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + }, + // Input terminal descriptor ( speakerphone ) + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDInputTerminalDescriptor_SPEAKERPHONE, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDDLoopRecDriver_NUMCHANNELS, + #if AUDDLoopRecDriver_NUMCHANNELS > 1 + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + #else + 0, // Mono sets no position bits. + #endif + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor ( speakerphone ) + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + AUDOutputTerminalDescriptor_USBTREAMING, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + 0 // No string descriptor + }, + // Feature unit descriptor ( speakerphone ) + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC, + 1 + }, + { + AUDFeatureUnitDescriptor_MUTE, // Mic controls + 0, + 0 + }, + 0 // No string descriptor + } + }, + // - AUIDO OUT + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDDLoopRecDriverDescriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_FS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + }, + //- AUDIO IN + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDLoopRecDriverDescriptors_STREAMINGIN, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDLoopRecDriver_NUMCHANNELS, + AUDDLoopRecDriver_BYTESPERSAMPLE, + AUDDLoopRecDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDLoopRecDriver_SAMPLERATE & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDLoopRecDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + AUDDLoopRecDriverDescriptors_DATAIN), + USBEndpointDescriptor_ISOCHRONOUS, + AUDDLoopRecDriver_BYTESPERFRAME, + AUDDLoopRecDriverDescriptors_FS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + } +}; + +/// String descriptor with the supported languages. +const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(15), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('k'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('a'), + USBStringDescriptor_UNICODE('k'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('r') +}; + +/// Product serial number. +const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB Audio Loop Recorder device driver. +const USBDDriverDescriptors auddLoopRecDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &fsConfigurationDescriptors, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &fsOtherSpeedConfigurationDescriptors, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &hsConfigurationDescriptors, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &hsOtherSpeedConfigurationDescriptors, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; + diff --git a/usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.h b/usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.h new file mode 100644 index 0000000..4b333d7 --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecDriverDescriptors.h @@ -0,0 +1,148 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Declaration of the descriptors required by a USB audio speaker driver. + + !!!Usage + + -# Initialize a USBDDriver instance using the + auddSpeakerDriverDescriptors list. +*/ + +#ifndef AUDDLOOPRECDESCRIPTORS_H +#define AUDDLOOPRECDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Endpoint Numbers" +/// +/// This page lists the endpoint number settings for USB Audio Speaker device. +/// +/// !Endpoints +/// - AUDDLoopRecDriverDescriptors_DATAOUT +/// - AUDDLoopRecDriverDescriptors_DATAIN +/// - AUDDLoopRecDriverDescriptors_HS_INTERVAL +/// - AUDDLoopRecDriverDescriptors_FS_INTERVAL + +#if defined(at91sam7s) || defined(at91sam9xe) + /// Data out endpoint number, size 64B. + #define AUDDLoopRecDriverDescriptors_DATAOUT 0x01 + /// Data in endpoint number, size 64B + #define AUDDLoopRecDriverDescriptors_DATAIN 0x02 +#elif defined(CHIP_USB_UDP) + /// Data out endpoint number, size 192B. + #define AUDDLoopRecDriverDescriptors_DATAOUT 0x04 + /// Data in endpoint number, size 192B. + #define AUDDLoopRecDriverDescriptors_DATAIN 0x05 +#elif defined(at91sam9m10ek) || defined(at91sam9m10ekes) + /// Data out endpoint number, size 192B. + #define AUDDLoopRecDriverDescriptors_DATAOUT 0x01 + /// Data in endpoint number, size 192B + #define AUDDLoopRecDriverDescriptors_DATAIN 0x06 +#else + /// Data out endpoint number, size 192B. + #define AUDDLoopRecDriverDescriptors_DATAOUT 0x05 + /// Data in endpoint number, size 192B + #define AUDDLoopRecDriverDescriptors_DATAIN 0x06 +#endif + +/// Endpoint polling interval 2^(x-1) * 125us +#define AUDDLoopRecDriverDescriptors_HS_INTERVAL 0x04 +/// Endpoint polling interval 2^(x-1) * ms +#define AUDDLoopRecDriverDescriptors_FS_INTERVAL 0x01 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Interface IDs" +/// +/// This page lists the interface numbers for USB Audio Speaker device. +/// +/// !Interfaces +/// - AUDDLoopRecDriverDescriptors_CONTROL +/// - AUDDLoopRecDriverDescriptors_STREAMING +/// - AUDDLoopRecDriverDescriptors_STREAMINGIN + +/// Audio control interface ID. +#define AUDDLoopRecDriverDescriptors_CONTROL 0 +/// Audio streaming interface ID (OUT, for playback). +#define AUDDLoopRecDriverDescriptors_STREAMING 1 +/// Audio streaming interface ID (IN, for record). +#define AUDDLoopRecDriverDescriptors_STREAMINGIN 2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Entity IDs" +/// +/// This page lists the entity IDs for USB Audio Speaker device. +/// +/// !Entities +/// - AUDDLoopRecDriverDescriptors_INPUTTERMINAL +/// - AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL +/// - AUDDLoopRecDriverDescriptors_FEATUREUNIT +/// - AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC +/// - AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC +/// - AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC + +/// Playback input terminal ID. +#define AUDDLoopRecDriverDescriptors_INPUTTERMINAL 0 +/// Playback output terminal ID. +#define AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL 1 +/// Playback feature unit ID. +#define AUDDLoopRecDriverDescriptors_FEATUREUNIT 2 +/// Record input terminal ID. +#define AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC 3 +/// Record output terminal ID. +#define AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC 4 +/// Record feature unit ID +#define AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC 5 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +extern const USBDDriverDescriptors auddLoopRecDriverDescriptors; + +#endif //#ifndef AUDDLOOPRECDESCRIPTORS_H + diff --git a/usb/device/audio-looprec/audio-looprec.dir b/usb/device/audio-looprec/audio-looprec.dir new file mode 100644 index 0000000..bbd6c6a --- /dev/null +++ b/usb/device/audio-looprec/audio-looprec.dir @@ -0,0 +1,51 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB Audio +/// Class device - USB audio-looprec demo, to implement an USB desktop speaker +/// which echos audio stream back to host. +/// +/// !!!Contents +/// There are two things for the implement of the audio-speaker device driver: +/// - Implement the audio-looprec driver structs and functions for the device, +/// to initialize, to handle audio class specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// endpoints, +/// - Create the audio-speaker device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the device as a USB audio "desktop speaker" device. +/// +/// This project is simply addon to the audio speaker demo. For more +/// information, please refer to "USB Audio Speaker Device". +//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/usb/device/audio-speaker/AUDDSpeakerChannel.c b/usb/device/audio-speaker/AUDDSpeakerChannel.c new file mode 100644 index 0000000..30f39c5 --- /dev/null +++ b/usb/device/audio-speaker/AUDDSpeakerChannel.c @@ -0,0 +1,129 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: AUDDSpeakerChannel implementation + + About: Purpose + Implementation of the AUDDSpeakerChannel functions. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDSpeakerChannel.h" +#include + +//------------------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------------------ +/* + Function: AUDDSpeakerChannel_MuteChanged + Callback triggered when the mute status of a channel changes. This is + a default implementation which does nothing and must be overriden. + + Parameters: + channel - Pointer to a AUDDSpeakerChannel instance. + muted - New mute status. +*/ +//__attribute__ ((weak)) void AUDDSpeakerChannel_MuteChanged( +// AUDDSpeakerChannel *channel, +// unsigned char muted) +//{ +// TRACE_DEBUG("MuteChanged(%d, %d) ", channel->number, muted); +//} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the member variables of an AUDDSpeakerChannel object to the +/// given values. +/// \param channel Pointer to an AUDDSpeakerChannel instance. +/// \param number Channel number in the audio function. +/// \param muted Indicates if the channel is muted. +//------------------------------------------------------------------------------ +void AUDDSpeakerChannel_Initialize(AUDDSpeakerChannel *channel, + unsigned char number, + unsigned char muted) +{ + channel->number = number; + channel->muted = muted; +} + +//------------------------------------------------------------------------------ +/// Indicates the number of a channel. +/// \param channel Pointer to an AUDDSpeakerChannel instance. +/// \return Channel number. +//------------------------------------------------------------------------------ +unsigned char AUDDSpeakerChannel_GetNumber(const AUDDSpeakerChannel *channel) +{ + return channel->number; +} + +//------------------------------------------------------------------------------ +/// Mutes the given channel and triggers the MuteChanged callback if +/// necessary. +/// \param channel Pointer to an AUDDSpeakerChannelInstance. +//------------------------------------------------------------------------------ +void AUDDSpeakerChannel_Mute(AUDDSpeakerChannel *channel) +{ + if (!channel->muted) { + + channel->muted = 1; + AUDDSpeakerChannel_MuteChanged(channel, 1); + } +} + +//------------------------------------------------------------------------------ +/// Unmutes the given channel and triggers the MuteChanged callback if +/// necessary. +/// \param channel Pointer to an AUDDSpeakerChannelInstance. +//------------------------------------------------------------------------------ +void AUDDSpeakerChannel_Unmute(AUDDSpeakerChannel *channel) +{ + if (channel->muted) { + + channel->muted = 0; + AUDDSpeakerChannel_MuteChanged(channel, 0); + } +} + +//------------------------------------------------------------------------------ +/// Indicates if the given channel is currently muted or not. +/// \param channel Pointer an AUDDSpeakerChannel instance. +/// \return 1 if the channel is muted; otherwise 0. +//------------------------------------------------------------------------------ +unsigned char AUDDSpeakerChannel_IsMuted(const AUDDSpeakerChannel *channel) +{ + return channel->muted; +} + diff --git a/usb/device/audio-speaker/AUDDSpeakerChannel.h b/usb/device/audio-speaker/AUDDSpeakerChannel.h new file mode 100644 index 0000000..5eeca7e --- /dev/null +++ b/usb/device/audio-speaker/AUDDSpeakerChannel.h @@ -0,0 +1,92 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !Purpose + + Manipulation of the channels of an USB audio speaker device. + + !Usage + + -# Initialize a AUDDSpeakerChannel instance using + AUDDSpeakerChannel_Initialize. + -# Retrieves the current status of a channel with the + AUDDSpeakerChannel_IsMuted method. + -# Re-implement the AUDDSpeakerChannel_MuteChanged callback to get + notified when the status of a channel changes. +*/ + +#ifndef AUDDSPEAKERCHANNEL_H +#define AUDDSPEAKERCHANNEL_H + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Modelizes a channel of an USB audio speaker device. +//------------------------------------------------------------------------------ +typedef struct { + + /// Zero-based channel number in the audio function. + unsigned char number; + /// Indicates if the channel is currently muted. + unsigned char muted; + +} AUDDSpeakerChannel; + +//------------------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------------------ + +extern void AUDDSpeakerChannel_MuteChanged(AUDDSpeakerChannel *channel, + unsigned char muted); + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void AUDDSpeakerChannel_Initialize(AUDDSpeakerChannel *channel, + unsigned char number, + unsigned char muted); + +extern unsigned char AUDDSpeakerChannel_GetNumber( + const AUDDSpeakerChannel *channel); + +extern void AUDDSpeakerChannel_Mute(AUDDSpeakerChannel *channel); + +extern void AUDDSpeakerChannel_Unmute(AUDDSpeakerChannel *channel); + +extern unsigned char AUDDSpeakerChannel_IsMuted( + const AUDDSpeakerChannel *channel); + +#endif //#ifndef AUDDSPEAKERCHANNEL_H + diff --git a/usb/device/audio-speaker/AUDDSpeakerDriver.c b/usb/device/audio-speaker/AUDDSpeakerDriver.c new file mode 100644 index 0000000..7ca58bd --- /dev/null +++ b/usb/device/audio-speaker/AUDDSpeakerDriver.c @@ -0,0 +1,378 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDSpeakerDriver.h" +#include "AUDDSpeakerDriverDescriptors.h" +#include "AUDDSpeakerChannel.h" +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Audio speaker driver internal state. +//------------------------------------------------------------------------------ +typedef struct { + + /// USB device driver instance. + USBDDriver usbdDriver; + /// List of AUDDSpeakerChannel instances for playback. + AUDDSpeakerChannel channels[AUDDSpeakerDriver_NUMCHANNELS+1]; + +} AUDDSpeakerDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Global USB audio speaker driver instance. +static AUDDSpeakerDriver auddSpeakerDriver; +/// Array for storing the current setting of each interface. +static unsigned char auddSpeakerDriverInterfaces[2]; +/// Intermediate storage variable for the mute status of a channel. +static unsigned char muted; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback triggered after the new mute status of a channel has been read +/// by AUDDSpeakerDriver_SetFeatureCurrentValue. Changes the mute status +/// of the given channel accordingly. +/// \param channel Number of the channel whose mute status has changed. +//------------------------------------------------------------------------------ +static void AUDDSpeakerDriver_MuteReceived(unsigned int channel) +{ + if (muted) { + + AUDDSpeakerChannel_Mute(&(auddSpeakerDriver.channels[channel])); + } + else { + + AUDDSpeakerChannel_Unmute(&(auddSpeakerDriver.channels[channel])); + } + + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sets the current value of a particular Feature control of a channel. +/// \param entity Entity number. +/// \param channel Channel number. +/// \param control Control selector value (see TODO). +/// \param length Length of the data containing the new value. +//------------------------------------------------------------------------------ +static void AUDDSpeakerDriver_SetFeatureCurrentValue(unsigned char entity, + unsigned char channel, + unsigned char control, + unsigned short length) +{ + TRACE_INFO_WP("sFeature "); + TRACE_DEBUG("\b(E%d, CS%d, CN%d, L%d) ", + entity, control, channel, length); + + // Check the the requested control is supported + // Control/channel combination not supported + if ((control == AUDFeatureUnitRequest_MUTE) + && (channel < (AUDDSpeakerDriver_NUMCHANNELS+1)) + && (length == 1)) { + + unsigned int argument = channel; // Avoids compiler warning + USBD_Read(0, // Endpoint #0 + &muted, + sizeof(muted), + (TransferCallback) AUDDSpeakerDriver_MuteReceived, + (void *) argument); + } + // Control/channel combination not supported + else { + + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Sends the current value of a particular channel Feature to the USB host. +/// \param entity Entity number. +/// \param channel Channel number. +/// \param control Control selector value (see TODO). +/// \param length Length of the data to return. +//------------------------------------------------------------------------------ +static void AUDDSpeakerDriver_GetFeatureCurrentValue(unsigned char entity, + unsigned char channel, + unsigned char control, + unsigned char length) +{ + TRACE_INFO_WP("gFeature "); + TRACE_DEBUG("\b(CS%d, CN%d, L%d) ", control, channel, length); + + // Check that the requested control is supported + // Master channel mute control + if ((control == AUDFeatureUnitRequest_MUTE) + && (channel < (AUDDSpeakerDriver_NUMCHANNELS+1)) + && (length == 1)) { + + muted = AUDDSpeakerChannel_IsMuted(&(auddSpeakerDriver.channels[channel])); + USBD_Write(0, &muted, sizeof(muted), 0, 0); + } + else { + + USBD_Stall(0); + } +} + +///* +// Function: AUDDSpeakerDriver_SetInterface +// Changes the current active alternate setting of the given interface. +// +// Parameters: +// +//*/ +//TRACE_DEBUG_M("SetInterface(%d,%d) ", setup->wIndex, setup->wValue); +// +// /* Check that interface is AudioStreaming OUT */ +// if (setup->wIndex == INTERFACE_ID_AUDIOSTREAMING_OUT) { +// +// /* Check desired alternate setting */ +// switch (setup->wValue) { +// case 0: +// case 1: +// if (speakerDriver->isOutStreamEnabled != setup->wValue) { +// +// speakerDriver->isOutStreamEnabled = setup->wValue; +// SPK_OutStreamStatusChanged(speakerDriver); +// LED_SetGlowing(LED_OTHER, setup->wValue); +// } +// USB_SendZLP0(usbDriver, 0, 0); +// break; +// +// default: +// USB_Stall(usbDriver, 0); +// } +// } +// else { +// +// USB_Stall(usbDriver, 0); +// } +// + +//------------------------------------------------------------------------------ +// Callback re-implementation +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Triggered when the USB host emits a new SETUP request. The request is +/// forward to . +/// \param request Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + AUDDSpeakerDriver_RequestHandler(request); +} +#endif + +//------------------------------------------------------------------------------ +/// Invoked whenever the active setting of an interface is changed by the +/// host. Changes the status of the third LED accordingly. +/// \param interface Interface number. +/// \param setting Newly active setting. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting) +{ + if ((interface == AUDDSpeakerDriverDescriptors_STREAMING) + && (setting == 0)) { + + LED_Clear(USBD_LEDOTHER); + } + else { + + LED_Set(USBD_LEDOTHER); + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an USB audio speaker device driver, as well as the underlying +/// USB controller. +//------------------------------------------------------------------------------ +void AUDDSpeakerDriver_Initialize() +{ + // Initialize speaker channels + AUDDSpeakerChannel_Initialize(&(auddSpeakerDriver.channels[0]), + AUDDSpeakerDriver_MASTERCHANNEL, + 0); + AUDDSpeakerChannel_Initialize(&(auddSpeakerDriver.channels[1]), + AUDDSpeakerDriver_LEFTCHANNEL, + 0); + AUDDSpeakerChannel_Initialize(&(auddSpeakerDriver.channels[2]), + AUDDSpeakerDriver_RIGHTCHANNEL, + 0); + + // Initialize the USB driver + USBDDriver_Initialize(&(auddSpeakerDriver.usbdDriver), + &auddSpeakerDriverDescriptors, + auddSpeakerDriverInterfaces); + USBD_Init(); + + // Initialize the third LED to indicate when the audio interface is active + LED_Configure(USBD_LEDOTHER); +} + +//------------------------------------------------------------------------------ +/// Handles audio-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +void AUDDSpeakerDriver_RequestHandler(const USBGenericRequest *request) +{ + unsigned char entity; + unsigned char interface; + + TRACE_INFO_WP("NewReq "); + + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + // Check if the request is supported + switch (USBGenericRequest_GetRequest(request)) { + + case AUDGenericRequest_SETCUR: + TRACE_INFO_WP( + "sCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if ((entity == AUDDSpeakerDriverDescriptors_FEATUREUNIT) + && (interface == AUDDSpeakerDriverDescriptors_CONTROL)) { + + AUDDSpeakerDriver_SetFeatureCurrentValue( + entity, + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDSpeakerDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + case AUDGenericRequest_GETCUR: + TRACE_INFO_WP( + "gCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if ((entity == AUDDSpeakerDriverDescriptors_FEATUREUNIT) + && (interface == AUDDSpeakerDriverDescriptors_CONTROL)) { + + AUDDSpeakerDriver_GetFeatureCurrentValue( + entity, + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDSpeakerDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + default: + + TRACE_WARNING( + "AUDDSpeakerDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // Forward request to the standard handler + USBDDriver_RequestHandler(&(auddSpeakerDriver.usbdDriver), request); + } + // Unsupported request type + else { + + TRACE_WARNING( + "AUDDSpeakerDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Reads incoming audio data sent by the USB host into the provided +/// buffer. When the transfer is complete, an optional callback function is +/// invoked. +/// \param buffer Pointer to the data storage buffer. +/// \param length Size of the buffer in bytes. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer is started successfully; +/// otherwise an error code. +//------------------------------------------------------------------------------ +unsigned char AUDDSpeakerDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument) +{ + return USBD_Read(AUDDSpeakerDriverDescriptors_DATAOUT, + buffer, + length, + callback, + argument); +} + diff --git a/usb/device/audio-speaker/AUDDSpeakerDriver.h b/usb/device/audio-speaker/AUDDSpeakerDriver.h new file mode 100644 index 0000000..133d6df --- /dev/null +++ b/usb/device/audio-speaker/AUDDSpeakerDriver.h @@ -0,0 +1,201 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !Purpose + + Definition of a USB Audio Speaker Driver with two playback channels and one + record channel. + + !Usage + + -# Enable and setup USB related pins (see pio & board.h). + -# Configure the USB Audio Speaker driver using + AUDDSpeakerDriver_Initialize + -# To get %audio stream frames from host, use + AUDDSpeakerDriver_Read + -# To send %audio sampling stream to host, use + AUDDSpeakerDriver_Write + +*/ + +#ifndef AUDDSPEAKERDRIVER_H +#define AUDDSPEAKERDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker stream information" +/// +/// This page lists codes for USB Audio Speaker stream information. +/// +/// !Code +/// - AUDDSpeakerDriver_SAMPLERATE +/// - AUDDSpeakerDriver_NUMCHANNELS +/// - AUDDSpeakerDriver_BYTESPERSAMPLE +/// - AUDDSpeakerDriver_BITSPERSAMPLE +/// - AUDDSpeakerDriver_SAMPLESPERFRAME +/// - AUDDSpeakerDriver_BYTESPERFRAME + +#if defined(at91sam7s) + /// Sample rate in Hz. + #define AUDDSpeakerDriver_SAMPLERATE 32000 + /// Number of channels in audio stream. + #define AUDDSpeakerDriver_NUMCHANNELS 1 + /// Number of bytes in one sample. + #define AUDDSpeakerDriver_BYTESPERSAMPLE 2 +#else + /// Sample rate in Hz. + #define AUDDSpeakerDriver_SAMPLERATE 48000 + /// Number of channels in audio stream. + #define AUDDSpeakerDriver_NUMCHANNELS 2 + /// Number of bytes in one sample. + #define AUDDSpeakerDriver_BYTESPERSAMPLE 2 +#endif +/// Number of bits in one sample. +#define AUDDSpeakerDriver_BITSPERSAMPLE (AUDDSpeakerDriver_BYTESPERSAMPLE * 8) +/// Number of bytes in one USB subframe. +#define AUDDSpeakerDriver_BYTESPERSUBFRAME (AUDDSpeakerDriver_NUMCHANNELS * \ + AUDDSpeakerDriver_BYTESPERSAMPLE) +/// Number of samples in one USB frame. +#define AUDDSpeakerDriver_SAMPLESPERFRAME (AUDDSpeakerDriver_SAMPLERATE / 1000 \ + * AUDDSpeakerDriver_NUMCHANNELS) +/// Number of bytes in one USB frame. +#define AUDDSpeakerDriver_BYTESPERFRAME (AUDDSpeakerDriver_SAMPLESPERFRAME * \ + AUDDSpeakerDriver_BYTESPERSAMPLE) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Channel Numbers" +/// +/// This page lists codes for USB Audio Speaker channel numbers. +/// +/// !Playback channel numbers +/// - AUDDSpeakerDriver_MASTERCHANNEL +/// - AUDDSpeakerDriver_LEFTCHANNEL +/// - AUDDSpeakerDriver_RIGHTCHANNEL +/// +/// !Record channel number +/// - AUDDSpeakerDriver_RECCHANNEL + +/// Master channel of playback. +#define AUDDSpeakerDriver_MASTERCHANNEL 0 +/// Front left channel of playback. +#define AUDDSpeakerDriver_LEFTCHANNEL 1 +/// Front right channel of playback. +#define AUDDSpeakerDriver_RIGHTCHANNEL 2 +/// Channel of record. +#define AUDDSpeakerDriver_RECCHANNEL 0 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void AUDDSpeakerDriver_Initialize(); + +extern void AUDDSpeakerDriver_RequestHandler(const USBGenericRequest *request); + +extern unsigned char AUDDSpeakerDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument); + +#endif //#ifndef AUDDSPEAKERDRIVER_H + +//#ifndef SPEAKER_DRIVER_H +//#define SPEAKER_DRIVER_H +// +////------------------------------------------------------------------------------ +//// Definitions +////------------------------------------------------------------------------------ +// +///*! Sampling frequency in Hz */ +//#define SPEAKER_SAMPLERATE 48000 +///*! Number of samples in one isochronous packet (1ms frame) */ +//#define SPEAKER_SAMPLESPERPACKET (SPEAKER_SAMPLERATE / 1000) +///*! Size of one sample (in bytes) */ +//#define SPEAKER_SAMPLESIZE 2 +///*! Number of channels */ +//#define SPEAKER_NUMCHANNELS 2 +///*! Size of one frame (number of bytes sent for one sample on all channels) */ +//#define SPEAKER_FRAMESIZE (SPEAKER_SAMPLESIZE * SPEAKER_NUMCHANNELS) +///*! Required bit rate given the sample frequency, sample size and number of +// channels. */ +//#define SPEAKER_BITRATE (SPEAKER_SAMPLERATE * SPEAKER_FRAMESIZE) +///*! Size of one isochronous packet */ +//#define SPEAKER_PACKETSIZE (SPEAKER_SAMPLESPERPACKET * SPEAKER_FRAMESIZE) +// +////------------------------------------------------------------------------------ +//// Structures +////------------------------------------------------------------------------------ +///*! +// Holds the speaker driver state. +// */ +//typedef struct { +// +// S_std_class standardDriver; +// +// unsigned char isOutStreamEnabled; +// unsigned char isChannelMuted[SPEAKER_NUMCHANNELS+1]; +// +// Callback_f outStreamStatusChanged; +// Callback_f outStreamMuteChanged; +// +//} __attribute__((packed)) S_speaker; +// +////------------------------------------------------------------------------------ +//// Exported functions +////------------------------------------------------------------------------------ +// +//extern void SPK_Init(S_speaker *speakerDriver, const S_usb *usbDriver); +//extern void SPK_SetCallbacks(S_speaker *speakerDriver, +// Callback_f outStreamStatusChanged, +// Callback_f outStreamMuteChanged); +//extern void SPK_RequestHandler(S_speaker *speakerDriver); +//extern char SPK_Read(S_speaker *speakerDriver, +// void *buffer, +// unsigned int length, +// Callback_f callback, +// void *argument); +// +//#endif //#ifndef SPEAKER_DRIVER_H +// diff --git a/usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.c b/usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.c new file mode 100644 index 0000000..2501120 --- /dev/null +++ b/usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.c @@ -0,0 +1,620 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDSpeakerDriverDescriptors.h" +#include "AUDDSpeakerDriver.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Device Codes" +/// +/// This page lists the %device IDs and release number of the USB Audio Speaker +/// %device. +/// +/// !Codes +/// - AUDDSpeakerDriverDescriptors_VENDORID +/// - AUDDSpeakerDriverDescriptors_PRODUCTID +/// - AUDDSpeakerDriverDescriptors_RELEASE + +/// Device vendor ID. +#define AUDDSpeakerDriverDescriptors_VENDORID 0x03EB +/// Device product ID. +#define AUDDSpeakerDriverDescriptors_PRODUCTID 0x6128 +/// Device release number in BCD format. +#define AUDDSpeakerDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Audio control header descriptor with one slave interface. +//------------------------------------------------------------------------------ +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// Header descriptor with 1 interface. +//------------------------------------------------------------------------------ +typedef struct { + + /// Header descriptor. + AUDHeaderDescriptor header; + /// Id of the first grouped interface - Speaker. + unsigned char bInterface0; + +} __attribute__ ((packed)) AUDHeaderDescriptor1; // GCC + +//------------------------------------------------------------------------------ +/// Feature unit descriptor with 3 channel controls (master, right, left). +//------------------------------------------------------------------------------ +typedef struct { + + /// Feature unit descriptor. + AUDFeatureUnitDescriptor feature; + /// Available controls for each channel. + unsigned char bmaControls[3]; + /// Index of a string descriptor for the feature unit. + unsigned char iFeature; + +} __attribute__ ((packed)) AUDFeatureUnitDescriptor3; // GCC + +//------------------------------------------------------------------------------ +/// List of descriptors for detailling the audio control interface of a +/// device using a USB audio speaker driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Header descriptor (with one slave interface). + AUDHeaderDescriptor1 header; + /// Input terminal descriptor. + AUDInputTerminalDescriptor input; + /// Output terminal descriptor. + AUDOutputTerminalDescriptor output; + /// Feature unit descriptor. + AUDFeatureUnitDescriptor3 feature; + +} __attribute__ ((packed)) AUDDSpeakerDriverAudioControlDescriptors; // GCC + +//------------------------------------------------------------------------------ +/// Format type I descriptor with one discrete sampling frequency. +//------------------------------------------------------------------------------ +typedef struct { + + /// Format type I descriptor. + AUDFormatTypeOneDescriptor formatType; + /// Sampling frequency in Hz. + unsigned char tSamFreq[3]; + +} __attribute__ ((packed)) AUDFormatTypeOneDescriptor1; // GCC + +//------------------------------------------------------------------------------ +/// Holds a list of descriptors returned as part of the configuration of +/// a USB audio speaker device. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard configuration. + USBConfigurationDescriptor configuration; + /// Audio control interface. + USBInterfaceDescriptor control; + /// Descriptors for the audio control interface. + AUDDSpeakerDriverAudioControlDescriptors controlDescriptors; + //- AUDIO OUT + /// Streaming out interface descriptor (with no endpoint, required). + USBInterfaceDescriptor streamingOutNoIsochronous; + /// Streaming out interface descriptor. + USBInterfaceDescriptor streamingOut; + /// Audio class descriptor for the streaming out interface. + AUDStreamingInterfaceDescriptor streamingOutClass; + /// Stream format descriptor. + AUDFormatTypeOneDescriptor1 streamingOutFormatType; + /// Streaming out endpoint descriptor. + AUDEndpointDescriptor streamingOutEndpoint; + /// Audio class descriptor for the streaming out endpoint. + AUDDataEndpointDescriptor streamingOutDataEndpoint; + +} __attribute__ ((packed)) AUDDSpeakerDriverConfigurationDescriptors; // GCC + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Device descriptor for a USB audio speaker driver. +const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + AUDDeviceDescriptor_CLASS, + AUDDeviceDescriptor_SUBCLASS, + AUDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + AUDDSpeakerDriverDescriptors_VENDORID, + AUDDSpeakerDriverDescriptors_PRODUCTID, + AUDDSpeakerDriverDescriptors_RELEASE, + 1, // Manufacturer string descriptor index + 2, // Product string descriptor index + 3, // Index of serial number string descriptor + 1 // One possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + AUDDeviceDescriptor_CLASS, + AUDDeviceDescriptor_SUBCLASS, + AUDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +#endif + +/// Configuration descriptors for a USB audio speaker driver. +const AUDDSpeakerDriverConfigurationDescriptors fsConfigurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(AUDDSpeakerDriverConfigurationDescriptors), + 2, // This configuration has 2 interfaces + 1, // This is configuration #1 + 0, // No string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDSpeakerDriverDescriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDSpeakerDriverAudioControlDescriptors), + 1 // One streaming interface + }, + AUDDSpeakerDriverDescriptors_STREAMING + }, + // Input terminal descriptor + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL, + AUDDSpeakerDriver_NUMCHANNELS, + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + AUDDSpeakerDriverDescriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDSpeakerDriverDescriptors_FEATUREUNIT, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + } + }, + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDSpeakerDriverDescriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDSpeakerDriverDescriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDSpeakerDriver_NUMCHANNELS, + AUDDSpeakerDriver_BYTESPERSAMPLE, + AUDDSpeakerDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDSpeakerDriver_SAMPLERATE & 0xFF, + (AUDDSpeakerDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDSpeakerDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDDSpeakerDriverDescriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(AUDDSpeakerDriverDescriptors_DATAOUT), + AUDDSpeakerDriverDescriptors_FS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + } +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Configuration descriptors for a USB audio speaker driver. +const AUDDSpeakerDriverConfigurationDescriptors hsConfigurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(AUDDSpeakerDriverConfigurationDescriptors), + 2, // This configuration has 2 interfaces + 1, // This is configuration #1 + 0, // No string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDSpeakerDriverDescriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDSpeakerDriverAudioControlDescriptors), + 1 // One streaming interface + }, + AUDDSpeakerDriverDescriptors_STREAMING + }, + // Input terminal descriptor + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL, + AUDDSpeakerDriver_NUMCHANNELS, + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + AUDDSpeakerDriverDescriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDDSpeakerDriverDescriptors_FEATUREUNIT, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + } + }, + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDSpeakerDriverDescriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDDSpeakerDriverDescriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDDSpeakerDriverDescriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDDSpeakerDriver_NUMCHANNELS, + AUDDSpeakerDriver_BYTESPERSAMPLE, + AUDDSpeakerDriver_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDDSpeakerDriver_SAMPLERATE & 0xFF, + (AUDDSpeakerDriver_SAMPLERATE >> 8) & 0xFF, + (AUDDSpeakerDriver_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDDSpeakerDriverDescriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(AUDDSpeakerDriverDescriptors_DATAOUT), + AUDDSpeakerDriverDescriptors_HS_INTERVAL, // Polling interval = 1 ms + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + } +}; +#endif // defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// String descriptor with the supported languages. +const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(15), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('k'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('a'), + USBStringDescriptor_UNICODE('k'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('r') +}; + +/// Product serial number. +const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB audio speaker device driver. +const USBDDriverDescriptors auddSpeakerDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &fsConfigurationDescriptors, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &hsConfigurationDescriptors, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &hsConfigurationDescriptors, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &hsConfigurationDescriptors, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; + diff --git a/usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.h b/usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.h new file mode 100644 index 0000000..7bc7352 --- /dev/null +++ b/usb/device/audio-speaker/AUDDSpeakerDriverDescriptors.h @@ -0,0 +1,146 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Declaration of the descriptors required by a USB audio speaker driver. + + !!!Usage + + -# Initialize a USBDDriver instance using the + auddSpeakerDriverDescriptors list. +*/ + +#ifndef AUDDSPEAKERDRIVERDESCRIPTORS_H +#define AUDDSPEAKERDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Endpoint Numbers" +/// +/// This page lists the endpoint number settings for USB Audio Speaker device. +/// +/// !Endpoints +/// - AUDDSpeakerDriverDescriptors_DATAOUT +/// - AUDDSpeakerDriverDescriptors_DATAIN +/// - AUDDSpeakerDriverDescriptors_FS_INTERVAL +/// - AUDDSpeakerDriverDescriptors_HS_INTERVAL +/// +/// \note for UDP, uses IN EPs that support double buffer; for UDPHS, uses +/// IN EPs that support DMA and High bandwidth. + +#if defined(at91sam7s) || defined(at91sam9xe) + /// Data out endpoint number. + #define AUDDSpeakerDriverDescriptors_DATAOUT 0x01 + /// Data in endpoint number. + #define AUDDSpeakerDriverDescriptors_DATAIN 0x02 +#elif defined(CHIP_USB_UDP) + /// Data out endpoint number. + #define AUDDSpeakerDriverDescriptors_DATAOUT 0x04 + /// Data in endpoint number. + #define AUDDSpeakerDriverDescriptors_DATAIN 0x05 +#else + /// Data out endpoint number. + #define AUDDSpeakerDriverDescriptors_DATAOUT 0x05 + /// Data in endpoint number. + #define AUDDSpeakerDriverDescriptors_DATAIN 0x06 +#endif + +/// Endpoint polling interval 2^(x-1) * 125us +#define AUDDSpeakerDriverDescriptors_HS_INTERVAL 0x04 +/// Endpoint polling interval 2^(x-1) * ms +#define AUDDSpeakerDriverDescriptors_FS_INTERVAL 0x01 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Interface IDs" +/// +/// This page lists the interface numbers for USB Audio Speaker device. +/// +/// !Interfaces +/// - AUDDSpeakerDriverDescriptors_CONTROL +/// - AUDDSpeakerDriverDescriptors_STREAMING +/// - AUDDSpeakerDriverDescriptors_STREAMINGIN + +/// Audio control interface ID. +#define AUDDSpeakerDriverDescriptors_CONTROL 0 +/// Audio streaming interface ID (OUT, for playback). +#define AUDDSpeakerDriverDescriptors_STREAMING 1 +/// Audio streaming interface ID (IN, for record). +#define AUDDSpeakerDriverDescriptors_STREAMINGIN 2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Audio Speaker Entity IDs" +/// +/// This page lists the entity IDs for USB Audio Speaker device. +/// +/// !Entities +/// - AUDDSpeakerDriverDescriptors_INPUTTERMINAL +/// - AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL +/// - AUDDSpeakerDriverDescriptors_FEATUREUNIT +/// - AUDDSpeakerDriverDescriptors_INPUTTERMINAL_REC +/// - AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL_REC +/// - AUDDSpeakerDriverDescriptors_FEATUREUNIT_REC + +/// Playback input terminal ID. +#define AUDDSpeakerDriverDescriptors_INPUTTERMINAL 0 +/// Playback output terminal ID. +#define AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL 1 +/// Playback feature unit ID. +#define AUDDSpeakerDriverDescriptors_FEATUREUNIT 2 +/// Record input terminal ID. +#define AUDDSpeakerDriverDescriptors_INPUTTERMINAL_REC 3 +/// Record output terminal ID. +#define AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL_REC 4 +/// Record feature unit ID +#define AUDDSpeakerDriverDescriptors_FEATUREUNIT_REC 5 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +extern const USBDDriverDescriptors auddSpeakerDriverDescriptors; + +#endif //#ifndef AUDDSPEAKERDRIVERDESCRIPTORS_H + diff --git a/usb/device/audio-speaker/USBAudioSpeaker.png b/usb/device/audio-speaker/USBAudioSpeaker.png new file mode 100644 index 0000000..bcae300 Binary files /dev/null and b/usb/device/audio-speaker/USBAudioSpeaker.png differ diff --git a/usb/device/audio-speaker/USBAudioSpeakerDescriptors.png b/usb/device/audio-speaker/USBAudioSpeakerDescriptors.png new file mode 100644 index 0000000..251a2d1 Binary files /dev/null and b/usb/device/audio-speaker/USBAudioSpeakerDescriptors.png differ diff --git a/usb/device/audio-speaker/USBAudioSpeakerRecorder.png b/usb/device/audio-speaker/USBAudioSpeakerRecorder.png new file mode 100644 index 0000000..ddbd234 Binary files /dev/null and b/usb/device/audio-speaker/USBAudioSpeakerRecorder.png differ diff --git a/usb/device/audio-speaker/USBAudioSpeakerRecorderDescriptors.png b/usb/device/audio-speaker/USBAudioSpeakerRecorderDescriptors.png new file mode 100644 index 0000000..c5c3324 Binary files /dev/null and b/usb/device/audio-speaker/USBAudioSpeakerRecorderDescriptors.png differ diff --git a/usb/device/audio-speaker/audio-speaker.dir b/usb/device/audio-speaker/audio-speaker.dir new file mode 100644 index 0000000..be90df0 --- /dev/null +++ b/usb/device/audio-speaker/audio-speaker.dir @@ -0,0 +1,617 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB Audio +/// Class device - USB audio-speaker demo, to implement an USB desktop speaker. +/// +/// !!!Contents +/// There are two things for the implement of the audio-speaker device driver: +/// - Implement the audio-speaker driver structs and functions for the device, +/// to initialize, to handle audio class specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// endpoints, +/// - Create the audio-speaker device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the device as a USB audio "desktop speaker" device. +/// +/// For more information about what a particular group contains, please refer to +/// "USB Audio Speaker Device". +//------------------------------------------------------------------------------ + +/** + \page "USB Audio Speaker Device" + This page describes how to use the USB framework to produce a USB Audio Class + Device driver. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Audio Device Document 1.0 + - + Audio Data Formats 1.0 + - + Autio Terminal Types 1.0 + + !!!Audio Speaker Driver API + - AUDDSpeakerDriver_Initialize + - AUDDSpeakerDriver_RequestHandler + - AUDDSpeakerDriver_Read + + !!!Audio Desktop Speaker Description + The %device described here is a USB desktop speaker. It receives a stero + %audio data stream from the Host over its Audio Streaming interface. The used + Audio Data Format is 16-bit 48KHz 2-channel PCM (or mono 16-bit 32KHz PCM for + sam7s chips). The following is the internal topology of the speaker. + + \image USBAudioSpeaker.png "USB Desktop Speaker Topology" + + The %audio function contains one Onput Terminal that represents the actual + speaker output element, followed by the Digital-to-Analog Converter (DAC). + The digital input stream of the host enters the %audio function through the + single Input %Pin of the Output terminal. There is a Feature Unit on the + %audio stream, to mute or unmute the speaker. The Input Terminal is the + representation within the %audio fucntion of the USB OUT endpoint that + eventually receives the %audio data stream from the Host. The internals of the + %audio function are presented to the Host through the (mandatory) AudioControl + interface wheras the USB OUT endpoint resides in the AudioStreaming interface. + + !!!Descriptor Hierarchy + This USB Desktop Speaker %device includes the AudioControl interface + (interface 0) and a single AudioStreaming interface (interface 1). The + AudioStreaming interface features two alternate settings. The first alternate + setting (Alternate Setting 0) has zero bandwidth associated with it so that + switching to this alternate setting effectively frees all allocated bandwidth + on the USB for this device. Zero bandwidth is indicated by the lack of a + streaming endpoint. Alternate Setting 1 is the operational part of the + interface and it has one isochronous OUT endpoint. Figure presents the + descriptor hierarchy. + + \image UsbAudioSpeakerDescriptors.png "USB Desktop Speaker Hierarchy" + + !!!Descriptors + The following sections present all the descriptors that are used to describe + the %device. + + All descriptors are combined into a list and pass to USBDDriver_Initialize + invoked in AUDDSpeakerDriver_Initialize. + +\code +const USBDDriverDescriptors auddSpeakerDriverDescriptors; +\endcode + + !!Device Descriptor +\code +const USBDeviceDescriptor deviceDescriptor; +\endcode +||Offset||Field||Size||Value||Description +|0|bLength|1|0x12|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x01|DEVICE descriptor (USBGenericDescriptor_DEVICE). +|2|bcdUSB|2|0x0200|2.00 - current revision of USB specification. +|4|bDeviceClass|1|0x00|Device defined at interface level. +|5|bDeviceSubClass|1|0x00|Unused. +|6|bDeviceProtocol|1|0x00|Unused. +|7|bMaxPacketSize0|1|0x08|8 bytes (BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0)). +|8|idVendor|2|0x03EB|Atmel Vendor ID (AUDDSpeakerDriverDescriptors_VENDORID). +|10|idProduct|2|0x6128|Product ID (AUDDSpeakerDriverDescriptors_PRODUCTID). +|12|bcdDevice|2|0x0100|Device Release Code\n + (AUDDSpeakerDriverDescriptors_RELEASE). +|14|iManufacturer|1|0x01|Index to manufacture name in Unicode\n + (manufacturerDescriptor). +|15|iProduct|1|0x02|Index to product name in Unicode (productDescriptor). +|16|iSerialNumber|1|0x03|Index to serial number in Unicode\n + (serialNumberDescriptor). +|17|bNumConfigurations|1|0x01|One configuration. + + !!Configuration Descriptor +\code +const AUDDSpeakerDriverConfigurationDescriptors configurationDescriptors; +\endcode +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x02|CONFIGURATION descriptor\n + (USBGenericDescriptor_CONFIGURATION). +|2|wTotalLength|2|0x????|Length of the total configuration block in bytes\n + including this descriptor\n + (AUDDSpeakerDriverConfigurationDescriptors) +|4|bNumInterfaces|1|0x02|Two interfaces. +|5|bConfigurationValue|1|0x01|ID of this configuration. +|6|iConfiguration|1|0x00|Unused. +|7|bmAttributes|1|0x??|BOARD_USB_BMATTRIBUTES +|8|bMaxPower|1|0x32|100mA Max. %power consumption.\n + USBConfigurationDescriptor_POWER(100) + + !!AudioControl Interface Descriptor + The AudioControl interface describes the %device structure (%audio function + topology) and is used to manipulate the Audio Controls. + + !Standard AC Interface Descriptor + The AudioControl interface has no dedicated endpoints associated with it. It + uses the default pipe (endpoint 0) for all communication purposes. Class- + specific AudioControl Requests are sent using the default pipe. There is no + Status Interrupt endpoint provided. + + See USBInterfaceDescriptor. +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x04|INTERFACE descriptor\n + (USBGenericDescriptor_INTERFACE). +|2|bInterfaceNumber|1|0x00|Index of this interface. +|3|bAlternateSetting|1|0x00|Index of this setting. +|4|bNumEndpoints|1|0x00|0 endpoints. +|5|bInterfaceClass|1|0x01|AUDIO (AUDControlInterfaceDescriptor_CLASS). +|6|bInterfaceSubClass|1|0x01|AUDIO_CONTROL\n + (AUDControlInterfaceDescriptor_SUBCLASS). +|7|bInterfaceProtocol|1|0x00|Unused. +|8|iInterface|1|0x00|Unused. + + !Class-specific AC Interface Descriptor + The Class-specific AC interface descriptor is always headed by a Header + descriptor that contains general information about the AudioControl interface. + It contains all the pointers needed to describe the AudioInterface Collection, + associated with the described %audio function. +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubtype|1|0x01|HEADER subtype (AUDGenericDescriptor_HEADER). +|3|bcdADC|2|0x0100|Revision of class specification - 1.0 +|5|wTotalLength|2|0x????|Total size of class specific descriptors\n + (AUDDSpeakerDriverAudioControlDescriptors). +|7|bInCollection|1|0x01|Number of streaming interfaces. +|8|baInterfaceNr(1)|1|0x01|AudioStreaming interface 1 belongs to this AudioControl interface. + + !Input Terminal Descriptor for playback + This descriptor describes the Input Terminal that represents the USB pipe from + the Host PC. Its Output Pin is connected to the Input Pin of the Feature Unit + ... +||Offset||Field||Size||Value||Description +|0|bLength|1|0x0C|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x02|INPUT_TERMINAL subtype\n + (AUDGenericDescriptor_INPUTTERMINAL). +|3|bTerminalID|1|0x00|ID of this Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL). +|4|wTerminalType|2|0x0101|Terminal is USB stream\n + (AUDInputTerminalDescriptor_USBSTREAMING). +|6|bAssocTerminal|1|0x01|Associated to Output Terminal\n + (AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL). +|7|bNrChannels|1|0x02|Two channel. +|8|wChannelConfig|2|0x0003|Left plus right front channel. +|10|iChannelNames|1|0x00|Unused. +|11|iTerminal|1|0x00|Unused. + + !Output Terminal Descriptor for playback + ... +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x03|OUTPUT_TERMINAL subtype\n + (AUDGenericDescriptor_OUTPUTTERMINAL). +|3|bTerminalID|1|0x01|ID of this Output Terminal\n + (AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL). +|4|wTerminalType|2|0x0301|Terminal is Desktop speaker. +|6|bAssocTerminal|1|0x01|Associated to Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL). +|7|bSourceID|1|0x02|From Feature Unit\n + (AUDDSpeakerDriverDescriptors_FEATUREUNIT). +|8|iTerminal|1|0x00|Unused. + + !Feature Unit Descriptor for playback + ... +||Offset||Field||Size||Value||Description +|0|bLength|1|0x0A|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x02|FEATURE_UNIT subtype\n + (AUDGenericDescriptor_FEATUREUNIT). +|3|bUnitID|1|0x02|ID of this Feature Unit\n + (AUDDSpeakerDriverDescriptors_FEATUREUNIT). +|4|bSourceID|1|0x00|From Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL). +|5|bControlSize|1|0x01|1 byte per channel for controls +|6|bmaControls|3|0x000001|Master channel mute control, no other controls. +|9|iFeature|1|0x00|Unused. + + !!AudioStreaming Interface Descriptor + The AudioStreaming interface has two possible alternate settings. + + !Zero-bandwidth Alternate Setting 0 + Alternate setting 0 is a zero-bandwidth setting, used to relinquish the + claimed bandwidth on the bus when the microphone is not in use. It is the + default setting after power-up. The zero bandwidth is implemented by + specifying that this alternate setting of the interface has no endpoints + associated with it (bNumEndpoints=0). The collection of descriptors for this + alternate setting reduces to the standard interface descriptor. + + Standard AS Interface Descriptor (USBInterfaceDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x04|INTERFACE descriptor\n + (USBGenericDescriptor_INTERFACE). +|2|bInterfaceNumber|1|0x01|Index of this interface. +|3|bAlternateSetting|1|0x00|Index of this setting. +|4|bNumEndpoints|1|0x00|0 endpoint. +|5|bInterfaceClass|1|0x01|AUDIO (AUDStreamingInterfaceDescriptor_CLASS). +|6|bInterfaceSubClass|1|0x02|AUDIO_STREAMING\n + (AUDStreamingInterfaceDescriptor_SUBCLASS). +|7|bInterfaceProtocol|1|0x00|Unused (AUDStreamingInterfaceDescriptor_PROTOCOL). +|8|iInterface|1|0x00|Unused. + + !Operational Alternate Setting 1 + Alternate setting 1 is the operational setting of the interface. It contains + the standard and class-specific interface and endpoint descriptors. + + Standard AS Interface Descriptor (USBInterfaceDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of USBInterfaceDescriptor in bytes. +|1|bDescriptorType|1|0x04|INTERFACE descriptor\n + (USBGenericDescriptor_INTERFACE). +|2|bInterfaceNumber|1|0x01|Index of this interface. +|3|bAlternateSetting|1|0x01|Index of this setting. +|4|bNumEndpoints|1|0x01|1 endpoint. +|5|bInterfaceClass|1|0x01|AUDIO (AUDStreamingInterfaceDescriptor_CLASS). +|6|bInterfaceSubClass|1|0x02|AUDIO_STREAMING\n + (AUDStreamingInterfaceDescriptor_SUBCLASS). +|7|bInterfaceProtocol|1|0x00|Unused (AUDStreamingInterfaceDescriptor_PROTOCOL). +|8|iInterface|1|0x00|Unused. + + Class-specific AS General Interface Descriptor (AUDStreamingInterfaceDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x06|Size of AUDStreamingInterfaceDescriptor in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x01|GENERAL subtype\n + (AUDStreamingInterfaceDescriptor_GENERAL). +|3|bTerminalLink|1|0x02|Unit ID of the Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL). +|4|bDelay|1|0x00|No interface delay. +|5|wFormatTag|2|0x0001|PCM Format (AUDFormatTypeOneDescriptor_PCM). + + Type I Format Type Descriptor (AUDFormatTypeOneDescriptor1) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x0B|Size of AUDFormatTypeOneDescriptor1 in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\ + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x02|FORMAT_TYPE subtype\n + (AUDStreamingInterfaceDescriptor_FORMATTYPE). +|3|bFormatType|1|0x01|FORMAT_TYPE_I (AUDFormatTypeOneDescriptor_FORMATTYPEONE). +|4|bNrChannels|1|0x02|2 channels (AUDDSpeakerDriver_NUMCHANNELS). +|5|bSubFrameSize|1|0x02|Two bytes per audio subframe\n + (AUDDSpeakerDriver_BYTESPERSAMPLE). +|6|bBitResolution|1|0x10|16 bits per sample\n + (AUDDSpeakerDriver_BYTESPERSAMPLE * 2). +|7|bSamFreqType|1|0x01|One frequency supported. +|8|tSamFreq|3|4800|4800Hz (AUDDSpeakerDriver_SAMPLERATE). + + Standard %Endpoint Descriptor (AUDEndpointDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of AUDFormatTypeOneDescriptor1 in bytes. +|1|bDescriptorType|1|0x24|ENDPOINT descriptor (USBGenericDescriptor_ENDPOINT). +|2|bEndpointAddress|1|0x04\nTest|OUT endpoint 4\n + See USBEndpointDescriptor_ADDRESS\n + See AUDDSpeakerDriverDescriptors_DATAOUT. +|3|bmAttributes|1|0x01|Isochronous, not shared\n + (USBEndpointDescriptor_ISOCHRONOUS). +|4|wMaxPacketSize|2|0x????|BOARD_USB_ENDPOINTS_MAXPACKETSIZE(). +|6|bInterval|1|0x01|One packet per frame. +|7|bRefresh|1|0x00|Unused. +|8|bSynchAddress|1|0x00|Unused. + + Class-specific Isochronous Audio Data Endpoint Descriptor + (AUDDataEndpointDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x07|Size of AUDDataEndpointDescriptor in bytes. +|1|bDescriptorType|1|0x25|CS_ENDPOINT descriptor\n + (AUDGenericDescriptor_ENDPOINT). +|2|bDescriptorSubType|1|0x01|GENERAL subtype\n + (AUDDataEndpointDescriptor_SUBTYPE). +|3|bmAttributes|1|0x00|No sampling frequency control\n + no pitch control\n + no packet padding. +|4|bLockDelayUnits|1|0x00|Unused. +|5|wLockDelay|2|0x0000|Unused. + + !!String Descriptors + There are three string descriptors available. The Manufacturer, Product and + Serial Number descriptor. + + See manufacturerDescriptor, productDescriptor, serialNumberDescriptor. + + !!!Requests + The Audio Speaker Driver supports all necessary standard requests, and some + class-specific requests. + + USBDCallbacks_RequestReceived is used to filter all requests, + AUDDSpeakerDriver_RequestHandler is invoked to handle Audio Class requests + and forward standard request to AUDDSpeakerDriver_RequestHandler. + + !!Standard requests + Set Interface request should be processed to control bandwidth allocation. + + !Set Interface + USBDDriverCallbacks_InterfaceSettingChanged is re-implemented to handle the + event. +||Offset||Field||Size||Value||Description +|0|bmRequestType|1|0x01|D7:0=Host to Device.\n + D6..5:00=Standard Request.\n + D4..0:00001=Recipient is interface. +|1|bRequest|1|0x0B|SET_INTERFACE. +|2|wValue|2|0x0000\n + or\n + 0x0001|Zero bandwidth or normal isochronous operation. +|4|wIndex|2|0x0001|Interface number of the AudioStreaming interface. +|6|wLength|2|0x0000|No Parameter Block. + + !!Class-specific requests + The only class-specific Request supported is the Set/Get Feature Unit Control + Request. For mute control of the Feature Unit. + + !Set Feature Unit Control Request +||Offset||Field||Size||Value||Description +|0|bmRequestType|1|0x01|D7:0=Host to Device.\n + D6..5:01=Class Request.\n + D4..0:00001=Recipient is interface. +|1|bRequest|1|0x01|SET_CUR. +|2|wValue|2|0x0100|Mute control (AUDFeatureUnitRequest_MUTE) of\n + Master channel (AUDDSpeakerDriver_MASTERCHANNEL). +|4|wIndex|2|0x0200|Feature Unit (AUDDSpeakerDriverDescriptors_FEATUREUNIT)\n + and\n + AudioControl Interface (AUDDSpeakerDriverDescriptors_CONTROL). +|6|wLength|2|0x0001|Paramter Block Length + + The one-byte Parameter Block contains the new bMuted value for Feature + Control. + + !Get Feature Unit Control Request +||Offset||Field||Size||Value||Description +|0|bmRequestType|1|0x01|D7:0=Host to Device.\n + D6..5:01=Class Request.\n + D4..0:00001=Recipient is interface. +|1|bRequest|1|0x81|GET_CUR. +|2|wValue|2|0x0100|Mute control (AUDFeatureUnitRequest_MUTE) of\n + Master channel (AUDDSpeakerDriver_MASTERCHANNEL). +|4|wIndex|2|0x0200|Feature Unit (AUDDSpeakerDriverDescriptors_FEATUREUNIT)\n + and\n + AudioControl Interface (AUDDSpeakerDriverDescriptors_CONTROL). +|6|wLength|2|0x0001|Paramter Block Length + + The one-byte Parameter Block contains the new bMuted value for Feature + Control. + + !!!Modify the Device Driver + You can modify your project from the USB Audio Demoes: + - usb-device-audio-speaker-ac97-project + - usb-device-audio-speaker-project + + !!Change Device ID and Display + All Device ID and Display Strings are in AUDDSpeakerDriverDescriptors.c. + + !Device IDs + You can find "Audio Speaker Device Codes" + - AUDDSpeakerDriverDescriptors_VENDORID + - AUDDSpeakerDriverDescriptors_PRODUCTID + - AUDDSpeakerDriverDescriptors_RELEASE + + !Display Strings + You can modify the string descriptors + - manufacturerDescriptor + - productDescriptor + - serialNumberDescriptor + + !!!Add Recorder Function + See "USB Audio Recorder". + +*/ + +/** + \page "USB Audio Recorder" + This page describes how to add recorder function into the + "USB Audio Speaker Device", So that you can learn how to extend your audio + device driver from current Audio Speaker demo. + + !!!Description + To add %audio record function, new Input Terminal, Output Termnial and Feature + Unit is added. + + \image UsbAudioSpeakerRecorder.png "USB Desktop Speaker Hierarchy" + + + !!!Modify the configuration descriptor: + New descriptor for the terminals and unit should be added, and according + interface, too. + + \image UsbAudioSpeakerRecorderDescriptors.png "USB Desktop Speaker Descriptors" + + + + !!Terminal Descriptors and Unit Descriptor + ... + !Input Terminal Descriptor for recording + ... +||Offset||Field||Size||Value||Description +|0|bLength|1|0x0C|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x03|INPUT_TERMINAL subtype\n + (AUDGenericDescriptor_INPUTTERMINAL). +|3|bTerminalID|1|0x03|ID of this Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL_REC). +|4|wTerminalType|2|0x0403|Terminal is Speaker Phone\n + (AUDInputTerminalDescriptor_SPEAKERPHONE). +|6|bAssocTerminal|1|0x04|Associated to Output Terminal\n + (AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL_REC). +|7|bNrChannels|1|0x02|Two channel. +|8|wChannelConfig|2|0x0003|Left plus right front channel. +|10|iChannelNames|1|0x00|Unused. +|11|iTerminal|1|0x00|Unused. + + !Output Terminal Descriptor for recording + ... +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x04|OUTPUT_TERMINAL subtype\n + (AUDGenericDescriptor_OUTPUTTERMINAL). +|3|bTerminalID|1|0x04|ID of this Output Terminal\n + (AUDDSpeakerDriverDescriptors_OUTPUTTERMINAL_REC). +|4|wTerminalType|2|0x0301|Terminal is USB stream\n + (AUDOutputTerminalDescriptor_USBTREAMING). +|6|bAssocTerminal|1|0x03|Associated to Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL_REC). +|7|bSourceID|1|0x05|From Feature Unit\n + (AUDDSpeakerDriverDescriptors_FEATUREUNIT_REC). +|8|iTerminal|1|0x00|Unused. + + !Feature Unit Descriptor for recording + ... +||Offset||Field||Size||Value||Description +|0|bLength|1|0x0A|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x02|FEATURE_UNIT subtype\n + (AUDGenericDescriptor_FEATUREUNIT). +|3|bUnitID|1|0x05|ID of this Feature Unit\n + (AUDDSpeakerDriverDescriptors_FEATUREUNIT_REC). +|4|bSourceID|1|0x03|From Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL_REC). +|5|bControlSize|1|0x01|1 byte per channel for controls +|6|bmaControls|3|0x000001|Master channel mute control, no other controls. +|9|iFeature|1|0x00|Unused. + + !!Interface Descriptor and Endpoint Descriptor for recording + ... + + !Zero-bandwidth Alternate Setting 0 + Standard AS Interface Descriptor (USBInterfaceDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of this descriptor, in bytes. +|1|bDescriptorType|1|0x04|INTERFACE descriptor\n + (USBGenericDescriptor_INTERFACE). +|2|bInterfaceNumber|1|0x02|Index of this interface. +|3|bAlternateSetting|1|0x00|Index of this setting. +|4|bNumEndpoints|1|0x00|0 endpoint. +|5|bInterfaceClass|1|0x01|AUDIO (AUDStreamingInterfaceDescriptor_CLASS). +|6|bInterfaceSubClass|1|0x02|AUDIO_STREAMING\n + (AUDStreamingInterfaceDescriptor_SUBCLASS). +|7|bInterfaceProtocol|1|0x00|Unused (AUDStreamingInterfaceDescriptor_PROTOCOL). +|8|iInterface|1|0x00|Unused. + + !Operational Alternate Setting 1 + Standard AS Interface Descriptor (USBInterfaceDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of USBInterfaceDescriptor in bytes. +|1|bDescriptorType|1|0x04|INTERFACE descriptor\n + (USBGenericDescriptor_INTERFACE). +|2|bInterfaceNumber|1|0x02|Index of this interface. +|3|bAlternateSetting|1|0x01|Index of this setting. +|4|bNumEndpoints|1|0x01|1 endpoint. +|5|bInterfaceClass|1|0x01|AUDIO (AUDStreamingInterfaceDescriptor_CLASS). +|6|bInterfaceSubClass|1|0x02|AUDIO_STREAMING\n + (AUDStreamingInterfaceDescriptor_SUBCLASS). +|7|bInterfaceProtocol|1|0x00|Unused (AUDStreamingInterfaceDescriptor_PROTOCOL). +|8|iInterface|1|0x00|Unused. + + Class-specific AS General Interface Descriptor (AUDStreamingInterfaceDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x06|Size of AUDStreamingInterfaceDescriptor in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x01|GENERAL subtype\n + (AUDStreamingInterfaceDescriptor_GENERAL). +|3|bTerminalLink|1|0x02|Unit ID of the Input Terminal\n + (AUDDSpeakerDriverDescriptors_INPUTTERMINAL). +|4|bDelay|1|0x00|No interface delay. +|5|wFormatTag|2|0x0001|PCM Format (AUDFormatTypeOneDescriptor_PCM). + + Type I Format Type Descriptor (AUDFormatTypeOneDescriptor1) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x0B|Size of AUDFormatTypeOneDescriptor1 in bytes. +|1|bDescriptorType|1|0x24|CS_INTERFACE descriptor\n + (AUDGenericDescriptor_INTERFACE). +|2|bDescriptorSubType|1|0x02|FORMAT_TYPE subtype\n + (AUDStreamingInterfaceDescriptor_FORMATTYPE). +|3|bFormatType|1|0x01|FORMAT_TYPE_I (AUDFormatTypeOneDescriptor_FORMATTYPEONE). +|4|bNrChannels|1|0x02|2 channels (AUDDSpeakerDriver_NUMCHANNELS). +|5|bSubFrameSize|1|0x02|Two bytes per audio subframe\n + (AUDDSpeakerDriver_BYTESPERSAMPLE). +|6|bBitResolution|1|0x10|16 bits per sample\n + (AUDDSpeakerDriver_BYTESPERSAMPLE * 2). +|7|bSamFreqType|1|0x01|One frequency supported. +|8|tSamFreq|3|4800|4800Hz (AUDDSpeakerDriver_SAMPLERATE). + + Standard %Endpoint Descriptor (AUDEndpointDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x09|Size of AUDFormatTypeOneDescriptor1 in bytes. +|1|bDescriptorType|1|0x24|ENDPOINT descriptor (USBGenericDescriptor_ENDPOINT). +|2|bEndpointAddress|1|0x85|IN endpoint 5\n + USBEndpointDescriptor_ADDRESS()\n + AUDDSpeakerDriverDescriptors_DATAIN +|3|bmAttributes|1|0x01|Isochronous, not shared\n + (USBEndpointDescriptor_ISOCHRONOUS). +|4|wMaxPacketSize|2|0x????|BOARD_USB_ENDPOINTS_MAXPACKETSIZE(5). +|6|bInterval|1|0x01|One packet per frame. +|7|bRefresh|1|0x00|Unused. +|8|bSynchAddress|1|0x00|Unused. + + Class-specific Isochronous Audio Data Endpoint Descriptor + (AUDDataEndpointDescriptor) +||Offset||Field||Size||Value||Description +|0|bLength|1|0x07|Size of AUDDataEndpointDescriptor in bytes. +|1|bDescriptorType|1|0x25|CS_ENDPOINT descriptor\n + (AUDGenericDescriptor_ENDPOINT). +|2|bDescriptorSubType|1|0x01|GENERAL subtype\n + (AUDDataEndpointDescriptor_SUBTYPE). +|3|bmAttributes|1|0x00|No sampling frequency control\n + no pitch control\n + no packet padding +|4|bLockDelayUnits|1|0x00|Unused. +|5|wLockDelay|2|0x0000|Unused. + + !!!Modified methods for new function + Several methods modified for new recorder function. + + !!Request handlers callbacks + Add handler for new Interface, Terminal and Unit IDs. + See AUDDSpeakerDriver_RequestHandler. + + !!Add function for recording USB stream + See AUDDSpeakerDriver_Write. +*/ diff --git a/usb/device/ccid/cciddriver.c b/usb/device/ccid/cciddriver.c new file mode 100644 index 0000000..d72b4ab --- /dev/null +++ b/usb/device/ccid/cciddriver.c @@ -0,0 +1,1503 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// +/// !Purpose +/// +/// CCID driver +/// +/// !Usage +/// +/// Explanation on the usage of the code made available through the header file. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Local definition +//------------------------------------------------------------------------------ + +/// Constants: IDs: Device product ID. +#define CCIDDriverDescriptors_PRODUCTID 0x6129 +/// Constants: IDs: Device vendor ID. +#define CCIDDriverDescriptors_VENDORID 0x03EB +/// Constants: IDs: Device release number. +#define CCIDDriverDescriptors_RELEASE 0x0100 + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// CCIDDriverConfiguration Descriptors +/// List of descriptors that make up the configuration descriptors of a +/// device using the CCID driver. +typedef struct { + + /// Configuration descriptor + USBConfigurationDescriptor configuration; + /// Interface descriptor + USBInterfaceDescriptor interface; + /// CCID descriptor + CCIDDescriptor ccid; + /// Bulk OUT endpoint descriptor + USBEndpointDescriptor bulkOut; + /// Bulk IN endpoint descriptor + USBEndpointDescriptor bulkIn; + /// Interrupt OUT endpoint descriptor + USBEndpointDescriptor interruptIn; + +} __attribute__ ((packed)) CCIDDriverConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Driver structure for an CCID device +typedef struct { + + /// Standard USB device driver instance + USBDDriver usbdDriver; + /// CCID message + S_ccid_bulk_in_header sCcidMessage; + /// CCID command + S_ccid_bulk_out_header sCcidCommand; + /// Interrupt message answer + unsigned char BufferINT[4]; + /// Buffer data of message + unsigned char ProtocolDataStructure[10]; + /// Protocol used + unsigned char bProtocol; + /// SlotStatus + /// Bit 0 = Slot 0 current state + /// Bit 1 = Slot 0 changed status + /// Bit 2 = Slot 1 current state + /// Bit 3 = Slot 1 changed status + /// Bit 4 = Slot 2 current state + /// Bit 5 = Slot 2 changed status + unsigned char SlotStatus; + +} CCIDDriver; + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// Static instance of the CCID device driver. +static CCIDDriver ccidDriver; + +/// Standard USB device descriptor. +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + 0, + 0, + 0, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + CCIDDriverDescriptors_VENDORID, + CCIDDriverDescriptors_PRODUCTID, + CCIDDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; + + +/// List of configuration descriptors. +static const CCIDDriverConfigurationDescriptors configurationDescriptorsFS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 0x10 + } +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +static const CCIDDriverConfigurationDescriptors configurationDescriptorsHS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_HS), + 0x10 + } +}; + +/// Qualifier descriptor +const USBDeviceQualifierDescriptor deviceQualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), // Size of this descriptor in bytes + USBGenericDescriptor_DEVICEQUALIFIER, // Qualifier Descriptor Type + USBDeviceDescriptor_USB2_00, // USB specification 2.00 + 0x00, // Class is specified in interface + 0x00, // Subclass is specified in interface + 0x00, // Protocol is specified in interface + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 0x01, // One possible configuration + 0x00 // Reserved for future use, must be zero +}; + +/// OtherSpeed configuration descriptor in Full Speed mode +static const CCIDDriverConfigurationDescriptors sOtherSpeedConfigurationFS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 0x10 + } +}; + +/// OtherSpeed configuration descriptor in High Speed mode +static const CCIDDriverConfigurationDescriptors sOtherSpeedConfigurationHS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_HS), + 0x10 + } +}; +#endif + +/// Language ID string descriptor. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(23), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('R'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('V'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('R'), + USBStringDescriptor_UNICODE(' ') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(12), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3'), + USBStringDescriptor_UNICODE('4'), + USBStringDescriptor_UNICODE('5'), + USBStringDescriptor_UNICODE('6'), + USBStringDescriptor_UNICODE('7'), + USBStringDescriptor_UNICODE('8'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('F') +}; + +/// Array of pointers to string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor +}; + + +/// List of standard descriptors for the serial driver. +const USBDDriverDescriptors ccidDriverDescriptors = { + + &deviceDescriptor, // FS + (USBConfigurationDescriptor *) &configurationDescriptorsFS, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + (USBDeviceQualifierDescriptor *) &deviceQualifierDescriptor, // FS + (USBConfigurationDescriptor *) &sOtherSpeedConfigurationFS, + &deviceDescriptor, // HS + (USBConfigurationDescriptor *) &configurationDescriptorsHS, + (USBDeviceQualifierDescriptor *) &deviceQualifierDescriptor, // HS + (USBConfigurationDescriptor *) &sOtherSpeedConfigurationHS, +#else + 0, // No qualifier descriptor FS + 0, // No other-speed configuration FS + 0, // No device descriptor HS + 0, // No configuration HS + 0, // No qualifier descriptor HS + 0, // No other-speed configuration HS +#endif + stringDescriptors, + 4 // Four string descriptors in array +}; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Response Pipe, Bulk-IN Messages +/// Return the Slot Status to the host +/// Answer to: +/// PC_to_RDR_IccPowerOff +/// PC_to_RDR_GetSlotStatus +/// PC_to_RDR_IccClock +/// PC_to_RDR_T0APDU +/// PC_to_RDR_Mechanical +/// PC_to_RDR_Abort and Class specific ABORT request +//------------------------------------------------------------------------------ +static void RDRtoPCSlotStatus( void ) +{ + TRACE_DEBUG("RDRtoPCSlotStatus\n\r"); + + // Header fields settings + ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_SLOTSTATUS; + ccidDriver.sCcidMessage.wLength = 0; + ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus; + ccidDriver.sCcidMessage.bError = 0; + // 00h Clock running + // 01h Clock stopped in state L + // 02h Clock stopped in state H + // 03h Clock stopped in an unknown state + // All other values are Reserved for Future Use. + ccidDriver.sCcidMessage.bSpecific = 0; +} + +//------------------------------------------------------------------------------ +/// Response Pipe, Bulk-IN Messages +/// Answer to PC_to_RDR_IccPowerOn +//------------------------------------------------------------------------------ +static void RDRtoPCDatablock_ATR( void ) +{ + unsigned char i; + unsigned char Atr[ATR_SIZE_MAX]; + unsigned char length; + + //TRACE_DEBUG("RDRtoPCDatablock\n\r"); + + ISO7816_Datablock_ATR( Atr, &length ); + + if( length > 5 ) { + ccidDriver.ProtocolDataStructure[1] = Atr[5]&0x0F; // TD(1) + ccidDriver.bProtocol = Atr[5]&0x0F; // TD(1) + } + + // S_ccid_protocol_t0 + // bmFindexDindex + ccidDriver.ProtocolDataStructure[0] = Atr[2]; // TA(1) + + // bmTCCKST0 + // For T=0 ,B0 – 0b, B7-2 – 000000b + // B1 – Convention used (b1=0 for direct, b1=1 for inverse) + + // bGuardTimeT0 + // Extra Guardtime between two characters. Add 0 to 254 etu to the normal + // guardtime of 12etu. FFh is the same as 00h. + ccidDriver.ProtocolDataStructure[2] = Atr[4]; // TC(1) + // AT91C_BASE_US0->US_TTGR = 0; // TC1 + + // bWaitingIntegerT0 + // WI for T=0 used to define WWT + ccidDriver.ProtocolDataStructure[3] = Atr[7]; // TC(2) + + // bClockStop + // ICC Clock Stop Support + // 00 = Stopping the Clock is not allowed + // 01 = Stop with Clock signal Low + // 02 = Stop with Clock signal High + // 03 = Stop with Clock either High or Low + ccidDriver.ProtocolDataStructure[4] = 0x00; // 0 to 3 + + // Header fields settings + ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_DATABLOCK; + ccidDriver.sCcidMessage.wLength = length; // Size of ATR + ccidDriver.sCcidMessage.bSizeToSend += length; // Size of ATR + // bChainParameter: 00 the response APDU begins and ends in this command + ccidDriver.sCcidMessage.bSpecific = 0; + + for( i=0; i (configurationDescriptorsFS.ccid.dwMaxCCIDMessageLength-10) ) { + + ccidDriver.sCcidMessage.bStatus = 1; + ccidDriver.sCcidMessage.bError = 0; + } + // check bBWI + else if ( 0 != ccidDriver.sCcidCommand.bSpecific_0 ) { + + TRACE_ERROR("Bad bBWI\n\r"); + } + else { + + // APDU or TPDU + switch(configurationDescriptorsFS.ccid.dwFeatures + & (CCID_FEATURES_EXC_TPDU|CCID_FEATURES_EXC_SAPDU|CCID_FEATURES_EXC_APDU)) { + + case CCID_FEATURES_EXC_TPDU: + if (ccidDriver.ProtocolDataStructure[1] == PROTOCOL_TO) { + + // Send commande APDU + indexMessage = ISO7816_XfrBlockTPDU_T0( ccidDriver.sCcidCommand.APDU , + ccidDriver.sCcidMessage.abData, + ccidDriver.sCcidCommand.wLength ); + } + else { + if (ccidDriver.ProtocolDataStructure[1] == PROTOCOL_T1) { + TRACE_INFO("Not supported T=1\n\r"); + } + else { + TRACE_INFO("Not supported\n\r"); + } + } + break; + + case CCID_FEATURES_EXC_APDU: + TRACE_INFO("Not supported\n\r"); + break; + + default: + break; + } + + } + + ccidDriver.sCcidMessage.wLength = indexMessage; + TRACE_DEBUG("USB: 0x%X, 0x%X, 0x%X, 0x%X, 0x%X\n\r", ccidDriver.sCcidMessage.abData[0], + ccidDriver.sCcidMessage.abData[1], + ccidDriver.sCcidMessage.abData[2], + ccidDriver.sCcidMessage.abData[3], + ccidDriver.sCcidMessage.abData[4] ); + RDRtoPCDatablock(); + +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// return parameters by the command: RDR_to_PC_Parameters +//------------------------------------------------------------------------------ +static void PCtoRDRGetParameters( void ) +{ + TRACE_DEBUG("PCtoRDRGetParameters\n\r"); + + // We support only one slot + + // bmIccStatus + if( ISO7816_StatusReset() ) { + // 0: An ICC is present and active (power is on and stable, RST is inactive + ccidDriver.sCcidMessage.bStatus = 0; + } + else { + // 1: An ICC is present and inactive (not activated or shut down by hardware error) + ccidDriver.sCcidMessage.bStatus = 1; + } + + RDRtoPCParameters(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command resets the slot parameters to their default values +//------------------------------------------------------------------------------ +static void PCtoRDRResetParameters( void ) +{ + TRACE_DEBUG("PCtoRDRResetParameters\n\r"); + + ccidDriver.SlotStatus = ICC_NOT_PRESENT; + ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus; + + RDRtoPCParameters(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used to change the parameters for a given slot. +//------------------------------------------------------------------------------ +static void PCtoRDRSetParameters( void ) +{ + TRACE_DEBUG("PCtoRDRSetParameters\n\r"); + + ccidDriver.SlotStatus = ccidDriver.sCcidCommand.bSlot; + ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus; + // Not all feature supported + + RDRtoPCParameters(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command allows the CCID manufacturer to define and access extended +/// features. +/// Information sent via this command is processed by the CCID control logic. +//------------------------------------------------------------------------------ +static void PCtoRDREscape( void ) +{ + TRACE_DEBUG("PCtoRDREscape\n\r"); + + // If needed by the user + ISO7816_Escape(); + + // stub, return all value send + RDRtoPCEscape( ccidDriver.sCcidCommand.wLength, ccidDriver.sCcidCommand.APDU); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command stops or restarts the clock. +//------------------------------------------------------------------------------ +static void PCtoRDRICCClock( void ) +{ + TRACE_DEBUG("PCtoRDRICCClock\n\r"); + + if( 0 == ccidDriver.sCcidCommand.bSpecific_0 ) { + // restarts the clock + ISO7816_RestartClock(); + } + else { + // stop clock in the state shown in the bClockStop field + ISO7816_StopClock(); + } + + RDRtoPCSlotStatus( ); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command changes the parameters used to perform the transportation of +/// APDU messages by the T=0 protocol. +//------------------------------------------------------------------------------ +static void PCtoRDRtoAPDU( void ) +{ + unsigned char bmChanges; + unsigned char bClassGetResponse; + unsigned char bClassEnvelope; + + TRACE_DEBUG("PCtoRDRtoAPDU\n\r"); + + if( configurationDescriptorsFS.ccid.dwFeatures == (CCID_FEATURES_EXC_SAPDU|CCID_FEATURES_EXC_APDU) ) { + + bmChanges = ccidDriver.sCcidCommand.bSpecific_0; + bClassGetResponse = ccidDriver.sCcidCommand.bSpecific_1; + bClassEnvelope = ccidDriver.sCcidCommand.bSpecific_2; + + ISO7816_toAPDU(); + } + + RDRtoPCSlotStatus(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This is a command message to allow entering the PIN for verification or +/// modification. +//------------------------------------------------------------------------------ +static void PCtoRDRSecure( void ) +{ + TRACE_DEBUG("PCtoRDRSecure\n\r"); + + TRACE_DEBUG("For user\n\r"); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used to manage motorized type CCID functionality. +/// The Lock Card function is used to hold the ICC. +/// This prevents an ICC from being easily removed from the CCID. +/// The Unlock Card function is used to remove the hold initiated by the Lock +/// Card function +//------------------------------------------------------------------------------ +static void PCtoRDRMechanical( void ) +{ + TRACE_DEBUG("PCtoRDRMechanical\n\r"); + TRACE_DEBUG("Not implemented\n\r"); + + RDRtoPCSlotStatus(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used with the Control pipe Abort request to tell the CCID +/// to stop any current transfer at the specified slot and return to a state +/// where the slot is ready to accept a new command pipe Bulk-OUT message. +//------------------------------------------------------------------------------ +static void PCtoRDRAbort( void ) +{ + TRACE_DEBUG("PCtoRDRAbort\n\r"); + + RDRtoPCSlotStatus(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used to manually set the data rate and clock frequency of +/// a specific slot. +//------------------------------------------------------------------------------ +static void PCtoRDRSetDataRateAndClockFrequency( void ) +{ + unsigned int dwClockFrequency; + unsigned int dwDataRate; + + TRACE_DEBUG("PCtoRDRSetDatarateandClockFrequency\n\r"); + + dwClockFrequency = ccidDriver.sCcidCommand.APDU[0] + + (ccidDriver.sCcidCommand.APDU[1]<<8) + + (ccidDriver.sCcidCommand.APDU[2]<<16) + + (ccidDriver.sCcidCommand.APDU[3]<<24); + + dwDataRate = ccidDriver.sCcidCommand.APDU[4] + + (ccidDriver.sCcidCommand.APDU[5]<<8) + + (ccidDriver.sCcidCommand.APDU[6]<<16) + + (ccidDriver.sCcidCommand.APDU[7]<<24); + + ISO7816_SetDataRateandClockFrequency( dwClockFrequency, dwDataRate ); + + RDRtoPCDataRateAndClockFrequency( dwClockFrequency, dwDataRate ); + +} + +//------------------------------------------------------------------------------ +/// Report the CMD_NOT_SUPPORTED error to the host +//------------------------------------------------------------------------------ +static void vCCIDCommandNotSupported( void ) +{ + // Command not supported + // vCCIDReportError(CMD_NOT_SUPPORTED); + + TRACE_DEBUG("CMD_NOT_SUPPORTED\n\r"); + + // Header fields settings + ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_SLOTSTATUS; + ccidDriver.sCcidMessage.wLength = 0; + ccidDriver.sCcidMessage.bSpecific = 0; + + ccidDriver.sCcidMessage.bStatus |= ICC_CS_FAILED; + + // Send the response to the host + //vCCIDSendResponse(); +} + +//------------------------------------------------------------------------------ +/// Sent CCID response on USB +//------------------------------------------------------------------------------ +static void vCCIDSendResponse( void ) +{ + unsigned char bStatus; + + do { + bStatus = USBD_Write( CCID_EPT_DATA_IN, (void*)&ccidDriver.sCcidMessage, + ccidDriver.sCcidMessage.bSizeToSend, 0, 0 ); + } + while (bStatus != USBD_STATUS_SUCCESS); +} + + +//------------------------------------------------------------------------------ +/// Description: CCID Command dispatcher +//------------------------------------------------------------------------------ +static void CCIDCommandDispatcher( void ) +{ + unsigned char MessageToSend = 0; + + //TRACE_DEBUG("Command: 0x%X 0x%x 0x%X 0x%X 0x%X 0x%X 0x%X\n\r\n\r", + // (unsigned int)ccidDriver.sCcidCommand.bMessageType, + // (unsigned int)ccidDriver.sCcidCommand.wLength, + // (unsigned int)ccidDriver.sCcidCommand.bSlot, + // (unsigned int)ccidDriver.sCcidCommand.bSeq, + // (unsigned int)ccidDriver.sCcidCommand.bSpecific_0, + // (unsigned int)ccidDriver.sCcidCommand.bSpecific_1, + // (unsigned int)ccidDriver.sCcidCommand.bSpecific_2); + + // Check the slot number + if ( ccidDriver.sCcidCommand.bSlot > 0 ) { + + TRACE_ERROR("BAD_SLOT_NUMBER\n\r"); + } + + TRACE_DEBUG("typ=0x%X\n\r", ccidDriver.sCcidCommand.bMessageType); + + ccidDriver.sCcidMessage.bStatus = 0; + + ccidDriver.sCcidMessage.bSeq = ccidDriver.sCcidCommand.bSeq; + ccidDriver.sCcidMessage.bSlot = ccidDriver.sCcidCommand.bSlot; + + ccidDriver.sCcidMessage.bSizeToSend = sizeof(S_ccid_bulk_in_header)-(ABDATA_SIZE+1); + + + // Command dispatcher + switch ( ccidDriver.sCcidCommand.bMessageType ) { + + case PC_TO_RDR_ICCPOWERON: + PCtoRDRIccPowerOn(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ICCPOWEROFF: + PCtoRDRIccPowerOff(); + MessageToSend = 1; + break; + + case PC_TO_RDR_GETSLOTSTATUS: + PCtoRDRGetSlotStatus(); + MessageToSend = 1; + break; + + case PC_TO_RDR_XFRBLOCK: + PCtoRDRXfrBlock(); + MessageToSend = 1; + break; + + case PC_TO_RDR_GETPARAMETERS: + PCtoRDRGetParameters(); + MessageToSend = 1; + break; + + case PC_TO_RDR_RESETPARAMETERS: + PCtoRDRResetParameters(); + MessageToSend = 1; + break; + + case PC_TO_RDR_SETPARAMETERS: + PCtoRDRSetParameters(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ESCAPE: + PCtoRDREscape(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ICCCLOCK: + PCtoRDRICCClock(); + MessageToSend = 1; + break; + + case PC_TO_RDR_T0APDU: + // Only CCIDs reporting a short or extended APDU level in the dwFeatures + // field of the CCID class descriptor may take this command into account. + if( (CCID_FEATURES_EXC_SAPDU == (CCID_FEATURES_EXC_SAPDU&configurationDescriptorsFS.ccid.dwFeatures)) + || (CCID_FEATURES_EXC_APDU == (CCID_FEATURES_EXC_APDU &configurationDescriptorsFS.ccid.dwFeatures)) ) { + + // command supported + PCtoRDRtoAPDU(); + } + else { + // command not supported + TRACE_DEBUG("PC_TO_RDR_T0APDU\n\r"); + vCCIDCommandNotSupported(); + } + MessageToSend = 1; + break; + + case PC_TO_RDR_SECURE: + PCtoRDRSecure(); + MessageToSend = 1; + break; + + case PC_TO_RDR_MECHANICAL: + PCtoRDRMechanical(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ABORT: + PCtoRDRAbort(); + MessageToSend = 1; + break; + + case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY: + PCtoRDRSetDataRateAndClockFrequency(); + MessageToSend = 1; + break; + + default: + TRACE_DEBUG("default: 0x%X\n\r", ccidDriver.sCcidCommand.bMessageType); + vCCIDCommandNotSupported(); + MessageToSend = 1; + break; + + } + + if( MessageToSend == 1 ) { + vCCIDSendResponse(); + } +} + + +//------------------------------------------------------------------------------ +/// SETUP request handler for a CCID device +/// \param pRequest Pointer to a USBGenericRequest instance +//------------------------------------------------------------------------------ +static void CCID_RequestHandler(const USBGenericRequest *pRequest) +{ + TRACE_DEBUG("CCID_RHl\n\r"); + + // Check if this is a class request + if (USBGenericRequest_GetType(pRequest) == USBGenericRequest_CLASS) { + + // Check if the request is supported + switch (USBGenericRequest_GetRequest(pRequest)) { + + case CCIDGenericRequest_ABORT: + TRACE_DEBUG("CCIDGenericRequest_ABORT\n\r"); + break; + + case CCIDGenericRequest_GET_CLOCK_FREQUENCIES: + TRACE_DEBUG("Not supported\n\r"); + // A CCID with bNumClockSupported equal to 00h does not have + // to support this request + break; + + case CCIDGenericRequest_GET_DATA_RATES: + TRACE_DEBUG("Not supported\n\r"); + // A CCID with bNumDataRatesSupported equal to 00h does not have + // to support this request. + break; + + default: + TRACE_WARNING( "CCIDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(pRequest)); + USBD_Stall(0); + } + } + + else if (USBGenericRequest_GetType(pRequest) == USBGenericRequest_STANDARD) { + + // Forward request to the standard handler + USBDDriver_RequestHandler(&(ccidDriver.usbdDriver), pRequest); + } + else { + + // Unsupported request type + TRACE_WARNING( "CCIDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(pRequest)); + USBD_Stall(0); + } +} + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Optional callback re-implementation +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) +// not static function +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + CCID_RequestHandler(request); +} +#endif + + +//------------------------------------------------------------------------------ +/// Handles SmartCart request +//------------------------------------------------------------------------------ +void CCID_SmartCardRequest( void ) +{ + unsigned char bStatus; + + do { + + bStatus = CCID_Read( (void*)&ccidDriver.sCcidCommand, + sizeof(S_ccid_bulk_out_header), + (TransferCallback)&CCIDCommandDispatcher, + (void*)0 ); + } + while (bStatus != USBD_STATUS_SUCCESS); + +} + +//------------------------------------------------------------------------------ +/// Initializes the CCID device driver. +//------------------------------------------------------------------------------ +void CCIDDriver_Initialize( void ) +{ + TRACE_DEBUG("CCID_Init\n\r"); + USBDDriver_Initialize(&(ccidDriver.usbdDriver), + &ccidDriverDescriptors, + 0); // Multiple interface settings not supported + USBD_Init(); +} + +//------------------------------------------------------------------------------ +/// Reads data from the Data OUT endpoint +/// \param pBuffer Buffer to store the received data +/// \param dLength data buffer length +/// \param fCallback Optional callback function +/// \param pArgument Optional parameter for the callback function +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Read(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument) +{ + return USBD_Read(CCID_EPT_DATA_OUT, pBuffer, dLength, fCallback, pArgument); +} + +//------------------------------------------------------------------------------ +/// Sends data through the Data IN endpoint +/// \param pBuffer Buffer holding the data to transmit +/// \param dLength Length of data buffer +/// \param fCallback Optional callback function +/// \param pArgument Optional parameter for the callback function +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Write(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument) +{ + return USBD_Write(CCID_EPT_DATA_IN, pBuffer, dLength, fCallback, pArgument); +} + +//------------------------------------------------------------------------------ +/// Sends data through the interrupt endpoint, ICC insertion event +/// RDR_to_PC_NotifySlotChange +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Insertion( void ) +{ + TRACE_DEBUG("CCID_Insertion\n\r"); + + // Build the Interrupt-IN message + ccidDriver.BufferINT[0] = RDR_TO_PC_NOTIFYSLOTCHANGE; + ccidDriver.BufferINT[1] = ICC_INSERTED_EVENT; + ccidDriver.SlotStatus = ICC_INSERTED_EVENT; + + // Notify the host that a ICC is inserted + return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 2, 0, 0 ); +} + +//------------------------------------------------------------------------------ +/// Sends data through the interrupt endpoint, ICC removal event +/// RDR_to_PC_NotifySlotChange +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Removal( void ) +{ + TRACE_DEBUG("CCID_Removal\n\r"); + + // Build the Interrupt-IN message + ccidDriver.BufferINT[0] = RDR_TO_PC_NOTIFYSLOTCHANGE; + ccidDriver.BufferINT[1] = ICC_NOT_PRESENT; + ccidDriver.SlotStatus = ICC_NOT_PRESENT; + + // Notify the host that a ICC is inserted + return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 2, 0, 0 ); +} + +//------------------------------------------------------------------------------ +/// Interrupt-IN Messages +/// This message is sent when any bit in the bHardwareErrorCode field is set. +/// If this message is sent when there is no “outstanding” command, the bSeq +/// field will be undefined. +/// \param bSlot ICC slot number +/// \param bSeq Sequence number of the bulk OUT command when the hardware error +/// occured +/// \param bHardwareErrorCode Hardware error code +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char RDRtoPCHardwareError( unsigned char bSlot, + unsigned char bSeq, + unsigned char bHardwareErrorCode ) +{ + TRACE_DEBUG("RDRtoPCHardwareError\n\r"); + + // Build the Interrupt-IN message + ccidDriver.BufferINT[0] = RDR_TO_PC_HARDWAREERROR; + ccidDriver.BufferINT[1] = bSlot; + ccidDriver.BufferINT[2] = bSeq; + ccidDriver.BufferINT[3] = bHardwareErrorCode; + + // Notify the host that a ICC is inserted + return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 4, 0, 0 ); +} + + diff --git a/usb/device/ccid/cciddriver.h b/usb/device/ccid/cciddriver.h new file mode 100644 index 0000000..8bac286 --- /dev/null +++ b/usb/device/ccid/cciddriver.h @@ -0,0 +1,378 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// +/// !Purpose +/// +/// Definition of methods for using a CCID device driver. +/// +/// !Usage +/// +/// -# CCIDDriver_Initialize +/// -# CCID_Read +/// -# CCID_Write +/// -# CCID_SmartCardRequest +/// -# CCID_Insertion +/// -# CCID_Removal +/// -# RDRtoPCHardwareError +//------------------------------------------------------------------------------ + +#ifndef CCID_DRIVER_H +#define CCID_DRIVER_H + +/// For reference, the absolute maximum block size +/// for a TPDU T=0 block is 260 bytes (5 bytes command; 255 bytes data), or +/// for a TPDU T=1 block is 259 bytes, or +/// for a short APDU T=1 block is 261 bytes, or +/// for an extended APDU T=1 block is 65544 bytes. +#define ABDATA_SIZE 260 + +/// define protocol T=0 +#define PROTOCOL_TO 0 +/// define protocol T=1 +#define PROTOCOL_T1 1 + +/// define for dwFeatures see Table 5.1-1 Smart Card Device Class Descriptors +/// No special characteristics +#define CCID_FEATURES_NADA 0x00000000 +/// Automatic parameter configuration based on ATR data +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/// Automatic activation of ICC on inserting +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/// Automatic ICC voltage selection +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/// Automatic ICC clock frequency change according to active parameters provided +/// by the Host or self determined +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/// Automatic baud rate change according to active parameters provided by the +/// Host or self determined +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/// Automatic parameters negotiation made by the CCID (use of warm or cold +/// resets or PPS according to a manufacturer proprietary algorithm to select +/// the communication parameters with the ICC) +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/// Automatic PPS made by the CCID according to the active parameters +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/// CCID can set ICC in clock stop mode +#define CCID_FEATURES_ICCSTOP 0x00000100 +/// NAD value other than 00 accepted (T=1 protocol in use) +#define CCID_FEATURES_NAD 0x00000200 +/// Automatic IFSD exchange as first exchange (T=1 protocol in use) +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/// TPDU level exchanges with CCID +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/// Short APDU level exchange with CCID +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/// Short and Extended APDU level exchange with CCID +#define CCID_FEATURES_EXC_APDU 0x00040000 +/// USB Wake up signaling supported on card insertion and removal +#define CCID_FEATURES_WAKEUP 0x00100000 + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Bulk CCID Message header structure +typedef struct +{ + unsigned char bMessageType; + /// Message-specific data length + unsigned long wLength; + /// Identifies the slot number for this command + unsigned char bSlot; + /// Sequence number for command. + unsigned char bSeq; + /// Slot status register + unsigned char bStatus; + /// Slot error + unsigned char bError; + /// specific register + unsigned char bSpecific; + /// Data block sent to the CCID. + unsigned char abData[ABDATA_SIZE]; + unsigned char bSizeToSend; +} __attribute__ ((packed)) S_ccid_bulk_in_header; + +/// 6.1 Bulk Transfers +typedef struct +{ + unsigned char bMessageType; + /// Message-specific data length + unsigned long wLength; + /// Identifies the slot number for this command + unsigned char bSlot; + /// Sequence number for command. + unsigned char bSeq; + /// specific register + unsigned char bSpecific_0; + unsigned char bSpecific_1; + unsigned char bSpecific_2; + /// Application Protocol Data Unit + unsigned char APDU[ABDATA_SIZE]; +} __attribute__ ((packed)) S_ccid_bulk_out_header; + + +/// 6.1.11.2 PIN Verification Data Structure +typedef struct +{ + /// Number of seconds. + unsigned char bTimerOut; + /// Several parameters for the PIN format options + unsigned char bmFormatString; + /// Define the length of the PIN to present in the APDU command + unsigned char bmPINBlockString; + /// Allows the length PIN insertion in the APDU command + unsigned char bmPINLengthFormat; + /// Minimum PIN size in digit and Maximum PIN size in digit + unsigned char wPINMaxExtraDigit; + /// The value is a bit wise OR operation. + unsigned char bEntryValidationCondition; + /// Number of messages to display for the PIN modify command + unsigned char bNumberMessage; + /// Language used to display the messages. + unsigned char wLangId; + /// Message index in the Reader message table + unsigned char bMsgIndex; + /// T=1 I-block prologue field to use + unsigned char bTeoPrologue[3]; + /// APDU to send to the ICC + unsigned char abPINApdu[255]; +}__attribute__ ((packed)) S_ccid_PIN_Verification; + + +/// 6.1.11.7 PIN Modification Data Structure +typedef struct +{ + /// Number of seconds. If 00h then CCID default value is used. + unsigned char bTimeOut; + /// Several parameters for the PIN format options (defined in § 6.1.11.4) + unsigned char bmFormatString4; + /// Define the length of the PIN to present in the APDU command + unsigned char bmPINBlockString; + /// Allows the length PIN insertion in the APDU command (defined in § 6.1.11.6) + unsigned char bmPinLengthFormat; + /// Insertion position offset in byte for the current PIN + unsigned char bInsertionOffsetOld; + /// Insertion position offset in byte for the new PIN + unsigned char bInsertionOffsetNew; + /// XXYYh + /// XX: Minimum PIN size in digit + /// YY: Maximum PIN size in digit + unsigned char wPINMaxExtraDigit; + /// 00h,01h,02h,03h + /// Indicates if a confirmation is requested before acceptance of a new PIN (meaning that the user has to enter this new PIN twice before it is accepted) + /// Indicates if the current PIN must be entered and set in the same APDU field of not. + unsigned char bConfirmPIN; + /// The value is a bit wise OR operation. + /// 01h Max size reached + /// 02h Validation key pressed + /// 04h Timeout occurred + unsigned char bEntryValidationCondition; + /// 00h,01h,02h,03h,or FFh + /// Number of messages to display for the PIN modify command. + unsigned char bNumberMessage; + /// Language used to display the messages. The 16 bit + unsigned char wLangId; + /// Message index in the Reader message table (should be 00h or 01h). + unsigned char bMsgIndex1; + /// Message index in the Reader message table (should be 01h or 02h). + unsigned char bMsgIndex2; + /// Message index in the Reader message table (should be 02h). + unsigned char bMsgIndex3; + /// T=1 I-block prologue field to use. Significant only if protocol in use is T=1. + unsigned char bTeoPrologue[3]; + /// Byte array APDU to send to the ICC + unsigned char abPINApdu[255]; +}__attribute__ ((packed)) S_ccid_PIN_Modification; + +/// Protocol Data Structure for Protocol T=0 (bProtocolNum=0, dwLength=00000005h) +typedef struct +{ + /// B7-4 – FI – Index into the table 7 in ISO/IEC 7816-3:1997 selecting a + /// clock rate conversion factor + /// B3-0 – DI - Index into the table 8 in ISO/IEC 7816-3:1997 selecting a + /// baud rate conversion factor + unsigned char bmFindexDindex; + /// For T=0 ,B0 – 0b, B7-2 – 000000b + /// B1 – Convention used (b1=0 for direct, b1=1 for inverse) + unsigned char bmTCCKST0; // 0 to 2 + /// Extra Guardtime between two characters. Add 0 to 254 etu to the normal + /// guardtime of 12etu. FFh is the same as 00h. + unsigned char bGuardTimeT0; // 0 to FF + /// WI for T=0 used to define WWT + unsigned char bWaitingIntegerT0; // 0 to FF + /// ICC Clock Stop Support + /// 00 = Stopping the Clock is not allowed + /// 01 = Stop with Clock signal Low + /// 02 = Stop with Clock signal High + /// 03 = Stop with Clock either High or Low + unsigned char bClockStop; // 0 to 3 +} __attribute__ ((packed)) S_ccid_protocol_t0; + + +/// Protocol Data Structure for Protocol T=1 (bProtocolNum=1, dwLength=00000007h) +typedef struct +{ + /// B7-4 – FI – Index into the table 7 in ISO/IEC 7816-3:1997 selecting a + /// clock rate conversion factor + /// B3-0 – DI - Index into the table 8 in ISO/IEC 7816-3:1997 selecting a + /// baud rate conversion factor + unsigned char bmFindexDindex; + /// For T=1, B7-2 – 000100b + /// B0 – Checksum type (b0=0 for LRC, b0=1 for CRC + /// B1 – Convention used (b1=0 for direct, b1=1 for inverse) + unsigned char bmTCCKST1; // 10h, 11h, 12h, 13h + /// Extra Guardtime (0 to 254 etu between two characters). + /// If value is FFh, then guardtime is reduced by 1. + unsigned char bGuardTimeT1; // 0 to FF + /// B7-4 = BWI + /// B3-0 = CWI + unsigned char bmWaitingIntegersT1; // 0 to 9 + /// ICC Clock Stop Support + /// 00 = Stopping the Clock is not allowed + /// 01 = Stop with Clock signal Low + /// 02 = Stop with Clock signal High + /// 03 = Stop with Clock either High or Low + unsigned char bClockStop; // 0 to 3 + /// Size of negotiated IFSC + unsigned char bIFSC; // 0 to FE + /// Nad value used by CCID + unsigned char bNadValue; // 0 to FF +} __attribute__ ((packed)) S_ccid_protocol_t1; + + +/// Identifies the length of type of subordinate descriptors of a CCID device +/// Table 5.1-1 Smart Card Device Class descriptors +typedef struct +{ + /// Size of this descriptor, in bytes. + unsigned char bLength; + /// Functional Descriptor type + unsigned char bDescriptorType; + /// Integrated Circuit(s) Cards Interface Devices (CCID) Specification + /// Release Number + unsigned short bcdCCID; + /// Index of the highest available slot. An USB-ICC is regarded as a single + /// slot CCID. + unsigned char bMaxSlotIndex; + /// This value indicates what voltages the CCID can supply to its slots. + /// It is a bitwise OR operation performed on the following values: + /// - 01h 5.0V + /// - 02h 3.0V + /// - 04h 1.8V + /// Other bits are RFU. + unsigned char bVoltageSupport; + /// RRRR –Upper Word- is RFU = 0000h + /// PPPP –Lower Word- Encodes the supported protocol types. A ‘1’ in a given + /// bit position indicates support for the associated ISO protocol. + /// 0001h = Protocol T=0 + /// 0002h = Protocol T=1 + /// All other bits are reserved and must be set to zero. The field is + /// intended to correspond to the PCSC specification definitions. + unsigned long dwProtocols; + /// Default ICC clock frequency in KHz. This is an integer value. + unsigned long dwDefaultClock; + /// Maximum supported ICC clock frequency in KHz. This is an integer value. + unsigned long dwMaximumClock; + /// The number of clock frequencies that are supported by the CCID. If the + /// value is 00h, the supported clock frequencies are assumed to be the + /// default clock frequency defined by dwDefaultClock and the maximum clock + /// frequency defined by dwMaximumClock. + unsigned char bNumClockSupported; + /// Default ICC I/O data rate in bps. This is an integer value + unsigned long dwDataRate; + /// Maximum supported ICC I/O data rate in bps + unsigned long dwMaxDataRate; + /// The number of data rates that are supported by the CCID. + unsigned char bNumDataRatesSupported; + /// Indicates the maximum IFSD supported by CCID for protocol T=1. + unsigned long dwMaxIFSD; + /// - RRRR-Upper Word- is RFU = 0000h + /// - PPPP-Lower Word- encodes the supported protocol types. A ‘1’ in a given + /// bit position indicates support for the associated protocol. + /// 0001h indicates support for the 2-wire protocol 1 + /// 0002h indicates support for the 3-wire protocol 1 + /// 0004h indicates support for the I2C protocol 1 + /// All other values are outside of this specification, and must be handled + /// by vendor-supplied drivers. + unsigned long dwSynchProtocols; + /// The value is a bitwise OR operation performed on the following values: + /// - 00000000h No special characteristics + /// - 00000001h Card accept mechanism 2 + /// - 00000002h Card ejection mechanism 2 + /// - 00000004h Card capture mechanism 2 + /// - 00000008h Card lock/unlock mechanism + unsigned long dwMechanical; + /// This value indicates what intelligent features the CCID has. + unsigned long dwFeatures; + /// For extended APDU level the value shall be between 261 + 10 (header) and + /// 65544 +10, otherwise the minimum value is the wMaxPacketSize of the + /// Bulk-OUT endpoint. + unsigned long dwMaxCCIDMessageLength; + /// Significant only for CCID that offers an APDU level for exchanges. + unsigned char bClassGetResponse; + /// Significant only for CCID that offers an extended APDU level for exchanges. + unsigned char bClassEnvelope; + /// Number of lines and characters for the LCD display used to send messages for PIN entry. + unsigned short wLcdLayout; + /// This value indicates what PIN support features the CCID has. + unsigned char bPINSupport; + /// Maximum number of slots which can be simultaneously busy. + unsigned char bMaxCCIDBusySlots; + +} __attribute__ ((packed)) CCIDDescriptor; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char RDRtoPCHardwareError( unsigned char bSlot, + unsigned char bSeq, + unsigned char bHardwareErrorCode ); + +#if !defined(NOAUTOCALLBACK) +extern void USBDCallbacks_RequestReceived(const USBGenericRequest *pRequest); +#endif +extern void CCID_SmartCardRequest( void ); +extern void CCIDDriver_Initialize( void ); +extern unsigned char CCID_Read(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument); +extern unsigned char CCID_Write(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument); +extern unsigned char CCID_Insertion( void ); +extern unsigned char CCID_Removal( void ); + +#endif //#ifndef CCID_DRIVER_H + diff --git a/usb/device/ccid/cciddriverdescriptors.h b/usb/device/ccid/cciddriverdescriptors.h new file mode 100644 index 0000000..2daea67 --- /dev/null +++ b/usb/device/ccid/cciddriverdescriptors.h @@ -0,0 +1,152 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Title: cciddriverdescriptors.h +// +// About: Purpose +// Definitions of the descriptors required by the ccid device driver. +// DWG_Smart-Card_CCID_Rev110.pdf +//------------------------------------------------------------------------------ + +#ifndef CCID_DRIVER_DESCRIPTORS_H +#define CCID_DRIVER_DESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Constants: Endpoints +// CCID_EPT_DATA_OUT endpoint data out bulk 1 +// CCID_EPT_DATA_IN endpoint data in bulk 2 +// CCID_EPT_NOTIFICATION endpoint data interupt 3 +//------------------------------------------------------------------------------ +#define CCID_EPT_DATA_OUT 1 +#define CCID_EPT_DATA_IN 2 +#define CCID_EPT_NOTIFICATION 3 + +//------------------------------------------------------------------------------ +// USB-ICC protocol +//------------------------------------------------------------------------------ +// CCID specification version 1.10 +#define CCID1_10 0x0110 + +#define SMART_CARD_DEVICE_CLASS 0x0B +// Smart Card Device Class Descriptor Type +#define CCID_DECRIPTOR_TYPE 0x21 + +// Table 5.3-1 Summary of CCID Class Specific Request +#define CCIDGenericRequest_ABORT 0x01 +#define CCIDGenericRequest_GET_CLOCK_FREQUENCIES 0x02 +#define CCIDGenericRequest_GET_DATA_RATES 0x03 + +// 6.1 Command Pipe, Bulk-OUT Messages +#define PC_TO_RDR_ICCPOWERON 0x62 +#define PC_TO_RDR_ICCPOWEROFF 0x63 +#define PC_TO_RDR_GETSLOTSTATUS 0x65 +#define PC_TO_RDR_XFRBLOCK 0x6F +#define PC_TO_RDR_GETPARAMETERS 0x6C +#define PC_TO_RDR_RESETPARAMETERS 0x6D +#define PC_TO_RDR_SETPARAMETERS 0x61 +#define PC_TO_RDR_ESCAPE 0x6B +#define PC_TO_RDR_ICCCLOCK 0x6E +#define PC_TO_RDR_T0APDU 0x6A +#define PC_TO_RDR_SECURE 0x69 +#define PC_TO_RDR_MECHANICAL 0x71 +#define PC_TO_RDR_ABORT 0x72 +#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +// 6.2 Response Pipe, Bulk-IN Messages +#define RDR_TO_PC_DATABLOCK 0x80 +#define RDR_TO_PC_SLOTSTATUS 0x81 +#define RDR_TO_PC_PARAMETERS 0x82 +#define RDR_TO_PC_ESCAPE 0x83 +#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +// 6.3 Interrupt-IN Messages +#define RDR_TO_PC_NOTIFYSLOTCHANGE 0x50 +#define RDR_TO_PC_HARDWAREERROR 0x51 + +// Table 6.2-2 Slot error register when bmCommandStatus = 1 +#define CMD_ABORTED 0xFF +#define ICC_MUTE 0xFE +#define XFR_PARITY_ERROR 0xFD +#define XFR_OVERRUN 0xFC +#define HW_ERROR 0xFB +#define BAD_ATR_TS 0xF8 +#define BAD_ATR_TCK 0xF7 +#define ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define ICC_CLASS_NOT_SUPPORTED 0xF5 +#define PROCEDURE_BYTE_CONFLICT 0xF4 +#define DEACTIVATED_PROTOCOL 0xF3 +#define BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define PIN_TIMEOUT 0xF0 +#define PIN_CANCELLED 0xEF +#define CMD_SLOT_BUSY 0xE0 +// User defined 0xC0 to 0x81 +// Reserved for futur use 0x80 +// not supported incorrect message parameter 0x7F to 0x01 +// Command not supported 0x00 + +// CCID rev 1.1, p.27 +#define VOLTS_AUTO 0x00 +#define VOLTS_5_0 0x01 +#define VOLTS_3_0 0x02 +#define VOLTS_1_8 0x03 + +// 6.3.1 RDR_to_PC_NotifySlotChange +#define ICC_NOT_PRESENT 0x00 +#define ICC_PRESENT 0x01 +#define ICC_CHANGE 0x02 +#define ICC_INSERTED_EVENT ICC_PRESENT+ICC_CHANGE + +// ICCD: Table 6.1-8 Bitmap for bStatus field +#define ICC_BS_PRESENT_ACTIVATED 0x00 // USB-ICC is present and activated +#define ICC_BS_PRESENT_NOTACTIVATED 0x01 // USB-ICC is present but not activated +#define ICC_BS_NOTPRESENT 0x02 // USB-ICC is virtually not present +#define ICC_BS_RFU 0x03 // RFU +#define ICC_CS_NO_ERROR (0x00<<6) // Processed without error +#define ICC_CS_FAILED (0x01<<6) // Failed, error condition given by bError +#define ICC_CS_TIME_EXT (0x02<<6) // Time extension is requested +#define ICC_CS_RFU (0x03<<6) // RFU + +/* +#define NO_ERROR 0x00 +#define NO_EXTRA_BYTES 0x00 +#define CCID_FLAG_INITIAL_VALUE 0x05 +#define CCID_EVENT_SIZE 0x02 +#define STATUS_MASK 0x41 +*/ +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +#endif //#ifndef CCID_DRIVER_DESCRIPTORS_H + diff --git a/usb/device/cdc-serial/CDCDSerialDriver.c b/usb/device/cdc-serial/CDCDSerialDriver.c new file mode 100644 index 0000000..3e58592 --- /dev/null +++ b/usb/device/cdc-serial/CDCDSerialDriver.c @@ -0,0 +1,300 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: CDCDSerialDriver implementation + + About: Purpose + Implementation of the CDCDSerialDriver class methods. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "CDCDSerialDriver.h" +#include "CDCDSerialDriverDescriptors.h" +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USB driver for a CDC class implementing a virtual COM serial connection. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard USBDDriver instance. + USBDDriver usbdDriver; + /// Current line coding (baudrate, parity, stop bits). + CDCLineCoding lineCoding; + /// Indicates if the RS232 carrier is active. + unsigned char isCarrierActivated; + /// Current serial port states + unsigned short serialState; + +} CDCDSerialDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Static instance of the CDC serial driver. +static CDCDSerialDriver cdcdSerialDriver; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback function which should be invoked after the data of a +/// SetLineCoding request has been retrieved. Sends a zero-length packet +/// to the host for acknowledging the request. +//------------------------------------------------------------------------------ +static void CDCDSerialDriver_SetLineCodingCallback() +{ + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Receives new line coding information from the USB host. +//------------------------------------------------------------------------------ +static void CDCDSerialDriver_SetLineCoding() +{ + TRACE_INFO_WP("sLineCoding "); + + USBD_Read(0, + (void *) &(cdcdSerialDriver.lineCoding), + sizeof(CDCLineCoding), + (TransferCallback) CDCDSerialDriver_SetLineCodingCallback, + 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current line coding information to the host through Control +/// endpoint 0. +//------------------------------------------------------------------------------ +static void CDCDSerialDriver_GetLineCoding() +{ + TRACE_INFO_WP("gLineCoding "); + + USBD_Write(0, + (void *) &(cdcdSerialDriver.lineCoding), + sizeof(CDCLineCoding), + 0, + 0); +} + +//------------------------------------------------------------------------------ +/// Changes the state of the serial driver according to the information +/// sent by the host via a SetControlLineState request, and acknowledges +/// the request with a zero-length packet. +//------------------------------------------------------------------------------ +static void CDCDSerialDriver_SetControlLineState(unsigned char activateCarrier, + unsigned char isDTEPresent) +{ + TRACE_INFO_WP( + "sControlLineState(%d, %d) ", + activateCarrier, + isDTEPresent); + + cdcdSerialDriver.isCarrierActivated = activateCarrier; + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +// Optional RequestReceived() callback re-implementation +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) + +//------------------------------------------------------------------------------ +/// Re-implemented callback, invoked when a new USB Request is received. +//------------------------------------------------------------------------------ +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + CDCDSerialDriver_RequestHandler(request); +} + +#endif + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the USB Device CDC serial driver & USBD Driver. +//------------------------------------------------------------------------------ +void CDCDSerialDriver_Initialize() +{ + TRACE_INFO("CDCDSerialDriver_Initialize\n\r"); + + // Initialize Abstract Control Model attributes + CDCLineCoding_Initialize(&(cdcdSerialDriver.lineCoding), + 115200, + CDCLineCoding_ONESTOPBIT, + CDCLineCoding_NOPARITY, + 8); + cdcdSerialDriver.isCarrierActivated = 0; + cdcdSerialDriver.serialState = 0; + + // Initialize the standard driver + USBDDriver_Initialize(&(cdcdSerialDriver.usbdDriver), + &cdcdSerialDriverDescriptors, + 0); // Multiple settings for interfaces not supported + + // Initialize the USB driver + USBD_Init(); +} + +//------------------------------------------------------------------------------ +/// Handles CDC-specific SETUP requests. Should be called from a +/// re-implementation of USBDCallbacks_RequestReceived() method. +/// \param Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +void CDCDSerialDriver_RequestHandler(const USBGenericRequest *request) +{ + TRACE_INFO_WP("NewReq "); + + // Handle the request + switch (USBGenericRequest_GetRequest(request)) { + + case CDCGenericRequest_SETLINECODING: + + CDCDSerialDriver_SetLineCoding(); + break; + + case CDCGenericRequest_GETLINECODING: + + CDCDSerialDriver_GetLineCoding(); + break; + + case CDCGenericRequest_SETCONTROLLINESTATE: + + CDCDSerialDriver_SetControlLineState( + CDCSetControlLineStateRequest_ActivateCarrier(request), + CDCSetControlLineStateRequest_IsDtePresent(request)); + + break; + + default: + + USBDDriver_RequestHandler(&(cdcdSerialDriver.usbdDriver), request); + break; + } +} + +//------------------------------------------------------------------------------ +/// Receives data from the host through the virtual COM port created by +/// the CDC device serial driver. This function behaves like USBD_Read. +/// \param data Pointer to the data buffer to put received data. +/// \param size Size of the data buffer in bytes. +/// \param callback Optional callback function to invoke when the transfer +/// finishes. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the read operation has been started normally; +/// otherwise, the corresponding error code. +//------------------------------------------------------------------------------ +unsigned char CDCDSerialDriver_Read(void *data, + unsigned int size, + TransferCallback callback, + void *argument) +{ + return USBD_Read(CDCDSerialDriverDescriptors_DATAOUT, + data, + size, + callback, + argument); +} + +//------------------------------------------------------------------------------ +/// Sends a data buffer through the virtual COM port created by the CDC +/// device serial driver. This function behaves exactly like USBD_Write. +/// \param data Pointer to the data buffer to send. +/// \param size Size of the data buffer in bytes. +/// \param callback Optional callback function to invoke when the transfer +/// finishes. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the read operation has been started normally; +/// otherwise, the corresponding error code. +//------------------------------------------------------------------------------ +unsigned char CDCDSerialDriver_Write(void *data, + unsigned int size, + TransferCallback callback, + void *argument) +{ + return USBD_Write(CDCDSerialDriverDescriptors_DATAIN, + data, + size, + callback, + argument); +} + +//------------------------------------------------------------------------------ +/// Returns the current status of the RS-232 line. +//------------------------------------------------------------------------------ +unsigned short CDCDSerialDriver_GetSerialState() +{ + return cdcdSerialDriver.serialState; +} + +//------------------------------------------------------------------------------ +/// Sets the current serial state of the device to the given value. +/// \param serialState New device state. +//------------------------------------------------------------------------------ +void CDCDSerialDriver_SetSerialState(unsigned short serialState) +{ + ASSERT((serialState & 0xFF80) == 0, + "CDCDSerialDriver_SetSerialState: Bits D7-D15 are reserved\n\r"); + + // If new state is different from previous one, send a notification to the + // host + if (cdcdSerialDriver.serialState != serialState) { + + cdcdSerialDriver.serialState = serialState; + USBD_Write(CDCDSerialDriverDescriptors_NOTIFICATION, + &(cdcdSerialDriver.serialState), + 2, + 0, + 0); + + // Reset one-time flags + cdcdSerialDriver.serialState &= ~(CDCDSerialDriver_STATE_OVERRUN + | CDCDSerialDriver_STATE_PARITY + | CDCDSerialDriver_STATE_FRAMING + | CDCDSerialDriver_STATE_RINGSIGNAL + | CDCDSerialDriver_STATE_BREAK); + } +} + diff --git a/usb/device/cdc-serial/CDCDSerialDriver.h b/usb/device/cdc-serial/CDCDSerialDriver.h new file mode 100644 index 0000000..5ccc901 --- /dev/null +++ b/usb/device/cdc-serial/CDCDSerialDriver.h @@ -0,0 +1,117 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of a class for implementing a USB device CDC serial driver. + + !!!Usage + + -# Re-implement the USBDCallbacks_RequestReceived method to pass + received requests to CDCDSerialDriver_RequestHandler. *This is + automatically done unless the NOAUTOCALLBACK symbol is defined*. + -# Initialize the CDC serial and USB drivers using + CDCDSerialDriver_Initialize. + -# Logically connect the device to the host using USBD_Connect. + -# Send serial data to the USB host using CDCDSerialDriver_Write. + -# Receive serial data from the USB host using CDCDSerialDriver_Read. +*/ + +#ifndef CDCDSERIALDRIVER_H +#define CDCDSERIALDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "CDC Serial Port States" +/// This page lists the bit map for CDC Serial Port States. +/// +/// !BitMaps +/// - CDCDSerialDriver_STATE_RXDRIVER +/// - CDCDSerialDriver_STATE_TXCARRIER +/// - CDCDSerialDriver_STATE_BREAK +/// - CDCDSerialDriver_STATE_RINGSIGNAL +/// - CDCDSerialDriver_STATE_FRAMING +/// - CDCDSerialDriver_STATE_PARITY +/// - CDCDSerialDriver_STATE_OVERRUN + +/// Indicates the receiver carrier signal is present. +#define CDCDSerialDriver_STATE_RXDRIVER (1 << 0) +/// Indicates the transmission carrier signal is present. +#define CDCDSerialDriver_STATE_TXCARRIER (1 << 1) +/// Indicates a break has been detected. +#define CDCDSerialDriver_STATE_BREAK (1 << 2) +/// Indicates a ring signal has been detected. +#define CDCDSerialDriver_STATE_RINGSIGNAL (1 << 3) +/// Indicates a framing error has occured. +#define CDCDSerialDriver_STATE_FRAMING (1 << 4) +/// Indicates a parity error has occured. +#define CDCDSerialDriver_STATE_PARITY (1 << 5) +/// Indicates a data overrun error has occured. +#define CDCDSerialDriver_STATE_OVERRUN (1 << 6) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void CDCDSerialDriver_Initialize(); + +extern void CDCDSerialDriver_RequestHandler(const USBGenericRequest *request); + +extern unsigned char CDCDSerialDriver_Write( + void *data, + unsigned int size, + TransferCallback callback, + void *argument); + +extern unsigned char CDCDSerialDriver_Read( + void *data, + unsigned int size, + TransferCallback callback, + void *argument); + +extern unsigned short CDCDSerialDriver_GetSerialState(); + +extern void CDCDSerialDriver_SetSerialState(unsigned short serialState); + +#endif //#ifndef CDCSERIALDRIVER_H + diff --git a/usb/device/cdc-serial/CDCDSerialDriverDescriptors.c b/usb/device/cdc-serial/CDCDSerialDriverDescriptors.c new file mode 100644 index 0000000..8864451 --- /dev/null +++ b/usb/device/cdc-serial/CDCDSerialDriverDescriptors.c @@ -0,0 +1,661 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "CDCDSerialDriverDescriptors.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "CDC Serial Device IDs" +/// This page lists the IDs used in the CDC Serial Device Descriptor. +/// +/// !IDs +/// - CDCDSerialDriverDescriptors_PRODUCTID +/// - CDCDSerialDriverDescriptors_VENDORID +/// - CDCDSerialDriverDescriptors_RELEASE + +/// Device product ID. +#define CDCDSerialDriverDescriptors_PRODUCTID 0x6119 +/// Device vendor ID (Atmel). +#define CDCDSerialDriverDescriptors_VENDORID 0x03EB +/// Device release number. +#define CDCDSerialDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//------------------------------------------------------------------------------ +// Internal structures +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Configuration descriptor list for a device implementing a CDC serial driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + USBOtgDescriptor otgDescriptor; +#endif + /// Communication interface descriptor. + USBInterfaceDescriptor communication; + /// CDC header functional descriptor. + CDCHeaderDescriptor header; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor callManagement; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor abstractControlManagement; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor union1; + /// Notification endpoint descriptor. + USBEndpointDescriptor notification; + /// Data interface descriptor. + USBInterfaceDescriptor data; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor dataOut; + /// Data IN endpoint descriptor. + USBEndpointDescriptor dataIn; + +} __attribute__ ((packed)) CDCDSerialDriverConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Standard USB device descriptor for the CDC serial driver +const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + CDCDeviceDescriptor_CLASS, + CDCDeviceDescriptor_SUBCLASS, + CDCDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + CDCDSerialDriverDescriptors_VENDORID, + CDCDSerialDriverDescriptors_PRODUCTID, + CDCDSerialDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + CDCDeviceDescriptor_CLASS, + CDCDeviceDescriptor_SUBCLASS, + CDCDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +#endif + +/// Standard USB configuration descriptor for the CDC serial driver +const CDCDSerialDriverConfigurationDescriptors configurationDescriptorsFS = { + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CDCDSerialDriverConfigurationDescriptors), + 2, // There are two interfaces in this configuration + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + 0 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + 0, // Number of master interface is #0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 10 // Endpoint is polled every 10ms + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + } +}; + +/// Language ID string descriptor +const unsigned char languageIdStringDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Other-speed configuration descriptor (when in full-speed). +const CDCDSerialDriverConfigurationDescriptors otherSpeedDescriptorsFS = { + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(CDCDSerialDriverConfigurationDescriptors), + 2, // There are two interfaces in this configuration + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + 0 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + 0, // Number of master interface is #0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_HS), + 10 // Endpoint is polled every 10ms + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // Must be 0 for full-speed bulk endpoints + } +}; + + +/// Configuration descriptor (when in high-speed). +const CDCDSerialDriverConfigurationDescriptors configurationDescriptorsHS = { + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CDCDSerialDriverConfigurationDescriptors), + 2, // There are two interfaces in this configuration + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + 0 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + 0, // Number of master interface is #0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_HS), + 8 // Endpoint is polled every 16ms + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // Must be 0 for full-speed bulk endpoints + } +}; + +/// Other-speed configuration descriptor (when in high-speed). +const CDCDSerialDriverConfigurationDescriptors otherSpeedDescriptorsHS = { + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(CDCDSerialDriverConfigurationDescriptors), + 2, // There are two interfaces in this configuration + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + 0 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + 0, // Number of master interface is #0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 10 // Endpoint is polled every 10ms + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + } +}; +#endif + +/// Product string descriptor +const unsigned char productStringDescriptor[] = { + + USBStringDescriptor_LENGTH(13), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('U'), + USBStringDescriptor_UNICODE('S'), + USBStringDescriptor_UNICODE('B'), + USBStringDescriptor_UNICODE('S'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('r'), + USBStringDescriptor_UNICODE('i'), + USBStringDescriptor_UNICODE('a'), + USBStringDescriptor_UNICODE('l') +}; + +/// List of string descriptors used by the device +const unsigned char *stringDescriptors[] = { + + languageIdStringDescriptor, + productStringDescriptor, +}; + +/// List of standard descriptors for the serial driver. +USBDDriverDescriptors cdcdSerialDriverDescriptors = { + + &deviceDescriptor, + (USBConfigurationDescriptor *) &(configurationDescriptorsFS), +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (USBConfigurationDescriptor *) &(otherSpeedDescriptorsFS), + &deviceDescriptor, + (USBConfigurationDescriptor *) &(configurationDescriptorsHS), + &qualifierDescriptor, + (USBConfigurationDescriptor *) &(otherSpeedDescriptorsHS), +#else + 0, // No full-speed device qualifier descriptor + 0, // No full-speed other speed configuration + 0, // No high-speed device descriptor + 0, // No high-speed configuration descriptor + 0, // No high-speed device qualifier descriptor + 0, // No high-speed other speed configuration descriptor + +#endif + stringDescriptors, + 2 // 2 string descriptors in list +}; + diff --git a/usb/device/cdc-serial/CDCDSerialDriverDescriptors.h b/usb/device/cdc-serial/CDCDSerialDriverDescriptors.h new file mode 100644 index 0000000..8ab4edf --- /dev/null +++ b/usb/device/cdc-serial/CDCDSerialDriverDescriptors.h @@ -0,0 +1,77 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of the USB descriptors required by a CDC device serial + driver. +*/ + +#ifndef CDCDSERIALDRIVERDESCRIPTORS_H +#define CDCDSERIALDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "CDC Serial Endpoints" +/// This page lists the endpoints used in CDC Serial Device. +/// +/// !Endpoints +/// - CDCDSerialDriverDescriptors_DATAOUT +/// - CDCDSerialDriverDescriptors_DATAIN +/// - CDCDSerialDriverDescriptors_NOTIFICATION + +/// Data OUT endpoint number. +#define CDCDSerialDriverDescriptors_DATAOUT 1 +/// Data IN endpoint number. +#define CDCDSerialDriverDescriptors_DATAIN 2 +/// Notification endpoint number. +#define CDCDSerialDriverDescriptors_NOTIFICATION 3 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors for a CDC device serial driver. +extern USBDDriverDescriptors cdcdSerialDriverDescriptors; + +#endif //#ifndef CDCDDRIVERDESCRIPTORS_H + diff --git a/usb/device/cdc-serial/CDCarchitecture.png b/usb/device/cdc-serial/CDCarchitecture.png new file mode 100644 index 0000000..9d0e924 Binary files /dev/null and b/usb/device/cdc-serial/CDCarchitecture.png differ diff --git a/usb/device/cdc-serial/USB-SerialConverter.png b/usb/device/cdc-serial/USB-SerialConverter.png new file mode 100644 index 0000000..bc05c1f Binary files /dev/null and b/usb/device/cdc-serial/USB-SerialConverter.png differ diff --git a/usb/device/cdc-serial/cdc-serial.dir b/usb/device/cdc-serial/cdc-serial.dir new file mode 100644 index 0000000..ff18ab0 --- /dev/null +++ b/usb/device/cdc-serial/cdc-serial.dir @@ -0,0 +1,646 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB CDC +/// %device - USB CDC Serial Converter demo, to implement an USB Serial COM port +/// driver. +/// +/// !!!Contents +/// +/// There are two things for the implement of the USB CDC Serial %device driver: +/// - Implement the CDC Serial driver structs and functions for the %device, +/// to initialize, to handle CDC-specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// endpoints, +/// - Create the CDC Serial device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the %device as a USB CDC Serial COM port %device. +/// +/// For more information about what a particular group contains, please refer to +/// "USB CDC Serial Device". +//------------------------------------------------------------------------------ + +/** + \page "USB CDC Serial Device" + This page describes how to use the USB framework to produce a USB CDC Serial + Device driver, which appears as a virtual COM port on host. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Communication Device Class Documents (.zip file format) + - Abstract Control Model Serial Emulation (USB Class Definitions for + Communication Devices, section 3.6.2.1). + + !!!Communication Device Class + + You can get some basic information about the Communication Device Class. + + !!Purpose + + CDC is used to connect communication devices, such as modems (digital or + analog), telephones or networking devices. Its generic framework supports a + wide variety of physical layers (xDSL, ATM, etc.) and protocols. + + In this document, CDC is used to implement a USB to a serial data converter. + A USB to serial converter can be used in this case to bridge a legacy RS-232 + interface with a USB port. + + !!Architecture + ... + + !Communication Class Interface + The #Communication Class Interface# is used for %device management. It + includes requests to manage the %device state, its responses, as well as + event notifications. This interface can also be optionally used for call + management, i.e., setting up and terminating calls as well as managing + their parameters. + + The interface requires at least one endpoint (#Default EP0#) to used for + %device management. Optionally, another endpoint can be dedicated to + event notification. This will usually be an #Interrupt IN# endpoint. + + !Data Class Interface + The #Data Class Interface# is used for generic data transmissions. It provides + a means for a communication %device to actually transfer data to and from the + host. In addition, it also enables the multiplexing of data and commands on + the same interface, through the use of wrappers. + + %Endpoints for this interface must exist in pairs of the same type. This is + necessary to allow both IN and OUT communication. Only the #Bulk# and + #Isochronous# types can be used for these %endpoints. + + \image CDCArchitecture.png "CDC Class Driver Architecture" + + !Models + To account for the wide variety of existing communication devices, several + #models# have been defined, for more details you can refer to CDC spec. 1.1. + - POTS (Plain Old Telephone Service) + - Direct Line Control Model + - Datapump Model + - Abstract Control Model (ACM) + - Telephone + - Telephone Control Model + - ISDN + - Multi-Channel Model + - USB CAPI Model + - Networking + - Ethernet Networking Model + - ATM Networking Control Model + + !Class-specific Descriptors + CDC-specific information is described using Functional Descriptors. They + define various parameters of an interface, such as how the %device handles + call management, or model-specific attributes. + + Since the CDC specification defines quite a number of functional descriptors, + they are not detailed here. Instead, they are presented in the various case + studies of this document in which they are used. + + !!Host Drivers + Most Operating Systems (OS) now include generic drivers for a wide variety of + USB classes. This makes developing a %device simpler, since the host complexity + is now handled by the OS. Manufacturers can thus concentrate on the %device + itself, not on developing specific host drivers. + + Here is a brief list of the various CDC implementations supported by several + OS: + - Windows + - Abstract Control Model + - Remote NDIS + - Linux + - Abstract Control Model + - Ethernet Model + + !!!USB to Serial Converter + This section describes the implementation of the USB to serial converter using + the CDC class and the AT91 USB Device Framework. + + !!Bridging a Legacy Device and a Host with USB-Serial Converter + \image USB-SerialConverter.png + + !!Model + The CDC specification defines a model which suits this application perfectly: + the #Abstract Control Model (ACM)#. It implements the requests and + notifications necessary to communicate with an RS-232 interface. + + The Abstract Control Model requires two interfaces, one #Communication Class + Interface# and one #Data Class Interface#. Each of them must have two + associated endpoints. The former shall have one endpoint dedicated to %device + management (default Control endpoint 0) and one for events notification + (additional Interrupt IN endpoint). + + The Data Class Interface needs two endpoints through which to carry data to + and from the host. Depending on the application, these endpoints can either + be Bulk or Isochronous. In the case of a USB to serial converter, using Bulk + endpoints is probably more appropriate, since the reliability of the + transmission is important and the data transfers are not time-critical. + + !!Descriptors + The descriptors are modtly standard ones. The following code examples thus + use the structures described in the "AT91 USB device framework". + + For CDC-specific descriptors, some new types are defined: + - CDCHeaderDescriptor + - CDCCallManagementDescriptor + - CDCAbstractControlManagementDescriptor + - CDCUnionDescriptor + + All the descriptors can be found in CDCDSerialDriverDescriptors.c. + + !Device Descriptor + \code +const USBDeviceDescriptor deviceDescriptor = { + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + CDCDeviceDescriptor_CLASS, + CDCDeviceDescriptor_SUBCLASS, + CDCDeviceDescriptor_PROTOCOL, + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), + CDCDSerialDriverDescriptors_VENDORID, + CDCDSerialDriverDescriptors_PRODUCTID, + CDCDSerialDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + \endcode + The Vendor ID and Product ID fields are used to determine which driver to use + when the %device is enumerated. The Vendor ID is provided by the USB-IF + organization after registration; the product ID is completely vendor-specific. + In the example implementation provided with this document, the Atmel vendor ID + (03EBh) is used along with a custom product ID (6119h). + + The configuration descriptor is followed by interface, endpoint and class- + specific descriptors. +\code +const CDCDSerialDriverConfigurationDescriptors configurationDescriptors[]; +\endcode + + !Configuration Descriptor +\code + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CDCDSerialDriverConfigurationDescriptors), + 2, // There are two interfaces in this configuration + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +\endcode + + !Communication Class Interface Descriptor + The bInterfaceClass should be set to 0x02 and bInterfaceSubClass should be set + to 0x02. +\code + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, +\endcode + + !Functional - Header Descriptor +\code + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, +\endcode + + !Functional - Call Management Descriptor +\code + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + 0 // No associated data interface + }, +\endcode + + !Functional - Abstract Control Management Descriptor +\code + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, +\endcode + + !Functional - Union Descriptor +\code + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + 0, // Number of master interface is #0 + 1 // First slave interface is #1 + }, +\endcode + + !Notification Endpoint Descriptor + The EP is defined as CDCDSerialDriverDescriptors_NOTIFICATION. +\code + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_INTERRUPT, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE( + CDCDSerialDriverDescriptors_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 10 // Endpoint is polled every 10ms + }, +\endcode + + !Data Class Interface Descriptor +\code + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, +\endcode + + !Data Endpoint Descriptors + The EPs are defined as CDCDSerialDriverDescriptors_DATAOUT & + CDCDSerialDriverDescriptors_DATAIN. +\code + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE( + CDCDSerialDriverDescriptors_DATAOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE( + CDCDSerialDriverDescriptors_DATAIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, +\endcode + + !String Descriptors + Several descriptors (device, configuration, interface, etc.) can specify the + index of a string descriptor to comment their use. + + The actual string code is defined: + productStringDescriptor. + + !!Class-specific Requests + The CDC specification defines a set of #class-specific requests# for devices + implementing the ACM. This section details these requests. Please refer to + section 3.6.2.1 of the CDC spec. 1.1 for more information. + + !SetLineCoding, GetLineCoding + These requests are sent by the host to modify or retrieve the configuration of + the serial line, which includes: + - Baudrate + - Number of stop bits + - Parity check + - Number of data bits + + When the terminal application (such as HyperTerminal) on the host (PC) side + changes the setting of the COM port, a SetLineCoding request is sent with the + new parameters. The host may also retrieve the current setting using + GetLineCoding, not modifying them if they are correct. + + When a SET_LINE_CODING request is received, the %device should read the new + parameters. Then program the new parameters in the USART. A callback must be + provided to the USBD_Read function. + See CDCDSerialDriver_SetLineCoding. + + The code handling GET_LINE_CODING shall simply invoke the USBD_Write function + to send the current settings of the USART to the host. + See CDCDSerialDriver_GetLineCoding. + + !SetControlLineState + This request is sent by the host to notify the %device of two state changes. + The first bit (D0) of the wValue field of the request indicates whether or not + a terminal is connected to the virtual COM port. Bit D1 indicates that the + USART should enable/disable its carrier signal to start/stop receiving and + transmitting data. + + In practice, the USB to serial converter should operate only when those two + bits are set. Otherwise, it should not transmit or receive data. + + Since the SET_CONTROL_LINE_STATE request does not have a data payload, the + %device only has to acknowledge the request by sending a ZLP (zero-length + packet), using the USBD_Write method. + See CDCDSerialDriver_SetControlLineState. + + Before that, the wValue field should be parsed to retrieve the new control + line state. A single boolean variable can be used to keep track of the + connection state. If both the D0 and D1 bits are set, then the converter + should operate normally, i.e., forward data between the USART and the USB + host. Otherwise, it should stop its activity. + + !!Notifications + Notifications are sent by the %device when an event, such as a serial line + state change, has occurred. In this example, they are transmitted through a + dedicated Interrupt IN endpoint. A special header must precede the data + payload of each notification. This header has the same format of a SETUP + request, so the USBGenericRequest structure defined in the + "AT91 USB device framework" can be used. + + Note that the %device should only send a notification when there is a state + change, and not continuously. This does not really matter in practice, but + only sending notifications sporadically will reduce the stress on the %device. + + When the serial state is changed by CDCDSerialDriver_SetSerialState, the + notification is sent to the host. + + !!!CDC Serial Driver API + - CDCDSerialDriver_Initialize + - CDCDSerialDriver_RequestHandler + - CDCDSerialDriver_Read + - CDCDSerialDriver_Write + - CDCDSerialDriver_GetSerialState + - CDCDSerialDriver_SetSerialState + + !!!Main Application + The job of the main application is to bridge the USART and the USB. This means + that data read from one end must be forwarded to the other end. This section + describes several possibilities to do this. + + !!USB Operation + Reading data coming from the host is done using the CDCDSerialDriver_Read. + Since this is an asynchronous function, it does not block the execution flow. + This means that other actions (like reading data from the USART) can be + performed while the transfer is going on. Whenever some data is sent by the + host, the transfer terminates and the associated callback function is invoked. + This callback (UsbDataReceived) can be programmed to forward the received data + through the USART. + + Likewise, the CDCDSerialDriver_Write function can be called as soon as there + is data to transmit, again without block the program flow. However, there + cannot be two write operations at the same time, so the program must check + whether or not the last transfer is complete. This can be done by checking the + result code of the CDCDSerialDriver_Write method. If USB_STATUS_LOCKED is + returned, then there is already another operation in progress. The %device + will have to buffer the data retrieved from the USART until the endpoint + becomes free again. + + !!USART Operation + The USART peripheral present on AT91 chips can be used in two different ways. + The classic way is to read and write one byte at a time in the correct + registers to send and receive data. + + A more powerful method is available on AT91SAM chips, by using the embedded + Peripheral DMA Controller (PDC). The PDC can take care of transfers between + the processor, memory and %peripherals, thus freeing the processor to perform + other tasks. Since the PDC interrupt happens on the buffer full, Some timer + can be used to check if there is any data frags input from the USART. + + !!!Using a Generic Host Driver + See "USB CDC Serial Host Driver". + + !!!Add two or more ports in one USB device + See "USB Dual Port CDC Serial Device". + +*/ + +/** + \page "USB CDC Serial Host Driver" + Both Microsoft Windows and Linux offer a generic driver for using a USB to + serial converter %device. This page details the steps required to make use + of them. + + !!!Windows + On Microsoft Windows, the standard USB serial driver is named usbser.sys and + is part of the standard set of drivers. It has been available since Windows + 98SE. However, conversely to other generic driver such as the one for Mass + Storage Devices (MSD), usbser.sys is not automatically loaded when a CDC + %device is plugged in. + + !!Writing a Windows Driver File + For Windows to recognize the %device correctly, it is necessary to write a + .inf file. The Windows Driver Development Kit (DDK) contains information on + this topic. A basic driver, named 6119.inf in the example software provided, + will now be described. The driver file is made up of several sections. + + The first section of the .inf file must be the #[Version]# section. It + contains information about the driver version, provider, release data, and so + on. +\code +[Version] +Signature="$Chicago$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Provider=%ATMEL% +DriverVer=09/12/2006,1.1.1.1 +\endcode + + The Signature attribute is mandatory and can be either "$Windows 95$", + "$Windows NT$" or "$Chicago$", depending on which Windows version(s) the + driver supports. "$Chicago$" is used to notify that every Windows version is + supported. Since in this example, the USB to serial converter is a virtual COM + port, the Class attribute should be equal to "Ports". The value of ClassGuid + depends on which class the %device uses. The Provider value indicates that the + string descriptor for the driver provider will be defined further, under the + tag ATMEL. Finally, the last tag show the driver version and release date. For + the version number, each digit is optional (except the first one), but must + not be null if present. + + Next come two sections, #[SourceDisksNames]# and #[SourceDisksFiles]#. They + are used to specify the installation disks required and the location of each + needed files on these disks. But they are not implemented because the file + is offered by windows or its install disk automatically. +\code +;[SourceDisksNames] +;1="Windows Install CD" +;[SourceDisksFiles] +;usbser.sys=1 +\endcode + + The driver file must now specify where copied files will be stored, using the + #[DestinationDirs]# section. +\code +[DestinationDirs] +DefaultDestDir=12 +\endcode + The target directory must be identified by its ID, which is system-defined. + The ID for the drivers directory is 12. + + The #[Manufacturer]# section lists the possible manufacturers for all devices + supported by this driver. In this case, the only supported %device is an ATMEL + one, so this will be the only value. +\code +[Manufacturer] +%ATMEL%=AtmelMfg +\endcode + + The attribute must be a string tag; its value must be the name of the Models + section in which all supported devices from this manufacturer will be listed. + In this case, it will be named AtmelMfg, which is the next section. + + Each Models section must list the hardware ID of each supported %device. For + USB devices, the hardware ID is made up of the Vendor ID, the Product ID and + (optionally) the Device Release Number. Those values are extracted from the + %device descriptor provided during the enumeration phase. +\code +[AtmelMfg] +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6119 +\endcode + + The attribute name is again a string tag, which will be used to describe the + %device. The value is comprised of both the %device install section name + (USBtoSer.Install) and the hardware ID. The hardware ID is the same as the one + defined in "CDC Serial Device IDs". + + Now, the .inf file must detail the install section of each %device previously + listed. In this example, there is only one install section, named + #[USBtoSer.Install]#: +\code +[USBtoSer.Install] +CopyFiles=USBtoSer.CopyFiles +AddReg=USBtoSer.AddReg + +[USBtoSer.CopyFiles] +usbser.sys,,,0x00000002 + +[USBtoSer.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys + +[USBtoSer.Install.Services] +AddService=usbser,0x00000002,USBtoSer.AddService + +[USBtoSer.AddService] +DisplayName=%USBSer% +ServiceType=1r +StartType=3 +ServiceBinary=%12%\usbser.sys +\endcode + + The install section is actually divided in five. In the first section, two + other section names are specified: one for the list of files to copy, and one + for the keys to add to the Windows registry. There is only one file to copy, + usbser.sys; a flag (0x00000002) is used to specify that the user cannot skip + copying it. The registry keys are needed to install the driver on older + versions of Windows (such as Windows 98). For newer versions, the + #[USBtoSer.Install.Services]# registers the needed kernel services; each + service is actually listed in a section on its own. + + Finally, the last section, [Strings], defines all the string constants used + through this file: +\code +[Strings] +ATMEL="ATMEL Corp." +USBtoSerialConverter="AT91 USB to Serial Converter" +USBSer="USB Serial Driver" +\endcode + + !!Using the Driver + When a new %device is plugged in for the first time, Windows looks for an + appropriate specific or generic driver to use it. If it does not find one, the + user is asked what to do. + + This is the case with the USB to serial converter, since there is no generic + driver for it. To install the custom driver given in the previous section, + Windows must be told where to look for it. This can be done by selecting the + second option, "Install from a list or specific location", when the driver + installation wizards pops up. It will then ask for the directory where the + driver is located. After that, it should recognize the "AT91 USB to Serial + Converter" driver as an appropriate one and display it in the list. + + During the installation, the wizard asks for the location of the usbser.sys + file. If it is already installed on the system, it can be found in + "C:\Windows\System32\Drivers\". Otherwise, it is present on the Windows + installation CD. + + Once the driver is installed properly, a new COM port is added to the system + and can be used with HyperTerminal, for example. + + !!!Linux + Linux has two different generic drivers which are appropriate for a USB to + serial converter. The first one is an Abstract Control Model driver designed + for modem devices, and is simply named #acm#. The other one is a generic USB + to serial driver named #usbserial#. + + If the support for the #acm# driver has been compiled in the kernel, Linux + will automatically load it. A new terminal %device will be created under + /dev/ttyACMx. + + The usbserial driver must be loaded manually by using the modprobe command + with the vendor ID and product ID values used by the %device: +\code + modprobe usbserial vendor=0x03EB product=0x6119 +\endcode + + Once the driver is loaded, a new terminal entry appears and should be named + /dev/ttyUSBx. +*/ + +/** + \page "USB Dual Port CDC Serial Device" + +*/ diff --git a/usb/device/cdc-serial/drv/6119.inf b/usb/device/cdc-serial/drv/6119.inf new file mode 100644 index 0000000..14bd8d3 --- /dev/null +++ b/usb/device/cdc-serial/drv/6119.inf @@ -0,0 +1,45 @@ +; $Id: 6119.inf,v 1.1.2.1 2006/12/05 08:33:25 danielru Exp $ + +[Version] ; Version section +Signature="$Chicago$" ; All Windows versions +Class=Ports ; This is a serial port driver +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} ; Associated GUID +Provider=%ATMEL% ; Driver is provided by ATMEL +DriverVer=09/12/2006,1.1.1.5 ; Driver version 1.1.1.5 published on 23 February 2007 + +[DestinationDirs] ; DestinationDirs section +DefaultDestDir=12 ; Default install directory is \drivers or \IOSubSys + +[Manufacturer] ; Manufacturer section +%ATMEL%=AtmelMfg ; Only one manufacturer (ATMEL), models section is named + ; AtmelMfg + +[AtmelMfg] ; Models section corresponding to ATMEL +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6119 ; Identifies a device with ATMEL Vendor ID (03EBh) and + ; Product ID equal to 6119h. Corresponding Install section + ; is named USBtoSer.Install + +[USBtoSer.Install] ; Install section +include=mdmcpq.inf +CopyFiles=FakeModemCopyFileSection +AddReg=USBtoSer.AddReg ; Registry keys to add are listed in USBtoSer.AddReg + +[USBtoSer.AddReg] ; AddReg section +HKR,,DevLoader,,*ntkern ; +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[USBtoSer.Install.Services] ; Services section +AddService=usbser,0x00000002,USBtoSer.AddService ; Assign usbser as the PnP driver for the device + +[USBtoSer.AddService] ; Service install section +DisplayName=%USBSer% ; Name of the serial driver +ServiceType=1 ; Service kernel driver +StartType=3 ; Driver is started by the PnP manager +ErrorControl=1 ; Warn about errors +ServiceBinary=%12%\usbser.sys ; Driver filename + +[Strings] ; Strings section +ATMEL="ATMEL Corp." ; String value for the ATMEL symbol +USBtoSerialConverter="AT91 USB to Serial Converter" ; String value for the USBtoSerialConverter symbol +USBSer="USB Serial Driver" ; String value for the USBSer symbol \ No newline at end of file diff --git a/usb/device/cdc-serial/drv/drv.dir b/usb/device/cdc-serial/drv/drv.dir new file mode 100644 index 0000000..a2ff78a --- /dev/null +++ b/usb/device/cdc-serial/drv/drv.dir @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides install file for a CDC serial port %device. +/// +/// !!!Contents +/// +/// 6119.inf +/// +//------------------------------------------------------------------------------ diff --git a/usb/device/composite/AUDDFunctionDriver.c b/usb/device/composite/AUDDFunctionDriver.c new file mode 100644 index 0000000..a1645aa --- /dev/null +++ b/usb/device/composite/AUDDFunctionDriver.c @@ -0,0 +1,282 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +#include +// USB +#include +// AUDIO +#include +#include + +#include "AUDDFunctionDriver.h" +#include "AUDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// USB audio speaker driver instance. +static AUDDSpeakerChannel auddSpeakerChannels[AUDD_NUMCHANNELS+1]; +/// Intermediate storage variable for the mute status of a channel. +static unsigned char muted; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Changes the mute status of the given channel accordingly. +/// \param channel Number of the channel whose mute status has changed. +//----------------------------------------------------------------------------- +static void AUDD_MuteReceived(unsigned char channel) +{ + AUDDSpeakerChannel *pChannel = &auddSpeakerChannels[channel]; + if (muted) { + + if (!pChannel->muted) { + + pChannel->muted = 1; + AUDDSpeakerChannel_MuteChanged(pChannel, 1); + } + } + else { + + if (pChannel->muted) { + + pChannel->muted = 0; + AUDDSpeakerChannel_MuteChanged(pChannel, 0); + } + } + + USBD_Write(0, 0, 0, 0, 0); +} + +//----------------------------------------------------------------------------- +/// Sets the current value of a particular Feature control of a channel. +/// \param channel Number of the channel whose feature will change. +/// \param control The feature control that will change. +/// \param length The feature data size. +//----------------------------------------------------------------------------- +static void AUDD_SetFeatureCurrentValue(unsigned char channel, + unsigned char control, + unsigned short length) +{ + TRACE_INFO_WP("sFeature "); + TRACE_DEBUG("\b(CS%d, CN%d, L%d) ", control, channel, length); + + // Check the the requested control is supported + // Mute control on master channel + if ((control == AUDFeatureUnitRequest_MUTE) + && (channel < (AUDD_NUMCHANNELS+1)) + && (length == 1)) { + + unsigned int argument = channel; // Avoids compiler warning + USBD_Read(0, // Endpoint #0 + &muted, + sizeof(muted), + (TransferCallback) AUDD_MuteReceived, + (void *) argument); + } + // Control/channel combination not supported + else { + + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Sends the current value of a particular channel Feature to the USB host. +/// \param channel Number of the channel whose feature will be sent. +/// \param control The feature control that will be sent. +/// \param length The feature data size. +//----------------------------------------------------------------------------- +static void AUDD_GetFeatureCurrentValue(unsigned char channel, + unsigned char control, + unsigned char length) +{ + TRACE_INFO_WP("gFeature "); + TRACE_DEBUG("\b(CS%d, CN%d, L%d) ", control, channel, length); + + // Check that the requested control is supported + // Master channel mute control + if ((control == AUDFeatureUnitRequest_MUTE) + && (channel < (AUDD_NUMCHANNELS+1)) + && (length == 1)) { + + muted = auddSpeakerChannels[channel].muted; + USBD_Write(0, &muted, sizeof(muted), 0, 0); + } + else { + + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes an USB audio speaker function driver +//----------------------------------------------------------------------------- +void AUDDFunctionDriver_Initialize() +{ + auddSpeakerChannels[0].muted = 0; + auddSpeakerChannels[0].number = AUDD_MASTERCHANNEL; + auddSpeakerChannels[1].muted = 0; + auddSpeakerChannels[1].number = AUDD_LEFTCHANNEL; + auddSpeakerChannels[2].muted = 0; + auddSpeakerChannels[2].number = AUDD_RIGHTCHANNEL; + + // Initialize the third LED to indicate when the audio interface is active + LED_Configure(USBD_LEDOTHER); +} + +//----------------------------------------------------------------------------- +/// Handles AUDIO-specific USB requests sent by the host +/// \param request Pointer to a USBGenericRequest instance. +/// \return 0 if the request is Unsupported, 1 if the request handled. +//----------------------------------------------------------------------------- +unsigned char AUDDFunctionDriver_RequestHandler( + const USBGenericRequest *request) +{ + unsigned char entity; + unsigned char interface; + + // Check if the request is supported + switch (USBGenericRequest_GetRequest(request)) { + + case AUDGenericRequest_SETCUR: + TRACE_INFO_WP( + "sCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if ((entity == AUDD_Descriptors_FEATUREUNIT) + && (interface == AUDD_Descriptors_CONTROL)) { + + AUDD_SetFeatureCurrentValue( + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDSpeakerDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + case AUDGenericRequest_GETCUR: + TRACE_INFO_WP( + "gCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if ((entity == AUDD_Descriptors_FEATUREUNIT) + && (interface == AUDD_Descriptors_CONTROL)) { + + AUDD_GetFeatureCurrentValue( + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDSpeakerDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + default: + return 0; + } + return 1; +} + +//----------------------------------------------------------------------------- +/// Invoked whenever the active setting of an interface is changed by the +/// host. Changes the status of the third LED accordingly. +/// \param interface Interface number. +/// \param setting Newly active setting. +//----------------------------------------------------------------------------- +void AUDDFunctionCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting) +{ + if ((interface == AUDD_Descriptors_STREAMING) && (setting == 0)) { + + LED_Clear(USBD_LEDOTHER); + } + else { + + LED_Set(USBD_LEDOTHER); + } +} + +//----------------------------------------------------------------------------- +/// Reads incoming audio data sent by the USB host into the provided buffer. +/// When the transfer is complete, an optional callback function is invoked. +/// \param buffer Pointer to the data storage buffer. +/// \param length Size of the buffer in bytes. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +/// \return if the transfer is started successfully; +/// otherwise an error code. +//----------------------------------------------------------------------------- +unsigned char AUDDSpeakerDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument) +{ + return USBD_Read(AUDD_Descriptors_DATAOUT, + buffer, + length, + callback, + argument); +} + +#endif // (AUDIO defined) + diff --git a/usb/device/composite/AUDDFunctionDriver.h b/usb/device/composite/AUDDFunctionDriver.h new file mode 100644 index 0000000..637fc67 --- /dev/null +++ b/usb/device/composite/AUDDFunctionDriver.h @@ -0,0 +1,105 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef AUDDFUNCTIONDRIVER_H +#define AUDDFUNCTIONDRIVER_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Sample rate in Hz. +#define AUDD_SAMPLERATE 48000 +/// Number of channels in audio stream. +#define AUDD_NUMCHANNELS 2 +/// Number of bytes in one sample. +#define AUDD_BYTESPERSAMPLE 2 +/// Number of bits in one sample. +#define AUDD_BITSPERSAMPLE (AUDD_BYTESPERSAMPLE * 8) +/// Number of samples in one USB subframe. +#define AUDD_BYTESPERSUBFRAME (AUDD_NUMCHANNELS * AUDD_BYTESPERSAMPLE) +/// Number of samples in one USB frame. +#define AUDD_SAMPLESPERFRAME (AUDD_SAMPLERATE / 1000 * AUDD_NUMCHANNELS) +/// Number of bytes in one USB frame. +#define AUDD_BYTESPERFRAME (AUDD_SAMPLESPERFRAME * AUDD_BYTESPERSAMPLE) +/// Master channel. +#define AUDD_MASTERCHANNEL 0 +/// Front left channel. +#define AUDD_LEFTCHANNEL 1 +/// Front right channel. +#define AUDD_RIGHTCHANNEL 2 + +//----------------------------------------------------------------------------- +// Structs +//----------------------------------------------------------------------------- + +/// AUDIO Speaker channel struct +typedef struct { + + unsigned char number; + unsigned char muted; + +} AUDDSpeakerChannel; + +//----------------------------------------------------------------------------- +// Callbacks +//----------------------------------------------------------------------------- +extern void AUDDSpeakerChannel_MuteChanged(AUDDSpeakerChannel *channel, + unsigned char muted); + +extern void AUDDFunctionCallbacks_InterfaceSettingChanged( + unsigned char interface, + unsigned char setting); + + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//- Function API For composite device +extern void AUDDFunctionDriver_Initialize(); + +extern unsigned char AUDDFunctionDriver_RequestHandler( + const USBGenericRequest * request); + +//- AUDIO Speaker API +extern unsigned char AUDDSpeakerDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument); + +#endif // #define AUDDFUNCTIONDRIVER_H + diff --git a/usb/device/composite/AUDDFunctionDriverDescriptors.h b/usb/device/composite/AUDDFunctionDriverDescriptors.h new file mode 100644 index 0000000..a5a64f4 --- /dev/null +++ b/usb/device/composite/AUDDFunctionDriverDescriptors.h @@ -0,0 +1,69 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef AUDDFUNCTIONDRIVERDESCRIPTORS_H +#define AUDDFUNCTIONDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// COMPOSITE option +#if defined(usb_CDCAUDIO) +#define AUDD_Descriptors_INTERFACE 2 +#elif defined(usb_HIDAUDIO) +#define AUDD_Descriptors_INTERFACE 1 +#endif + +/// - Endpoint numbers +/// Data out endpoint number +#define AUDD_Descriptors_DATAOUT 0x05 + +/// - Interface IDs +/// Audio control interface ID +#define AUDD_Descriptors_CONTROL (AUDD_Descriptors_INTERFACE + 0) +/// Audio streaming interface ID +#define AUDD_Descriptors_STREAMING (AUDD_Descriptors_INTERFACE + 1) + +/// - Entity IDs +/// Input terminal ID. +#define AUDD_Descriptors_INPUTTERMINAL 0 +/// Output terminal ID. +#define AUDD_Descriptors_OUTPUTTERMINAL 1 +/// Feature unit ID. +#define AUDD_Descriptors_FEATUREUNIT 2 + +#endif // #define AUDDFUNCTIONDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/CDCDFunctionDriver.c b/usb/device/composite/CDCDFunctionDriver.c new file mode 100644 index 0000000..4e42015 --- /dev/null +++ b/usb/device/composite/CDCDFunctionDriver.c @@ -0,0 +1,337 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +// USB +#include +#include +// CDC +#include +#include + +#include "CDCDFunctionDriver.h" +#include "CDCDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +/// CDC Function Driver Struct +typedef struct { + USBDDriver * pUsbdDriver; + CDCDSerialPort * pCdcPorts; + unsigned char numPorts; +} CDCFunDriver; + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// CDC Function Driver instance +static CDCFunDriver cdcFunDriver; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Callback function which should be invoked after the data of a +/// SetLineCoding request has been retrieved. Sends a zero-length packet +/// to the host for acknowledging the request. +//----------------------------------------------------------------------------- +static void CDCD_SetLineCodingCallback() +{ + USBD_Write(0, 0, 0, 0, 0); +} + +//----------------------------------------------------------------------------- +/// Return the port index that host send this request for. +//----------------------------------------------------------------------------- +static char CDCD_GetSerialPort(const USBGenericRequest *request) +{ + unsigned char i; + for (i = 0; i < cdcFunDriver.numPorts; i ++) { + if (request->wIndex == cdcFunDriver.pCdcPorts[i].interfaceNum + 1) + return i; + } + return 0xFF; +} + +//----------------------------------------------------------------------------- +/// Receives new line coding information from the USB host. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +static void CDCD_SetLineCoding(const USBGenericRequest *request) +{ + unsigned char serial; + serial = CDCD_GetSerialPort(request); + + TRACE_INFO_WP("sLineCoding_%d ", serial); + + USBD_Read(0, + (void *) &(cdcFunDriver.pCdcPorts[serial].lineCoding), + sizeof(CDCLineCoding), + (TransferCallback) CDCD_SetLineCodingCallback, + 0); +} + +//----------------------------------------------------------------------------- +/// Sends the current line coding information to the host through Control +/// endpoint 0. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +static void CDCD_GetLineCoding(const USBGenericRequest *request) +{ + unsigned char serial; + serial = CDCD_GetSerialPort(request); + + TRACE_INFO_WP("gLineCoding_%d ", serial); + + USBD_Write(0, + (void *) &(cdcFunDriver.pCdcPorts[serial].lineCoding), + sizeof(CDCLineCoding), + 0, + 0); +} + +//----------------------------------------------------------------------------- +/// Changes the state of the serial driver according to the information +/// sent by the host via a SetControlLineState request, and acknowledges +/// the request with a zero-length packet. +/// \param request Pointer to a USBGenericRequest instance. +/// \param activateCarrier The active carrier state to set. +/// \param isDTEPresent The DTE status. +//----------------------------------------------------------------------------- +static void CDCD_SetControlLineState(const USBGenericRequest *request, + unsigned char activateCarrier, + unsigned char isDTEPresent) +{ + unsigned char serial; + serial = CDCD_GetSerialPort(request); + + TRACE_INFO_WP( + "sControlLineState_%d(%d, %d) ", + serial, + activateCarrier, + isDTEPresent); + + cdcFunDriver.pCdcPorts[serial].isCarrierActivated = activateCarrier; + USBD_Write(0, 0, 0, 0, 0); +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the USB device CDC serial function driver. +//----------------------------------------------------------------------------- +void CDCDFunctionDriver_Initialize(USBDDriver * pUsbdDriver, + CDCDSerialPort * pCdcPorts, + unsigned char numPorts) +{ + unsigned char serial; + + TRACE_INFO("CDCDFunctionDriver_Initialize\n\r"); + + cdcFunDriver.pUsbdDriver = pUsbdDriver; + cdcFunDriver.pCdcPorts = pCdcPorts; + cdcFunDriver.numPorts = numPorts; + + for (serial = 0; serial < numPorts; serial ++) { + + CDCDSerialPort * pSerial = &cdcFunDriver.pCdcPorts[serial]; + + // Initialize Abstract Control Model attributes + CDCLineCoding_Initialize(&(pSerial->lineCoding), + 115200, + CDCLineCoding_ONESTOPBIT, + CDCLineCoding_NOPARITY, + 8); + pSerial->isCarrierActivated = 0; + pSerial->serialState = 0; + } +} + +//----------------------------------------------------------------------------- +/// Handles CDC/ACM-specific USB requests sent by the host +/// \param request Pointer to a USBGenericRequest instance. +/// \return 0 if the request is Unsupported, 1 if the request handled. +//----------------------------------------------------------------------------- +unsigned char CDCDFunctionDriver_RequestHandler( + const USBGenericRequest *request) +{ + switch (USBGenericRequest_GetRequest(request)) { + + case CDCGenericRequest_SETLINECODING: + + CDCD_SetLineCoding(request); + break; + + case CDCGenericRequest_GETLINECODING: + + CDCD_GetLineCoding(request); + break; + + case CDCGenericRequest_SETCONTROLLINESTATE: + + CDCD_SetControlLineState(request, + CDCSetControlLineStateRequest_ActivateCarrier(request), + CDCSetControlLineStateRequest_IsDtePresent(request)); + + break; + + // Unsupported request + default: + return 0; + + } + return 1; +} + +//----------------------------------------------------------------------------- +/// Receives data from the host through the virtual COM port created by +/// the CDC function serial driver. This function behaves like . +/// \param Port Port index to receive. +/// \param Pointer to the data buffer to send. +/// \param Size of the data buffer in bytes. +/// \param callback Optional callback function to invoke when the transfer +/// finishes. +/// \param argument Optional argument to the callback function. +/// \return if the read operation started normally; +/// otherwise, the corresponding error code. +//----------------------------------------------------------------------------- +unsigned char CDCDSerialDriver_Read(unsigned char port, + void *data, + unsigned int size, + TransferCallback callback, + void *argument) +{ + unsigned char ep = cdcFunDriver.pCdcPorts[port].bulkOutEndpoint; + + if (port > cdcFunDriver.numPorts) + return USBD_STATUS_INVALID_PARAMETER; + + return USBD_Read(ep, + data, + size, + callback, + argument); +} + +//----------------------------------------------------------------------------- +/// Sends a data buffer through the virtual COM port created by the CDC +/// function serial driver. This function behaves exactly like . +/// \param port Port index to receive. +/// \param data - Pointer to the data buffer to send. +/// \param size - Size of the data buffer in bytes. +/// \param callback - Optional callback function to invoke when the transfer +/// finishes. +/// \param argument - Optional argument to the callback function. +/// \return if the write operation started normally; +/// otherwise, the corresponding error code. +//----------------------------------------------------------------------------- +unsigned char CDCDSerialDriver_Write(unsigned char port, + void *data, + unsigned int size, + TransferCallback callback, + void *argument) +{ + unsigned char ep = cdcFunDriver.pCdcPorts[port].bulkInEndpoint; + + if (port > cdcFunDriver.numPorts) + return USBD_STATUS_INVALID_PARAMETER; + + return USBD_Write(ep, + data, + size, + callback, + argument); +} + +//------------------------------------------------------------------------------ +/// Returns the current status of the RS-232 line. +/// \param port The port number that checked. +//------------------------------------------------------------------------------ +unsigned short CDCDSerialDriver_GetSerialState(unsigned char port) +{ + if (port > cdcFunDriver.numPorts) + return USBD_STATUS_INVALID_PARAMETER; + + return cdcFunDriver.pCdcPorts[port].serialState; +} + +//------------------------------------------------------------------------------ +/// Sets the current serial state of the device to the given value. +/// \param port The port number that the port state should be changed. +/// \param serialState New device state. +//------------------------------------------------------------------------------ +void CDCDSerialDriver_SetSerialState(unsigned char port, + unsigned short serialState) +{ + CDCDSerialPort * pPort; + unsigned char ep; + + ASSERT((serialState & 0xFF80) == 0, + "CDCDSerialDriver_SetSerialState: Bits D7-D15 are reserved!\n\r"); + + if (port > cdcFunDriver.numPorts) + return; + + // If new state is different from previous one, send a notification to the + // host + pPort = &cdcFunDriver.pCdcPorts[port]; + ep = pPort->interruptInEndpoint; + if (pPort->serialState != serialState) { + + pPort->serialState = serialState; + USBD_Write(ep, + &(pPort->serialState), + 2, + 0, + 0); + + // Reset one-time flags + pPort->serialState &= ~(CDCD_STATE_OVERRUN + | CDCD_STATE_PARITY + | CDCD_STATE_FRAMING + | CDCD_STATE_RINGSIGNAL + | CDCD_STATE_BREAK); + } +} diff --git a/usb/device/composite/CDCDFunctionDriver.h b/usb/device/composite/CDCDFunctionDriver.h new file mode 100644 index 0000000..0a9e5d2 --- /dev/null +++ b/usb/device/composite/CDCDFunctionDriver.h @@ -0,0 +1,128 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef CDCDFUNCTIONDRIVER_H +#define CDCDFUNCTIONDRIVER_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Indicates the receiver carrier signal is present. +#define CDCD_STATE_RXDRIVER (1 << 0) +/// Indicates the transmission carrier signal is present. +#define CDCD_STATE_TXCARRIER (1 << 1) +/// Indicates a break has been detected. +#define CDCD_STATE_BREAK (1 << 2) +/// Indicates a ring signal has been detected. +#define CDCD_STATE_RINGSIGNAL (1 << 3) +/// Indicates a framing error has occured. +#define CDCD_STATE_FRAMING (1 << 4) +/// Indicates a parity error has occured. +#define CDCD_STATE_PARITY (1 << 5) +/// Indicates a data overrun error has occured. +#define CDCD_STATE_OVERRUN (1 << 6) + +//----------------------------------------------------------------------------- +// Structs +//----------------------------------------------------------------------------- + +/// CDC Serial port struct +typedef struct _CDCDSerialPort{ + + /// USB interface settings + unsigned char interfaceNum; + unsigned char interruptInEndpoint; + unsigned char bulkInEndpoint; + unsigned char bulkOutEndpoint; + /// serial port settings + CDCLineCoding lineCoding; + unsigned char isCarrierActivated; + unsigned short serialState; + +} CDCDSerialPort; + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +#define CDCDFunctionDriver_ConfigurePort(pPort,ifNum,intIN,bulkIN,bulkOUT) \ +{\ + (pPort)->interfaceNum=(ifNum);(pPort)->interruptInEndpoint=(intIN); \ + (pPort)->bulkInEndpoint=(bulkIN);(pPort)->bulkOutEndpoint=(bulkOUT); \ +} + +//----------------------------------------------------------------------------- +// Callbacks +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//- Function API for composite device +extern void CDCDFunctionDriver_Initialize(USBDDriver * pUsbdDriver, + CDCDSerialPort * pCdcPorts, + unsigned char numPorts); + +extern unsigned char CDCDFunctionDriver_RequestHandler( + const USBGenericRequest * request); + +//- CDC Serial Port API +extern unsigned char CDCDSerialDriver_Write( + unsigned char port, + void *data, + unsigned int size, + TransferCallback callback, + void *argument); + +extern unsigned char CDCDSerialDriver_Read( + unsigned char port, + void *data, + unsigned int size, + TransferCallback callback, + void *argument); + +extern unsigned short CDCDSerialDriver_GetSerialState(unsigned char port); + +extern void CDCDSerialDriver_SetSerialState( + unsigned char port, + unsigned short serialState); + + +#endif // #define CDCDFUNCTIONDRIVER_H + diff --git a/usb/device/composite/CDCDFunctionDriverDescriptors.h b/usb/device/composite/CDCDFunctionDriverDescriptors.h new file mode 100644 index 0000000..33490ae --- /dev/null +++ b/usb/device/composite/CDCDFunctionDriverDescriptors.h @@ -0,0 +1,65 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef CDCDFUNCTIONDRIVERDESCRIPTORS_H +#define CDCDFUNCTIONDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +#if defined(usb_CDCAUDIO) +#define CDCD_Descriptors_INTERFACENUM0 0 +#define CDCD_Descriptors_NOTIFICATION0 3 +#define CDCD_Descriptors_DATAIN0 2 +#define CDCD_Descriptors_DATAOUT0 1 +#endif + +/// Default CDC interrupt endpoints max packat size (8). +#define CDCD_Descriptors_INTERRUPT_MAXPACKETSIZE 8 +/// Default CDC bulk endpoints max packat size (128, for HS actually). +#define CDCD_Descriptors_BULK_MAXPACKETSIZE 128 + +/// Default CDC interrupt IN endpoint polling rate of Full Speed (16ms). +#define CDCD_Descriptors_INTERRUPTIN_POLLING_FS 16 +/// Default CDC interrupt IN endpoint polling rate of High Speed (16ms). +#define CDCD_Descriptors_INTERRUPTIN_POLLING_HS 8 +/// Default interrupt OUT endpoint polling rate of Full Speed (16ms). +#define CDCD_Descriptors_INTERRUPTOUT_POLLING_FS 16 +/// Default interrupt OUT endpoint polling rate of High Speed (16ms). +#define CDCD_Descriptors_INTERRUPTOUT_POLLING_HS 8 + +#endif // #define CDCFUNCTIONDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/CDCHIDDDriver.c b/usb/device/composite/CDCHIDDDriver.c new file mode 100644 index 0000000..84c16a6 --- /dev/null +++ b/usb/device/composite/CDCHIDDDriver.c @@ -0,0 +1,227 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +#include + +// USB +#include +#include + +//- HID +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//- MSD +#if defined(usb_CDCMSD) || defined(usb_HIDMSD) +#endif + +//- CDCHID +#include "CDCHIDDDriver.h" +#include "CDCHIDDDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +/// Interface setting spaces (4 byte aligned) +#define NUM_INTERFACES ((CDCHIDDDriverDescriptors_NUMINTERFACE+3)&0xFC) + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// USBDDriver instance +static USBDDriver usbdDriver; + +/// CDCDSeriaoPort instance +static CDCDSerialPort cdcdPort; + +/// Array for storing the current setting of each interface +static unsigned char cdchiddDriverInterfaces[NUM_INTERFACES]; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Optional RequestReceived() callback re-implementation +//----------------------------------------------------------------------------- +#if !defined(NOAUTOCALLBACK) + +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + CDCHIDDDriver_RequestHandler(request); +} + +#endif + +//----------------------------------------------------------------------------- +// ConfigurationChanged() callback re-implementation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Invoked whenever the configuration value of a device is changed by the host +/// \param cfgnum Configuration number. +//----------------------------------------------------------------------------- +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + // HID + HIDDFunctionCallbacks_ConfigurationChanged(cfgnum); +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the USB device composite device driver. +//----------------------------------------------------------------------------- +void CDCHIDDDriver_Initialize() +{ + // CDC + CDCDFunctionDriver_ConfigurePort(&cdcdPort, + CDCD_Descriptors_INTERFACENUM0, + CDCD_Descriptors_NOTIFICATION0, + CDCD_Descriptors_DATAIN0, + CDCD_Descriptors_DATAOUT0); + CDCDFunctionDriver_Initialize(&usbdDriver, + &cdcdPort, + 1); + + // HID + HIDDFunctionDriver_Initialize(&usbdDriver, + HIDD_Descriptors_INTERFACENUM, + HIDD_Descriptors_INTERRUPTIN, + HIDD_Descriptors_INTERRUPTOUT); + + // Initialize the standard USB driver + USBDDriver_Initialize(&usbdDriver, + &cdchiddDriverDescriptors, + cdchiddDriverInterfaces); + + // Initialize the USB driver + USBD_Init(); +} + +//----------------------------------------------------------------------------- +/// Handles composite-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +void CDCHIDDDriver_RequestHandler(const USBGenericRequest *request) +{ + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + unsigned char rc = 0; + + // CDC class request + if (rc == 0) { + + rc = CDCDFunctionDriver_RequestHandler(request); + } + + // HID class request + if (rc == 0) { + + rc = HIDDFunctionDriver_RequestHandler(request); + } + + if (!rc) { + + TRACE_WARNING( + "CDCHIDDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + unsigned char rc = 0; + + rc = HIDDFunctionDriver_RequestHandler(request); + + // Forward request to the standard handler + if (rc == 0) { + + USBDDriver_RequestHandler(&(usbdDriver), request); + } + } + // Unsupported request type + else { + + TRACE_WARNING( + "CDCHIDDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void CDCHIDDDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) { + + USBD_RemoteWakeUp(); + } + // Remote wake-up NOT enabled + else { + + TRACE_WARNING("CDCHIDDDriver_RemoteWakeUp: not enabled\n\r"); + } +} + + diff --git a/usb/device/composite/CDCHIDDDriver.h b/usb/device/composite/CDCHIDDDriver.h new file mode 100644 index 0000000..d7ef755 --- /dev/null +++ b/usb/device/composite/CDCHIDDDriver.h @@ -0,0 +1,78 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// +/// !Purpose +/// +/// Definitions and methods for USB composite device implement. +/// +/// !Usage +/// +/// -# Initialize USB function specified driver ( for MSD currently ) +/// - MSDDFunctionDriver_Initialize +/// +/// -# Initialize USB composite driver and USB driver +/// - CDCHIDDDriver_Initialize +/// +/// -# Handle and dispach USB requests +/// - CDCHIDDDriver_RequestHandler +/// +/// -# Try starting a remote wake-up sequence +/// - CDCHIDDDriver_RemoteWakeUp +//----------------------------------------------------------------------------- + +#ifndef CDCHIDDDRIVER_H +#define CDCHIDDDRIVER_H + + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +#include "CDCDFunctionDriver.h" +#include "HIDDFunctionDriver.h" + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +// -CDCHID +extern void CDCHIDDDriver_Initialize(); + +extern void CDCHIDDDriver_RequestHandler(const USBGenericRequest *request); + +extern void CDCHIDDDriver_RemoteWakeUp(void); + +#endif //#ifndef CDCHIDDDRIVER_H + diff --git a/usb/device/composite/CDCHIDDDriverDescriptors.c b/usb/device/composite/CDCHIDDDriverDescriptors.c new file mode 100644 index 0000000..e19ba2d --- /dev/null +++ b/usb/device/composite/CDCHIDDDriverDescriptors.c @@ -0,0 +1,592 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//- Composite +#include "CDCHIDDDriver.h" +#include "CDCHIDDDriverDescriptors.h" + +//- USB Generic +#include +#include +#include +#include +#include +#include + +//- CDC +#include +#include +#include +#include +#include +#include +#include +#include +#include "CDCDFunctionDriverDescriptors.h" + +//- HID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HIDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Device product ID. +#define CDCHIDDDriverDescriptors_PRODUCTID 0x6130 + +/// Device vendor ID (Atmel). +#define CDCHIDDDriverDescriptors_VENDORID 0x03EB + +/// Device release number. +#define CDCHIDDDriverDescriptors_RELEASE 0x0003 + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//----------------------------------------------------------------------------- +// Internal structures +//----------------------------------------------------------------------------- + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//----------------------------------------------------------------------------- +/// Configuration descriptor list for a device implementing a composite driver. +//----------------------------------------------------------------------------- +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; + + /// --- CDC 0 + /// IAD 0 + USBInterfaceAssociationDescriptor cdcIAD0; + /// Communication interface descriptor + USBInterfaceDescriptor cdcCommunication0; + /// CDC header functional descriptor. + CDCHeaderDescriptor cdcHeader0; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor cdcCallManagement0; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor cdcUnion0; + /// Notification endpoint descriptor. + USBEndpointDescriptor cdcNotification0; + /// Data interface descriptor. + USBInterfaceDescriptor cdcData0; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor cdcDataOut0; + /// Data IN endpoint descriptor. + USBEndpointDescriptor cdcDataIn0; + + /// --- HID + USBInterfaceDescriptor hidInterface; + HIDDescriptor hid; + USBEndpointDescriptor hidInterruptIn; + USBEndpointDescriptor hidInterruptOut; + +} __attribute__ ((packed)) CdcHidDriverConfigurationDescriptors; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Standard USB device descriptor for the composite device driver +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + 0xEF,// MI + 0x02,// + 0x01,// + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + CDCHIDDDriverDescriptors_VENDORID, + CDCHIDDDriverDescriptors_PRODUCTID, + CDCHIDDDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + 0xEF,// MI + 0x02,// + 0x01,// + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +/// USB configuration descriptors for the composite device driver +static const CdcHidDriverConfigurationDescriptors configurationDescriptorsHS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CdcHidDriverConfigurationDescriptors), + CDCHIDDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // CDC 0 + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + CDCD_Descriptors_INTERRUPT_MAXPACKETSIZE), + CDCD_Descriptors_INTERRUPTOUT_POLLING_HS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + + // HID + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + HIDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDD_Descriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDD_Descriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING_HS + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDD_Descriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDD_Descriptors_INTERRUPTOUT_POLLING_HS + } +}; + +#endif + +/// USB configuration descriptors for the composite device driver +static const CdcHidDriverConfigurationDescriptors configurationDescriptorsFS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CdcHidDriverConfigurationDescriptors), + CDCHIDDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // CDC 0 + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + CDCD_Descriptors_INTERRUPTOUT_POLLING_FS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + + // HID + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + HIDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDD_Descriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDD_Descriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING_FS + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDD_Descriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDD_Descriptors_INTERRUPTOUT_POLLING_FS + } +}; + +/// String descriptor with the supported languages. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(14), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('i'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('o') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB audio speaker device driver. +const USBDDriverDescriptors cdchiddDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; diff --git a/usb/device/composite/CDCHIDDDriverDescriptors.h b/usb/device/composite/CDCHIDDDriverDescriptors.h new file mode 100644 index 0000000..0a34f8a --- /dev/null +++ b/usb/device/composite/CDCHIDDDriverDescriptors.h @@ -0,0 +1,70 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef CDCHIDDDRIVERDESCRIPTORS_H +#define CDCHIDDDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Number of interfaces of the device +#define CDCHIDDDriverDescriptors_NUMINTERFACE 3 + +/// Number of the CDC interface. +#define CDCD_Descriptors_INTERFACENUM0 0 +/// Address of the CDC interrupt-in endpoint. +#define CDCD_Descriptors_NOTIFICATION0 3 +/// Address of the CDC bulk-in endpoint. +#define CDCD_Descriptors_DATAIN0 2 +/// Address of the CDC bulk-out endpoint. +#define CDCD_Descriptors_DATAOUT0 1 + +/// Number of the HID interface. +#define HIDD_Descriptors_INTERFACENUM 2 +/// Address of the HID interrupt IN endpoint. +#define HIDD_Descriptors_INTERRUPTIN 4 +/// Address of the HID interrupt OUT endpoint. +#define HIDD_Descriptors_INTERRUPTOUT 5 + + +//----------------------------------------------------------------------------- +// Exported variables +//----------------------------------------------------------------------------- + +extern const USBDDriverDescriptors cdchiddDriverDescriptors; + +#endif //#ifndef CDCHIDDDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/CDCMSDDDriver.c b/usb/device/composite/CDCMSDDDriver.c new file mode 100644 index 0000000..9a605a7 --- /dev/null +++ b/usb/device/composite/CDCMSDDDriver.c @@ -0,0 +1,213 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +#include + +// USB +#include +#include + +//- CDCMSD +#include "CDCMSDDDriver.h" +#include "CDCMSDDDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +/// Interface setting spaces (4 byte aligned) +#define NUM_INTERFACES ((CDCMSDDDriverDescriptors_NUMINTERFACE+3)&0xFC) + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// USBDDriver instance +static USBDDriver usbdDriver; + +/// CDCDSeriaoPort instance +static CDCDSerialPort cdcdPort; + +/// Array for storing the current setting of each interface +static unsigned char cdcmsddDriverInterfaces[NUM_INTERFACES]; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Optional RequestReceived() callback re-implementation +//----------------------------------------------------------------------------- +#if !defined(NOAUTOCALLBACK) + +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + CDCMSDDDriver_RequestHandler(request); +} + +#endif + +//----------------------------------------------------------------------------- +// ConfigurationChanged() callback re-implementation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Invoked whenever the configuration value of a device is changed by the host +/// \param cfgnum Configuration number. +//----------------------------------------------------------------------------- +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + // MSD + MSDDFunctionCallbacks_ConfigurationChanged(cfgnum); +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the USB device CDCMSD device driver. +//----------------------------------------------------------------------------- +void CDCMSDDDriver_Initialize(MSDLun *pLuns, unsigned char numLuns) +{ + // CDC + CDCDFunctionDriver_ConfigurePort(&cdcdPort, + CDCD_Descriptors_INTERFACENUM0, + CDCD_Descriptors_NOTIFICATION0, + CDCD_Descriptors_DATAIN0, + CDCD_Descriptors_DATAOUT0); + CDCDFunctionDriver_Initialize(&usbdDriver, + &cdcdPort, + 1); + + // MSD + MSDDFunctionDriver_Initialize(&usbdDriver, + pLuns, numLuns, + MSDD_Descriptors_INTERFACENUM, + MSDD_Descriptors_BULKIN, + MSDD_Descriptors_BULKOUT); + + // Initialize the standard USB driver + USBDDriver_Initialize(&usbdDriver, + &cdcmsddDriverDescriptors, + cdcmsddDriverInterfaces); + + // Initialize the USB driver + USBD_Init(); +} + +//----------------------------------------------------------------------------- +/// Handles CDCMSD-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +void CDCMSDDDriver_RequestHandler(const USBGenericRequest *request) +{ + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + unsigned char rc = 0; + + // CDC class request + if (rc == 0) { + + rc = CDCDFunctionDriver_RequestHandler(request); + } + + // MSD class request + if (rc == 0) { + + rc = MSDDFunctionDriver_RequestHandler(request); + } + + if (!rc) { + + TRACE_WARNING( + "CDCMSDDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + unsigned char rc = 0; + + if (rc == 0) { + + rc = MSDDFunctionDriver_RequestHandler(request); + } + + // Forward request to the standard handler + if (rc == 0) { + + USBDDriver_RequestHandler(&(usbdDriver), request); + } + } + // Unsupported request type + else { + + TRACE_WARNING( + "CDCMSDDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void CDCMSDDDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) { + + USBD_RemoteWakeUp(); + } + // Remote wake-up NOT enabled + else { + + TRACE_WARNING("CDCMSDDDriver_RemoteWakeUp: not enabled\n\r"); + } +} + + diff --git a/usb/device/composite/CDCMSDDDriver.h b/usb/device/composite/CDCMSDDDriver.h new file mode 100644 index 0000000..e6a6834 --- /dev/null +++ b/usb/device/composite/CDCMSDDDriver.h @@ -0,0 +1,77 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// +/// !Purpose +/// +/// Definitions and methods for USB CDCMSD device implement. +/// +/// !Usage +/// +/// -# Initialize USB function specified driver ( for MSD currently ) +/// - MSDDFunctionDriver_Initialize +/// +/// -# Initialize USB CDCMSD driver and USB driver +/// - CDCMSDDDriver_Initialize +/// +/// -# Handle and dispach USB requests +/// - CDCMSDDDriver_RequestHandler +/// +/// -# Try starting a remote wake-up sequence +/// - CDCMSDDDriver_RemoteWakeUp +//----------------------------------------------------------------------------- + +#ifndef CDCMSDDDRIVER_H +#define CDCMSDDDRIVER_H + + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include + +#include "CDCDFunctionDriver.h" +#include "MSDDFunctionDriver.h" + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +// -CDCMSD +extern void CDCMSDDDriver_Initialize(MSDLun *pLuns, unsigned char numLuns); + +extern void CDCMSDDDriver_RequestHandler(const USBGenericRequest *request); + +extern void CDCMSDDDriver_RemoteWakeUp(void); + +#endif //#ifndef CDCMSDDDRIVER_H + diff --git a/usb/device/composite/CDCMSDDDriverDescriptors.c b/usb/device/composite/CDCMSDDDriverDescriptors.c new file mode 100644 index 0000000..27932ac --- /dev/null +++ b/usb/device/composite/CDCMSDDDriverDescriptors.c @@ -0,0 +1,568 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "CDCMSDDDriver.h" +#include "CDCMSDDDriverDescriptors.h" +#include + +//- USB Generic +#include +#include +#include +#include +#include +#include + +//- CDC +#include +#include +#include +#include +#include +#include +#include +#include +#include "CDCDFunctionDriverDescriptors.h" + +//- MSD +#include +#include +#include "MSDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Device product ID. +#define CDCMSDDDriverDescriptors_PRODUCTID 0x6132 + +/// Device vendor ID (Atmel). +#define CDCMSDDDriverDescriptors_VENDORID 0x03EB + +/// Device release number. +#define CDCMSDDDriverDescriptors_RELEASE 0x0003 + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//----------------------------------------------------------------------------- +// Internal structures +//----------------------------------------------------------------------------- + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//----------------------------------------------------------------------------- +/// Configuration descriptor list for a device implementing a CDCMSD driver. +//----------------------------------------------------------------------------- +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; + + /// --- CDC 0 + /// IAD 0 + USBInterfaceAssociationDescriptor cdcIAD0; + /// Communication interface descriptor + USBInterfaceDescriptor cdcCommunication0; + /// CDC header functional descriptor. + CDCHeaderDescriptor cdcHeader0; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor cdcCallManagement0; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor cdcUnion0; + /// Notification endpoint descriptor. + USBEndpointDescriptor cdcNotification0; + /// Data interface descriptor. + USBInterfaceDescriptor cdcData0; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor cdcDataOut0; + /// Data IN endpoint descriptor. + USBEndpointDescriptor cdcDataIn0; + + /// --- MSD + /// Mass storage interface descriptor. + USBInterfaceDescriptor msdInterface; + /// Bulk-out endpoint descriptor. + USBEndpointDescriptor msdBulkOut; + /// Bulk-in endpoint descriptor. + USBEndpointDescriptor msdBulkIn; + +} __attribute__ ((packed)) CDCMSDDriverConfigurationDescriptors; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Standard USB device descriptor for the CDCMSD device driver +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + 0xEF,// MI + 0x02,// + 0x01,// + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + CDCMSDDDriverDescriptors_VENDORID, + CDCMSDDDriverDescriptors_PRODUCTID, + CDCMSDDDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + 0xEF,// MI + 0x02,// + 0x01,// + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +/// USB configuration descriptors for the CDCMSD device driver +static const CDCMSDDriverConfigurationDescriptors configurationDescriptorsHS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CDCMSDDriverConfigurationDescriptors), + CDCMSDDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // CDC + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + CDCD_Descriptors_INTERRUPT_MAXPACKETSIZE), + CDCD_Descriptors_INTERRUPTIN_POLLING_HS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + MSDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + } + +}; + +#endif + +/// USB configuration descriptors for the CDCMSD device driver +static const CDCMSDDriverConfigurationDescriptors configurationDescriptorsFS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CDCMSDDriverConfigurationDescriptors), + CDCMSDDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // CDC + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + CDCD_Descriptors_INTERRUPT_MAXPACKETSIZE), + CDCD_Descriptors_INTERRUPTIN_POLLING_FS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + MSDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + } + +}; + +/// String descriptor with the supported languages. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(14), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('i'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('o') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB audio speaker device driver. +const USBDDriverDescriptors cdcmsddDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; diff --git a/usb/device/composite/CDCMSDDDriverDescriptors.h b/usb/device/composite/CDCMSDDDriverDescriptors.h new file mode 100644 index 0000000..5db1a0c --- /dev/null +++ b/usb/device/composite/CDCMSDDDriverDescriptors.h @@ -0,0 +1,71 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef CDCMSDDDRIVERDESCRIPTORS_H +#define CDCMSDDDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Number of interfaces of the device +#define CDCMSDDDriverDescriptors_NUMINTERFACE 3 + +/// Number of the CDC interface. +#define CDCD_Descriptors_INTERFACENUM0 0 +/// Address of the CDC interrupt-in endpoint. +#define CDCD_Descriptors_NOTIFICATION0 3 +/// Address of the CDC bulk-in endpoint. +#define CDCD_Descriptors_DATAIN0 2 +/// Address of the CDC bulk-out endpoint. +#define CDCD_Descriptors_DATAOUT0 1 + +/// Number of the Mass Storage interface. +#define MSDD_Descriptors_INTERFACENUM 2 + +/// Address of the Mass Storage bulk-out endpoint. +#define MSDD_Descriptors_BULKOUT 4 + +/// Address of the Mass Storage bulk-in endpoint. +#define MSDD_Descriptors_BULKIN 5 + +//----------------------------------------------------------------------------- +// Exported variables +//----------------------------------------------------------------------------- + +extern const USBDDriverDescriptors cdcmsddDriverDescriptors; + +#endif //#ifndef CDCMSDDDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/COMPOSITEDDriver.c b/usb/device/composite/COMPOSITEDDriver.c new file mode 100644 index 0000000..88d64b6 --- /dev/null +++ b/usb/device/composite/COMPOSITEDDriver.c @@ -0,0 +1,292 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +#include + +// USB +#include +#include + +//- HID +#if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +#endif // (HID defined) + +//- MSD +#if defined(usb_CDCMSD) || defined(usb_HIDMSD) +#endif + +//- COMPOSITE +#include "COMPOSITEDDriver.h" +#include "COMPOSITEDDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +/// Interface setting spaces (4 byte aligned) +#define NUM_INTERFACES ((COMPOSITEDDriverDescriptors_NUMINTERFACE+3)&0xFC) + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// USBDDriver instance +static USBDDriver usbdDriver; + +// CDC +#if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCCDC) || defined(usb_CDCMSD) +/// CDCDSeriaoPort instance +static CDCDSerialPort cdcdPort; +#endif // CDC defined + +/// Array for storing the current setting of each interface +static unsigned char compositedDriverInterfaces[NUM_INTERFACES]; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Optional RequestReceived() callback re-implementation +//----------------------------------------------------------------------------- +#if !defined(NOAUTOCALLBACK) + +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + COMPOSITEDDriver_RequestHandler(request); +} + +#endif + +//----------------------------------------------------------------------------- +/// Invoked whenever the active setting of an interface is changed by the +/// host. Changes the status of the third LED accordingly. +/// \param interface Interface number. +/// \param setting Newly active setting. +//----------------------------------------------------------------------------- +void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting) +{ + // AUDIO + #if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + AUDDFunctionCallbacks_InterfaceSettingChanged(interface, setting); + #endif +} + +//----------------------------------------------------------------------------- +// ConfigurationChanged() callback re-implementation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Invoked whenever the configuration value of a device is changed by the host +/// \param cfgnum Configuration number. +//----------------------------------------------------------------------------- +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + // HID + #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + HIDDFunctionCallbacks_ConfigurationChanged(cfgnum); + #endif +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the USB device composite device driver. +//----------------------------------------------------------------------------- +void COMPOSITEDDriver_Initialize() +{ + // CDC + #if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCCDC) || defined(usb_CDCMSD) + CDCDFunctionDriver_ConfigurePort(&cdcdPort, + CDCD_Descriptors_INTERFACENUM0, + CDCD_Descriptors_NOTIFICATION0, + CDCD_Descriptors_DATAIN0, + CDCD_Descriptors_DATAOUT0); + CDCDFunctionDriver_Initialize(&usbdDriver, + &cdcdPort, + 1); + #endif + + // AUDIO + #if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + AUDDFunctionDriver_Initialize(); + #endif + + // HID + #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + HIDDFunctionDriver_Initialize(&usbdDriver, + HIDD_Descriptors_INTERFACENUM, + HIDD_Descriptors_INTERRUPTIN, + HIDD_Descriptors_INTERRUPTOUT); + #endif + + // MSD + #if defined(usb_CDCMSD) || defined(usb_HIDMSD) + // Function driver initialize is put to main() for additional LUN list + #endif + + // Initialize the standard USB driver + USBDDriver_Initialize(&usbdDriver, + &compositedDriverDescriptors, + compositedDriverInterfaces); + + // Initialize the USB driver + USBD_Init(); +} + +//----------------------------------------------------------------------------- +/// Handles composite-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +void COMPOSITEDDriver_RequestHandler(const USBGenericRequest *request) +{ + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + unsigned char rc = 0; + + // AUDIO class request + #if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + if (rc == 0) { + + rc = AUDDFunctionDriver_RequestHandler(request); + } + #endif + + // CDC class request + #if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCMSD) || defined(usb_CDCCDC) + if (rc == 0) { + + rc = CDCDFunctionDriver_RequestHandler(request); + } + #endif + + // MSD class request + #if defined(usb_CDCMSD) || defined(usb_HIDMSD) + if (rc == 0) { + + rc = MSDDFunctionDriver_RequestHandler(request); + } + #endif + + // HID class request + #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + if (rc == 0) { + + rc = HIDDFunctionDriver_RequestHandler(request); + } + #endif + + if (!rc) { + + TRACE_WARNING( + "COMPOSITEDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + unsigned char rc = 0; + + #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + rc = HIDDFunctionDriver_RequestHandler(request); + #endif + + #if defined(usb_CDCMSD) || defined(usb_HIDMSD) + if (rc == 0) { + + rc = MSDDFunctionDriver_RequestHandler(request); + } + #endif + + // Forward request to the standard handler + if (rc == 0) { + + USBDDriver_RequestHandler(&(usbdDriver), request); + } + } + // Unsupported request type + else { + + TRACE_WARNING( + "COMPOSITEDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void COMPOSITEDDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) { + + USBD_RemoteWakeUp(); + } + // Remote wake-up NOT enabled + else { + + TRACE_WARNING("COMPOSITEDDriver_RemoteWakeUp: not enabled\n\r"); + } +} + + diff --git a/usb/device/composite/COMPOSITEDDriver.h b/usb/device/composite/COMPOSITEDDriver.h new file mode 100644 index 0000000..6106a9f --- /dev/null +++ b/usb/device/composite/COMPOSITEDDriver.h @@ -0,0 +1,93 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// +/// !Purpose +/// +/// Definitions and methods for USB composite device implement. +/// +/// !Usage +/// +/// -# Initialize USB function specified driver ( for MSD currently ) +/// - MSDDFunctionDriver_Initialize +/// +/// -# Initialize USB composite driver and USB driver +/// - COMPOSITEDDriver_Initialize +/// +/// -# Handle and dispach USB requests +/// - COMPOSITEDDriver_RequestHandler +/// +/// -# Try starting a remote wake-up sequence +/// - COMPOSITEDDriver_RemoteWakeUp +//----------------------------------------------------------------------------- + +#ifndef COMPOSITEDDRIVER_H +#define COMPOSITEDDRIVER_H + + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +#if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCCDC) || defined(usb_CDCMSD) + #include "CDCDFunctionDriver.h" + #include "CDCDFunctionDriverDescriptors.h" +#endif + +#if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + #include "AUDDFunctionDriver.h" +#endif + +#if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + #include "HIDDFunctionDriver.h" + #include "HIDDFunctionDriverDescriptors.h" +#endif + +#if defined(usb_CDCMSD) || defined(usb_HIDMSD) + #include "MSDDFunctionDriver.h" +#endif + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +// -COMPOSITE +extern void COMPOSITEDDriver_Initialize(); + +extern void COMPOSITEDDriver_RequestHandler(const USBGenericRequest *request); + +extern void COMPOSITEDDriver_RemoteWakeUp(void); + +#endif //#ifndef COMPOSITEDDRIVER_H + diff --git a/usb/device/composite/COMPOSITEDDriverDescriptors.c b/usb/device/composite/COMPOSITEDDriverDescriptors.c new file mode 100644 index 0000000..db195af --- /dev/null +++ b/usb/device/composite/COMPOSITEDDriverDescriptors.c @@ -0,0 +1,954 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "COMPOSITEDDriver.h" +#include "COMPOSITEDDriverDescriptors.h" +#include + +//- USB Generic +#include +#include +#include +#include +#include +#include + +//- CDC +#if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCCDC) || defined(usb_CDCMSD) + #include + #include + #include + #include + #include + #include + #include + #include + #include "CDCDFunctionDriverDescriptors.h" +#endif // (CDC defined) + +//- HID +#if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include "HIDDFunctionDriverDescriptors.h" +#endif // (HID defined) + +//- AUDIO +#if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include "AUDDFunctionDriverDescriptors.h" +#endif // (AUDIO defined) + +//- MSD +#if defined(usb_CDCMSD) || defined(usb_HIDMSD) + #include + #include + #include "MSDDFunctionDriverDescriptors.h" +#endif // (MSD defined) + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Device product ID. +#if defined(usb_CDCHID) +#define COMPOSITEDDriverDescriptors_PRODUCTID 0x6130 +#elif defined(usb_CDCAUDIO) +#define COMPOSITEDDriverDescriptors_PRODUCTID 0x6131 +#elif defined(usb_CDCMSD) +#define COMPOSITEDDriverDescriptors_PRODUCTID 0x6132 +#elif defined(usb_CDCCDC) +#define COMPOSITEDDriverDescriptors_PRODUCTID 0x6133 +#elif defined(usb_HIDAUDIO) +#define COMPOSITEDDriverDescriptors_PRODUCTID 0x6134 +#elif defined(usb_HIDMSD) +#define COMPOSITEDDriverDescriptors_PRODUCTID 0x6135 +#else +#error COMPOSITE Device Classes not defined! +#endif + +/// Device vendor ID (Atmel). +#define COMPOSITEDDriverDescriptors_VENDORID 0x03EB + +/// Device release number. +#define COMPOSITEDDriverDescriptors_RELEASE 0x0003 + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//----------------------------------------------------------------------------- +// Internal structures +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Audio control header descriptor with one slave interface. +//----------------------------------------------------------------------------- +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//- AUDIO +#if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) +typedef struct { + + /// Header descriptor. + AUDHeaderDescriptor header; + /// Id of the first grouped interface. + unsigned char bInterface0; + +} __attribute__ ((packed)) AUDHeaderDescriptor1; // GCC + +//----------------------------------------------------------------------------- +/// Feature unit descriptor with 3 channel controls (master, right, left). +//----------------------------------------------------------------------------- +typedef struct { + + /// Feature unit descriptor. + AUDFeatureUnitDescriptor feature; + /// Available controls for each channel. + unsigned char bmaControls[3]; + /// Index of a string descriptor for the feature unit. + unsigned char iFeature; + +} __attribute__ ((packed)) AUDFeatureUnitDescriptor3; // GCC + +//----------------------------------------------------------------------------- +/// List of descriptors for detailling the audio control interface of a +/// device using a USB audio speaker driver. +//----------------------------------------------------------------------------- +typedef struct { + + /// Header descriptor (with one slave interface). + AUDHeaderDescriptor1 header; + /// Input terminal descriptor. + AUDInputTerminalDescriptor input; + /// Output terminal descriptor. + AUDOutputTerminalDescriptor output; + /// Feature unit descriptor. + AUDFeatureUnitDescriptor3 feature; + +} __attribute__ ((packed)) AUDDSpeakerDriverAudioControlDescriptors; // GCC + +//----------------------------------------------------------------------------- +/// Format type I descriptor with one discrete sampling frequency. +//----------------------------------------------------------------------------- +typedef struct { + + /// Format type I descriptor. + AUDFormatTypeOneDescriptor formatType; + /// Sampling frequency in Hz. + unsigned char tSamFreq[3]; + +} __attribute__ ((packed)) AUDFormatTypeOneDescriptor1; // GCC +#endif // (AUDIO defined) + +//----------------------------------------------------------------------------- +/// Configuration descriptor list for a device implementing a composite driver. +//----------------------------------------------------------------------------- +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; + + #if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCCDC) || defined(usb_CDCMSD) + /// --- CDC 0 + /// IAD 0 + USBInterfaceAssociationDescriptor cdcIAD0; + /// Communication interface descriptor + USBInterfaceDescriptor cdcCommunication0; + /// CDC header functional descriptor. + CDCHeaderDescriptor cdcHeader0; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor cdcCallManagement0; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor cdcUnion0; + /// Notification endpoint descriptor. + USBEndpointDescriptor cdcNotification0; + /// Data interface descriptor. + USBInterfaceDescriptor cdcData0; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor cdcDataOut0; + /// Data IN endpoint descriptor. + USBEndpointDescriptor cdcDataIn0; + #endif // (CDC defined) + + #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + /// --- HID + USBInterfaceDescriptor hidInterface; + HIDDescriptor hid; + USBEndpointDescriptor hidInterruptIn; + USBEndpointDescriptor hidInterruptOut; + #endif // (HID defined) + + #if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + /// --- AUDIO + /// IAD 1 + USBInterfaceAssociationDescriptor audIAD; + /// Audio control interface. + USBInterfaceDescriptor audInterface; + /// Descriptors for the audio control interface. + AUDDSpeakerDriverAudioControlDescriptors audControl; + /// -- AUDIO out + /// Streaming out interface descriptor (with no endpoint, required). + USBInterfaceDescriptor audStreamingOutNoIsochronous; + /// Streaming out interface descriptor. + USBInterfaceDescriptor audStreamingOut; + /// Audio class descriptor for the streaming out interface. + AUDStreamingInterfaceDescriptor audStreamingOutClass; + /// Stream format descriptor. + AUDFormatTypeOneDescriptor1 audStreamingOutFormatType; + /// Streaming out endpoint descriptor. + AUDEndpointDescriptor audStreamingOutEndpoint; + /// Audio class descriptor for the streaming out endpoint. + AUDDataEndpointDescriptor audStreamingOutDataEndpoint; + #endif // (AUDIO defined) + + #if defined(usb_CDCCDC) + /// --- CDC 1 + /// IAD 1 + USBInterfaceAssociationDescriptor cdcIAD1; + /// Communication interface descriptor + USBInterfaceDescriptor cdcCommunication1; + /// CDC header functional descriptor. + CDCHeaderDescriptor cdcHeader1; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor cdcCallManagement1; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor cdcAbstractControlManagement1; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor cdcUnion1; + /// Notification endpoint descriptor. + USBEndpointDescriptor cdcNotification1; + /// Data interface descriptor. + USBInterfaceDescriptor cdcData1; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor cdcDataOut1; + /// Data IN endpoint descriptor. + USBEndpointDescriptor cdcDataIn1; + #endif // (Another CDC defined) + + #if defined(usb_CDCMSD) || defined(usb_HIDMSD) + /// --- MSD + /// Mass storage interface descriptor. + USBInterfaceDescriptor msdInterface; + /// Bulk-out endpoint descriptor. + USBEndpointDescriptor msdBulkOut; + /// Bulk-in endpoint descriptor. + USBEndpointDescriptor msdBulkIn; + #endif // (MSD defined) + +} __attribute__ ((packed)) CompositeDriverConfigurationDescriptors; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Standard USB device descriptor for the composite device driver +const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + #if defined(usb_HIDMSD) + 0x00, + 0x00, + 0x00, + #else + 0xEF,// MI + 0x02,// + 0x01,// + #endif + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + COMPOSITEDDriverDescriptors_VENDORID, + COMPOSITEDDriverDescriptors_PRODUCTID, + COMPOSITEDDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + #if defined(usb_HIDMSD) + 0x00, + 0x00, + 0x00, + #else + 0xEF,// MI + 0x02,// + 0x01,// + #endif + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +#endif + +/// USB configuration descriptors for the composite device driver +const CompositeDriverConfigurationDescriptors configurationDescriptors = { + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CompositeDriverConfigurationDescriptors), + COMPOSITEDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + #if defined(usb_CDCAUDIO) || defined(usb_CDCHID) || defined(usb_CDCCDC) || defined(usb_CDCMSD) + // CDC + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 10 // Endpoint is polled every 10ms + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + #endif // (CDC defined) + + #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + HIDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDD_Descriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDD_Descriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDD_Descriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING + }, + #endif // (HID defined) + + #if defined(usb_CDCAUDIO) || defined(usb_HIDAUDIO) + // AUDIO + // IAD for AUDIO function + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + AUDD_Descriptors_INTERFACE, + 2, + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor for this interface + }, + // Audio control interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDD_Descriptors_CONTROL, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoint + AUDControlInterfaceDescriptor_CLASS, + AUDControlInterfaceDescriptor_SUBCLASS, + AUDControlInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio control interface descriptors + { + // Header descriptor + { + { + sizeof(AUDHeaderDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_HEADER, + AUDHeaderDescriptor_AUD1_00, + sizeof(AUDDSpeakerDriverAudioControlDescriptors), + 1 // One streaming interface + }, + AUDD_Descriptors_STREAMING + }, + // Input terminal descriptor + { + sizeof(AUDInputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_INPUTTERMINAL, + AUDD_Descriptors_INPUTTERMINAL, + AUDInputTerminalDescriptor_USBSTREAMING, + AUDD_Descriptors_OUTPUTTERMINAL, + AUDD_NUMCHANNELS, + AUDInputTerminalDescriptor_LEFTFRONT + | AUDInputTerminalDescriptor_RIGHTFRONT, + 0, // No string descriptor for channels + 0 // No string descriptor for input terminal + }, + // Output terminal descriptor + { + sizeof(AUDOutputTerminalDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_OUTPUTTERMINAL, + AUDD_Descriptors_OUTPUTTERMINAL, + AUDOutputTerminalDescriptor_SPEAKER, + AUDD_Descriptors_INPUTTERMINAL, + AUDD_Descriptors_FEATUREUNIT, + 0 // No string descriptor + }, + // Feature unit descriptor + { + { + sizeof(AUDFeatureUnitDescriptor3), + AUDGenericDescriptor_INTERFACE, + AUDGenericDescriptor_FEATUREUNIT, + AUDD_Descriptors_FEATUREUNIT, + AUDD_Descriptors_INPUTTERMINAL, + 1, // 1 byte per channel for controls + }, + { + AUDFeatureUnitDescriptor_MUTE, // Master channel controls + 0, // Right channel controls + 0 // Left channel controls + }, + 0 // No string descriptor + } + }, + // Audio streaming interface with 0 endpoints + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDD_Descriptors_STREAMING, + 0, // This is alternate setting #0 + 0, // This interface uses no endpoints + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming interface with data endpoint + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + AUDD_Descriptors_STREAMING, + 1, // This is alternate setting #1 + 1, // This interface uses 1 endpoint + AUDStreamingInterfaceDescriptor_CLASS, + AUDStreamingInterfaceDescriptor_SUBCLASS, + AUDStreamingInterfaceDescriptor_PROTOCOL, + 0 // No string descriptor + }, + // Audio streaming class-specific descriptor + { + sizeof(AUDStreamingInterfaceDescriptor), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_GENERAL, + AUDD_Descriptors_INPUTTERMINAL, + 0, // No internal delay because of data path + AUDFormatTypeOneDescriptor_PCM + }, + // Format type I descriptor + { + { + sizeof(AUDFormatTypeOneDescriptor1), + AUDGenericDescriptor_INTERFACE, + AUDStreamingInterfaceDescriptor_FORMATTYPE, + AUDFormatTypeOneDescriptor_FORMATTYPEONE, + AUDD_NUMCHANNELS, + AUDD_BYTESPERSAMPLE, + AUDD_BYTESPERSAMPLE*8, + 1 // One discrete frequency supported + }, + { + AUDD_SAMPLERATE & 0xFF, + (AUDD_SAMPLERATE >> 8) & 0xFF, + (AUDD_SAMPLERATE >> 16) & 0xFF + } + }, + // Audio streaming endpoint standard descriptor + { + sizeof(AUDEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + AUDD_Descriptors_DATAOUT), + USBEndpointDescriptor_ISOCHRONOUS, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(AUDD_Descriptors_DATAOUT), + 1, // Polling interval = 2^(x-1) milliseconds (1 ms) + 0, // This is not a synchronization endpoint + 0 // No associated synchronization endpoint + }, + // Audio streaming endpoint class-specific descriptor + { + sizeof(AUDDataEndpointDescriptor), + AUDGenericDescriptor_ENDPOINT, + AUDDataEndpointDescriptor_SUBTYPE, + 0, // No attributes + 0, // Endpoint is not synchronized + 0 // Endpoint is not synchronized + }, + #endif // (AUDIO defined) + + #if defined(usb_CDCCDC) + // CDC 1 + // IAD for CDC/ACM port 1 + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM1, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM1, // This is interface #2 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM1 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM1, // Number of master interface is #2 + CDCD_Descriptors_INTERFACENUM1+1 // First slave interface is #3 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION1), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION1), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 10 // Endpoint is polled every 10ms + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM1 + 1, // This is interface #3 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT1), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT1), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN1), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN1), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + #endif // (2 CDCs defined) + + #if defined(usb_CDCMSD) || defined(usb_HIDMSD) + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + MSDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + }, + #endif // (MSD defined) + +}; + +/// String descriptor with the supported languages. +const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(14), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('i'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('o') +}; + +/// Product serial number. +const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB audio speaker device driver. +const USBDDriverDescriptors compositedDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptors, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptors, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptors, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptors, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; + +#if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) +/// Report descriptor used by the driver. +const unsigned char hiddReportDescriptor[] = { + + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_KEYBOARD, + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, + + // Input report: modifier keys + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_REPORTCOUNT + 1, 8, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDD_Descriptors_FIRSTMODIFIERKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDD_Descriptors_LASTMODIFIERKEY, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_INPUT + 1, HIDReport_VARIABLE, + + // Input report: standard keys + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, + HIDD_Descriptors_FIRSTSTANDARDKEY, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, + HIDD_Descriptors_LASTSTANDARDKEY, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDD_Descriptors_FIRSTSTANDARDKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDD_Descriptors_LASTSTANDARDKEY, + HIDReport_INPUT + 1, 0 /* Data array */, + + // Output report: LEDs + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDLeds_PAGEID, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_LOCAL_USAGEMINIMUM + 1, HIDLeds_NUMLOCK, + HIDReport_LOCAL_USAGEMAXIMUM + 1, HIDLeds_SCROLLLOCK, + HIDReport_OUTPUT + 1, HIDReport_VARIABLE, + + // Output report: padding + HIDReport_GLOBAL_REPORTCOUNT + 1, 1, + HIDReport_GLOBAL_REPORTSIZE + 1, 5, + HIDReport_OUTPUT + 1, HIDReport_CONSTANT, + + HIDReport_ENDCOLLECTION +}; +#endif // (HID defined) + diff --git a/usb/device/composite/COMPOSITEDDriverDescriptors.h b/usb/device/composite/COMPOSITEDDriverDescriptors.h new file mode 100644 index 0000000..c82f9bc --- /dev/null +++ b/usb/device/composite/COMPOSITEDDriverDescriptors.h @@ -0,0 +1,61 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef COMPOSITEDDRIVERDESCRIPTORS_H +#define COMPOSITEDDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Number of interfaces of the device +#if defined(usb_CDCAUDIO) || defined(usb_CDCCDC) + #define COMPOSITEDDriverDescriptors_NUMINTERFACE 4 +#elif defined(usb_CDCHID) || defined(usb_CDCMSD) || defined(usb_HIDAUDIO) + #define COMPOSITEDDriverDescriptors_NUMINTERFACE 3 +#elif defined(usb_HIDMSD) + #define COMPOSITEDDriverDescriptors_NUMINTERFACE 2 +#else + #error USB Composite class not defined. +#endif + +//----------------------------------------------------------------------------- +// Exported variables +//----------------------------------------------------------------------------- + +extern const USBDDriverDescriptors compositedDriverDescriptors; + +#endif //#ifndef COMPOSITEDDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/DUALCDCDDriver.c b/usb/device/composite/DUALCDCDDriver.c new file mode 100644 index 0000000..cced6f3 --- /dev/null +++ b/usb/device/composite/DUALCDCDDriver.c @@ -0,0 +1,189 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +#include + +// USB +#include +#include + +//- DUALCDC +#include "DUALCDCDDriver.h" +#include "DUALCDCDDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +/// Interface setting spaces (4 byte aligned) +#define NUM_INTERFACES ((DUALCDCDDriverDescriptors_NUMINTERFACE+3)&0xFC) + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// USBDDriver instance +static USBDDriver usbdDriver; + +/// CDCDSeriaoPort instance for 2 CDC/CAM serial ports. +static CDCDSerialPort cdcdPorts[2]; + +/// Array for storing the current setting of each interface +static unsigned char compositedDriverInterfaces[NUM_INTERFACES]; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Optional RequestReceived() callback re-implementation +//----------------------------------------------------------------------------- +#if !defined(NOAUTOCALLBACK) + +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + DUALCDCDDriver_RequestHandler(request); +} + +#endif + +//----------------------------------------------------------------------------- +// ConfigurationChanged() callback re-implementation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the USB device composite device driver. +//----------------------------------------------------------------------------- +void DUALCDCDDriver_Initialize() +{ + // CDC + CDCDFunctionDriver_ConfigurePort(&cdcdPorts[0], + CDCD_Descriptors_INTERFACENUM0, + CDCD_Descriptors_NOTIFICATION0, + CDCD_Descriptors_DATAIN0, + CDCD_Descriptors_DATAOUT0); + CDCDFunctionDriver_ConfigurePort(&cdcdPorts[1], + CDCD_Descriptors_INTERFACENUM1, + CDCD_Descriptors_NOTIFICATION1, + CDCD_Descriptors_DATAIN1, + CDCD_Descriptors_DATAOUT1); + CDCDFunctionDriver_Initialize(&usbdDriver, + cdcdPorts, + 2); + + // Initialize the standard USB driver + USBDDriver_Initialize(&usbdDriver, + &dualcdcdDriverDescriptors, + compositedDriverInterfaces); + + // Initialize the USB driver + USBD_Init(); +} + +//----------------------------------------------------------------------------- +/// Handles composite-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +void DUALCDCDDriver_RequestHandler(const USBGenericRequest *request) +{ + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + unsigned char rc = 0; + + if (rc == 0) { + + rc = CDCDFunctionDriver_RequestHandler(request); + } + + if (!rc) { + + TRACE_WARNING( + "DUALCDCDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + unsigned char rc = 0; + + // Forward request to the standard handler + if (rc == 0) { + + USBDDriver_RequestHandler(&(usbdDriver), request); + } + } + // Unsupported request type + else { + + TRACE_WARNING( + "DUALCDCDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void DUALCDCDDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) { + + USBD_RemoteWakeUp(); + } + // Remote wake-up NOT enabled + else { + + TRACE_WARNING("DUALCDCDDriver_RemoteWakeUp: not enabled\n\r"); + } +} + + diff --git a/usb/device/composite/DUALCDCDDriver.h b/usb/device/composite/DUALCDCDDriver.h new file mode 100644 index 0000000..8ff2c66 --- /dev/null +++ b/usb/device/composite/DUALCDCDDriver.h @@ -0,0 +1,77 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// +/// !Purpose +/// +/// Definitions and methods for USB composite device implement. +/// +/// !Usage +/// +/// -# Initialize USB function specified driver ( for MSD currently ) +/// - MSDDFunctionDriver_Initialize +/// +/// -# Initialize USB composite driver and USB driver +/// - DUALCDCDDriver_Initialize +/// +/// -# Handle and dispach USB requests +/// - DUALCDCDDriver_RequestHandler +/// +/// -# Try starting a remote wake-up sequence +/// - DUALCDCDDriver_RemoteWakeUp +//----------------------------------------------------------------------------- + +#ifndef DUALCDCDDRIVER_H +#define DUALCDCDDRIVER_H + + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +#include "CDCDFunctionDriver.h" + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +// -DUALCDC +extern void DUALCDCDDriver_Initialize(); + +extern void DUALCDCDDriver_RequestHandler(const USBGenericRequest *request); + +extern void DUALCDCDDriver_RemoteWakeUp(void); + +#endif //#ifndef DUALCDCDDRIVER_H + diff --git a/usb/device/composite/DUALCDCDDriverDescriptors.c b/usb/device/composite/DUALCDCDDriverDescriptors.c new file mode 100644 index 0000000..f39b29e --- /dev/null +++ b/usb/device/composite/DUALCDCDDriverDescriptors.c @@ -0,0 +1,702 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "DUALCDCDDriver.h" +#include "DUALCDCDDriverDescriptors.h" +#include + +//- USB Generic +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "CDCDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Device product ID. +#define DUALCDCDDriverDescriptors_PRODUCTID 0x6133 + +/// Device vendor ID (Atmel). +#define DUALCDCDDriverDescriptors_VENDORID 0x03EB + +/// Device release number. +#define DUALCDCDDriverDescriptors_RELEASE 0x0003 + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//----------------------------------------------------------------------------- +// Internal structures +//----------------------------------------------------------------------------- + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//----------------------------------------------------------------------------- +/// Configuration descriptor list for a device implementing a composite driver. +//----------------------------------------------------------------------------- +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; + + /// --- CDC 0 + /// IAD 0 + USBInterfaceAssociationDescriptor cdcIAD0; + /// Communication interface descriptor + USBInterfaceDescriptor cdcCommunication0; + /// CDC header functional descriptor. + CDCHeaderDescriptor cdcHeader0; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor cdcCallManagement0; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor cdcUnion0; + /// Notification endpoint descriptor. + USBEndpointDescriptor cdcNotification0; + /// Data interface descriptor. + USBInterfaceDescriptor cdcData0; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor cdcDataOut0; + /// Data IN endpoint descriptor. + USBEndpointDescriptor cdcDataIn0; + + /// --- CDC 1 + /// IAD 1 + USBInterfaceAssociationDescriptor cdcIAD1; + /// Communication interface descriptor + USBInterfaceDescriptor cdcCommunication1; + /// CDC header functional descriptor. + CDCHeaderDescriptor cdcHeader1; + /// CDC call management functional descriptor. + CDCCallManagementDescriptor cdcCallManagement1; + /// CDC abstract control management functional descriptor. + CDCAbstractControlManagementDescriptor cdcAbstractControlManagement1; + /// CDC union functional descriptor (with one slave interface). + CDCUnionDescriptor cdcUnion1; + /// Notification endpoint descriptor. + USBEndpointDescriptor cdcNotification1; + /// Data interface descriptor. + USBInterfaceDescriptor cdcData1; + /// Data OUT endpoint descriptor. + USBEndpointDescriptor cdcDataOut1; + /// Data IN endpoint descriptor. + USBEndpointDescriptor cdcDataIn1; + +} __attribute__ ((packed)) DualCdcDriverConfigurationDescriptors; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Standard USB device descriptor for the composite device driver +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + 0xEF,// MI + 0x02,// + 0x01,// + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + DUALCDCDDriverDescriptors_VENDORID, + DUALCDCDDriverDescriptors_PRODUCTID, + DUALCDCDDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + 0xEF,// MI + 0x02,// + 0x01,// + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +/// USB configuration descriptors for the composite device driver +static const DualCdcDriverConfigurationDescriptors configurationDescriptorsHS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(DualCdcDriverConfigurationDescriptors), + DUALCDCDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // CDC 0 + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + CDCD_Descriptors_INTERRUPT_MAXPACKETSIZE), + CDCD_Descriptors_INTERRUPTIN_POLLING_HS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + + // CDC 1 + // IAD for CDC/ACM port 1 + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM1, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM1, // This is interface #2 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM1 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM1, // Number of master interface is #2 + CDCD_Descriptors_INTERFACENUM1+1 // First slave interface is #3 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION1), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION1), + CDCD_Descriptors_INTERRUPT_MAXPACKETSIZE), + CDCD_Descriptors_INTERRUPTIN_POLLING_HS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM1 + 1, // This is interface #3 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT1), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT1), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN1), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN1), + CDCD_Descriptors_BULK_MAXPACKETSIZE), + 0 // Must be 0 for full-speed bulk endpoints + } + +}; + +#endif + +/// USB configuration descriptors for the composite device driver +static const DualCdcDriverConfigurationDescriptors configurationDescriptorsFS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(DualCdcDriverConfigurationDescriptors), + DUALCDCDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // CDC 0 + // IAD for CDC/ACM port + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM0, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0, // This is interface #0 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM0 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM0, // Number of master interface is #0 + CDCD_Descriptors_INTERFACENUM0 + 1 // First slave interface is #1 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION0), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + CDCD_Descriptors_INTERRUPTIN_POLLING_FS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM0 + 1, // This is interface #1 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN0), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + + // CDC 1 + // IAD for CDC/ACM port 1 + { + sizeof(USBInterfaceAssociationDescriptor), + USBGenericDescriptor_INTERFACEASSOCIATION, + CDCD_Descriptors_INTERFACENUM1, + 2, + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Communication class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM1, // This is interface #2 + 0, // This is alternate setting #0 for this interface + 1, // This interface uses 1 endpoint + CDCCommunicationInterfaceDescriptor_CLASS, + CDCCommunicationInterfaceDescriptor_ABSTRACTCONTROLMODEL, + CDCCommunicationInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Class-specific header functional descriptor + { + sizeof(CDCHeaderDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_HEADER, + CDCGenericDescriptor_CDC1_10 + }, + // Class-specific call management functional descriptor + { + sizeof(CDCCallManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_CALLMANAGEMENT, + CDCCallManagementDescriptor_SELFCALLMANAGEMENT, + CDCD_Descriptors_INTERFACENUM1 + 1 // No associated data interface + }, + // Class-specific abstract control management functional descriptor + { + sizeof(CDCAbstractControlManagementDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_ABSTRACTCONTROLMANAGEMENT, + CDCAbstractControlManagementDescriptor_LINE + }, + // Class-specific union functional descriptor with one slave interface + { + sizeof(CDCUnionDescriptor), + CDCGenericDescriptor_INTERFACE, + CDCGenericDescriptor_UNION, + CDCD_Descriptors_INTERFACENUM1, // Number of master interface is #2 + CDCD_Descriptors_INTERFACENUM1+1 // First slave interface is #3 + }, + // Notification endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_NOTIFICATION1), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_NOTIFICATION1), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + CDCD_Descriptors_INTERRUPTIN_POLLING_FS + }, + // Data class interface standard descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + CDCD_Descriptors_INTERFACENUM1 + 1, // This is interface #3 + 0, // This is alternate setting #0 for this interface + 2, // This interface uses 2 endpoints + CDCDataInterfaceDescriptor_CLASS, + CDCDataInterfaceDescriptor_SUBCLASS, + CDCDataInterfaceDescriptor_NOPROTOCOL, + 0 // No string descriptor for this interface + }, + // Bulk-OUT endpoint standard descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, + CDCD_Descriptors_DATAOUT1), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAOUT1), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, + CDCD_Descriptors_DATAIN1), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(CDCD_Descriptors_DATAIN1), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed bulk endpoints + } + +}; + +/// String descriptor with the supported languages. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(14), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('i'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('o') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB audio speaker device driver. +const USBDDriverDescriptors dualcdcdDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; diff --git a/usb/device/composite/DUALCDCDDriverDescriptors.h b/usb/device/composite/DUALCDCDDriverDescriptors.h new file mode 100644 index 0000000..13e1f4d --- /dev/null +++ b/usb/device/composite/DUALCDCDDriverDescriptors.h @@ -0,0 +1,72 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef DUALCDCDDRIVERDESCRIPTORS_H +#define DUALCDCDDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Number of interfaces of the device +#define DUALCDCDDriverDescriptors_NUMINTERFACE 4 + +/// Number of the CDC0 interface. +#define CDCD_Descriptors_INTERFACENUM0 0 +/// Address of the CDC0 interrupt-in endpoint. +#define CDCD_Descriptors_NOTIFICATION0 3 +/// Address of the CDC0 bulk-in endpoint. +#define CDCD_Descriptors_DATAIN0 2 +/// Address of the CDC0 bulk-out endpoint. +#define CDCD_Descriptors_DATAOUT0 1 + +/// Number of the CDC1 interface. +#define CDCD_Descriptors_INTERFACENUM1 2 +/// Address of the CDC1 interrupt-in endpoint. +#define CDCD_Descriptors_NOTIFICATION1 6 +/// Address of the CDC1 bulk-in endpoint. +#define CDCD_Descriptors_DATAIN1 5 +/// Address of the CDC1 bulk-out endpoint. +#define CDCD_Descriptors_DATAOUT1 4 + + +//----------------------------------------------------------------------------- +// Exported variables +//----------------------------------------------------------------------------- + +extern const USBDDriverDescriptors dualcdcdDriverDescriptors; + +#endif //#ifndef DUALCDCDDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/HIDDFunctionDriver.c b/usb/device/composite/HIDDFunctionDriver.c new file mode 100644 index 0000000..03f8a1c --- /dev/null +++ b/usb/device/composite/HIDDFunctionDriver.c @@ -0,0 +1,477 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +// USB +#include +#include +#include +// HID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HIDDFunctionDriver.h" +#include "HIDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Internal types +//----------------------------------------------------------------------------- + +/// Driver structure for an HID device implementing keyboard functionalities. +typedef struct { + + /// Pointer to USB device driver instance + USBDDriver * pUsbdDriver; + /// Interface Number to access this function + unsigned char interfaceNum; + /// Interrupt IN endpoint address + unsigned char interruptInEndpoint; + /// Interrupt OUT endpoint address + unsigned char interruptOutEndpoint; + /// Idle rate (in milliseconds) of the input report + unsigned char inputReportIdleRate; + /// Input report instance. + HIDDKeyboardInputReport inputReport; + /// Output report instance. + HIDDKeyboardOutputReport outputReport; + +} HIDDKeyboardDriver; + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// Static instance of the HID keyboard device driver. +static HIDDKeyboardDriver hiddKeyboardDriver; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Returns the descriptor requested by the host. +/// \param type Descriptor type. +/// \param length Maximum number of bytes to send. +/// \return 1 if the request has been handled by this function, otherwise 0. +//----------------------------------------------------------------------------- +static unsigned char HIDD_GetDescriptor(unsigned char type, + unsigned char length) +{ + const USBConfigurationDescriptor *pConfiguration; + HIDDescriptor *hidDescriptors[2]; + + switch (type) { + + case HIDGenericDescriptor_REPORT: + TRACE_INFO_WP("Report "); + + // Adjust length and send report descriptor + if (length > HIDD_Descriptors_REPORTSIZE) { + + length = HIDD_Descriptors_REPORTSIZE; + } + USBD_Write(0, &hiddReportDescriptor, length, 0, 0); + break; + + case HIDGenericDescriptor_HID: + TRACE_INFO_WP("HID "); + + // Configuration descriptor is different depending on speed + if (USBD_IsHighSpeed()) { + + pConfiguration = hiddKeyboardDriver + .pUsbdDriver->pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = hiddKeyboardDriver + .pUsbdDriver->pDescriptors->pFsConfiguration; + } + + // Parse the device configuration to get the HID descriptor + USBConfigurationDescriptor_Parse(pConfiguration, + 0, + 0, + (USBGenericDescriptor **) hidDescriptors); + + // Adjust length and send HID descriptor + if (length > sizeof(HIDDescriptor)) { + + length = sizeof(HIDDescriptor); + } + USBD_Write(0, hidDescriptors[0], length, 0, 0); + break; + + default: + return 0; + } + + return 1; +} + +//----------------------------------------------------------------------------- +/// Sends the current Idle rate of the input report to the host. +//----------------------------------------------------------------------------- +static void HIDD_GetIdle() +{ + TRACE_INFO_WP("gIdle "); + + USBD_Write(0, &(hiddKeyboardDriver.inputReportIdleRate), 1, 0, 0); +} + +//----------------------------------------------------------------------------- +/// Retrieves the new idle rate of the input report from the USB host. +/// \param idleRate New input report idle rate. +//----------------------------------------------------------------------------- +static void HIDD_SetIdle(unsigned char idleRate) +{ + TRACE_INFO_WP("sIdle(%d) ", idleRate); + + hiddKeyboardDriver.inputReportIdleRate = idleRate; + USBD_Write(0, 0, 0, 0, 0); +} + +//----------------------------------------------------------------------------- +/// Sends the requested report to the host. +/// \param type Report type. +/// \param length Maximum number of bytes to send. +//----------------------------------------------------------------------------- +static void HIDD_GetReport(unsigned char type, + unsigned short length) +{ + TRACE_INFO_WP("gReport "); + + // Check report type + switch (type) { + + case HIDReportRequest_INPUT: + TRACE_INFO_WP("In "); + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardInputReport)) { + + length = sizeof(HIDDKeyboardInputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.inputReport), + length, + 0, // No callback + 0); + break; + + case HIDReportRequest_OUTPUT: + TRACE_INFO_WP("Out "); + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardOutputReport)) { + + length = sizeof(HIDDKeyboardOutputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + 0, // No callback + 0); + break; + + default: + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Callback invoked when an output report has been received from the host. +/// Forward the new status of the LEDs to the user program via the +//----------------------------------------------------------------------------- +static void HIDD_ReportReceived() +{ + TRACE_INFO_WP("oReport "); + + // Trigger callback + HIDDKeyboardCallbacks_LedsChanged( + HIDDKeyboardOutputReport_GetNumLockStatus( + &(hiddKeyboardDriver.outputReport)), + HIDDKeyboardOutputReport_GetCapsLockStatus( + &(hiddKeyboardDriver.outputReport)), + HIDDKeyboardOutputReport_GetScrollLockStatus( + &(hiddKeyboardDriver.outputReport))); + + // Restart transfer + USBD_Read(hiddKeyboardDriver.interruptOutEndpoint, + &(hiddKeyboardDriver.outputReport), + sizeof(HIDDKeyboardOutputReport), + (TransferCallback) HIDD_ReportReceived, + 0); // No argument for callback function +} + +//----------------------------------------------------------------------------- +/// Retrieves the new value of a report from the host and saves it. +/// \param type Report type. +/// \param length Report length. +//----------------------------------------------------------------------------- +static void HIDD_SetReport(unsigned char type, + unsigned short length) +{ + TRACE_INFO_WP("sReport "); + + // Check report type + switch (type) { + + case HIDReportRequest_INPUT: + // SET_REPORT requests on input reports are ignored + USBD_Stall(0); + break; + + case HIDReportRequest_OUTPUT: + // Check report length + if (length != sizeof(HIDDKeyboardOutputReport)) { + + USBD_Stall(0); + } + else { + + USBD_Read(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + (TransferCallback) HIDD_ReportReceived, + 0); // No argument to the callback function + } + break; + + default: + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes an USB HID keyboard function driver. +/// \param pUsbdDriver Pointer to the USB driver instance. +/// \param interfaceNum Interface number to access the MSD function +/// \param interruptInEndpoint Interrupt IN endpoint address +/// \param interruptOutEndpoint Interrupt OUT endpoint address +//----------------------------------------------------------------------------- +void HIDDFunctionDriver_Initialize(USBDDriver * pUsbdDriver, + unsigned char interfaceNum, + unsigned char interruptInEndpoint, + unsigned char interruptOutEndpoint) +{ + hiddKeyboardDriver.inputReportIdleRate = 0; + HIDDKeyboardInputReport_Initialize(&(hiddKeyboardDriver.inputReport)); + HIDDKeyboardOutputReport_Initialize(&(hiddKeyboardDriver.outputReport)); + + hiddKeyboardDriver.pUsbdDriver = pUsbdDriver; + hiddKeyboardDriver.interfaceNum = interfaceNum; + hiddKeyboardDriver.interruptInEndpoint = interruptInEndpoint; + hiddKeyboardDriver.interruptOutEndpoint = interruptOutEndpoint; +} + +//----------------------------------------------------------------------------- +/// Handles HID-specific SETUP request sent by the host. +/// \param request Pointer to a USBGenericRequest instance. +/// \return 0 if the request is Unsupported, 1 if the request handled. +//----------------------------------------------------------------------------- +unsigned char HIDDFunctionDriver_RequestHandler( + const USBGenericRequest *request) +{ + TRACE_INFO_WP("NewReq "); + + // Check if this is a standard request + if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // This is a standard request + switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, otherwise forward it to + // the standard driver + if (!HIDD_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(hiddKeyboardDriver.pUsbdDriver, + request); + } + break; + + default: + return 0; + } + } + // Check if this is a class request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + // This is a class-specific request + switch (USBGenericRequest_GetRequest(request)) { + + case HIDGenericRequest_GETIDLE: + HIDD_GetIdle(); + break; + + case HIDGenericRequest_SETIDLE: + HIDD_SetIdle(HIDIdleRequest_GetIdleRate(request)); + break; + + case HIDGenericRequest_GETREPORT: + HIDD_GetReport( + HIDReportRequest_GetReportType(request), + USBGenericRequest_GetLength(request)); + break; + + case HIDGenericRequest_SETREPORT: + HIDD_SetReport( + HIDReportRequest_GetReportType(request), + USBGenericRequest_GetLength(request)); + break; + + default: + return 0; + } + } + return 1; +} + +//----------------------------------------------------------------------------- +/// Invoked whenever the configuration of the device is changed by the host. +/// \param cfgnum Newly configuration number. +//----------------------------------------------------------------------------- +void HIDDFunctionCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + if (cfgnum > 0) { + + // Start receiving output reports + USBD_Read(hiddKeyboardDriver.interruptOutEndpoint, + &(hiddKeyboardDriver.outputReport), + sizeof(HIDDKeyboardOutputReport), + (TransferCallback) HIDD_ReportReceived, + 0); // No argument for callback function + } +} + +//----------------------------------------------------------------------------- +/// Reports a change in which keys are currently pressed or release to the +/// host. +/// \param pressedKeys Pointer to an array of key codes indicating keys that +/// have been pressed since the last call to +/// . +/// \param pressedKeysSize Number of key codes in the pressedKeys array. +/// \param releasedKeys Pointer to an array of key codes indicates keys that +/// have been released since the last call to +/// . +/// \param releasedKeysSize Number of key codes in the releasedKeys array. +/// \return if the report has been sent to the host; +/// otherwise an error code. +//----------------------------------------------------------------------------- +unsigned char HIDDKeyboardDriver_ChangeKeys(unsigned char *pressedKeys, + unsigned char pressedKeysSize, + unsigned char *releasedKeys, + unsigned char releasedKeysSize) +{ + // Press keys + while (pressedKeysSize > 0) { + + // Check if this is a standard or modifier key + if (HIDKeypad_IsModifierKey(*pressedKeys)) { + + // Set the corresponding bit in the input report + HIDDKeyboardInputReport_PressModifierKey( + &(hiddKeyboardDriver.inputReport), + *pressedKeys); + } + else { + + HIDDKeyboardInputReport_PressStandardKey( + &(hiddKeyboardDriver.inputReport), + *pressedKeys); + } + + pressedKeysSize--; + pressedKeys++; + } + + // Release keys + while (releasedKeysSize > 0) { + + // Check if this is a standard or modifier key + if (HIDKeypad_IsModifierKey(*releasedKeys)) { + + // Set the corresponding bit in the input report + HIDDKeyboardInputReport_ReleaseModifierKey( + &(hiddKeyboardDriver.inputReport), + *releasedKeys); + } + else { + + HIDDKeyboardInputReport_ReleaseStandardKey( + &(hiddKeyboardDriver.inputReport), + *releasedKeys); + } + + releasedKeysSize--; + releasedKeys++; + } + + // Send input report through the interrupt IN endpoint + return USBD_Write(hiddKeyboardDriver.interruptInEndpoint, + &(hiddKeyboardDriver.inputReport), + sizeof(HIDDKeyboardInputReport), + 0, + 0); +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void HIDDKeyboardDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(hiddKeyboardDriver.pUsbdDriver)) { + + USBD_RemoteWakeUp(); + } +} diff --git a/usb/device/composite/HIDDFunctionDriver.h b/usb/device/composite/HIDDFunctionDriver.h new file mode 100644 index 0000000..a788d57 --- /dev/null +++ b/usb/device/composite/HIDDFunctionDriver.h @@ -0,0 +1,64 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef HIDDFUNCTIONDRIVER_H +#define HIDDFUNCTIONDRIVER_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//- Function API for composite device +extern void HIDDFunctionDriver_Initialize(USBDDriver * pUsbdDriver, + unsigned char interfaceNum, + unsigned char interruptInEndpoint, + unsigned char interruptOutEndpoint); + +extern unsigned char HIDDFunctionDriver_RequestHandler( + const USBGenericRequest *request); + +extern void HIDDFunctionCallbacks_ConfigurationChanged(unsigned char cfgnum); + +//- HID Keyboard API +extern unsigned char HIDDKeyboardDriver_ChangeKeys( + unsigned char *pressedKeys, + unsigned char pressedKeysSize, + unsigned char *releasedKeys, + unsigned char releasedKeysSize); + +extern void HIDDKeyboardDriver_RemoteWakeUp(void); + +#endif // #define HIDDFUNCTIONDRIVER_H diff --git a/usb/device/composite/HIDDFunctionDriverDescriptors.c b/usb/device/composite/HIDDFunctionDriverDescriptors.c new file mode 100644 index 0000000..747a5fb --- /dev/null +++ b/usb/device/composite/HIDDFunctionDriverDescriptors.c @@ -0,0 +1,118 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//- HID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HIDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Internal structures +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// Report descriptor used by the HID function driver. +const unsigned char hiddReportDescriptor[] = { + + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_KEYBOARD, + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, + + // Input report: modifier keys + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_REPORTCOUNT + 1, 8, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDD_Descriptors_FIRSTMODIFIERKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDD_Descriptors_LASTMODIFIERKEY, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_INPUT + 1, HIDReport_VARIABLE, + + // Input report: standard keys + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, + HIDD_Descriptors_FIRSTSTANDARDKEY, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, + HIDD_Descriptors_LASTSTANDARDKEY, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDD_Descriptors_FIRSTSTANDARDKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDD_Descriptors_LASTSTANDARDKEY, + HIDReport_INPUT + 1, 0 /* Data array */, + + // Output report: LEDs + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDLeds_PAGEID, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_LOCAL_USAGEMINIMUM + 1, HIDLeds_NUMLOCK, + HIDReport_LOCAL_USAGEMAXIMUM + 1, HIDLeds_SCROLLLOCK, + HIDReport_OUTPUT + 1, HIDReport_VARIABLE, + + // Output report: padding + HIDReport_GLOBAL_REPORTCOUNT + 1, 1, + HIDReport_GLOBAL_REPORTSIZE + 1, 5, + HIDReport_OUTPUT + 1, HIDReport_CONSTANT, + + HIDReport_ENDCOLLECTION +}; diff --git a/usb/device/composite/HIDDFunctionDriverDescriptors.h b/usb/device/composite/HIDDFunctionDriverDescriptors.h new file mode 100644 index 0000000..7f64321 --- /dev/null +++ b/usb/device/composite/HIDDFunctionDriverDescriptors.h @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef HIDDFUNCTIONDRIVERDESCRIPTORS_H +#define HIDDFUNCTIONDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +//- Interface & Endpoints +/// Interface Number. +/// Interrupt IN endpoint number. +/// Interrupt OUT endpoint number. +/// Interrupt IN endpoint polling rate (in milliseconds). +/// Interrupt OUT endpoint polling rate (in milliseconds). +#if defined(usb_HIDAUDIO) +#define HIDD_Descriptors_INTERFACENUM 0 +#define HIDD_Descriptors_INTERRUPTIN 1 +#define HIDD_Descriptors_INTERRUPTOUT 2 +#define HIDD_Descriptors_INTERRUPTIN_POLLING 10 +#endif + +/// Default HID interrupt IN endpoint polling rate (16ms). +#define HIDD_Descriptors_INTERRUPTIN_POLLING_FS 16 +#define HIDD_Descriptors_INTERRUPTIN_POLLING_HS 8 +/// Default interrupt OUT endpoint polling rate (16ms). +#define HIDD_Descriptors_INTERRUPTOUT_POLLING_FS 16 +#define HIDD_Descriptors_INTERRUPTOUT_POLLING_HS 8 + +//- Keypad keys +/// Key code of the first accepted modifier key. +#define HIDD_Descriptors_FIRSTMODIFIERKEY HIDKeypad_LEFTCONTROL +/// Key code of the last accepted modifier key. +#define HIDD_Descriptors_LASTMODIFIERKEY HIDKeypad_RIGHTGUI +/// Key code of the first accepted standard key. +#define HIDD_Descriptors_FIRSTSTANDARDKEY 0 +/// Key code of the last accepted standard key. +#define HIDD_Descriptors_LASTSTANDARDKEY HIDKeypad_NUMLOCK + +//- Report descriptor +/// Size of the report descriptor in bytes. +#define HIDD_Descriptors_REPORTSIZE 61 + +//----------------------------------------------------------------------------- +// Exported variables +//----------------------------------------------------------------------------- + +/// Report descriptor used by the driver. +extern const unsigned char hiddReportDescriptor[]; + +#endif // #define HIDDFUNCTIONDRIVERDESCRIPTORS_H + diff --git a/usb/device/composite/HIDMSDDDriver.c b/usb/device/composite/HIDMSDDDriver.c new file mode 100644 index 0000000..2ef2ea7 --- /dev/null +++ b/usb/device/composite/HIDMSDDDriver.c @@ -0,0 +1,227 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +#include + +// USB +#include +#include + +//- HID +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//- MSD + +//- HIDMSD +#include "HIDMSDDDriver.h" +#include "HIDMSDDDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +/// Interface setting spaces (4 byte aligned) +#define NUM_INTERFACES ((HIDMSDDDriverDescriptors_NUMINTERFACE+3)&0xFC) + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// USBDDriver instance +static USBDDriver usbdDriver; + +/// Array for storing the current setting of each interface +static unsigned char hidmsddDriverInterfaces[NUM_INTERFACES]; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Optional RequestReceived() callback re-implementation +//----------------------------------------------------------------------------- +#if !defined(NOAUTOCALLBACK) + +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + HIDMSDDDriver_RequestHandler(request); +} + +#endif + +//----------------------------------------------------------------------------- +// ConfigurationChanged() callback re-implementation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Invoked whenever the configuration value of a device is changed by the host +/// \param cfgnum Configuration number. +//----------------------------------------------------------------------------- +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + // HID + HIDDFunctionCallbacks_ConfigurationChanged(cfgnum); + + // MSD + MSDDFunctionCallbacks_ConfigurationChanged(cfgnum); +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the USB device HIDMSD device driver. +//----------------------------------------------------------------------------- +void HIDMSDDDriver_Initialize(MSDLun *pLuns, unsigned char numLuns) +{ + // HID + HIDDFunctionDriver_Initialize(&usbdDriver, + HIDD_Descriptors_INTERFACENUM, + HIDD_Descriptors_INTERRUPTIN, + HIDD_Descriptors_INTERRUPTOUT); + + // MSD + MSDDFunctionDriver_Initialize(&usbdDriver, + pLuns, numLuns, + MSDD_Descriptors_INTERFACENUM, + MSDD_Descriptors_BULKIN, + MSDD_Descriptors_BULKOUT); + + // Initialize the standard USB driver + USBDDriver_Initialize(&usbdDriver, + &hidmsddDriverDescriptors, + hidmsddDriverInterfaces); + + // Initialize the USB driver + USBD_Init(); +} + +//----------------------------------------------------------------------------- +/// Handles HIDMSD-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +void HIDMSDDDriver_RequestHandler(const USBGenericRequest *request) +{ + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + unsigned char rc = 0; + + // MSD class request + if (rc == 0) { + + rc = MSDDFunctionDriver_RequestHandler(request); + } + + // HID class request + if (rc == 0) { + + rc = HIDDFunctionDriver_RequestHandler(request); + } + + if (!rc) { + + TRACE_WARNING( + "HIDMSDDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + unsigned char rc = 0; + + rc = HIDDFunctionDriver_RequestHandler(request); + + if (rc == 0) { + + rc = MSDDFunctionDriver_RequestHandler(request); + } + + // Forward request to the standard handler + if (rc == 0) { + + USBDDriver_RequestHandler(&(usbdDriver), request); + } + } + // Unsupported request type + else { + + TRACE_WARNING( + "HIDMSDDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void HIDMSDDDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) { + + USBD_RemoteWakeUp(); + } + // Remote wake-up NOT enabled + else { + + TRACE_WARNING("HIDMSDDDriver_RemoteWakeUp: not enabled\n\r"); + } +} + + diff --git a/usb/device/composite/HIDMSDDDriver.h b/usb/device/composite/HIDMSDDDriver.h new file mode 100644 index 0000000..ba37205 --- /dev/null +++ b/usb/device/composite/HIDMSDDDriver.h @@ -0,0 +1,79 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// +/// !Purpose +/// +/// Definitions and methods for USB HID + MSD HIDMSD device implement. +/// +/// !Usage +/// +/// -# Initialize USB function specified driver ( for MSD currently ) +/// - MSDDFunctionDriver_Initialize +/// +/// -# Initialize USB HIDMSD driver and USB driver +/// - HIDMSDDDriver_Initialize +/// +/// -# Handle and dispach USB requests +/// - HIDMSDDDriver_RequestHandler +/// +/// -# Try starting a remote wake-up sequence +/// - HIDMSDDDriver_RemoteWakeUp +//----------------------------------------------------------------------------- + +#ifndef HIDMSDDDRIVER_H +#define HIDMSDDDRIVER_H + + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +#include "HIDDFunctionDriver.h" +#include "MSDDFunctionDriver.h" + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +// -HIDMSD Composite device +extern void HIDMSDDDriver_Initialize(MSDLun *pLuns, + unsigned char numLuns); + +extern void HIDMSDDDriver_RequestHandler(const USBGenericRequest *request); + +extern void HIDMSDDDriver_RemoteWakeUp(void); + +#endif //#ifndef HIDMSDDDRIVER_H + diff --git a/usb/device/composite/HIDMSDDDriverDescriptors.c b/usb/device/composite/HIDMSDDDriverDescriptors.c new file mode 100644 index 0000000..aecfd61 --- /dev/null +++ b/usb/device/composite/HIDMSDDDriverDescriptors.c @@ -0,0 +1,449 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDMSDDDriver.h" +#include "HIDMSDDDriverDescriptors.h" +#include + +//- USB Generic +#include +#include +#include +#include +#include +#include + +//- HID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HIDDFunctionDriverDescriptors.h" + +//- MSD +#include +#include +#include "MSDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Device product ID. +#define HIDMSDDDriverDescriptors_PRODUCTID 0x6135 + +/// Device vendor ID (Atmel). +#define HIDMSDDDriverDescriptors_VENDORID 0x03EB + +/// Device release number. +#define HIDMSDDDriverDescriptors_RELEASE 0x0003 + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//----------------------------------------------------------------------------- +// Internal structures +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Audio control header descriptor with one slave interface. +//----------------------------------------------------------------------------- +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//----------------------------------------------------------------------------- +/// Configuration descriptor list for a device implementing a composite driver. +//----------------------------------------------------------------------------- +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; + + /// --- HID + USBInterfaceDescriptor hidInterface; + HIDDescriptor hid; + USBEndpointDescriptor hidInterruptIn; + USBEndpointDescriptor hidInterruptOut; + + /// --- MSD + /// Mass storage interface descriptor. + USBInterfaceDescriptor msdInterface; + /// Bulk-out endpoint descriptor. + USBEndpointDescriptor msdBulkOut; + /// Bulk-in endpoint descriptor. + USBEndpointDescriptor msdBulkIn; + +} __attribute__ ((packed)) HidMsdDriverConfigurationDescriptors; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Standard USB device descriptor for the composite device driver +static const USBDeviceDescriptor deviceDescriptor = +{ + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + 0x00, + 0x00, + 0x00, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDMSDDDriverDescriptors_VENDORID, + HIDMSDDDriverDescriptors_PRODUCTID, + HIDMSDDDriverDescriptors_RELEASE, + 0, // No string descriptor for manufacturer + 1, // Index of product string descriptor is #1 + 0, // No string descriptor for serial number + 1 // Device has 1 possible configuration +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + +/// USB device qualifier descriptor. +static const USBDeviceQualifierDescriptor qualifierDescriptor = +{ + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + 0x00, + 0x00, + 0x00, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration + 0 // Reserved +}; + +/// USB configuration descriptors for the composite device driver +static const HidMsdDriverConfigurationDescriptors configurationDescriptorsHS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HidMsdDriverConfigurationDescriptors), + HIDMSDDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + HIDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDD_Descriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDD_Descriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING_HS + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDD_Descriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING_HS + }, + + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + MSDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + } + +}; + +#endif + +/// USB FS configuration descriptors for the composite device driver +static const HidMsdDriverConfigurationDescriptors configurationDescriptorsFS = +{ + + // Standard configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HidMsdDriverConfigurationDescriptors), + HIDMSDDDriverDescriptors_NUMINTERFACE, + 1, // This is configuration #1 + 0, // No string descriptor for this configuration + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + HIDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDD_Descriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDD_Descriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING_FS + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDD_Descriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDD_Descriptors_INTERRUPTIN_POLLING_FS + }, + + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + MSDD_Descriptors_INTERFACENUM, + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDD_Descriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + } + +}; + +/// String descriptor with the supported languages. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('l') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(14), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('p'), + USBStringDescriptor_UNICODE('o'), + USBStringDescriptor_UNICODE('s'), + USBStringDescriptor_UNICODE('i'), + USBStringDescriptor_UNICODE('t'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('e'), + USBStringDescriptor_UNICODE('m'), + USBStringDescriptor_UNICODE('o') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(4), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3') +}; + +/// Array of pointers to the four string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors required by an USB audio speaker device driver. +const USBDDriverDescriptors hidmsddDriverDescriptors = { + + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &deviceDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsHS, + &qualifierDescriptor, + (const USBConfigurationDescriptor *) &configurationDescriptorsFS, +#else + 0, 0, 0, 0, 0, 0, +#endif + stringDescriptors, + 4 // Number of string descriptors +}; diff --git a/usb/device/composite/HIDMSDDDriverDescriptors.h b/usb/device/composite/HIDMSDDDriverDescriptors.h new file mode 100644 index 0000000..85739be --- /dev/null +++ b/usb/device/composite/HIDMSDDDriverDescriptors.h @@ -0,0 +1,66 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef HIDMSDDDRIVERDESCRIPTORS_H +#define HIDMSDDDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +/// Number of interfaces of the device +#define HIDMSDDDriverDescriptors_NUMINTERFACE 2 + +#define HIDD_Descriptors_INTERFACENUM 0 +/// Address of the HID interrupt IN endpoint. +#define HIDD_Descriptors_INTERRUPTIN 1 +/// Address of the HID interrupt OUT endpoint. +#define HIDD_Descriptors_INTERRUPTOUT 2 + +#define MSDD_Descriptors_INTERFACENUM 1 +/// Address of the Mass Storage bulk-out endpoint. +#define MSDD_Descriptors_BULKOUT 4 +/// Address of the Mass Storage bulk-in endpoint. +#define MSDD_Descriptors_BULKIN 5 + + +//----------------------------------------------------------------------------- +// Exported variables +//----------------------------------------------------------------------------- + +extern const USBDDriverDescriptors hidmsddDriverDescriptors; + +#endif //#ifndef HIDMSDDDRIVERDESCRIPTORS_H diff --git a/usb/device/composite/MSDDFunctionDriver.c b/usb/device/composite/MSDDFunctionDriver.c new file mode 100644 index 0000000..840294e --- /dev/null +++ b/usb/device/composite/MSDDFunctionDriver.c @@ -0,0 +1,327 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- + +// GENERAL +#include +#include +// USB +#include +#include +#include +#include +// MSD +#include +#include +#include "MSDDFunctionDriverDescriptors.h" + +//----------------------------------------------------------------------------- +// Internal types +//----------------------------------------------------------------------------- + +/// Driver structure for an HID device implementing keyboard functionalities. +typedef struct { + + /// Pointer to USB device driver instance + USBDDriver * pUsbdDriver; + /// Pointer to MSD driver instance + MSDDriver * pMsdDriver; + /// Interface Number of MS Function + unsigned char interfaceNum; + /// Interrupt IN endpoint address + unsigned char bulkInEndpoint; + /// Interrupt OUT endpoint address + unsigned char bulkOutEndpoint; + +} MSDFunctionDriver; + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// Mass storage general driver instance. +static MSDDriver msdDriver; + +/// Mass storage function driver instance. +static MSDFunctionDriver msdFunDriver; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Resets the state of the MSD driver +//----------------------------------------------------------------------------- +static void MSDD_Reset() +{ + TRACE_INFO_WP("MSDReset "); + + msdDriver.state = MSDD_STATE_READ_CBW; + msdDriver.waitResetRecovery = 0; + msdDriver.commandState.state = 0; +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes a MSD driver +/// \param luns Pointer to a list of LUNs +/// \param numLuns Number of LUN in list +/// \param interfaceNum Interface number to access the MSD function +/// \param bulkInEndpoint Bulk IN endpoint address +/// \param bulkOutEndpoint Bulk OUT endpoint address +//----------------------------------------------------------------------------- +void MSDDFunctionDriver_Initialize(USBDDriver * pUsbdDriver, + MSDLun *luns, unsigned char numLuns, + unsigned char interfaceNum, + unsigned char bulkInEndpoint, + unsigned char bulkOutEndpoint) +{ + TRACE_INFO("MSD init\n\r"); + + msdFunDriver.pUsbdDriver = pUsbdDriver; + msdFunDriver.pMsdDriver = &msdDriver; + msdFunDriver.interfaceNum = interfaceNum; + msdFunDriver.bulkInEndpoint = bulkInEndpoint; + msdFunDriver.bulkOutEndpoint = bulkOutEndpoint; + + // Command state initialization + msdDriver.commandState.state = 0; + msdDriver.commandState.postprocess = 0; + msdDriver.commandState.length = 0; + msdDriver.commandState.transfer.semaphore = 0; + + // LUNs + msdDriver.luns = luns; + msdDriver.maxLun = (unsigned char) (numLuns - 1); + + // Reset BOT driver + MSDD_Reset(); +} + +//----------------------------------------------------------------------------- +/// Invoked when a new SETUP request is received from the host. Forwards the +/// request to the Mass Storage device driver handler function. +/// \param request Pointer to a USBGenericRequest instance. +/// \return 0 if the request is Unsupported, 1 if the request handled. +//----------------------------------------------------------------------------- +unsigned char MSDDFunctionDriver_RequestHandler( + const USBGenericRequest *request) +{ + // Handle requests + switch (USBGenericRequest_GetRequest(request)) { + //--------------------- + case USBGenericRequest_CLEARFEATURE: + //--------------------- + TRACE_INFO_WP("ClrFeat "); + + switch (USBFeatureRequest_GetFeatureSelector(request)) { + + //--------------------- + case USBFeatureRequest_ENDPOINTHALT: + //--------------------- + TRACE_INFO_WP("Hlt "); + + // Do not clear the endpoint halt status if the device is waiting + // for a reset recovery sequence + if (!msdDriver.waitResetRecovery) { + + // Forward the request to the standard handler + return 0; + } + else { + + TRACE_INFO_WP("No "); + } + + USBD_Write(0, 0, 0, 0, 0); + break; + + //------ + default: + //------ + // Forward the request to the standard handler + return 0; + } + break; + + //------------------- + case MSD_GET_MAX_LUN: + //------------------- + TRACE_INFO_WP("gMaxLun "); + + // Check request parameters + if ((request->wValue == 0) + && (request->wIndex == msdFunDriver.interfaceNum) + && (request->wLength == 1)) { + + USBD_Write(0, &(msdDriver.maxLun), 1, 0, 0); + + } + else { + + TRACE_WARNING( + "MSDDriver_RequestHandler: GetMaxLUN(%d,%d,%d)\n\r", + request->wValue, request->wIndex, request->wLength); + USBD_Stall(0); + } + break; + + //----------------------- + case MSD_BULK_ONLY_RESET: + //----------------------- + TRACE_INFO_WP("Rst "); + + // Check parameters + if ((request->wValue == 0) + && (request->wIndex == msdFunDriver.interfaceNum) + && (request->wLength == 0)) { + + // Reset the MSD driver + MSDD_Reset(); + USBD_Write(0, 0, 0, 0, 0); + } + else { + + TRACE_WARNING( + "MSDDriver_RequestHandler: Reset(%d,%d,%d)\n\r", + request->wValue, request->wIndex, request->wLength); + USBD_Stall(0); + } + break; + + //------ + default: + //------ + // Forward request to standard handler + return 0; + } + + return 1; +} + +//----------------------------------------------------------------------------- +/// Invoked whenever the configuration of the device is changed by the host. +/// \param cfgnum Newly configuration number. +//----------------------------------------------------------------------------- +void MSDDFunctionCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + if (cfgnum > 0) { + + MSDD_Reset(); + } +} + +//----------------------------------------------------------------------------- +/// Reads from host through MSD defined pipe. Act as USBD_Read. +/// \param data Pointer to the data buffer that contains data read from host. +/// \param size The number of bytes should be read (buffer size). +/// \param callback Pointer to the function invoked on end of reading. +/// \param argument Pointer to additional argument data struct. +//----------------------------------------------------------------------------- +char MSDD_Read(void *data, + unsigned int size, + TransferCallback callback, + void *argument) + +{ + return USBD_Read(msdFunDriver.bulkOutEndpoint, + data, + size, + callback, + argument); +} + +//----------------------------------------------------------------------------- +/// Writes to host through MSD defined pipe. Act as USBD_Write. +/// \param data Pointer to the data that writes to the host. +/// \param size The number of bytes should be write. +/// \param callback Pointer to the function invoked on end of writing. +/// \param argument Pointer to additional argument data struct. +//----------------------------------------------------------------------------- +char MSDD_Write(void *data, + unsigned int size, + TransferCallback callback, + void *argument) +{ + return USBD_Write(msdFunDriver.bulkInEndpoint, + data, + size, + callback, + argument); +} + +//----------------------------------------------------------------------------- +/// HALT Specified USB pipe. +/// \param stallCASE Case of the stall condition (Bulk In/Out/Both). +//----------------------------------------------------------------------------- +void MSDD_Halt(unsigned int stallCASE) +{ + if (stallCASE & MSDD_CASE_STALL_OUT) { + + USBD_Halt(msdFunDriver.bulkOutEndpoint); + } + + if (stallCASE & MSDD_CASE_STALL_IN) { + + USBD_Halt(msdFunDriver.bulkInEndpoint); + } +} + +//----------------------------------------------------------------------------- +/// Return halted status +/// \return stallCASE bitmap, case of the stall condition +/// (bit: MSDD_CASE_STALL_OUT or MSDD_CASE_STALL_IN) +//----------------------------------------------------------------------------- +unsigned int MSDD_IsHalted(void) +{ + unsigned int stallCASE = 0; + if (USBD_IsHalted(msdFunDriver.bulkOutEndpoint)) { + + stallCASE |= MSDD_CASE_STALL_OUT; + } + if (USBD_IsHalted(msdFunDriver.bulkInEndpoint)) { + + stallCASE |= MSDD_CASE_STALL_IN; + } + return stallCASE; +} + +//----------------------------------------------------------------------------- +/// State machine for the MSD driver +//----------------------------------------------------------------------------- +void MSDDriver_StateMachine(void) +{ + MSDD_StateMachine(&msdDriver); +} diff --git a/usb/device/composite/MSDDFunctionDriver.h b/usb/device/composite/MSDDFunctionDriver.h new file mode 100644 index 0000000..c96fbfd --- /dev/null +++ b/usb/device/composite/MSDDFunctionDriver.h @@ -0,0 +1,72 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// !Purpose +/// +/// Mass storage function driver implementation. +/// +/// !Usage +/// TODO +//----------------------------------------------------------------------------- + +#ifndef MSDDFUNCTIONDRIVER_H +#define MSDDFUNCTIONDRIVER_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//- Function API for composite device +extern void MSDDFunctionDriver_Initialize(USBDDriver * pUsbdDriver, + MSDLun *luns, unsigned char numLuns, + unsigned char interfaceNum, + unsigned char bulkInEndpoint, + unsigned char bulkOutEndpoint); + +extern unsigned char MSDDFunctionDriver_RequestHandler( + const USBGenericRequest *request); + +extern void MSDDFunctionCallbacks_ConfigurationChanged(unsigned char cfgnum); + +//- MSD APIs +extern void MSDDriver_StateMachine(void); + +#endif // #define MSDDFUNCTIONDRIVER_H + diff --git a/usb/device/composite/MSDDFunctionDriverDescriptors.h b/usb/device/composite/MSDDFunctionDriverDescriptors.h new file mode 100644 index 0000000..6da6214 --- /dev/null +++ b/usb/device/composite/MSDDFunctionDriverDescriptors.h @@ -0,0 +1,69 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef MSDDFUNCTIONDRIVERDESCRIPTORS_H +#define MSDDFUNCTIONDRIVERDESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// \page "MS interface & endpoint descriptor settings" +/// This page lists the definition used by the Mass Storage driver. +/// +/// !Settings +/// +/// - MSDD_Descriptors_INTERFACENUM +/// - MSDD_Descriptors_BULKOUT +/// - MSDD_Descriptors_BULKIN + +/// Number of the Mass Storage interface. +#if defined(usb_CDCMSD) +#define MSDD_Descriptors_INTERFACENUM 2 +#elif defined(usb_HIDMSD) +#define MSDD_Descriptors_INTERFACENUM 1 +#endif + +/// Address of the Mass Storage bulk-out endpoint. +#define MSDD_Descriptors_BULKOUT 4 + +/// Address of the Mass Storage bulk-in endpoint. +#define MSDD_Descriptors_BULKIN 5 +//----------------------------------------------------------------------------- + + +#endif // #define MSDDFUNCTIONDRIVERDESCRIPTORS_H + diff --git a/usb/device/composite/drv/CompositeCDCSerial.inf b/usb/device/composite/drv/CompositeCDCSerial.inf new file mode 100644 index 0000000..973a14b --- /dev/null +++ b/usb/device/composite/drv/CompositeCDCSerial.inf @@ -0,0 +1,57 @@ +; $Id: 6119.inf,v 1.1.2.1 2006/12/05 08:33:25 danielru Exp $ + +[Version] ; Version section +Signature="$Chicago$" ; All Windows versions +Class=Ports ; This is a serial port driver +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} ; Associated GUID +Provider=%ATMEL% ; Driver is provided by ATMEL +DriverVer=09/12/2006,1.1.1.5 ; Driver version 1.1.1.5 published on 23 February 2007 + +[DestinationDirs] ; DestinationDirs section +DefaultDestDir=12 ; Default install directory is \drivers or \IOSubSys + +[Manufacturer] ; Manufacturer section +%ATMEL%=AtmelMfg ; Only one manufacturer (ATMEL), models section is named + ; AtmelMfg + +[AtmelMfg] ; Models section corresponding to ATMEL +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6130&MI_00 ; Identifies a device with ATMEL Vendor ID (03EBh) and + ; Product ID equal to 6130h. Corresponding Install section + ; is named USBtoSer.Install ( CDCHID ) +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6131&MI_00 ; Identifies a device with ATMEL Vendor ID (03EBh) and + ; Product ID equal to 6131h. Corresponding Install section + ; is named USBtoSer.Install ( CDCAUDIO ) +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6132&MI_00 ; Identifies a device with ATMEL Vendor ID (03EBh) and + ; Product ID equal to 6132h. Corresponding Install section + ; is named USBtoSer.Install ( CDCMSD ) +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6133&MI_00 ; Identifies a device with ATMEL Vendor ID (03EBh) and + ; Product ID equal to 6133h. Corresponding Install section + ; is named USBtoSer.Install ( CDCCDC ) +%USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6133&MI_02 ; Identifies a device with ATMEL Vendor ID (03EBh) and + ; Product ID equal to 6133h. Corresponding Install section + ; is named USBtoSer.Install ( CDCCDC ) + +[USBtoSer.Install] ; Install section +include=mdmcpq.inf +CopyFiles=FakeModemCopyFileSection +AddReg=USBtoSer.AddReg ; Registry keys to add are listed in USBtoSer.AddReg + +[USBtoSer.AddReg] ; AddReg section +HKR,,DevLoader,,*ntkern ; +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[USBtoSer.Install.Services] ; Services section +AddService=usbser,0x00000002,USBtoSer.AddService ; Assign usbser as the PnP driver for the device + +[USBtoSer.AddService] ; Service install section +DisplayName=%USBSer% ; Name of the serial driver +ServiceType=1 ; Service kernel driver +StartType=3 ; Driver is started by the PnP manager +ErrorControl=1 ; Warn about errors +ServiceBinary=%12%\usbser.sys ; Driver filename + +[Strings] ; Strings section +ATMEL="ATMEL Corp." ; String value for the ATMEL symbol +USBtoSerialConverter="AT91 USB to Serial Converter" ; String value for the USBtoSerialConverter symbol +USBSer="USB Composite Serial Driver" ; String value for the USBSer symbol \ No newline at end of file diff --git a/usb/device/composite/drv/drv.dir b/usb/device/composite/drv/drv.dir new file mode 100644 index 0000000..1f2d5d6 --- /dev/null +++ b/usb/device/composite/drv/drv.dir @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides install file for +/// - CDC HID +/// - CDC AUDIO +/// - CDC MSD +/// - CDC CDC +/// +/// !!!Contents +/// +/// CompositeCDCSerial.inf +/// +//------------------------------------------------------------------------------ diff --git a/usb/device/core/USBD.h b/usb/device/core/USBD.h new file mode 100644 index 0000000..e6c2e9b --- /dev/null +++ b/usb/device/core/USBD.h @@ -0,0 +1,276 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// Collection of methods for using the USB device controller on AT91 +/// microcontrollers. +/// +/// !!!Usage +/// +/// Please refer to the corresponding application note. +/// - "AT91 USB device framework" +/// - "USBD API" . "USBD API Methods" +//------------------------------------------------------------------------------ + +#ifndef USBD_H +#define USBD_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Compile Options +//------------------------------------------------------------------------------ + +/// Compile option for HS or OTG, use DMA. Remove this define for not use DMA. +#if defined(CHIP_USB_OTGHS) || defined(CHIP_USB_UDPHS) +#define DMA +#endif + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "USB device API return values" +/// +/// This page lists the return values of the USB %device driver API +/// +/// !Return codes +/// - USBD_STATUS_SUCCESS +/// - USBD_STATUS_LOCKED +/// - USBD_STATUS_ABORTED +/// - USBD_STATUS_RESET + +/// Indicates the operation was successful. +#define USBD_STATUS_SUCCESS 0 +/// Endpoint/device is already busy. +#define USBD_STATUS_LOCKED 1 +/// Operation has been aborted. +#define USBD_STATUS_ABORTED 2 +/// Operation has been aborted because the device has been reset. +#define USBD_STATUS_RESET 3 +/// Part ot operation successfully done. +#define USBD_STATUS_PARTIAL_DONE 4 +/// Operation failed because parameter error +#define USBD_STATUS_INVALID_PARAMETER 5 +/// Operation failed because in unexpected state +#define USBD_STATUS_WRONG_STATE 6 +/// Operation failed because HW not supported +#define USBD_STATUS_HW_NOT_SUPPORTED 0xFE +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "USB device states" +/// +/// This page lists the %device states of the USB %device driver. +/// +/// !States +/// - USBD_STATE_SUSPENDED +/// - USBD_STATE_ATTACHED +/// - USBD_STATE_POWERED +/// - USBD_STATE_DEFAULT +/// - USBD_STATE_ADDRESS +/// - USBD_STATE_CONFIGURED + +/// The device is currently suspended. +#define USBD_STATE_SUSPENDED 0 +/// USB cable is plugged into the device. +#define USBD_STATE_ATTACHED 1 +/// Host is providing +5V through the USB cable. +#define USBD_STATE_POWERED 2 +/// Device has been reset. +#define USBD_STATE_DEFAULT 3 +/// The device has been given an address on the bus. +#define USBD_STATE_ADDRESS 4 +/// A valid configuration has been selected. +#define USBD_STATE_CONFIGURED 5 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "USB device LEDs" +/// +/// This page lists the LEDs used in the USB %device driver. +/// +/// !LEDs +/// - USBD_LEDPOWER +/// - USBD_LEDUSB +/// - USBD_LEDOTHER + +/// LED for indicating that the device is powered. +#define USBD_LEDPOWER 0 +/// LED for indicating USB activity. +#define USBD_LEDUSB 1 +/// LED for custom usage. +#define USBD_LEDOTHER 2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Buffer struct used for multi-buffer-listed transfer. +/// The driver can process 255 bytes of buffers or buffer list window. +//------------------------------------------------------------------------------ +typedef struct _USBDTransferBuffer { + /// Pointer to frame buffer + unsigned char * pBuffer; + /// Size of the frame (up to 64K-1) + unsigned short size; + /// Bytes transferred + unsigned short transferred; + /// Bytes in FIFO + unsigned short buffered; + /// Bytes remaining + unsigned short remaining; +} USBDTransferBuffer; + +#ifdef __ICCARM__ // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// Struct used for USBD DMA Link List Transfer Descriptor, must be 16-bytes +/// aligned. +/// (For USB, DMA transfer is linked to EPs and FIFO address is EP defined) +//------------------------------------------------------------------------------ +typedef struct _USBDDmaDescriptor { + /// Pointer to Next Descriptor + void* pNxtDesc; + /// Pointer to data buffer address + void* pDataAddr; + /// DMA Control setting register value + unsigned int ctrlSettings:8, /// Control settings + reserved:8, /// Not used + bufferLength:16; /// Length of buffer + /// Loaded to DMA register, OK to modify + unsigned int used; +} __attribute__((aligned(16))) USBDDmaDescriptor; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// Callback used by transfer functions (USBD_Read & USBD_Write) to notify +/// that a transaction is complete. +//------------------------------------------------------------------------------ +typedef void (*TransferCallback)(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining); + +//------------------------------------------------------------------------------ +/// Callback used by MBL transfer functions (USBD_Read & USBD_Write) to notify +/// that a transaction is complete. +/// \param pArg Pointer to callback arguments. +/// \param status USBD status. +/// \param nbFreed Number of buffers that is freed since last callback. +//------------------------------------------------------------------------------ +typedef void (*MblTransferCallback)(void *pArg, + unsigned char status, + unsigned int nbFreed); + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBD_IrqHandler(void); + +extern void USBD_Init(void); + +extern void USBD_ConfigureSpeed(unsigned char forceFS); + +extern void USBD_Connect(void); + +extern void USBD_Disconnect(void); + +extern char USBD_Write( + unsigned char bEndpoint, + const void *pData, + unsigned int size, + TransferCallback callback, + void *pArg); + +extern char USBD_MblWrite( + unsigned char bEndpoint, + void * pMbl, + unsigned short wListSize, + unsigned char bCircList, + unsigned short wStartNdx, + MblTransferCallback fCallback, + void * pArgument); + +extern char USBD_MblReuse( + unsigned char bEndpoint, + unsigned char * pNewBuffer, + unsigned short wNewSize); + +extern char USBD_Read( + unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArg); + +extern unsigned char USBD_Stall(unsigned char bEndpoint); + +extern void USBD_Halt(unsigned char bEndpoint); + +extern void USBD_Unhalt(unsigned char bEndpoint); + +extern void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor); + +extern unsigned char USBD_IsHalted(unsigned char bEndpoint); + +extern void USBD_RemoteWakeUp(void); + +extern void USBD_SetAddress(unsigned char address); + +extern void USBD_SetConfiguration(unsigned char cfgnum); + +extern unsigned char USBD_GetState(void); + +extern unsigned char USBD_IsHighSpeed(void); + +extern void USBD_Test(unsigned char bIndex); + +#endif //#ifndef USBD_H + diff --git a/usb/device/core/USBDCallbacks.h b/usb/device/core/USBDCallbacks.h new file mode 100644 index 0000000..d4d5c7e --- /dev/null +++ b/usb/device/core/USBDCallbacks.h @@ -0,0 +1,65 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definitions of callbacks used by the USBD API to notify the user + application of incoming events. These functions are declared as 'weak', + so they can be re-implemented elsewhere in the application in a + transparent way. +*/ + +#ifndef USBDCALLBACKS_H +#define USBDCALLBACKS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBDCallbacks_Initialized(void); + +extern void USBDCallbacks_Reset(void); + +extern void USBDCallbacks_Suspended(void); + +extern void USBDCallbacks_Resumed(void); + +extern void USBDCallbacks_RequestReceived(const USBGenericRequest *request); + +#endif //#ifndef USBDCALLBACKS_H + diff --git a/usb/device/core/USBDCallbacks_Initialized.c b/usb/device/core/USBDCallbacks_Initialized.c new file mode 100644 index 0000000..0ac4ffe --- /dev/null +++ b/usb/device/core/USBDCallbacks_Initialized.c @@ -0,0 +1,67 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDCallbacks.h" +#include "USBD.h" +#include +#include + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Invoked after the USB driver has been initialized. By default, configures +/// the UDP/UDPHS interrupt. +//------------------------------------------------------------------------------ +void USBDCallbacks_Initialized(void) +{ +#if defined(CHIP_USB_UDP) + // Configure and enable the UDP interrupt + IRQ_ConfigureIT(AT91C_ID_UDP, 0, USBD_IrqHandler); + IRQ_EnableIT(AT91C_ID_UDP); + +#elif defined(CHIP_USB_UDPHS) + // Configure and enable the UDPHS interrupt + IRQ_ConfigureIT(AT91C_ID_UDPHS, 0, USBD_IrqHandler); + IRQ_EnableIT(AT91C_ID_UDPHS); + +#elif defined(CHIP_USB_OTGHS) + IRQ_ConfigureIT(AT91C_ID_OTGHS, 1, (void*) 0); + IRQ_EnableIT(AT91C_ID_OTGHS); + +#else + #error Unsupported controller. +#endif +} + diff --git a/usb/device/core/USBDCallbacks_RequestReceived.c b/usb/device/core/USBDCallbacks_RequestReceived.c new file mode 100644 index 0000000..29468ff --- /dev/null +++ b/usb/device/core/USBDCallbacks_RequestReceived.c @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDCallbacks.h" + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USBDCallbacks_RequestReceived - Invoked when a new SETUP request is +/// received. Does nothing by default. +/// \param pRequest Pointer to the request to handle. +//------------------------------------------------------------------------------ +void USBDCallbacks_RequestReceived(const USBGenericRequest *pRequest) +{ + // Does nothing +} + diff --git a/usb/device/core/USBDCallbacks_Reset.c b/usb/device/core/USBDCallbacks_Reset.c new file mode 100644 index 0000000..64d9aaa --- /dev/null +++ b/usb/device/core/USBDCallbacks_Reset.c @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDCallbacks.h" + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Invoked when the USB driver is reset. Does nothing by default. +//------------------------------------------------------------------------------ +void USBDCallbacks_Reset(void) +{ + // Does nothing +} + diff --git a/usb/device/core/USBDCallbacks_Resumed.c b/usb/device/core/USBDCallbacks_Resumed.c new file mode 100644 index 0000000..3d58646 --- /dev/null +++ b/usb/device/core/USBDCallbacks_Resumed.c @@ -0,0 +1,54 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDCallbacks.h" +#include "USBD.h" +#include + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Invoked when the USB device leaves the Suspended state. By default, +/// configures the LEDs. +//------------------------------------------------------------------------------ +void USBDCallbacks_Resumed(void) +{ + // Initialize LEDs + LED_Configure(USBD_LEDPOWER); + LED_Set(USBD_LEDPOWER); + LED_Configure(USBD_LEDUSB); + LED_Clear(USBD_LEDUSB); +} + diff --git a/usb/device/core/USBDCallbacks_Suspended.c b/usb/device/core/USBDCallbacks_Suspended.c new file mode 100644 index 0000000..dbc10b9 --- /dev/null +++ b/usb/device/core/USBDCallbacks_Suspended.c @@ -0,0 +1,51 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDCallbacks.h" +#include "USBD.h" +#include + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Invoked when the USB device gets suspended. By default, turns off all LEDs. +//------------------------------------------------------------------------------ +void USBDCallbacks_Suspended(void) +{ + // Turn off LEDs + LED_Clear(USBD_LEDPOWER); + LED_Clear(USBD_LEDUSB); +} + diff --git a/usb/device/core/USBDDriver.c b/usb/device/core/USBDDriver.c new file mode 100644 index 0000000..6a7c000 --- /dev/null +++ b/usb/device/core/USBDDriver.c @@ -0,0 +1,753 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDDriver.h" +#include "USBDDriverCallbacks.h" +#include "USBD.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ +#if defined(CHIP_USB_OTGHS) +static unsigned char otg_features_supported = 0; +#endif + +//------------------------------------------------------------------------------ +/// Send a NULL packet +//------------------------------------------------------------------------------ +static void TerminateCtrlInWithNull(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining) +{ + USBD_Write(0, // Endpoint #0 + 0, // No data buffer + 0, // No data buffer + (TransferCallback) 0, + (void *) 0); +} + +//------------------------------------------------------------------------------ +/// Configures the device by setting it into the Configured state and +/// initializing all endpoints. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param cfgnum Configuration number to set. +//------------------------------------------------------------------------------ +static void SetConfiguration(USBDDriver *pDriver, unsigned char cfgnum) +{ + USBEndpointDescriptor *pEndpoints[CHIP_USB_NUMENDPOINTS+1]; + const USBConfigurationDescriptor *pConfiguration; + + // Use different descriptor depending on device speed + if (USBD_IsHighSpeed()) { + + pConfiguration = pDriver->pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = pDriver->pDescriptors->pFsConfiguration; + } + + // Set & save the desired configuration + USBD_SetConfiguration(cfgnum); + pDriver->cfgnum = cfgnum; + + // If the configuration is not 0, configure endpoints + if (cfgnum != 0) { + + // Parse configuration to get endpoint descriptors + USBConfigurationDescriptor_Parse(pConfiguration, 0, pEndpoints, 0); + + // Configure endpoints + int i = 0; + while (pEndpoints[i] != 0) { + + USBD_ConfigureEndpoint(pEndpoints[i]); + i++; + } + } + // Should be done before send the ZLP + USBDDriverCallbacks_ConfigurationChanged(cfgnum); + + // Acknowledge the request + USBD_Write(0, // Endpoint #0 + 0, // No data buffer + 0, // No data buffer + (TransferCallback) 0, + (void *) 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current configuration number to the host. +/// \param pDriver Pointer to a USBDDriver instance. +//------------------------------------------------------------------------------ +static void GetConfiguration(const USBDDriver *pDriver) +{ + USBD_Write(0, &(pDriver->cfgnum), 1, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current status of the device to the host. +/// \param pDriver Pointer to a USBDDriver instance. +//------------------------------------------------------------------------------ +static void GetDeviceStatus(const USBDDriver *pDriver) +{ + static unsigned short data; + const USBConfigurationDescriptor *pConfiguration; + + data = 0; + // Use different configuration depending on device speed + if (USBD_IsHighSpeed()) { + + pConfiguration = pDriver->pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = pDriver->pDescriptors->pFsConfiguration; + } + + // Check current configuration for power mode (if device is configured) + if (pDriver->cfgnum != 0) { + + if (USBConfigurationDescriptor_IsSelfPowered(pConfiguration)) { + + data |= 1; + } + } + + // Check if remote wake-up is enabled + if (pDriver->isRemoteWakeUpEnabled) { + + data |= 2; + } + + // Send the device status + USBD_Write(0, &data, 2, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current status of an endpoints to the USB host. +/// \param bEndpoint Endpoint number. +//------------------------------------------------------------------------------ +static void GetEndpointStatus(unsigned char bEndpoint) +{ + static unsigned short data; + + data = 0; + // Check if the endpoint exists + if (bEndpoint > CHIP_USB_NUMENDPOINTS) { + + USBD_Stall(0); + } + else { + + // Check if the endpoint if currently halted + if (USBD_IsHalted(bEndpoint)) { + + data = 1; + } + + // Send the endpoint status + USBD_Write(0, &data, 2, 0, 0); + } +} + +//------------------------------------------------------------------------------ +/// Sends the requested USB descriptor to the host if available, or STALLs the +/// request. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param type Type of the requested descriptor +/// \param index Index of the requested descriptor. +/// \param length Maximum number of bytes to return. +//------------------------------------------------------------------------------ +static void GetDescriptor( + const USBDDriver *pDriver, + unsigned char type, + unsigned char indexRDesc, + unsigned int length) +{ + const USBDeviceDescriptor *pDevice; + const USBConfigurationDescriptor *pConfiguration; + const USBDeviceQualifierDescriptor *pQualifier; + const USBConfigurationDescriptor *pOtherSpeed; + const USBGenericDescriptor **pStrings = + (const USBGenericDescriptor **) pDriver->pDescriptors->pStrings; + const USBGenericDescriptor *pString; + unsigned char numStrings = pDriver->pDescriptors->numStrings; + unsigned char terminateWithNull = 0; + + // Use different set of descriptors depending on device speed + if (USBD_IsHighSpeed()) { + + TRACE_DEBUG("HS "); + pDevice = pDriver->pDescriptors->pHsDevice; + pConfiguration = pDriver->pDescriptors->pHsConfiguration; + pQualifier = pDriver->pDescriptors->pHsQualifier; + pOtherSpeed = pDriver->pDescriptors->pHsOtherSpeed; + } + else { + + TRACE_DEBUG("FS "); + pDevice = pDriver->pDescriptors->pFsDevice; + pConfiguration = pDriver->pDescriptors->pFsConfiguration; + pQualifier = pDriver->pDescriptors->pFsQualifier; + pOtherSpeed = pDriver->pDescriptors->pFsOtherSpeed; + } + + // Check the descriptor type + switch (type) { + + case USBGenericDescriptor_DEVICE: + TRACE_INFO_WP("Dev "); + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength((USBGenericDescriptor *) pDevice)) { + + length = USBGenericDescriptor_GetLength((USBGenericDescriptor *) pDevice); + } + USBD_Write(0, pDevice, length, 0, 0); + break; + + case USBGenericDescriptor_CONFIGURATION: + TRACE_INFO_WP("Cfg "); + + // Adjust length and send descriptor + if (length > USBConfigurationDescriptor_GetTotalLength(pConfiguration)) { + + length = USBConfigurationDescriptor_GetTotalLength(pConfiguration); + terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); + } + USBD_Write(0, + pConfiguration, + length, + terminateWithNull ? TerminateCtrlInWithNull : 0, + 0); + break; + + case USBGenericDescriptor_DEVICEQUALIFIER: + TRACE_INFO_WP("Qua "); + + // Check if descriptor exists + if (!pQualifier) { + + USBD_Stall(0); + } + else { + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength((USBGenericDescriptor *) pQualifier)) { + + length = USBGenericDescriptor_GetLength((USBGenericDescriptor *) pQualifier); + } + USBD_Write(0, pQualifier, length, 0, 0); + } + break; + + case USBGenericDescriptor_OTHERSPEEDCONFIGURATION: + TRACE_INFO_WP("OSC "); + + // Check if descriptor exists + if (!pOtherSpeed) { + + USBD_Stall(0); + } + else { + + // Adjust length and send descriptor + if (length > USBConfigurationDescriptor_GetTotalLength(pOtherSpeed)) { + + length = USBConfigurationDescriptor_GetTotalLength(pOtherSpeed); + terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); + } + USBD_Write(0, + pOtherSpeed, + length, + terminateWithNull ? TerminateCtrlInWithNull : 0, + 0); + } + break; + + case USBGenericDescriptor_STRING: + TRACE_INFO_WP("Str%d ", indexRDesc); + + // Check if descriptor exists + if (indexRDesc >= numStrings) { + + USBD_Stall(0); + } + else { + + pString = pStrings[indexRDesc]; + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength(pString)) { + + length = USBGenericDescriptor_GetLength(pString); + terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); + } + USBD_Write(0, + pString, + length, + terminateWithNull ? TerminateCtrlInWithNull : 0, + 0); + } + break; + + default: + TRACE_WARNING( + "USBDDriver_GetDescriptor: Unknown descriptor type (%d)\n\r", + type); + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Sets the active setting of the given interface if the configuration supports +/// it; otherwise, the control pipe is STALLed. If the setting of an interface +/// changes. +/// \parma pDriver Pointer to a USBDDriver instance. +/// \parma infnum Interface number. +/// \parma setting New active setting for the interface. +//------------------------------------------------------------------------------ +static void SetInterface( + USBDDriver *pDriver, + unsigned char infnum, + unsigned char setting) +{ + // Make sure alternate settings are supported + if (!pDriver->pInterfaces) { + + USBD_Stall(0); + } + else { + + // Change the current setting of the interface and trigger the callback + // if necessary + if (pDriver->pInterfaces[infnum] != setting) { + + pDriver->pInterfaces[infnum] = setting; + USBDDriverCallbacks_InterfaceSettingChanged(infnum, setting); + } + + // Acknowledge the request + USBD_Write(0, 0, 0, 0, 0); + } +} + +//------------------------------------------------------------------------------ +/// Sends the currently active setting of the given interface to the USB +/// host. If alternate settings are not supported, this function STALLs the +/// control pipe. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param infnum Interface number. +//------------------------------------------------------------------------------ +static void GetInterface( + const USBDDriver *pDriver, + unsigned char infnum) +{ + // Make sure alternate settings are supported, or STALL the control pipe + if (!pDriver->pInterfaces) { + + USBD_Stall(0); + } + else { + + // Sends the current interface setting to the host + USBD_Write(0, &(pDriver->pInterfaces[infnum]), 1, 0, 0); + } +} + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +//------------------------------------------------------------------------------ +// Performs the selected test on the USB device (high-speed only). +// \param test Test selector value. +//------------------------------------------------------------------------------ +static void USBDDriver_Test(unsigned char test) +{ + TRACE_DEBUG("UDPHS_Test\n\r"); + + // the lower byte of wIndex must be zero + // the most significant byte of wIndex is used to specify the specific test mode + switch (test) { + case USBFeatureRequest_TESTPACKET: + //Test mode Test_Packet: + //Upon command, a port must repetitively transmit the following test packet until + //the exit action is taken. This enables the testing of rise and fall times, eye + //patterns, jitter, and any other dynamic waveform specifications. + //The test packet is made up by concatenating the following strings. + //(Note: For J/K NRZI data, and for NRZ data, the bit on the left is the first one + //transmitted. “S” indicates that a bit stuff occurs, which inserts an “extra” NRZI data bit. + //“* N” is used to indicate N occurrences of a string of bits or symbols.) + //A port in Test_Packet mode must send this packet repetitively. The inter-packet timing + //must be no less than the minimum allowable inter-packet gap as defined in Section 7.1.18 and + //no greater than 125 us. + // Send ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + // Tst PACKET + USBD_Test(USBFeatureRequest_TESTPACKET); + while (1); + //break; not reached + + case USBFeatureRequest_TESTJ: + //Test mode Test_J: + //Upon command, a port’s transceiver must enter the high-speed J state and remain in that + //state until the exit action is taken. This enables the testing of the high output drive + //level on the D+ line. + // Send ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + // Tst J + USBD_Test(USBFeatureRequest_TESTJ); + while (1); + //break; not reached + + case USBFeatureRequest_TESTK: + //Test mode Test_K: + //Upon command, a port’s transceiver must enter the high-speed K state and remain in + //that state until the exit action is taken. This enables the testing of the high output drive + //level on the D- line. + // Send a ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + USBD_Test(USBFeatureRequest_TESTK); + while (1); + //break; not reached + + case USBFeatureRequest_TESTSE0NAK: + //Test mode Test_SE0_NAK: + //Upon command, a port’s transceiver must enter the high-speed receive mode + //and remain in that mode until the exit action is taken. This enables the testing + //of output impedance, low level output voltage, and loading characteristics. + //In addition, while in this mode, upstream facing ports (and only upstream facing ports) + //must respond to any IN token packet with a NAK handshake (only if the packet CRC is + //determined to be correct) within the normal allowed device response time. This enables testing of + //the device squelch level circuitry and, additionally, provides a general purpose stimulus/response + //test for basic functional testing. + // Send a ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + // Test SE0_NAK + USBD_Test(USBFeatureRequest_TESTSE0NAK); + while (1); + //break; not reached + + default: + USBD_Stall(0); + break; + + } + // The exit action is to power cycle the device. + // The device must be disconnected from the host +} +#endif + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a USBDDriver instance with a list of descriptors. If +/// interfaces can have multiple alternate settings, an array to store the +/// current setting for each interface must be provided. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param pDescriptors Pointer to a USBDDriverDescriptors instance. +/// \param pInterfaces Pointer to an array for storing the current alternate +/// setting of each interface (optional). +//------------------------------------------------------------------------------ +void USBDDriver_Initialize( + USBDDriver *pDriver, + const USBDDriverDescriptors *pDescriptors, + unsigned char *pInterfaces) +{ + + pDriver->cfgnum = 0; +#if (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_SELFPOWERED_RWAKEUP) \ + || (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_BUSPOWERED_RWAKEUP) + pDriver->isRemoteWakeUpEnabled = 1; +#else + pDriver->isRemoteWakeUpEnabled = 0; +#endif + + pDriver->pDescriptors = pDescriptors; + pDriver->pInterfaces = pInterfaces; + + // Initialize interfaces array if not null + if (pInterfaces != 0) { + + memset(pInterfaces, sizeof(pInterfaces), 0); + } +} + +//------------------------------------------------------------------------------ +/// Handles the given request if it is standard, otherwise STALLs it. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param pRequest Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +void USBDDriver_RequestHandler( + USBDDriver *pDriver, + const USBGenericRequest *pRequest) +{ + unsigned char cfgnum; + unsigned char infnum; + unsigned char eptnum; + unsigned char setting; + unsigned char type; + unsigned char indexDesc; + unsigned int length; + unsigned int address; + + TRACE_INFO_WP("Std "); + + // Check request code + switch (USBGenericRequest_GetRequest(pRequest)) { + + case USBGenericRequest_GETDESCRIPTOR: + TRACE_INFO_WP("gDesc "); + + // Send the requested descriptor + type = USBGetDescriptorRequest_GetDescriptorType(pRequest); + indexDesc = USBGetDescriptorRequest_GetDescriptorIndex(pRequest); + length = USBGenericRequest_GetLength(pRequest); + GetDescriptor(pDriver, type, indexDesc, length); + break; + + case USBGenericRequest_SETADDRESS: + TRACE_INFO_WP("sAddr "); + + // Sends a zero-length packet and then set the device address + address = USBSetAddressRequest_GetAddress(pRequest); + USBD_Write(0, 0, 0, (TransferCallback) USBD_SetAddress, (void *) address); + break; + + case USBGenericRequest_SETCONFIGURATION: + TRACE_INFO_WP("sCfg "); + + // Set the requested configuration + cfgnum = USBSetConfigurationRequest_GetConfiguration(pRequest); + SetConfiguration(pDriver, cfgnum); + break; + + case USBGenericRequest_GETCONFIGURATION: + TRACE_INFO_WP("gCfg "); + + // Send the current configuration number + GetConfiguration(pDriver); + break; + + case USBGenericRequest_GETSTATUS: + TRACE_INFO_WP("gSta "); + + // Check who is the recipient + switch (USBGenericRequest_GetRecipient(pRequest)) { + + case USBGenericRequest_DEVICE: + TRACE_INFO_WP("Dev "); + + // Send the device status + GetDeviceStatus(pDriver); + break; + + case USBGenericRequest_ENDPOINT: + TRACE_INFO_WP("Ept "); + + // Send the endpoint status + eptnum = USBGenericRequest_GetEndpointNumber(pRequest); + GetEndpointStatus(eptnum); + break; + + default: + TRACE_WARNING( + "USBDDriver_RequestHandler: Unknown recipient (%d)\n\r", + USBGenericRequest_GetRecipient(pRequest)); + USBD_Stall(0); + } + break; + + case USBGenericRequest_CLEARFEATURE: + TRACE_INFO_WP("cFeat "); + + // Check which is the requested feature + switch (USBFeatureRequest_GetFeatureSelector(pRequest)) { + + case USBFeatureRequest_ENDPOINTHALT: + TRACE_INFO_WP("Hlt "); + + // Unhalt endpoint and send a zero-length packet + USBD_Unhalt(USBGenericRequest_GetEndpointNumber(pRequest)); + USBD_Write(0, 0, 0, 0, 0); + break; + + case USBFeatureRequest_DEVICEREMOTEWAKEUP: + TRACE_INFO_WP("RmWU "); + + // Disable remote wake-up and send a zero-length packet + pDriver->isRemoteWakeUpEnabled = 0; + USBD_Write(0, 0, 0, 0, 0); + break; + + default: + TRACE_WARNING( + "USBDDriver_RequestHandler: Unknown feature selector (%d)\n\r", + USBFeatureRequest_GetFeatureSelector(pRequest)); + USBD_Stall(0); + } + break; + + case USBGenericRequest_SETFEATURE: + TRACE_INFO_WP("sFeat "); + + // Check which is the selected feature + switch (USBFeatureRequest_GetFeatureSelector(pRequest)) { + + case USBFeatureRequest_DEVICEREMOTEWAKEUP: + TRACE_INFO_WP("RmWU "); + + // Enable remote wake-up and send a ZLP + pDriver->isRemoteWakeUpEnabled = 1; + USBD_Write(0, 0, 0, 0, 0); + break; + + case USBFeatureRequest_ENDPOINTHALT: + TRACE_INFO_WP("Halt "); + // Halt endpoint + USBD_Halt(USBGenericRequest_GetEndpointNumber(pRequest)); + USBD_Write(0, 0, 0, 0, 0); + break; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + + case USBFeatureRequest_TESTMODE: + // 7.1.20 Test Mode Support, 9.4.9 SetFeature + if ((USBGenericRequest_GetRecipient(pRequest) == USBGenericRequest_DEVICE) + && ((USBGenericRequest_GetIndex(pRequest) & 0x000F) == 0)) { + + // Handle test request + USBDDriver_Test(USBFeatureRequest_GetTestSelector(pRequest)); + } + else { + + USBD_Stall(0); + } + break; +#endif +#if defined(CHIP_USB_OTGHS) + case USBFeatureRequest_OTG_B_HNP_ENABLE: + TRACE_INFO_WP("OTG_B_HNP_ENABLE "); + otg_features_supported |= 1<isRemoteWakeUpEnabled; +} + +#if defined(CHIP_USB_OTGHS) +//------------------------------------------------------------------------------ +/// Return OTG features supported +/// \return the OTG features +//------------------------------------------------------------------------------ +unsigned char USBDDriver_returnOTGFeatures(void) +{ + return otg_features_supported; +} + +//------------------------------------------------------------------------------ +/// Clear OTG features supported +/// \return none +//------------------------------------------------------------------------------ +void USBDDriver_clearOTGFeatures(void) +{ + otg_features_supported = 0; +} +#endif + diff --git a/usb/device/core/USBDDriver.h b/usb/device/core/USBDDriver.h new file mode 100644 index 0000000..40ca6d7 --- /dev/null +++ b/usb/device/core/USBDDriver.h @@ -0,0 +1,101 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + USB Device Driver class definition. + + !!!Usage + + -# Instanciate a USBDDriver object and initialize it using + USBDDriver_Initialize. + -# When a USB SETUP request is received, forward it to the standard + driver using USBDDriver_RequestHandler. + -# Check the Remote Wakeup setting via USBDDriver_IsRemoteWakeUpEnabled. +*/ + +#ifndef USBDDRIVER_H +#define USBDDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDDriverDescriptors.h" +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USB device driver structure, holding a list of descriptors identifying +/// the device as well as the driver current state. +//------------------------------------------------------------------------------ +typedef struct { + + /// List of descriptors used by the device. + const USBDDriverDescriptors *pDescriptors; + /// Current setting for each interface. + unsigned char *pInterfaces; + /// Current configuration number (0 -> device is not configured). + unsigned char cfgnum; + /// Indicates if remote wake up has been enabled by the host. + unsigned char isRemoteWakeUpEnabled; +#if defined(CHIP_USB_OTGHS) + /// Features supported by OTG + unsigned char otg_features_supported; +#endif +} USBDDriver; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBDDriver_Initialize( + USBDDriver *pDriver, + const USBDDriverDescriptors *pDescriptors, + unsigned char *pInterfaces); + +extern void USBDDriver_RequestHandler( + USBDDriver *pDriver, + const USBGenericRequest *pRequest); + +extern unsigned char USBDDriver_IsRemoteWakeUpEnabled(const USBDDriver *pDriver); + +#if defined(CHIP_USB_OTGHS) +extern unsigned char USBDDriver_returnOTGFeatures(void); +extern void USBDDriver_clearOTGFeatures(void); +#endif + +#endif //#ifndef USBDDRIVER_H + diff --git a/usb/device/core/USBDDriverCallbacks.h b/usb/device/core/USBDDriverCallbacks.h new file mode 100644 index 0000000..053e8ac --- /dev/null +++ b/usb/device/core/USBDDriverCallbacks.h @@ -0,0 +1,61 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of several callbacks which are triggered by the USB software + driver after receiving specific requests. + + !!!Usage + + -# Re-implement the USBDDriverCallbacks_ConfigurationChanged + callback to know when the hosts changes the active configuration of + the device. + -# Re-implement the USBDDriverCallbacks_InterfaceSettingChanged + callback to get notified whenever the active setting of an interface + is changed by the host. +*/ + +#ifndef USBDDRIVERCALLBACKS_H +#define USBDDRIVERCALLBACKS_H + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum); + +extern void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting); + +#endif //#ifndef USBDDRIVERCALLBACKS_H + diff --git a/usb/device/core/USBDDriverCb_CfgChanged.c b/usb/device/core/USBDDriverCb_CfgChanged.c new file mode 100644 index 0000000..08e2b2f --- /dev/null +++ b/usb/device/core/USBDDriverCb_CfgChanged.c @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDDriverCallbacks.h" +#include + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Indicates that the current configuration of the device has changed. +/// \param cfgnum New device configuration index. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + TRACE_INFO_WP("ConfigurationChanged "); +} + diff --git a/usb/device/core/USBDDriverCb_IfSettingChanged.c b/usb/device/core/USBDDriverCb_IfSettingChanged.c new file mode 100644 index 0000000..c9049e6 --- /dev/null +++ b/usb/device/core/USBDDriverCb_IfSettingChanged.c @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDDriverCallbacks.h" +#include + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Notifies of a change in the currently active setting of an interface. +/// \param interface Number of the interface whose setting has changed. +/// \param setting New interface setting. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_InterfaceSettingChanged( + unsigned char interface, + unsigned char setting) +{ + TRACE_INFO_WP("InterfaceSettingChanged "); +} + diff --git a/usb/device/core/USBDDriverDescriptors.h b/usb/device/core/USBDDriverDescriptors.h new file mode 100644 index 0000000..f1064e4 --- /dev/null +++ b/usb/device/core/USBDDriverDescriptors.h @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of a class for declaring USB descriptors required by the + device driver. +*/ + +#ifndef USBDDRIVERDESCRIPTORS_H +#define USBDDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// List of all descriptors used by a USB device driver. Each descriptor can +/// be provided in two versions: full-speed and high-speed. Devices which are +/// not high-speed capable do not need to provided high-speed descriptors and +/// the full-speed qualifier & other speed descriptors. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to the full-speed device descriptor. + const USBDeviceDescriptor *pFsDevice; + /// Pointer to the full-speed configuration descriptor. + const USBConfigurationDescriptor *pFsConfiguration; + /// Pointer to the full-speed qualifier descriptor. + const USBDeviceQualifierDescriptor *pFsQualifier; + /// Pointer to the full-speed other speed configuration descriptor. + const USBConfigurationDescriptor *pFsOtherSpeed; + /// Pointer to the high-speed device descriptor. + const USBDeviceDescriptor *pHsDevice; + /// Pointer to the high-speed configuration descriptor. + const USBConfigurationDescriptor *pHsConfiguration; + /// Pointer to the high-speed qualifier descriptor. + const USBDeviceQualifierDescriptor *pHsQualifier; + /// Pointer to the high-speed other speed configuration descriptor. + const USBConfigurationDescriptor *pHsOtherSpeed; + /// Pointer to the list of string descriptors. + const unsigned char **pStrings; + /// Number of string descriptors in list. + unsigned char numStrings; + +} USBDDriverDescriptors; + +#endif //#ifndef USBDDRIVERDESCRIPTORS_H + diff --git a/usb/device/core/USBD_OTGHS.c b/usb/device/core/USBD_OTGHS.c new file mode 100644 index 0000000..1795aaf --- /dev/null +++ b/usb/device/core/USBD_OTGHS.c @@ -0,0 +1,1697 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBD.h" +#include "USBDCallbacks.h" +#include "USBDDriver.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Maximum number of endpoints interrupts. +#define NUM_IT_MAX \ + (AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_EPT_NBR_MAX) +/// Maximum number of endpoint DMA interrupts +#define NUM_IT_MAX_DMA \ + ((AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_DMA_CHANNEL_NBR)>>4) +/// Bits that should be shifted to access DMA control bits. +#define SHIFT_DMA 24 +/// Bits that should be shifted to access interrupt bits. +#define SHIFT_INTERUPT 12 + +/// Max size of the FMA FIFO +#define DMA_MAX_FIFO_SIZE 32768 + +#define EPT_VIRTUAL_SIZE 8192 + +//------------------------------------------------------------------------------ +/// \page "Endpoint states" +/// This page lists the endpoint states. +/// !States +// - UDP_ENDPOINT_DISABLED +// - UDP_ENDPOINT_HALTED +// - UDP_ENDPOINT_IDLE +// - UDP_ENDPOINT_SENDING +// - UDP_ENDPOINT_RECEIVING + +/// Endpoint states: Endpoint is disabled +#define UDP_ENDPOINT_DISABLED 0 +/// Endpoint states: Endpoint is halted (i.e. STALLs every request) +#define UDP_ENDPOINT_HALTED 1 +/// Endpoint states: Endpoint is idle (i.e. ready for transmission) +#define UDP_ENDPOINT_IDLE 2 +/// Endpoint states: Endpoint is sending data +#define UDP_ENDPOINT_SENDING 3 +/// Endpoint states: Endpoint is receiving data +#define UDP_ENDPOINT_RECEIVING 4 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +/// Describes an ongoing transfer on a UDP endpoint. +typedef struct +{ + /// Pointer to a data buffer used for emission/reception. + char *pData; + /// Number of bytes which have been written into the UDP internal FIFO + /// buffers. + volatile int buffered; + /// Number of bytes which have been sent/received. + volatile int transferred; + /// Number of bytes which have not been buffered/transferred yet. + volatile int remaining; + /// Optional callback to invoke when the transfer completes. + volatile TransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} Transfer; + +//------------------------------------------------------------------------------ +/// Describes the state of an endpoint of the UDP controller. +//------------------------------------------------------------------------------ +typedef struct +{ + /// Current endpoint state. + volatile unsigned char state; + /// Current reception bank (0 or 1). + unsigned char bank; + /// Maximum packet size for the endpoint. + unsigned short size; + /// Describes an ongoing transfer (if current state is either + /// or ) + Transfer transfer; + /// Special case for send a ZLP + unsigned char sendZLP; +} Endpoint; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Holds the internal state for each endpoint of the UDP. +static Endpoint endpoints[CHIP_USB_NUMENDPOINTS]; +/// Device current state. +static unsigned char deviceState; +/// Indicates the previous device state +static unsigned char previousDeviceState; + +/// 7.1.20 Test Mode Support +/// Test codes for the USB HS test mode. +static const char test_packet_buffer[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // JKJKJKJK * 9 + 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, // JJKKJJKK * 8 + 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, // JJJJKKKK * 8 + 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // JJJJJJJKKKKKKK * 8 + 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD, // JJJJJJJK * 8 + 0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E // {JKKKKKKK * 10}, JK +}; + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Enable UDPHS clock +//------------------------------------------------------------------------------ +static inline void OTGHS_EnableUsbClock( void ) +{ + +} + +//------------------------------------------------------------------------------ +/// Disable UDPHS clock +//------------------------------------------------------------------------------ +static inline void OTGHS_DisableUsbClock( void ) +{ + +} + +//------------------------------------------------------------------------------ +/// Enables the transceiver of the USB controller +//------------------------------------------------------------------------------ +static void OTGHS_EnableTransceiver(void) +{ + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_OTGPADE; +} + +//------------------------------------------------------------------------------ +/// Disables the transceiver of the USB controller associated with the specified +/// USB driver +//------------------------------------------------------------------------------ +static void OTGHS_DisableTransceiver(void) +{ + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_OTGPADE; +} + +//------------------------------------------------------------------------------ +/// Handles a completed transfer on the given endpoint, invoking the +/// configured callback if any. +/// \param bEndpoint Number of the endpoint for which the transfer has completed. +/// \param bStatus Status code returned by the transfer operation +//------------------------------------------------------------------------------ +static void OTGHS_EndOfTransfer( unsigned char bEndpoint, char bStatus ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Check that endpoint was sending or receiving data + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) { + + TRACE_DEBUG_WP("Eo"); + if(pEndpoint->state == UDP_ENDPOINT_SENDING) { + pEndpoint->sendZLP = 0; + } + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Invoke callback is present + if (pTransfer->fCallback != 0) { + + ((TransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + bStatus, + pTransfer->transferred, + pTransfer->remaining + pTransfer->buffered); + } + else { + TRACE_DEBUG_WP("No callBack\n\r"); + } + } +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static void OTGHS_WritePayload( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + char *pFifo; + signed int size; + unsigned int dCtr; + + pFifo = (char*)((unsigned int *)AT91C_BASE_OTGHS_EPTFIFO + (EPT_VIRTUAL_SIZE * bEndpoint)); + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pTransfer->remaining) { + + size = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->buffered += size; + pTransfer->remaining -= size; + + // Write packet in the FIFO buffer + dCtr = 0; + while (size > 0) { + + pFifo[dCtr] = *(pTransfer->pData); + pTransfer->pData++; + size--; + dCtr++; + } +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from an endpoint FIFO to the current transfer buffer +/// \param bEndpoint Endpoint number. +/// \param wPacketSize Size of received data packet +//------------------------------------------------------------------------------ +static void OTGHS_ReadPayload( unsigned char bEndpoint, int wPacketSize ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + char *pFifo; + unsigned char dBytes=0; + + pFifo = (char*)((unsigned int *)AT91C_BASE_OTGHS_EPTFIFO + (EPT_VIRTUAL_SIZE * bEndpoint)); + + // Check that the requested size is not bigger than the remaining transfer + if (wPacketSize > pTransfer->remaining) { + + pTransfer->buffered += wPacketSize - pTransfer->remaining; + wPacketSize = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->remaining -= wPacketSize; + pTransfer->transferred += wPacketSize; + + // Retrieve packet + while (wPacketSize > 0) { + + *(pTransfer->pData) = pFifo[dBytes]; + pTransfer->pData++; + wPacketSize--; + dBytes++; + } +} + +//------------------------------------------------------------------------------ +/// Received SETUP packet from endpoint 0 FIFO +/// \param pRequest Generic USB SETUP request sent over Control endpoints +//------------------------------------------------------------------------------ +static void OTGHS_ReadRequest( USBGenericRequest *pRequest ) +{ + unsigned int *pData = (unsigned int *)pRequest; + unsigned int fifo; + + fifo = (AT91C_BASE_OTGHS_EPTFIFO->OTGHS_READEPT0[0]); + *pData = fifo; + fifo = (AT91C_BASE_OTGHS_EPTFIFO->OTGHS_READEPT0[0]); + pData++; + *pData = fifo; + //TRACE_ERROR("SETUP: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n\r", pData[0],pData[1],pData[2],pData[3],pData[4],pData[5],pData[6],pData[7]); +} + +//------------------------------------------------------------------------------ +/// Reset all endpoint transfer descriptors +//------------------------------------------------------------------------------ +static void OTGHS_ResetEndpoints( void ) +{ + Endpoint *pEndpoint; + Transfer *pTransfer; + + unsigned char bEndpoint; + + // Reset the transfer descriptor of every endpoint + for( bEndpoint = 0; bEndpoint < CHIP_USB_NUMENDPOINTS; bEndpoint++ ) { + + pEndpoint = &(endpoints[bEndpoint]); + pTransfer = &(pEndpoint->transfer); + + // Reset endpoint transfer descriptor + pTransfer->pData = 0; + pTransfer->transferred = -1; + pTransfer->buffered = -1; + pTransfer->remaining = -1; + pTransfer->fCallback = 0; + pTransfer->pArgument = 0; + + // Reset endpoint state + pEndpoint->bank = 0; + pEndpoint->state = UDP_ENDPOINT_DISABLED; + // Reset ZLP + pEndpoint->sendZLP = 0; + } +} + + +//------------------------------------------------------------------------------ +/// Disable all endpoints (except control endpoint 0), aborting current +/// transfers if necessary +//------------------------------------------------------------------------------ +static void OTGHS_DisableEndpoints( void ) +{ + unsigned char bEndpoint; + + // Disable each endpoint, terminating any pending transfer + + + // Control endpoint 0 is not disabled + for( bEndpoint = 1; bEndpoint < CHIP_USB_NUMENDPOINTS; bEndpoint++ ) { + + OTGHS_EndOfTransfer( bEndpoint, USBD_STATUS_ABORTED ); + endpoints[bEndpoint].state = UDP_ENDPOINT_DISABLED; + } +} + + +//------------------------------------------------------------------------------ +/// Endpoint interrupt handler. +/// Handle IN/OUT transfers, received SETUP packets and STALLing +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void OTGHS_EndpointHandler( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + unsigned int status = AT91C_BASE_OTGHS->OTGHS_DEVEPTISR[bEndpoint]; + unsigned short wPacketSize; + USBGenericRequest request; + + TRACE_DEBUG_WP("E%d ", bEndpoint); + TRACE_DEBUG_WP("st:0x%X ", status); + + // Handle interrupts + // IN packet sent + if((AT91C_BASE_OTGHS->OTGHS_DEVEPTIMR[bEndpoint] & AT91C_OTGHS_TXINI) + && (status & AT91C_OTGHS_TXINI )) { + + TRACE_DEBUG_WP("Wr "); + + // Check that endpoint was in Sending state + if( pEndpoint->state == UDP_ENDPOINT_SENDING ) { + + if (pTransfer->buffered > 0) { + pTransfer->transferred += pTransfer->buffered; + pTransfer->buffered = 0; + } + + if( ((pTransfer->buffered)==0) + &&((pTransfer->transferred)==0) + &&((pTransfer->remaining)==0) + &&(pEndpoint->sendZLP == 0)) { + pEndpoint->sendZLP = 1; + } + + // End of transfer ? + if( (pTransfer->remaining > 0) + ||(pEndpoint->sendZLP == 1)) { + + pEndpoint->sendZLP = 2; + // Transfer remaining data + TRACE_DEBUG_WP(" %d ", pEndpoint->size); + // Send next packet + OTGHS_WritePayload(bEndpoint); + + // Send Token IN + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_TXINI; + // For a non-control endpoint, the FIFOCON bit must be cleared + // to start the transfer + if ((AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + // Send IN + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_FIFOCON; + } + } + else { + + TRACE_DEBUG_WP("\n\r0pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("0pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("0pTransfer->remaining %d \n\r", pTransfer->remaining); + + TRACE_DEBUG_WP(" %d ", pTransfer->transferred); + + // Disable interrupt if this is not a control endpoint + if ((AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + + AT91C_BASE_OTGHS->OTGHS_DEVIDR = 1<OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_TXINI; + OTGHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + pEndpoint->sendZLP = 0; + } + } + else { + TRACE_DEBUG("Error Wr %d", pEndpoint->sendZLP); + } + } + + // OUT packet received + if( AT91C_OTGHS_RXOUT == (status & AT91C_OTGHS_RXOUT) ) { + + // Check that the endpoint is in Receiving state + if (pEndpoint->state != UDP_ENDPOINT_RECEIVING) { + + // Check if an ACK has been received on a Control endpoint + if( ((AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]) + == AT91C_OTGHS_EPT_TYPE_CTL_EPT) + && (0 == (status & AT91C_OTGHS_BYCT)) ) { + + // Control endpoint, 0 bytes received + // Acknowledge the data and finish the current transfer + TRACE_DEBUG_WP("Ack "); + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_RXOUT; + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_RXOUT; + //OTGHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Check if the data has been STALLed + else if( AT91C_OTGHS_STALL == (status & AT91C_OTGHS_STALL)) { + + // Discard STALLed data + TRACE_DEBUG_WP("Discard "); + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_RXOUT; + } + else { + // NAK the data + TRACE_DEBUG_WP("Nak "); + AT91C_BASE_OTGHS->OTGHS_DEVIDR = 1<> 20) & 0x7FF); + + //TRACE_ERROR_WP("out:%d ", wPacketSize); + OTGHS_ReadPayload(bEndpoint, wPacketSize); + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_RXOUT; + if((AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_FIFOCON; + } + + // Check if the transfer is finished + if ((pTransfer->remaining == 0) || (wPacketSize < pEndpoint->size)) { + + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_RXOUT; + + // Disable interrupt if this is not a control endpoint + if ((AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + + AT91C_BASE_OTGHS->OTGHS_DEVIDR = 1<OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_STALL; + + // If the endpoint is not halted, clear the STALL condition + if (pEndpoint->state != UDP_ENDPOINT_HALTED) { + + TRACE_WARNING("_ " ); + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_STALLRQ; + } + } + + // SETUP packet received + if( AT91C_OTGHS_RXSTP == (status & AT91C_OTGHS_RXSTP) ) { + + TRACE_DEBUG_WP("Stp "); + + // If a transfer was pending, complete it + // Handles the case where during the status phase of a control write + // transfer, the host receives the device ZLP and ack it, but the ack + // is not received by the device + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + OTGHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + + // Copy the setup packet + OTGHS_ReadRequest(&request); + + // Acknowledge setup packet + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_RXSTP; + + // Forward the request to the upper layer + USBDCallbacks_RequestReceived(&request); + } +} + +//------------------------------------------------------------------------------ +// Interrupt service routine +//------------------------------------------------------------------------------ +#ifdef DMA +//---------------------------------------------------------------------------- +/// Endpoint DMA interrupt handler. +/// This function (ISR) handles dma interrupts +/// \param bEndpoint Index of endpoint +//---------------------------------------------------------------------------- +static void OTGHS_DmaHandler( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + int justTransferred; + unsigned int status; + unsigned char result = USBD_STATUS_SUCCESS; + + status = AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMASTATUS; + TRACE_DEBUG_WP("Dma Ept%d ", bEndpoint); + + // Disable DMA interrupt to avoid receiving 2 interrupts (B_EN and TR_EN) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL &= + ~(unsigned int)(AT91C_OTGHS_END_TR_EN | AT91C_OTGHS_END_B_EN); + + AT91C_BASE_OTGHS->OTGHS_DEVIDR = (1<buffered + - ((status & (unsigned int)AT91C_OTGHS_BUFF_COUNT) >> 16); + pTransfer->transferred += justTransferred; + + pTransfer->buffered = ((status & (unsigned int)AT91C_OTGHS_BUFF_COUNT) >> 16); + + pTransfer->remaining -= justTransferred; + + TRACE_DEBUG_WP("\n\r1pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("1pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("1pTransfer->remaining %d \n\r", pTransfer->remaining); + + if( (pTransfer->remaining + pTransfer->buffered) > 0 ) { + + // Prepare an other transfer + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + pTransfer->buffered = pTransfer->remaining; + } + + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = + (unsigned int)((pTransfer->pData) + (pTransfer->transferred)); + + // Clear unwanted interrupts + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_OTGHS->OTGHS_DEVIER = (1 << SHIFT_DMA << bEndpoint); + // DMA config for receive the good size of buffer, or an error buffer + + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = 0; // raz + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_OTGHS_BUFF_COUNT) + | AT91C_OTGHS_END_TR_EN + | AT91C_OTGHS_END_TR_IT + | AT91C_OTGHS_END_B_EN + | AT91C_OTGHS_END_BUFFIT + | AT91C_OTGHS_CHANN_ENB ); + } + } + else if( AT91C_OTGHS_END_TR_ST == (status & AT91C_OTGHS_END_TR_ST) ) { + + TRACE_DEBUG_WP("EndTransf "); + + pTransfer->transferred = pTransfer->buffered + - ((status & (unsigned int)AT91C_OTGHS_BUFF_COUNT) >> 16); + pTransfer->remaining = 0; + TRACE_DEBUG_WP("\n\r0pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("0pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("0pTransfer->remaining %d \n\r", pTransfer->remaining); + } + else { + + TRACE_ERROR("OTGHS_DmaHandler: Error (0x%08X)\n\r", status); + result = USBD_STATUS_ABORTED; + } + + // Invoke callback + if( pTransfer->remaining == 0 ) { + + TRACE_DEBUG_WP("EOT "); + OTGHS_EndOfTransfer(bEndpoint, result); + } +} +#endif + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USB device interrupt handler +/// Manages device resume, suspend, end of bus reset. +/// Forwards endpoint interrupts to the appropriate handler. +//------------------------------------------------------------------------------ +void USBD_IrqHandler(void) +{ + unsigned int status; + unsigned char numIT; + + if (deviceState >= USBD_STATE_POWERED) { + + LED_Set(USBD_LEDUSB); + } + +#ifndef OTG_PROJECT + // Get interrupts status + status = AT91C_BASE_OTGHS->OTGHS_SR & AT91C_BASE_OTGHS->OTGHS_CTRL & 0xFF; + while (status != 0) { + //TRACE_ERROR_WP("~"); + if((status&AT91C_OTGHS_VBUSTI)==AT91C_OTGHS_VBUSTI) { + TRACE_DEBUG_WP("__VBus\n\r"); + + USBD_Connect(); + + // Acknowledge the interrupt + AT91C_BASE_OTGHS->OTGHS_SCR = AT91C_OTGHS_VBUSTI; + } + + // Don't treat others interrupt for this time + AT91C_BASE_OTGHS->OTGHS_SCR = AT91C_OTGHS_IDT | AT91C_OTGHS_SRP + | AT91C_OTGHS_VBERR | AT91C_OTGHS_BCERR + | AT91C_OTGHS_ROLEEX | AT91C_OTGHS_HNPERR + | AT91C_OTGHS_STO; + + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_IDT; + + status = AT91C_BASE_OTGHS->OTGHS_SR & AT91C_BASE_OTGHS->OTGHS_CTRL & 0xFF; + } +#endif + + // Get OTG Device interrupts status + status = AT91C_BASE_OTGHS->OTGHS_DEVISR & AT91C_BASE_OTGHS->OTGHS_DEVIMR; + //TRACE_ERROR_WP("S=0x%X\n\r", AT91C_BASE_OTGHS->OTGHS_DEVISR ); + //TRACE_ERROR_WP("M=0x%X\n\r", AT91C_BASE_OTGHS->OTGHS_DEVIMR ); + while (status != 0) { + //TRACE_ERROR_WP("="); + // Start Of Frame (SOF) + if((status&AT91C_OTGHS_SOF)==AT91C_OTGHS_SOF) { + TRACE_DEBUG_WP("SOF "); + + // Invoke the SOF callback + //USB_StartOfFrameCallback(); + + // Acknowledge interrupt + AT91C_BASE_OTGHS->OTGHS_DEVICR = AT91C_OTGHS_SOF; + status &= ~(unsigned int)AT91C_OTGHS_SOF; + } + + // Suspend + // This interrupt is always treated last (hence the '==') + else if (status == AT91C_OTGHS_SUSP) { + + TRACE_DEBUG_WP("S"); + + // The device enters the Suspended state + // MCK + UDPCK must be off + // Pull-Up must be connected + // Transceiver must be disabled + + LED_Clear(USBD_LEDUSB); + + // Enable wakeup + AT91C_BASE_OTGHS->OTGHS_DEVIER = AT91C_OTGHS_EORST | AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM; + + // Acknowledge interrupt + AT91C_BASE_OTGHS->OTGHS_DEVICR = AT91C_OTGHS_SUSP; + previousDeviceState = deviceState; + deviceState = USBD_STATE_SUSPENDED; + OTGHS_DisableTransceiver(); + OTGHS_DisableUsbClock(); + // Invoke the Suspend callback + USBDCallbacks_Suspended(); + } + + // Resume + else if( ((status & AT91C_OTGHS_WAKEUP) != 0) // line activity + || ((status & AT91C_OTGHS_EORSM) != 0)) { // pc wakeup + + // Invoke the Resume callback + USBDCallbacks_Resumed(); + + TRACE_DEBUG_WP("R"); + + OTGHS_EnableUsbClock(); + OTGHS_EnableTransceiver(); + + // The device enters Configured state + // MCK + UDPCK must be on + // Pull-Up must be connected + // Transceiver must be enabled + + deviceState = previousDeviceState; + + AT91C_BASE_OTGHS->OTGHS_DEVICR = + (AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM | AT91C_OTGHS_SUSP); + AT91C_BASE_OTGHS->OTGHS_DEVIER = (AT91C_OTGHS_EORST | AT91C_OTGHS_SUSP); + AT91C_BASE_OTGHS->OTGHS_DEVICR = (AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM); + AT91C_BASE_OTGHS->OTGHS_DEVIDR = AT91C_OTGHS_WAKEUP; + + } + + // End of bus reset + else if ((status & AT91C_OTGHS_EORST) == AT91C_OTGHS_EORST) { + + TRACE_DEBUG_WP("EoB "); + + // The device enters the Default state + deviceState = USBD_STATE_DEFAULT; + // MCK + UDPCK are already enabled + // Pull-Up is already connected + // Transceiver must be enabled + // Endpoint 0 must be enabled + + OTGHS_EnableTransceiver(); + USBDDriver_clearOTGFeatures(); + + // The device leaves the Address & Configured states + OTGHS_ResetEndpoints(); + OTGHS_DisableEndpoints(); + USBD_ConfigureEndpoint(0); + + // Flush and enable the Suspend interrupt + AT91C_BASE_OTGHS->OTGHS_DEVICR = AT91C_OTGHS_WAKEUP | AT91C_OTGHS_SUSP; + + //// Enable the Start Of Frame (SOF) interrupt if needed + //if (pCallbacks->startOfFrame != 0) + //{ + // AT91C_BASE_OTGHS->OTGHS_DEVIER |= AT91C_OTGHS_SOF; + //} + + // Invoke the Reset callback + USBDCallbacks_Reset(); + + // Acknowledge end of bus reset interrupt + AT91C_BASE_OTGHS->OTGHS_DEVICR = AT91C_OTGHS_EORST; + } + + // Handle upstream resume interrupt + else if (status & AT91C_OTGHS_UPRSM) { + + TRACE_DEBUG_WP("ExtRes "); + + // - Acknowledge the IT + AT91C_BASE_OTGHS->OTGHS_DEVICR = AT91C_OTGHS_UPRSM; + } + + // Endpoint interrupts + else { +#ifndef DMA + + // Handle endpoint interrupts + for (numIT = 0; numIT < NUM_IT_MAX; numIT++) { + + if ((status & (1 << SHIFT_INTERUPT << numIT)) != 0) { + + OTGHS_EndpointHandler(numIT); + } + } +#else + // Handle endpoint control interrupt + if ((status & (1 << SHIFT_INTERUPT << 0)) != 0) { + + OTGHS_EndpointHandler( 0 ); + } + else { + + numIT = 1; + while((status&(0x7E<OTGHS_DEVISR & AT91C_BASE_OTGHS->OTGHS_DEVIMR; + + TRACE_DEBUG_WP("\n\r"); + + if (status != 0) { + + TRACE_DEBUG_WP(" - "); + } + } + + if (deviceState >= USBD_STATE_POWERED) { + + LED_Clear(USBD_LEDUSB); + } +} + +//------------------------------------------------------------------------------ +/// Configure an endpoint with the provided endpoint descriptor +/// \param pDdescriptor Pointer to the endpoint descriptor +//------------------------------------------------------------------------------ +void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor) + +{ + Endpoint *pEndpoint; + unsigned char bEndpoint; + unsigned char bType; + unsigned char bEndpointDir; + unsigned char bSizeEpt = 0; + + // NULL descriptor -> Control endpoint 0 + if (pDescriptor == 0) { + + bEndpoint = 0; + pEndpoint = &(endpoints[bEndpoint]); + bType = USBEndpointDescriptor_CONTROL; + bEndpointDir = 0; + pEndpoint->size = CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0); + pEndpoint->bank = CHIP_USB_ENDPOINTS_BANKS(0); + } + else { + + // The endpoint number + bEndpoint = USBEndpointDescriptor_GetNumber(pDescriptor); + pEndpoint = &(endpoints[bEndpoint]); + // Transfer type: Control, Isochronous, Bulk, Interrupt + bType = USBEndpointDescriptor_GetType(pDescriptor); + // Direction, ignored for control endpoints + bEndpointDir = USBEndpointDescriptor_GetDirection(pDescriptor); + pEndpoint->size = USBEndpointDescriptor_GetMaxPacketSize(pDescriptor); + pEndpoint->bank = CHIP_USB_ENDPOINTS_BANKS(bEndpoint); + } + + // Abort the current transfer is the endpoint was configured and in + // Write or Read state + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) { + + OTGHS_EndOfTransfer(bEndpoint, USBD_STATUS_RESET); + } + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Disable endpoint + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_TXINI + | AT91C_OTGHS_RXOUT + | AT91C_OTGHS_RXSTP + | AT91C_OTGHS_NAKOUT + | AT91C_OTGHS_NAKIN + | AT91C_OTGHS_OVERFL + | AT91C_OTGHS_STALL + | AT91C_OTGHS_SHRTPACK + | AT91C_OTGHS_MADATA + | AT91C_OTGHS_DATAX + | AT91C_OTGHS_ERRTRANS + | AT91C_OTGHS_NBUSYBK + | AT91C_OTGHS_FIFOCON + | AT91C_OTGHS_EPDISHDMA + | AT91C_OTGHS_NYETDIS + | AT91C_OTGHS_STALLRQ; + + // Reset Endpoint Fifos + AT91C_BASE_OTGHS->OTGHS_DEVEPT |= (1<OTGHS_DEVEPT &= ~(1<size <= 8 ) { + bSizeEpt = 0; + } + else if ( pEndpoint->size <= 16 ) { + bSizeEpt = 1; + } + else if ( pEndpoint->size <= 32 ) { + bSizeEpt = 2; + } + else if ( pEndpoint->size <= 64 ) { + bSizeEpt = 3; + } + else if ( pEndpoint->size <= 128 ) { + bSizeEpt = 4; + } + else if ( pEndpoint->size <= 256 ) { + bSizeEpt = 5; + } + else if ( pEndpoint->size <= 512 ) { + bSizeEpt = 6; + } + else if ( pEndpoint->size <= 1024 ) { + bSizeEpt = 7; + } //else { + // sizeEpt = 0; // control endpoint + //} + + // Enable endpoint + AT91C_BASE_OTGHS->OTGHS_DEVEPT |= (1<OTGHS_DEVIER = 1<OTGHS_DEVEPTCFG[bEndpoint] = (bSizeEpt << 4) + | (bEndpointDir << 8) + | (bType << 11) + | (((pEndpoint->bank)-1) << 2); + + if (bType == USBEndpointDescriptor_CONTROL) { + + AT91C_BASE_OTGHS->OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_RXSTP; + } +#ifdef DMA + else { + AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint] |= AT91C_OTGHS_AUTOSW; + } +#endif + + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_NYETDIS;// with nyet + //AT91C_BASE_OTGHS->OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_NYETDIS; // without nyet + + // Check if the configuration is ok + AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint] |= AT91C_OTGHS_ALLOC; + if((AT91C_BASE_OTGHS->OTGHS_DEVEPTISR[bEndpoint]&AT91C_OTGHS_CFGOK)==0) { + + TRACE_ERROR("PB bEndpoint: 0x%X\n\r", bEndpoint); + TRACE_ERROR("PB bSizeEpt: 0x%X\n\r", bSizeEpt); + TRACE_ERROR("PB bEndpointDir: 0x%X\n\r", bEndpointDir); + TRACE_ERROR("PB bType: 0x%X\n\r", bType); + TRACE_ERROR("PB pEndpoint->bank: 0x%X\n\r", pEndpoint->bank); + TRACE_ERROR("PB OTGHS_EPTCFG: 0x%X\n\r", AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]); + for(;;); + } +} + +//------------------------------------------------------------------------------ +/// Sends data through an USB endpoint (IN) +/// Sets up the transfer descriptor, write one or two data payloads +/// (depending on the number of FIFO banks for the endpoint) and then +/// starts the actual transfer. The operation is complete when all +/// the data has been sent. +/// \param bEndpoint Index of endpoint +/// \param *pData Data to be written +/// \param dLength Data length to be send +/// \param fCallback Callback to be call after the success command +/// \param *pArgument Callback argument +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +char USBD_Write( unsigned char bEndpoint, + const void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); +//unsigned char i; +//unsigned char * data; + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength); + pEndpoint->sendZLP = 0; + // Setup the transfer descriptor + pTransfer->pData = (void *) pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Send one packet + pEndpoint->state = UDP_ENDPOINT_SENDING; + +#ifdef DMA + // Test if endpoint type control + if (AT91C_OTGHS_EPT_TYPE_CTL_EPT == (AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint])) { +#endif + // Enable endpoint IT + AT91C_BASE_OTGHS->OTGHS_DEVIER = (1<OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_TXINI; + +#ifdef DMA + } + else { + if( CHIP_USB_ENDPOINTS_DMA(bEndpoint) == 0 ) { + TRACE_FATAL("Endpoint has no DMA\n\r"); + } + if( pTransfer->remaining == 0 ) { + + // DMA not handle ZLP + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_TXINI; + // For a non-control endpoint, the FIFOCON bit must be cleared + // to start the transfer + if ((AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_FIFOCON; + } + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_TXINI; + + // Enable endpoint IT + AT91C_BASE_OTGHS->OTGHS_DEVIER = (1<remaining > DMA_MAX_FIFO_SIZE ) { + + // Transfer the max + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + // Transfer the good size + pTransfer->buffered = pTransfer->remaining; + } + + TRACE_DEBUG_WP("\n\r_WR:%d ", pTransfer->remaining ); + TRACE_DEBUG_WP("B:%d ", pTransfer->buffered ); + TRACE_DEBUG_WP("T:%d ", pTransfer->transferred ); + + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = (unsigned int)(pTransfer->pData); + + // Clear unwanted interrupts + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_OTGHS->OTGHS_DEVIER = (1<OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = 0; // raz + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = + (((pTransfer->buffered<<16)&AT91C_OTGHS_BUFF_LENGTH) + | AT91C_OTGHS_END_B_EN + | AT91C_OTGHS_END_BUFFIT + | AT91C_OTGHS_CHANN_ENB); + } + } +#endif + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reads incoming data on an USB endpoint (OUT) +/// \param bEndpoint Index of endpoint +/// \param *pData Data to be readen +/// \param dLength Data length to be receive +/// \param fCallback Callback to be call after the success command +/// \param *pArgument Callback argument +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +char USBD_Read( unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + //TRACE_ERROR_WP("Read%d(%d) ", bEndpoint, dLength); + + // Endpoint enters Receiving state + pEndpoint->state = UDP_ENDPOINT_RECEIVING; + + // Set the transfer descriptor + pTransfer->pData = pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + +#ifdef DMA + // Test if endpoint type control + if (AT91C_OTGHS_EPT_TYPE_CTL_EPT == (AT91C_OTGHS_EPT_TYPE & AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint])) { +#endif + // Control endpoint + // Enable endpoint IT + AT91C_BASE_OTGHS->OTGHS_DEVIER = (1<OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_RXOUT; +#ifdef DMA + } + else { + if( CHIP_USB_ENDPOINTS_DMA(bEndpoint) == 0 ) { + TRACE_FATAL("Endpoint has no DMA\n\r"); + } + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Others endpoints (not control) + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + // Transfer the max + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + // Transfer the good size + pTransfer->buffered = pTransfer->remaining; + } + + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = (unsigned int)(pTransfer->pData); + + // Clear unwanted interrupts + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_OTGHS->OTGHS_DEVIER = (1<remaining ); + TRACE_DEBUG_WP("B:%d ", pTransfer->buffered ); + TRACE_DEBUG_WP("T:%d ", pTransfer->transferred ); + + // DMA config + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = 0; // raz + AT91C_BASE_OTGHS->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = + (((pTransfer->buffered<<16)&AT91C_OTGHS_BUFF_LENGTH) + | AT91C_OTGHS_END_TR_EN + | AT91C_OTGHS_END_TR_IT + | AT91C_OTGHS_END_B_EN + | AT91C_OTGHS_END_BUFFIT + | AT91C_OTGHS_CHANN_ENB); + } +#endif + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Put endpoint into Halt state +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +void USBD_Halt( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + TRACE_INFO("usbd_Halt%d ", bEndpoint); + //TRACE_ERROR("usbd_Halt%d ", bEndpoint); + + // Check that endpoint is enabled and not already in Halt state + if( (pEndpoint->state != UDP_ENDPOINT_DISABLED) + && (pEndpoint->state != UDP_ENDPOINT_HALTED) ) { + + TRACE_INFO("Halt%d ", bEndpoint); + + // Abort the current transfer if necessary + OTGHS_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + + pEndpoint->state = UDP_ENDPOINT_HALTED; + // Put endpoint into Halt state + AT91C_BASE_OTGHS->OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_STALLRQ; + AT91C_BASE_OTGHS->OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_STALL; + } +} + +//------------------------------------------------------------------------------ +/// Clears the Halt feature on the given endpoint. +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +void USBD_Unhalt( unsigned char bEndpoint ) +{ + unsigned int cfgSav; + + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check if the endpoint is enabled + //if (pEndpoint->state != UDP_ENDPOINT_DISABLED) { + if (pEndpoint->state == UDP_ENDPOINT_HALTED) { + + TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); + //TRACE_ERROR("Unhalt%d ", bEndpoint); + + // Return endpoint to Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + cfgSav = AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[bEndpoint]; + + // Reset Endpoint Fifos + AT91C_BASE_OTGHS->OTGHS_DEVEPT |= (1<OTGHS_DEVEPT &= ~(1<OTGHS_DEVEPTCFG[bEndpoint] = cfgSav; + + if((AT91C_BASE_OTGHS->OTGHS_DEVEPTISR[bEndpoint]&AT91C_OTGHS_CFGOK)==0) { + + TRACE_ERROR("PB bEndpoint: 0x%X\n\r", bEndpoint); + for(;;); + } + + // Reset data-toggle + AT91C_BASE_OTGHS->OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_RSTDT; + + // Clear FORCESTALL flag + // Disable stall on endpoint + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[bEndpoint] = AT91C_OTGHS_STALLRQ; + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[bEndpoint] = AT91C_OTGHS_STALL; + } +} + +//------------------------------------------------------------------------------ +/// Returns the current Halt status of an endpoint. +/// \param bEndpoint Index of endpoint +/// \return 1 if the endpoint is currently halted; otherwise 0 +//------------------------------------------------------------------------------ +unsigned char USBD_IsHalted( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + unsigned char status = 0; + + if (pEndpoint->state == UDP_ENDPOINT_HALTED) { + status = 1; + } + return( status ); +} + +//------------------------------------------------------------------------------ +/// IS High Speed device working in High Speed ? +/// \return 1 if the device is in High Speed; otherwise 0 (Full Speed) +//------------------------------------------------------------------------------ +unsigned char USBD_IsHighSpeed( void ) +{ + unsigned char status = 0; + + if(AT91C_OTGHS_SPEED_SR_HS == (AT91C_BASE_OTGHS->OTGHS_SR & (0x03<<12))) { + // High Speed + TRACE_DEBUG_WP("High Speed\n\r"); + status = 1; + } + else { + TRACE_DEBUG_WP("Full Speed\n\r"); + } + return( status ); +} + +//------------------------------------------------------------------------------ +/// Causes the endpoint to acknowledge the next received packet with a STALL +/// handshake. +/// Further packets are then handled normally. +/// \param bEndpoint Index of endpoint +/// \return Operation result code: USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char USBD_Stall( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint); + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Stall%d ", bEndpoint); + + // Sends a STALL handshake for the next host request. + // A STALL handshake will be sent for each following request until a SETUP + // or a Clear Halt Feature occurs for this endpoint. + AT91C_BASE_OTGHS->OTGHS_DEVEPTIER[bEndpoint] = AT91C_OTGHS_STALLRQ|AT91C_OTGHS_RXSTP; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Activates a remote wakeup procedure +//------------------------------------------------------------------------------ +void USBD_RemoteWakeUp(void) +{ + TRACE_DEBUG_WP("Remote WakeUp\n\r"); + + // Device is currently suspended + if (deviceState == USBD_STATE_SUSPENDED) { + + TRACE_DEBUG_WP("RW\n\r"); + OTGHS_EnableUsbClock(); + OTGHS_EnableTransceiver(); + + // Activates a remote wakeup + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= AT91C_OTGHS_RMWKUP; + } + // Device is NOT suspended + else { + + TRACE_WARNING("USBD_RemoteWakeUp: Device is not suspended\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// Sets the device address +/// \param address Adress to be set +//------------------------------------------------------------------------------ +void USBD_SetAddress( unsigned char address ) +{ + TRACE_DEBUG_WP("SetAddr(%d) ", address); + + // Set address + AT91C_BASE_OTGHS->OTGHS_DEVCTRL &= ~(unsigned int)AT91C_OTGHS_UADD; + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= address & AT91C_OTGHS_UADD; + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= AT91C_OTGHS_ADDEN; + + // If the address is 0, the device returns to the Default state + if (address == 0) { + deviceState = USBD_STATE_DEFAULT; + } + // If the address is non-zero, the device enters the Address state + else { + deviceState = USBD_STATE_ADDRESS; + } +} + +//------------------------------------------------------------------------------ +/// Changes the device state from Address to Configured, or from Configured +/// to Address. +/// This method directly access the last received SETUP packet to decide on +/// what to do. +/// \param cfgnum configuration number +//------------------------------------------------------------------------------ +void USBD_SetConfiguration( unsigned char cfgnum ) +{ + TRACE_DEBUG_WP("SetCfg(%d) ", cfgnum); + + // Check the request + if( cfgnum != 0 ) { + + // Enter Configured state + deviceState = USBD_STATE_CONFIGURED; + } + // If the configuration number is zero, the device goes back to the Address + // state + else { + + // Go back to Address state + deviceState = USBD_STATE_ADDRESS; + + // Abort all transfers + OTGHS_DisableEndpoints(); + } +} + + +//------------------------------------------------------------------------------ +/// Enables the pull-up on the D+ line to connect the device to the USB. +//------------------------------------------------------------------------------ +void USBD_Connect( void ) +{ + TRACE_DEBUG_WP("Conn "); +#if defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_OTGHS->OTGHS_DEVCTRL &= ~(unsigned int)AT91C_OTGHS_DETACH; +#else + #error "not defined" +#endif +} + +//------------------------------------------------------------------------------ +/// Disables the pull-up on the D+ line to disconnect the device from the bus. +//------------------------------------------------------------------------------ +void USBD_Disconnect( void ) +{ + TRACE_DEBUG_WP("Disc "); +#if defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= AT91C_OTGHS_DETACH; + +#else + #error "not defined" +#endif + // Device returns to the Powered state + if (deviceState > USBD_STATE_POWERED) { + + deviceState = USBD_STATE_POWERED; + } +} + +//------------------------------------------------------------------------------ +/// Certification test for High Speed device. +/// \param bIndex Test to be done +//------------------------------------------------------------------------------ +void USBD_Test( unsigned char bIndex ) +{ + char *pFifo; + unsigned char i; + + AT91C_BASE_OTGHS->OTGHS_DEVIDR &= ~(unsigned int)AT91C_OTGHS_SUSP; + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= AT91C_OTGHS_SPDCONF_HS; // remove suspend ? + + switch( bIndex ) { + case USBFeatureRequest_TESTPACKET: + TRACE_DEBUG_WP("TEST_PACKET "); + + AT91C_BASE_OTGHS->OTGHS_DEVDMA[1].OTGHS_DEVDMACONTROL = 0; // raz + AT91C_BASE_OTGHS->OTGHS_DEVDMA[2].OTGHS_DEVDMACONTROL = 0; // raz + + // Configure endpoint 2, 64 bytes, direction IN, type BULK, 1 bank + AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[2] = AT91C_OTGHS_EPT_SIZE_64 + | AT91C_OTGHS_EPT_DIR_IN + | AT91C_OTGHS_EPT_TYPE_BUL_EPT + | AT91C_OTGHS_BK_NUMBER_1; + // Check if the configuration is ok + AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[2] |= AT91C_OTGHS_ALLOC; + while((AT91C_BASE_OTGHS->OTGHS_DEVEPTISR[2]&AT91C_OTGHS_CFGOK)==0) { + } + + AT91C_BASE_OTGHS->OTGHS_DEVEPT |= AT91C_OTGHS_EPEN2; + + // Write FIFO + pFifo = (char*)((unsigned int *)AT91C_BASE_OTGHS_EPTFIFO + (EPT_VIRTUAL_SIZE * 2)); + for( i=0; iOTGHS_DEVCTRL |= AT91C_OTGHS_TSTPCKT; + // Send packet + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[2] = AT91C_OTGHS_TXINI; + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[2] = AT91C_OTGHS_FIFOCON; + break; + + case USBFeatureRequest_TESTJ: + TRACE_DEBUG_WP("TEST_J "); + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTJ; + break; + + case USBFeatureRequest_TESTK: + TRACE_DEBUG_WP("TEST_K "); + AT91C_BASE_OTGHS->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTK; + break; + + case USBFeatureRequest_TESTSE0NAK: + TRACE_DEBUG_WP("TEST_SEO_NAK "); + AT91C_BASE_OTGHS->OTGHS_DEVIDR = 0xFFFFFFFF; + break; + + case USBFeatureRequest_TESTSENDZLP: + //while( 0 != (AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & AT91C_UDPHS_TX_PK_RDY ) ) {} + AT91C_BASE_OTGHS->OTGHS_DEVEPTICR[0] = AT91C_OTGHS_TXINI; + //while( 0 != (AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & AT91C_UDPHS_TX_PK_RDY ) ) {} + TRACE_DEBUG_WP("SEND_ZLP "); + break; + } + TRACE_DEBUG_WP("\n\r"); +} + +//------------------------------------------------------------------------------ +/// Initializes the specified USB driver +/// This function initializes the current FIFO bank of endpoints, +/// configures the pull-up and VBus lines, disconnects the pull-up and +/// then trigger the Init callback. +//------------------------------------------------------------------------------ +void USBD_Init(void) +{ + // forceFS must not be used ! + unsigned int i; + + TRACE_DEBUG_WP("USBD Init()\n\r"); + + // Enable USB macro + *AT91C_OTGHS_CTRL |= AT91C_OTGHS_USBECTRL; + + // Automatic mode speed for device + *AT91C_OTGHS_DEVCTRL &= ~(unsigned int)AT91C_OTGHS_SPDCONF_FS; // Normal mode + + *AT91C_OTGHS_DEVCTRL &= ~(unsigned int)( AT91C_OTGHS_LS | AT91C_OTGHS_TSTJ + | AT91C_OTGHS_TSTK | AT91C_OTGHS_TSTPCKT + | AT91C_OTGHS_OPMODE2 ); // Normal mode + + AT91C_BASE_OTGHS->OTGHS_DEVCTRL = 0; + AT91C_BASE_OTGHS->OTGHS_HSTCTRL = 0; + + // Enable OTG pad + *AT91C_OTGHS_CTRL |= AT91C_OTGHS_OTGPADE; + + // Enable clock OTG pad + *AT91C_OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_FRZCLKCTRL; + + //Usb_disable(); + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_USBECTRL; + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_OTGPADE; + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_FRZCLKCTRL; + //Usb_enable(); + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_USBECTRL; + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_OTGPADE; + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_FRZCLKCTRL; + //Usb_select_device(); + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_UIDE; + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_UIMOD; + + // Device is in the Attached state + deviceState = USBD_STATE_SUSPENDED; + previousDeviceState = USBD_STATE_POWERED; + + + PMC_EnablePeripheral(AT91C_ID_OTGHS); + + // Reset endpoint structures + OTGHS_ResetEndpoints(); + + // Enables the USB Clock + OTGHS_EnableUsbClock(); + + //926C + // Enable USB macro and clear all other bit + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_USBECTRL; + AT91C_BASE_OTGHS->OTGHS_CTRL = AT91C_OTGHS_USBECTRL; + + // Configure the pull-up on D+ and disconnect it + USBD_Disconnect(); + + // Enable clock OTG pad + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_FRZCLKCTRL; + TRACE_DEBUG("AT91C_OTGHS_CTRL: 0x%X\n\r", AT91C_BASE_OTGHS->OTGHS_CTRL ); + + // Clear General IT + AT91C_BASE_OTGHS->OTGHS_SCR = 0x01FF; + + // Clear OTG Device IT + AT91C_BASE_OTGHS->OTGHS_DEVICR = 0xFF; + + // Clear OTG Host IT + AT91C_BASE_OTGHS->OTGHS_HSTICR = 0x7F; + + // Reset all Endpoints Fifos + AT91C_BASE_OTGHS->OTGHS_DEVEPT |= (0x7F<<16); + AT91C_BASE_OTGHS->OTGHS_DEVEPT &= ~(unsigned int)(0x7F<<16); + + // Disable all endpoints + AT91C_BASE_OTGHS->OTGHS_DEVEPT &= ~(unsigned int)0x7F; + + AT91C_BASE_OTGHS->OTGHS_TSTA2 = 0; + + // Device is in the Attached state + deviceState = USBD_STATE_SUSPENDED; + previousDeviceState = USBD_STATE_POWERED; + + // Automatic mode speed for device + AT91C_BASE_OTGHS->OTGHS_DEVCTRL &= ~(unsigned int)AT91C_OTGHS_SPDCONF_FS; + // Force Full Speed mode for device + //*AT91C_OTGHS_DEVCTRL = AT91C_OTGHS_SPDCONF_FS; + // Force High Speed mode for device + //*AT91C_OTGHS_DEVCTRL = AT91C_OTGHS_SPDCONF_HS; + + AT91C_BASE_OTGHS->OTGHS_DEVCTRL &= ~(unsigned int)( AT91C_OTGHS_LS + | AT91C_OTGHS_TSTJ + | AT91C_OTGHS_TSTK + | AT91C_OTGHS_TSTPCKT + | AT91C_OTGHS_OPMODE2 ); + + + // Automatic mode speed for host + AT91C_BASE_OTGHS->OTGHS_HSTCTRL &= ~(unsigned int)AT91C_OTGHS_SPDCONF_HST_FS; + // Force Full Speed mode for host + //AT91C_BASE_OTGHS->OTGHS_HSTCTRL = AT91C_OTGHS_SPDCONF_HST_FS; + // Force High Speed mode for host + //*AT91C_BASE_OTGHS->OTGHS_HSTCTRL = AT91C_OTGHS_SPDCONF_HST_HS; + + // Enable USB macro + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_USBECTRL; + + // Enable the UID pin select + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_UIDE; + + // Enable OTG pad + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_OTGPADE; + + // Enable clock OTG pad + AT91C_BASE_OTGHS->OTGHS_CTRL &= ~(unsigned int)AT91C_OTGHS_FRZCLKCTRL; + + + // With OR without DMA !!! + // Initialization of DMA + for( i=1; i<=(unsigned int)((AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_DMA_CHANNEL_NBR)>>4); i++ ) { + + // RESET endpoint canal DMA: + // DMA stop channel command + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0; // STOP command + + // Disable endpoint + AT91C_BASE_OTGHS->OTGHS_DEVEPTIDR[i] = 0XFFFFFFFF; + + // Reset endpoint config + AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[i] = 0; + + // Reset DMA channel (Buff count and Control field) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0x02; // NON STOP command + + // Reset DMA channel 0 (STOP) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0; // STOP command + + // Clear DMA channel status (read the register for clear it) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMASTATUS = AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMASTATUS; + + } + + + // Configure interrupts + USBDCallbacks_Initialized(); + + AT91C_BASE_OTGHS->OTGHS_CTRL |= AT91C_OTGHS_VBUSTI; + + TRACE_DEBUG("AT91C_OTGHS_CTRL: 0x%X\n\r", AT91C_BASE_OTGHS->OTGHS_CTRL ); + TRACE_DEBUG("AT91C_OTGHS_SR: 0x%X\n\r", AT91C_BASE_OTGHS->OTGHS_SR ); + + AT91C_BASE_OTGHS->OTGHS_DEVIER = AT91C_OTGHS_WAKEUP; + + TRACE_DEBUG("NUM_IT_MAX_DMA: 0x%X\n\r", NUM_IT_MAX_DMA ); + TRACE_DEBUG("NUM_IT_MAX: 0x%X\n\r", NUM_IT_MAX ); + +} + + +//------------------------------------------------------------------------------ +/// Returns the current state of the USB device. +/// \return Device current state. +//------------------------------------------------------------------------------ +unsigned char USBD_GetState( void ) +{ + return deviceState; +} + diff --git a/usb/device/core/USBD_UDP.c b/usb/device/core/USBD_UDP.c new file mode 100644 index 0000000..437d185 --- /dev/null +++ b/usb/device/core/USBD_UDP.c @@ -0,0 +1,1692 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Implementation of USB device functions on a UDP controller. + + See "USBD API Methods". +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBD.h" +#include "USBDCallbacks.h" +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "UDP register field values" +/// +/// This page lists the initialize values of UDP registers. +/// +/// !Values +/// - UDP_RXDATA + +/// Bit mask for both banks of the UDP_CSR register. +#define UDP_RXDATA (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Endpoint states" +/// +/// This page lists the endpoint states. +/// +/// !States +// - UDP_ENDPOINT_DISABLED +// - UDP_ENDPOINT_HALTED +// - UDP_ENDPOINT_IDLE +// - UDP_ENDPOINT_SENDING +// - UDP_ENDPOINT_RECEIVING +// - UDP_ENDPOINT_SENDINGM +// - UDP_ENDPOINT_RECEIVINGM + +/// Endpoint states: Endpoint is disabled +#define UDP_ENDPOINT_DISABLED 0 +/// Endpoint states: Endpoint is halted (i.e. STALLs every request) +#define UDP_ENDPOINT_HALTED 1 +/// Endpoint states: Endpoint is idle (i.e. ready for transmission) +#define UDP_ENDPOINT_IDLE 2 +/// Endpoint states: Endpoint is sending data +#define UDP_ENDPOINT_SENDING 3 +/// Endpoint states: Endpoint is receiving data +#define UDP_ENDPOINT_RECEIVING 4 +/// Endpoint states: Endpoint is sending MBL +#define UDP_ENDPOINT_SENDINGM 5 +/// Endpoint states: Endpoint is receiving MBL +#define UDP_ENDPOINT_RECEIVINGM 6 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "UDP_CSR register access" +/// +/// This page lists the macroes to access UDP CSR register. +/// +/// !Macros +/// - CLEAR_CSR +/// - SET_CSR + +/// Bitmap for all status bits in CSR. +#define REG_NO_EFFECT_1_ALL AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1 \ + |AT91C_UDP_STALLSENT | AT91C_UDP_RXSETUP \ + |AT91C_UDP_TXCOMP + +/// Clears the specified bit(s) in the UDP_CSR register. +/// \param endpoint The endpoint number of the CSR to process. +/// \param flags The bitmap to set to 1. +#define SET_CSR(endpoint, flags) \ + { \ + volatile unsigned int reg; \ + int timeOut = 200; \ + reg = AT91C_BASE_UDP->UDP_CSR[endpoint] ; \ + reg |= REG_NO_EFFECT_1_ALL; \ + reg |= (flags); \ + AT91C_BASE_UDP->UDP_CSR[endpoint] = reg; \ + while ( (AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) != (flags)) \ + { \ + if (-- timeOut <= 0) break; \ + } \ + } + +/// Sets the specified bit(s) in the UDP_CSR register. +/// \param endpoint The endpoint number of the CSR to process. +/// \param flags The bitmap to clear to 0. +#define CLEAR_CSR(endpoint, flags) \ + { \ + volatile unsigned int reg; \ + reg = AT91C_BASE_UDP->UDP_CSR[endpoint]; \ + reg |= REG_NO_EFFECT_1_ALL; \ + reg &= ~(flags); \ + AT91C_BASE_UDP->UDP_CSR[endpoint] = reg; \ + while ( (AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) == (flags)); \ + } +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Describes an ongoing transfer on a UDP endpoint. +typedef struct { + + /// Pointer to a data buffer used for emission/reception. + char *pData; + /// Number of bytes which have been written into the UDP internal FIFO + /// buffers. + volatile int buffered; + /// Number of bytes which have been sent/received. + volatile int transferred; + /// Number of bytes which have not been buffered/transferred yet. + volatile int remaining; + /// Optional callback to invoke when the transfer completes. + volatile TransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} Transfer; + +/// Describes Multi Buffer List transfer on a UDP endpoint. +typedef struct { + /// Pointer to frame list + USBDTransferBuffer *pMbl; + /// Pointer to last loaded buffer + USBDTransferBuffer *pLastLoaded; + /// List size + unsigned short listSize; + /// Current processing frame + unsigned short currBuffer; + /// First freed frame to re-use + unsigned short freedBuffer; + /// Frame setting, circle frame list + unsigned char circList; + /// All buffer listed is used + unsigned char allUsed; + /// Optional callback to invoke when the transfer completes. + MblTransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} MblTransfer; + +//------------------------------------------------------------------------------ +/// Describes the state of an endpoint of the UDP controller. +//------------------------------------------------------------------------------ +typedef struct { + + /// Current endpoint state. + volatile unsigned char state; + /// Current reception bank (0 or 1). + volatile unsigned char bank; + /// Maximum packet size for the endpoint. + volatile unsigned short size; + /// Describes an ongoing transfer (if current state is either + /// or ) + union { + Transfer singleTransfer; + MblTransfer mblTransfer; + } transfer; +} Endpoint; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Holds the internal state for each endpoint of the UDP. +static Endpoint endpoints[CHIP_USB_NUMENDPOINTS]; + +/// Device current state. +static unsigned char deviceState; +/// Indicates the previous device state +static unsigned char previousDeviceState; + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Enables the clock of the UDP peripheral. +//------------------------------------------------------------------------------ +static inline void UDP_EnablePeripheralClock(void) +{ + AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_UDP; +} + +//------------------------------------------------------------------------------ +/// Disables the UDP peripheral clock. +//------------------------------------------------------------------------------ +static inline void UDP_DisablePeripheralClock(void) +{ + AT91C_BASE_PMC->PMC_PCDR = 1 << AT91C_ID_UDP; +} + +//------------------------------------------------------------------------------ +/// Enables the 48MHz USB clock. +//------------------------------------------------------------------------------ +static inline void UDP_EnableUsbClock(void) +{ + AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; +} + +//------------------------------------------------------------------------------ +/// Disables the 48MHz USB clock. +//------------------------------------------------------------------------------ +static inline void UDP_DisableUsbClock(void) +{ + AT91C_BASE_PMC->PMC_SCDR = AT91C_PMC_UDP; +} + +//------------------------------------------------------------------------------ +/// Enables the UDP transceiver. +//------------------------------------------------------------------------------ +static inline void UDP_EnableTransceiver(void) +{ + AT91C_BASE_UDP->UDP_TXVC &= ~AT91C_UDP_TXVDIS; +} + +//------------------------------------------------------------------------------ +/// Disables the UDP transceiver. +//------------------------------------------------------------------------------ +static inline void UDP_DisableTransceiver(void) +{ + AT91C_BASE_UDP->UDP_TXVC |= AT91C_UDP_TXVDIS; +} + +//------------------------------------------------------------------------------ +/// Handles a completed transfer on the given endpoint, invoking the +/// configured callback if any. +/// \param bEndpoint Number of the endpoint for which the transfer has completed. +/// \param bStatus Status code returned by the transfer operation +//------------------------------------------------------------------------------ +static void UDP_EndOfTransfer(unsigned char bEndpoint, char bStatus) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint was sending or receiving data + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + Transfer *pTransfer = (Transfer *)&(pEndpoint->transfer); + + TRACE_DEBUG_WP("EoT "); + + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Invoke callback is present + if (pTransfer->fCallback != 0) { + + ((TransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + bStatus, + pTransfer->transferred, + pTransfer->remaining + pTransfer->buffered); + } + else { + TRACE_DEBUG_WP("NoCB "); + } + } + else if ( (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) + || (pEndpoint->state == UDP_ENDPOINT_SENDINGM) ) { + + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + + TRACE_DEBUG_WP("EoMT "); + + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + // Invoke callback + if (pTransfer->fCallback != 0) { + + ((MblTransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + bStatus, + 0); + } + else { + TRACE_DEBUG_WP("NoCB "); + } + } +} + +//------------------------------------------------------------------------------ +/// Clears the correct reception flag (bank 0 or bank 1) of an endpoint +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDP_ClearRxFlag(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Clear flag and change banks + if (pEndpoint->bank == 0) { + + CLEAR_CSR(bEndpoint, AT91C_UDP_RX_DATA_BK0); + // Swap bank if in dual-fifo mode + if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) { + + pEndpoint->bank = 1; + } + } + else { + + CLEAR_CSR(bEndpoint, AT91C_UDP_RX_DATA_BK1); + pEndpoint->bank = 0; + } +} + +//------------------------------------------------------------------------------ +/// Update multi-buffer-transfer descriptors. +/// \param pTransfer Pointer to instance MblTransfer. +/// \param size Size of bytes that processed. +/// \param forceEnd Force the buffer END. +/// \return 1 if current buffer ended. +//------------------------------------------------------------------------------ +static char UDP_MblUpdate(MblTransfer *pTransfer, + USBDTransferBuffer * pBi, + unsigned short size, + unsigned char forceEnd) +{ + // Update transfer descriptor + pBi->remaining -= size; + // Check if current buffer ended + if (pBi->remaining == 0 || forceEnd) { + + // Process to next buffer + if ((++ pTransfer->currBuffer) == pTransfer->listSize) { + if (pTransfer->circList) { + pTransfer->currBuffer = 0; + } + else { + pTransfer->allUsed = 1; + } + } + // All buffer in the list is processed + if (pTransfer->currBuffer == pTransfer->freedBuffer) { + pTransfer->allUsed = 1; + } + // Continue transfer, prepare for next operation + if (pTransfer->allUsed == 0) { + pBi = &pTransfer->pMbl[pTransfer->currBuffer]; + pBi->buffered = 0; + pBi->transferred = 0; + pBi->remaining = pBi->size; + } + return 1; + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static char UDP_MblWriteFifo(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->currBuffer]); + signed int size; + + volatile unsigned char * pBytes; + volatile char bufferEnd = 1; + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pBi->remaining) { + + size = pBi->remaining; + } + + TRACE_DEBUG_WP("w%d.%d ", pTransfer->currBuffer, size); + if (size == 0) { + + return 1; + } + + pTransfer->pLastLoaded = pBi; + pBytes = &(pBi->pBuffer[pBi->transferred + pBi->buffered]); + pBi->buffered += size; + bufferEnd = UDP_MblUpdate(pTransfer, pBi, size, 0); + + // Write packet in the FIFO buffer + if (size) { + signed int c8 = size >> 3; + signed int c1 = size & 0x7; + //printf("%d[%x] ", pBi->transferred, pBytes); + for (; c8; c8 --) { + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + } + for (; c1; c1 --) { + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pBytes ++); + } + } + return bufferEnd; +} +/* +//------------------------------------------------------------------------------ +/// Transfers a data payload from an endpoint FIFO to the current transfer +/// buffer, if NULL packet received, the current buffer is ENDed. +/// \param bEndpoint Endpoint number. +/// \param wPacketSize Size of received data packet +/// \return 1 if the buffer ENDed. +//------------------------------------------------------------------------------ +static char UDP_MblReadFifo(unsigned char bEndpoint, unsigned short wPacketSize) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->currBuffer]); + char bufferEnd; + + // Check that the requested size is not bigger than the remaining transfer + if (wPacketSize > pTransfer->remaining) { + + pTransfer->buffered += wPacketSize - pTransfer->remaining; + wPacketSize = pTransfer->remaining; + } + + // Update transfer descriptor information + pBi->transferred += wPacketSize; + bufferEnd = UDP_MblUpdate(pTransfer, + wPacketSize, + (wPacketSize < pEndpoint->size)); + + // Retrieve packet + if (wPacketSize) { + unsigned char * pBytes = &pBi->pBuffer[pBi->transferred]; + signed int c8 = wPacketSize >> 3; + signed int c1 = wPacketSize & 0x7; + for (; c8; c8 --) { + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + } + for (; c1; c1 --) { + *(pBytes ++) = AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + } + } + return bufferEnd; +} +*/ +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static void UDP_WritePayload(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + signed int size; + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pTransfer->remaining) { + + size = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->buffered += size; + pTransfer->remaining -= size; + + // Write packet in the FIFO buffer + while (size > 0) { + + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pTransfer->pData); + pTransfer->pData++; + size--; + } +} + + +//------------------------------------------------------------------------------ +/// Transfers a data payload from an endpoint FIFO to the current transfer buffer +/// \param bEndpoint Endpoint number. +/// \param wPacketSize Size of received data packet +//------------------------------------------------------------------------------ +static void UDP_ReadPayload(unsigned char bEndpoint, int wPacketSize) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Check that the requested size is not bigger than the remaining transfer + if (wPacketSize > pTransfer->remaining) { + + pTransfer->buffered += wPacketSize - pTransfer->remaining; + wPacketSize = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->remaining -= wPacketSize; + pTransfer->transferred += wPacketSize; + + // Retrieve packet + while (wPacketSize > 0) { + + *(pTransfer->pData) = (char) AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + pTransfer->pData++; + wPacketSize--; + } +} + +//------------------------------------------------------------------------------ +/// Received SETUP packet from endpoint 0 FIFO +/// \param pRequest Generic USB SETUP request sent over Control endpoints +//------------------------------------------------------------------------------ +static void UDP_ReadRequest(USBGenericRequest *pRequest) +{ + unsigned char *pData = (unsigned char *)pRequest; + unsigned int i; + + // Copy packet + for (i = 0; i < 8; i++) { + + *pData = (unsigned char) AT91C_BASE_UDP->UDP_FDR[0]; + pData++; + } +} + +//------------------------------------------------------------------------------ +/// Reset all endpoint transfer descriptors +//------------------------------------------------------------------------------ +static void UDP_ResetEndpoints( void ) +{ + Endpoint *pEndpoint; + Transfer *pTransfer; + unsigned char bEndpoint; + + // Reset the transfer descriptor of every endpoint + for (bEndpoint = 0; bEndpoint < CHIP_USB_NUMENDPOINTS; bEndpoint++) { + + pEndpoint = &(endpoints[bEndpoint]); + pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Reset endpoint transfer descriptor + pTransfer->pData = 0; + pTransfer->transferred = -1; + pTransfer->buffered = -1; + pTransfer->remaining = -1; + pTransfer->fCallback = 0; + pTransfer->pArgument = 0; + + // Reset endpoint state + pEndpoint->bank = 0; + pEndpoint->state = UDP_ENDPOINT_DISABLED; + } +} + +//------------------------------------------------------------------------------ +/// Disable all endpoints (except control endpoint 0), aborting current +/// transfers if necessary +//------------------------------------------------------------------------------ +static void UDP_DisableEndpoints( void ) + +{ + unsigned char bEndpoint; + + // Disable each endpoint, terminating any pending transfer + // Control endpoint 0 is not disabled + for (bEndpoint = 1; bEndpoint < CHIP_USB_NUMENDPOINTS; bEndpoint++) { + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + endpoints[bEndpoint].state = UDP_ENDPOINT_DISABLED; + } +} + +//------------------------------------------------------------------------------ +/// Checks if an ongoing transfer on an endpoint has been completed. +/// \param bEndpoint Endpoint number. +/// \return 1 if the current transfer on the given endpoint is complete; +/// otherwise 0. +//------------------------------------------------------------------------------ +static unsigned char UDP_IsTransferFinished(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Check if it is a Control endpoint + // -> Control endpoint must always finish their transfer with a zero-length + // packet + if ((AT91C_BASE_UDP->UDP_CSR[bEndpoint] & AT91C_UDP_EPTYPE) + == AT91C_UDP_EPTYPE_CTRL) { + + return (pTransfer->buffered < pEndpoint->size); + } + // Other endpoints only need to transfer all the data + else { + + return (pTransfer->buffered <= pEndpoint->size) + && (pTransfer->remaining == 0); + } +} + +//------------------------------------------------------------------------------ +/// Endpoint interrupt handler. +/// Handle IN/OUT transfers, received SETUP packets and STALLing +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDP_EndpointHandler(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + MblTransfer *pMblt = (MblTransfer*)&(pEndpoint->transfer); + unsigned int status = AT91C_BASE_UDP->UDP_CSR[bEndpoint]; + unsigned short wPacketSize; + USBGenericRequest request; + + TRACE_DEBUG_WP("E%d ", bEndpoint); + TRACE_DEBUG_WP("st:0x%X ", status); + + // Handle interrupts + // IN packet sent + if ((status & AT91C_UDP_TXCOMP) != 0) { + + TRACE_DEBUG_WP("Wr "); + + // Check that endpoint was in MBL Sending state + if (pEndpoint->state == UDP_ENDPOINT_SENDINGM) { + + USBDTransferBuffer * pMbli = pMblt->pLastLoaded; + unsigned char bufferEnd = 0; + + TRACE_DEBUG_WP("TxM%d.%d ", pMblt->allUsed, pMbli->buffered); + + // End of transfer ? + if (pMblt->allUsed && pMbli->buffered == 0) { + + pMbli->transferred += pMbli->buffered; + pMbli->buffered = 0; + + // Disable interrupt + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + else { + + // Transfer remaining data + TRACE_DEBUG_WP("%d ", pEndpoint->size); + + if (pMbli->buffered > pEndpoint->size) { + pMbli->transferred += pEndpoint->size; + pMbli->buffered -= pEndpoint->size; + } + else { + pMbli->transferred += pMbli->buffered; + pMbli->buffered = 0; + } + + // Send next packet + if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) == 1) { + + // No double buffering + bufferEnd = UDP_MblWriteFifo(bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + else { + // Double buffering + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + bufferEnd = UDP_MblWriteFifo(bEndpoint); + } + + if (bufferEnd && pMblt->fCallback) { + ((MblTransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + USBD_STATUS_PARTIAL_DONE, + 1); + } + } + } + // Check that endpoint was in Sending state + else if (pEndpoint->state == UDP_ENDPOINT_SENDING) { + + // End of transfer ? + if (UDP_IsTransferFinished(bEndpoint)) { + + pTransfer->transferred += pTransfer->buffered; + pTransfer->buffered = 0; + + // Disable interrupt if this is not a control endpoint + if ((status & AT91C_UDP_EPTYPE) != AT91C_UDP_EPTYPE_CTRL) { + + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + } + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + else { + + // Transfer remaining data + TRACE_DEBUG_WP(" %d ", pEndpoint->size); + + pTransfer->transferred += pEndpoint->size; + pTransfer->buffered -= pEndpoint->size; + + // Send next packet + if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) == 1) { + + // No double buffering + UDP_WritePayload(bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + else { + // Double buffering + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + UDP_WritePayload(bEndpoint); + } + } + } + else { + // Acknowledge interrupt + TRACE_ERROR("Error Wr%d, %x\n\r", bEndpoint, pEndpoint->state); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + } + + // OUT packet received + if ((status & UDP_RXDATA) != 0) { + + TRACE_DEBUG_WP("Rd "); + + // Check that the endpoint is in Receiving state + if (pEndpoint->state != UDP_ENDPOINT_RECEIVING) { + + // Check if an ACK has been received on a Control endpoint + if (((status & AT91C_UDP_EPTYPE) == AT91C_UDP_EPTYPE_CTRL) + && ((status & AT91C_UDP_RXBYTECNT) == 0)) { + + // Acknowledge the data and finish the current transfer + UDP_ClearRxFlag(bEndpoint); + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Check if the data has been STALLed + else if ((status & AT91C_UDP_FORCESTALL) != 0) { + + // Discard STALLed data + TRACE_DEBUG_WP("Discard "); + UDP_ClearRxFlag(bEndpoint); + } + // NAK the data + else { + + TRACE_DEBUG_WP("Nak "); + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + } + } + // Endpoint is in Read state + else { + + // Retrieve data and store it into the current transfer buffer + wPacketSize = (unsigned short) (status >> 16); + TRACE_DEBUG_WP("%d ", wPacketSize); + UDP_ReadPayload(bEndpoint, wPacketSize); + UDP_ClearRxFlag(bEndpoint); + + // Check if the transfer is finished + if ((pTransfer->remaining == 0) || (wPacketSize < pEndpoint->size)) { + + // Disable interrupt if this is not a control endpoint + if ((status & AT91C_UDP_EPTYPE) != AT91C_UDP_EPTYPE_CTRL) { + + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + } + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + } + } + + // STALL sent + if ((status & AT91C_UDP_STALLSENT) != 0) { + + TRACE_WARNING( "Sta 0x%X [%d] ", status, bEndpoint); + + // If the endpoint is not halted, clear the STALL condition + CLEAR_CSR(bEndpoint, AT91C_UDP_STALLSENT); + if (pEndpoint->state != UDP_ENDPOINT_HALTED) { + + TRACE_WARNING( "_ " ); + CLEAR_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + } + } + + // SETUP packet received + if ((status & AT91C_UDP_RXSETUP) != 0) { + + TRACE_DEBUG_WP("Stp "); + + // If a transfer was pending, complete it + // Handles the case where during the status phase of a control write + // transfer, the host receives the device ZLP and ack it, but the ack + // is not received by the device + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Copy the setup packet + UDP_ReadRequest(&request); + + // Set the DIR bit before clearing RXSETUP in Control IN sequence + if (USBGenericRequest_GetDirection(&request) == USBGenericRequest_IN) { + + SET_CSR(bEndpoint, AT91C_UDP_DIR); + } + // Acknowledge setup packet + CLEAR_CSR(bEndpoint, AT91C_UDP_RXSETUP); + + // Forward the request to the upper layer + USBDCallbacks_RequestReceived(&request); + } + +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +/// USB device interrupt handler +/// Manages device resume, suspend, end of bus reset. +/// Forwards endpoint interrupts to the appropriate handler. +//------------------------------------------------------------------------------ +void USBD_IrqHandler(void) +{ + unsigned int status; + int eptnum = 0; + + // Get interrupt status + // Some interrupts may get masked depending on the device state + status = AT91C_BASE_UDP->UDP_ISR; + status &= AT91C_BASE_UDP->UDP_IMR; + + if (deviceState < USBD_STATE_POWERED) { + + status &= AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + AT91C_BASE_UDP->UDP_ICR = ~status; + } + + // Return immediately if there is no interrupt to service + if (status == 0) { + + TRACE_DEBUG_WP(".\n\r"); + return; + } + + // Toggle USB LED if the device is active + if (deviceState >= USBD_STATE_POWERED) { + + LED_Set(USBD_LEDUSB); + } + + // Service interrupts + + //// Start Of Frame (SOF) + //if (ISSET(dStatus, AT91C_UDP_SOFINT)) { + // + // TRACE_DEBUG("SOF"); + // + // // Invoke the SOF callback + // USB_StartOfFrameCallback(pUsb); + // + // // Acknowledge interrupt + // AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_SOFINT; + // dStatus &= ~AT91C_UDP_SOFINT; + //} + + // Resume + if ((status & (AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM)) != 0) { + + TRACE_INFO_WP("Res "); + + // Don't do anything if the device was not suspended + if (deviceState == USBD_STATE_SUSPENDED) { + + // The device enters its previous state + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + + // Enable the transceiver if the device was past the Default + // state + deviceState = previousDeviceState; + if (deviceState >= USBD_STATE_DEFAULT) { + + UDP_EnableTransceiver(); + + // Invoke the Resume callback + USBDCallbacks_Resumed(); + } + } + + // Clear and disable resume interrupts + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_WAKEUP + | AT91C_UDP_RXRSM + | AT91C_UDP_RXSUSP; + AT91C_BASE_UDP->UDP_IDR = AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + } + + // Suspend + // This interrupt is always treated last (hence the '==') + if (status == AT91C_UDP_RXSUSP) { + + TRACE_INFO_WP("Susp "); + + // Don't do anything if the device is already suspended + if (deviceState != USBD_STATE_SUSPENDED) { + + // The device enters the Suspended state + // Enable wakeup + AT91C_BASE_UDP->UDP_IER = AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + + // Acknowledge interrupt + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_RXSUSP; + + // Switch to the Suspended state + previousDeviceState = deviceState; + deviceState = USBD_STATE_SUSPENDED; + // Invoke the Suspended callback + USBDCallbacks_Suspended(); + UDP_DisableTransceiver(); + UDP_DisablePeripheralClock(); + UDP_DisableUsbClock(); + } + } + #if 0 + // Resume + else if ((status & (AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM)) != 0) { + + TRACE_INFO_WP("Res "); + + // Don't do anything if the device was not suspended + if (deviceState == USBD_STATE_SUSPENDED) { + + // The device enters its previous state + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + + // Enable the transceiver if the device was past the Default + // state + deviceState = previousDeviceState; + if (deviceState >= USBD_STATE_DEFAULT) { + + UDP_EnableTransceiver(); + + // Invoke the Resume callback + USBDCallbacks_Resumed(); + } + } + + // Clear and disable resume interrupts + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_WAKEUP + | AT91C_UDP_RXRSM + | AT91C_UDP_RXSUSP; + AT91C_BASE_UDP->UDP_IDR = AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + } + #endif + // End of bus reset + else if ((status & AT91C_UDP_ENDBUSRES) != 0) { + + TRACE_INFO_WP("EoBRes "); + + // The device enters the Default state + deviceState = USBD_STATE_DEFAULT; + UDP_EnableTransceiver(); + UDP_ResetEndpoints(); + UDP_DisableEndpoints(); + USBD_ConfigureEndpoint(0); + + // Flush and enable the Suspend interrupt + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_WAKEUP + | AT91C_UDP_RXRSM + | AT91C_UDP_RXSUSP; + AT91C_BASE_UDP->UDP_IER = AT91C_UDP_RXSUSP; + + //// Enable the Start Of Frame (SOF) interrupt if needed + //if (pUsb->pCallbacks->startOfFrame != 0) { + // + // AT91C_BASE_UDP->UDP_IER = AT91C_UDP_SOFINT; + //} + + // Invoke the Reset callback + USBDCallbacks_Reset(); + + // Acknowledge end of bus reset interrupt + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_ENDBUSRES; + } + // Endpoint interrupts + else { + + status &= ((1 << CHIP_USB_NUMENDPOINTS) - 1); + while (status != 0) { + + // Check if endpoint has a pending interrupt + if ((status & (1 << eptnum)) != 0) { + + UDP_EndpointHandler(eptnum); + status &= ~(1 << eptnum); + + if (status != 0) { + + TRACE_INFO_WP("\n\r - "); + } + } + eptnum++; + } + } + + // Toggle LED back to its previous state + TRACE_DEBUG_WP("!"); + TRACE_INFO_WP("\n\r"); + if (deviceState >= USBD_STATE_POWERED) { + + LED_Clear(USBD_LEDUSB); + } +} + +//------------------------------------------------------------------------------ +/// Configures an endpoint according to its Endpoint Descriptor. +/// \param pDescriptor Pointer to an Endpoint descriptor. +//------------------------------------------------------------------------------ +void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor) +{ + Endpoint *pEndpoint; + unsigned char bEndpoint; + unsigned char bType; + unsigned char bEndpointDir; + + // NULL descriptor -> Control endpoint 0 + if (pDescriptor == 0) { + + bEndpoint = 0; + pEndpoint = &(endpoints[bEndpoint]); + bType= USBEndpointDescriptor_CONTROL; + bEndpointDir = 0; + pEndpoint->size = CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0); + } + else { + + bEndpoint = USBEndpointDescriptor_GetNumber(pDescriptor); + pEndpoint = &(endpoints[bEndpoint]); + bType = USBEndpointDescriptor_GetType(pDescriptor); + bEndpointDir = USBEndpointDescriptor_GetDirection(pDescriptor); + pEndpoint->size = USBEndpointDescriptor_GetMaxPacketSize(pDescriptor); + } + + // Abort the current transfer is the endpoint was configured and in + // Write or Read state + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) + || (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) + || (pEndpoint->state == UDP_ENDPOINT_SENDINGM)) { + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_RESET); + } + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Reset Endpoint Fifos + AT91C_BASE_UDP->UDP_RSTEP |= (1 << bEndpoint); + AT91C_BASE_UDP->UDP_RSTEP &= ~(1 << bEndpoint); + + // Configure endpoint + SET_CSR(bEndpoint, (unsigned int)AT91C_UDP_EPEDS + | (bType << 8) | (bEndpointDir << 10)); + if (bType != USBEndpointDescriptor_CONTROL) { + + } + else { + + AT91C_BASE_UDP->UDP_IER = (1 << bEndpoint); + } + + TRACE_INFO_WP("CfgEpt%d ", bEndpoint); +} + +//------------------------------------------------------------------------------ +/// Sends data through a USB endpoint. Sets up the transfer descriptor, +/// writes one or two data payloads (depending on the number of FIFO bank +/// for the endpoint) and then starts the actual transfer. The operation is +/// complete when all the data has been sent. +/// +/// *If the size of the buffer is greater than the size of the endpoint +/// (or twice the size if the endpoint has two FIFO banks), then the buffer +/// must be kept allocated until the transfer is finished*. This means that +/// it is not possible to declare it on the stack (i.e. as a local variable +/// of a function which returns after starting a transfer). +/// +/// \param bEndpoint Endpoint number. +/// \param pData Pointer to a buffer with the data to send. +/// \param dLength Size of the data buffer. +/// \param fCallback Optional callback function to invoke when the transfer is +/// complete. +/// \param pArgument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer has been started; +/// otherwise, the corresponding error status code. +//------------------------------------------------------------------------------ +char USBD_Write( unsigned char bEndpoint, + const void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Check that the endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength); + + // Setup the transfer descriptor + pTransfer->pData = (void *) pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Send the first packet + pEndpoint->state = UDP_ENDPOINT_SENDING; + while((AT91C_BASE_UDP->UDP_CSR[bEndpoint]&AT91C_UDP_TXPKTRDY)==AT91C_UDP_TXPKTRDY); + UDP_WritePayload(bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + + // If double buffering is enabled and there is data remaining, + // prepare another packet + if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) && (pTransfer->remaining > 0)) { + + UDP_WritePayload(bEndpoint); + } + + // Enable interrupt on endpoint + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Sends data frames through a USB endpoint. Sets up the transfer descriptor +/// list, writes one or two data payloads (depending on the number of FIFO bank +/// for the endpoint) and then starts the actual transfer. The operation is +/// complete when all the data has been sent. +/// +/// *If the size of the frame is greater than the size of the endpoint +/// (or twice the size if the endpoint has two FIFO banks), then the buffer +/// must be kept allocated until the frame is finished*. This means that +/// it is not possible to declare it on the stack (i.e. as a local variable +/// of a function which returns after starting a transfer). +/// +/// \param bEndpoint Endpoint number. +/// \param pMbl Pointer to a frame (USBDTransferBuffer) list that describes +/// the buffer list to send. +/// \param wListSize Size of the frame list. +/// \param bCircList Circle the list. +/// \param wStartNdx For circled list only, the first buffer index to transfer. +/// \param fCallback Optional callback function to invoke when the transfer is +/// complete. +/// \param pArgument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer has been started; +/// otherwise, the corresponding error status code. +/// \see USBDTransferBuffer, MblTransferCallback, USBD_MblReuse +//------------------------------------------------------------------------------ +char USBD_MblWrite( unsigned char bEndpoint, + void *pMbl, + unsigned short wListSize, + unsigned char bCircList, + unsigned short wStartNdx, + MblTransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + unsigned short i; + + // EP0 is not suitable for Mbl + if (bEndpoint == 0) { + + return USBD_STATUS_INVALID_PARAMETER; + } + + // Check that the endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + pEndpoint->state = UDP_ENDPOINT_SENDINGM; + + TRACE_DEBUG_WP("WriteM%d(0x%x,%d) ", bEndpoint, pMbl, wListSize); + + // Start from first if not circled list + if (!bCircList) wStartNdx = 0; + + // Setup the transfer descriptor + pTransfer->pMbl = (USBDTransferBuffer*)pMbl; + pTransfer->listSize = wListSize; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + pTransfer->currBuffer = wStartNdx; + pTransfer->freedBuffer = 0; + pTransfer->pLastLoaded = &(((USBDTransferBuffer*)pMbl)[wStartNdx]); + pTransfer->circList = bCircList; + pTransfer->allUsed = 0; + + // Clear all buffer + for (i = 0; i < wListSize; i ++) { + + pTransfer->pMbl[i].transferred = 0; + pTransfer->pMbl[i].buffered = 0; + pTransfer->pMbl[i].remaining = pTransfer->pMbl[i].size; + } + + // Send the first packet + while((AT91C_BASE_UDP->UDP_CSR[bEndpoint]&AT91C_UDP_TXPKTRDY) + ==AT91C_UDP_TXPKTRDY); + UDP_MblWriteFifo(bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + + // If double buffering is enabled and there is data remaining, + // prepare another packet + if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) + && (pTransfer->pMbl[pTransfer->currBuffer].remaining > 0)) { + + UDP_MblWriteFifo(bEndpoint); + } + + // Enable interrupt on endpoint + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reads incoming data on an USB endpoint This methods sets the transfer +/// descriptor and activate the endpoint interrupt. The actual transfer is +/// then carried out by the endpoint interrupt handler. The Read operation +/// finishes either when the buffer is full, or a short packet (inferior to +/// endpoint maximum size) is received. +/// +/// *The buffer must be kept allocated until the transfer is finished*. +/// \param bEndpoint Endpoint number. +/// \param pData Pointer to a data buffer. +/// \param dLength Size of the data buffer in bytes. +/// \param fCallback Optional end-of-transfer callback function. +/// \param pArgument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the read operation has been started; +/// otherwise, the corresponding error code. +//------------------------------------------------------------------------------ +char USBD_Read(unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + // Endpoint enters Receiving state + pEndpoint->state = UDP_ENDPOINT_RECEIVING; + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Set the transfer descriptor + pTransfer->pData = pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Enable interrupt on endpoint + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reuse first used/released buffer with new buffer address and size to be used +/// in transfer again. Only valid when frame list is ringed. Can be used for +/// both read & write. +/// \param bEndpoint Endpoint number. +/// \param pNewBuffer Pointer to new buffer with data to send (0 to keep last). +/// \param wNewSize Size of the data buffer +//------------------------------------------------------------------------------ +char USBD_MblReuse( unsigned char bEndpoint, + unsigned char *pNewBuffer, + unsigned short wNewSize ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->freedBuffer]); + + TRACE_DEBUG_WP("MblReuse(%d), st%x, circ%d\n\r", + bEndpoint, pEndpoint->state, pTransfer->circList); + + // Only for Multi-buffer-circle list + if (bEndpoint != 0 + && (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM + || pEndpoint->state == UDP_ENDPOINT_SENDINGM) + && pTransfer->circList) { + } + else { + + return USBD_STATUS_WRONG_STATE; + } + + // Check if there is freed buffer + if (pTransfer->freedBuffer == pTransfer->currBuffer + && !pTransfer->allUsed) { + + return USBD_STATUS_LOCKED; + } + + // Update transfer information + if ((++ pTransfer->freedBuffer) == pTransfer->listSize) + pTransfer->freedBuffer = 0; + if (pNewBuffer) { + pBi->pBuffer = pNewBuffer; + pBi->size = wNewSize; + } + pBi->buffered = 0; + pBi->transferred = 0; + pBi->remaining = pBi->size; + + // At least one buffer is not processed + pTransfer->allUsed = 0; + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Sets the HALT feature on the given endpoint (if not already in this state). +/// \param bEndpoint Endpoint number. +//------------------------------------------------------------------------------ +void USBD_Halt(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is enabled and not already in Halt state + if ((pEndpoint->state != UDP_ENDPOINT_DISABLED) + && (pEndpoint->state != UDP_ENDPOINT_HALTED)) { + + TRACE_DEBUG_WP("Halt%d ", bEndpoint); + + // Abort the current transfer if necessary + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + + // Put endpoint into Halt state + SET_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + pEndpoint->state = UDP_ENDPOINT_HALTED; + + // Enable the endpoint interrupt + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + } +} + +//------------------------------------------------------------------------------ +/// Clears the Halt feature on the given endpoint. +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +void USBD_Unhalt(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check if the endpoint is enabled + //if (pEndpoint->state != UDP_ENDPOINT_DISABLED) { + if (pEndpoint->state == UDP_ENDPOINT_HALTED) { + + TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); + + // Return endpoint to Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Clear FORCESTALL flag + CLEAR_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + + // Reset Endpoint Fifos, beware this is a 2 steps operation + AT91C_BASE_UDP->UDP_RSTEP |= 1 << bEndpoint; + AT91C_BASE_UDP->UDP_RSTEP &= ~(1 << bEndpoint); + } +} + +//------------------------------------------------------------------------------ +/// Returns the current Halt status of an endpoint. +/// \param bEndpoint Index of endpoint +/// \return 1 if the endpoint is currently halted; otherwise 0 +//------------------------------------------------------------------------------ +unsigned char USBD_IsHalted(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + unsigned char status = 0; + + if (pEndpoint->state == UDP_ENDPOINT_HALTED) { + + status = 1; + } + return( status ); +} + +//------------------------------------------------------------------------------ +/// Indicates if the device is running in high or full-speed. Always returns 0 +/// since UDP does not support high-speed mode. +//------------------------------------------------------------------------------ +unsigned char USBD_IsHighSpeed(void) +{ + return 0; +} + +//------------------------------------------------------------------------------ +/// Causes the given endpoint to acknowledge the next packet it receives +/// with a STALL handshake. +/// \param bEndpoint Endpoint number. +/// \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED. +//------------------------------------------------------------------------------ +unsigned char USBD_Stall(unsigned char bEndpoint) + +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint); + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Stall%d ", bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Starts a remote wake-up procedure. +//------------------------------------------------------------------------------ +void USBD_RemoteWakeUp(void) +{ + // Device is NOT suspended + if (deviceState != USBD_STATE_SUSPENDED) { + + TRACE_WARNING("USBD_RemoteWakeUp: Device is not suspended\n\r"); + return; + } + + + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + UDP_EnableTransceiver(); + + TRACE_INFO_WP("RWUp "); + + // Activates a remote wakeup (edge on ESR), then clear ESR + AT91C_BASE_UDP->UDP_GLBSTATE |= AT91C_UDP_ESR; + AT91C_BASE_UDP->UDP_GLBSTATE &= ~AT91C_UDP_ESR; +} + +//------------------------------------------------------------------------------ +/// Sets the device address to the given value. +/// \param address New device address. +//------------------------------------------------------------------------------ +void USBD_SetAddress(unsigned char address) +{ + TRACE_INFO_WP("SetAddr(%d) ", address); + + // Set address + AT91C_BASE_UDP->UDP_FADDR = AT91C_UDP_FEN | address; + + // If the address is 0, the device returns to the Default state + if (address == 0) { + + AT91C_BASE_UDP->UDP_GLBSTATE = 0; + deviceState = USBD_STATE_DEFAULT; + } + // If the address is non-zero, the device enters the Address state + else { + + AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_FADDEN; + deviceState = USBD_STATE_ADDRESS; + } +} + +//------------------------------------------------------------------------------ +/// Sets the current device configuration. +/// \param cfgnum - Configuration number to set. +//------------------------------------------------------------------------------ +void USBD_SetConfiguration(unsigned char cfgnum) +{ + TRACE_INFO_WP("SetCfg(%d) ", cfgnum); + + // If the configuration number if non-zero, the device enters the + // Configured state + if (cfgnum != 0) { + + // Enter Configured state + deviceState = USBD_STATE_CONFIGURED; + AT91C_BASE_UDP->UDP_GLBSTATE |= AT91C_UDP_CONFG; + } + // If the configuration number is zero, the device goes back to the Address + // state + else { + + deviceState = USBD_STATE_ADDRESS; + AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_FADDEN; + + // Abort all transfers + UDP_DisableEndpoints(); + } +} + +//------------------------------------------------------------------------------ +/// Connects the pull-up on the D+ line of the USB. +//------------------------------------------------------------------------------ +void USBD_Connect(void) +{ + TRACE_DEBUG("Conn "); + +#if defined(CHIP_USB_PULLUP_EXTERNAL) + const Pin pinPullUp = PIN_USB_PULLUP; + if (pinPullUp.type == PIO_OUTPUT_0) { + + PIO_Set(&pinPullUp); + } + else { + + PIO_Clear(&pinPullUp); + } +#elif defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_UDP->UDP_TXVC |= AT91C_UDP_PUON; +#elif defined(CHIP_USB_PULLUP_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR |= AT91C_MATRIX_USBPCR_PUON; +#elif !defined(CHIP_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. +#endif +} + +//------------------------------------------------------------------------------ +/// Disconnects the pull-up from the D+ line of the USB. +//------------------------------------------------------------------------------ +void USBD_Disconnect(void) +{ + TRACE_DEBUG("Disc "); + +#if defined(CHIP_USB_PULLUP_EXTERNAL) + const Pin pinPullUp = PIN_USB_PULLUP; + if (pinPullUp.type == PIO_OUTPUT_0) { + + PIO_Clear(&pinPullUp); + } + else { + + PIO_Set(&pinPullUp); + } +#elif defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_UDP->UDP_TXVC &= ~AT91C_UDP_PUON; +#elif defined(CHIP_USB_PULLUP_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; +#elif !defined(CHIP_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. +#endif + + // Device returns to the Powered state + if (deviceState > USBD_STATE_POWERED) { + + deviceState = USBD_STATE_POWERED; + } + + if (previousDeviceState > USBD_STATE_POWERED) { + + previousDeviceState = USBD_STATE_POWERED; + } +} + +//------------------------------------------------------------------------------ +/// Initializes the USB driver. +//------------------------------------------------------------------------------ +void USBD_Init(void) +{ + TRACE_INFO_WP("USBD_Init\n\r"); + + // Reset endpoint structures + UDP_ResetEndpoints(); + + // Configure the pull-up on D+ and disconnect it +#if defined(CHIP_USB_PULLUP_EXTERNAL) + const Pin pinPullUp = PIN_USB_PULLUP; + PIO_Configure(&pinPullUp, 1); +#elif defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_UDP->UDP_TXVC &= ~AT91C_UDP_PUON; +#elif defined(CHIP_USB_PULLUP_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; +#elif !defined(CHIP_USB_PULLUP_ALWAYSON) + #error Missing pull-up definition. +#endif + + // Device is in the Attached state + deviceState = USBD_STATE_SUSPENDED; + previousDeviceState = USBD_STATE_POWERED; + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + + AT91C_BASE_UDP->UDP_IDR = 0xFE; + + AT91C_BASE_UDP->UDP_IER = AT91C_UDP_WAKEUP; + + // Configure interrupts + USBDCallbacks_Initialized(); +} + +//------------------------------------------------------------------------------ +/// Configure USB Speed, should be invoked before USB attachment. +/// \param forceFS Force to use FS mode. +//------------------------------------------------------------------------------ +void USBD_ConfigureSpeed(unsigned char forceFS) +{ +} + +//------------------------------------------------------------------------------ +/// Returns the current state of the USB device. +/// \return Device current state. +//------------------------------------------------------------------------------ +unsigned char USBD_GetState(void) +{ + return deviceState; +} + diff --git a/usb/device/core/USBD_UDPHS.c b/usb/device/core/USBD_UDPHS.c new file mode 100644 index 0000000..f02fa55 --- /dev/null +++ b/usb/device/core/USBD_UDPHS.c @@ -0,0 +1,2384 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBD.h" +#include "USBDCallbacks.h" +#include "USBDDriver.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Maximum number of endpoints interrupts. +#define NUM_IT_MAX \ + (AT91C_BASE_UDPHS->UDPHS_IPFEATURES & AT91C_UDPHS_EPT_NBR_MAX) +/// Maximum number of endpoint DMA interrupts +#define NUM_IT_MAX_DMA \ + ((AT91C_BASE_UDPHS->UDPHS_IPFEATURES & AT91C_UDPHS_DMA_CHANNEL_NBR)>>4) +/// Bits that should be shifted to access DMA control bits. +#define SHIFT_DMA 24 +/// Bits that should be shifted to access interrupt bits. +#define SHIFT_INTERUPT 8 + +/// Max size of the FMA FIFO +#define DMA_MAX_FIFO_SIZE 65536 + +#define EPT_VIRTUAL_SIZE 16384 + +//------------------------------------------------------------------------------ +/// \page "Endpoint states" +/// This page lists the endpoint states. +/// !States +// - UDP_ENDPOINT_DISABLED +// - UDP_ENDPOINT_HALTED +// - UDP_ENDPOINT_IDLE +// - UDP_ENDPOINT_SENDING +// - UDP_ENDPOINT_RECEIVING + +/// Endpoint states: Endpoint is disabled +#define UDP_ENDPOINT_DISABLED 0 +/// Endpoint states: Endpoint is halted (i.e. STALLs every request) +#define UDP_ENDPOINT_HALTED 1 +/// Endpoint states: Endpoint is idle (i.e. ready for transmission) +#define UDP_ENDPOINT_IDLE 2 +/// Endpoint states: Endpoint is sending data +#define UDP_ENDPOINT_SENDING 3 +/// Endpoint states: Endpoint is receiving data +#define UDP_ENDPOINT_RECEIVING 4 +/// Endpoint states: Endpoint is sending MBL +#define UDP_ENDPOINT_SENDINGM 5 +/// Endpoint states: Endpoint is receiving MBL +#define UDP_ENDPOINT_RECEIVINGM 6 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Check if DMA is used to transfer data +#define UDP_IsDmaUsed(ep) (CHIP_USB_ENDPOINTS_DMA(ep)) + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +/// Describes an ongoing transfer on a UDP endpoint. +typedef struct +{ + /// Pointer to a data buffer used for emission/reception. + char *pData; + /// Number of bytes which have been written into the UDP internal FIFO + /// buffers. + int buffered; + /// Number of bytes which have been sent/received. + int transferred; + /// Number of bytes which have not been buffered/transferred yet. + int remaining; + /// Optional callback to invoke when the transfer completes. + TransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} Transfer; + +/// Describes Multi Buffer List transfer on a UDP endpoint. +typedef struct { + /// Pointer to frame list + USBDTransferBuffer *pMbl; + /// Pointer to last loaded buffer + USBDTransferBuffer *pLastLoaded; + /// List size + unsigned short listSize; + /// Current processing frame + unsigned short currBuffer; + /// First freed frame to re-use + unsigned short freedBuffer; + /// Frame setting, circle frame list + unsigned char circList; + /// All buffer listed is used + unsigned char allUsed; + /// Optional callback to invoke when the transfer completes. + MblTransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} MblTransfer; + +/// Describes DMA Link List transfer on a UDPHS endpoint. +typedef struct { + /// Pointer to frame list + USBDDmaDescriptor *pMbl; + /// Pointer to current buffer + USBDDmaDescriptor *pCurrBuffer; + /// Pointer to freed buffer + USBDDmaDescriptor *pFreedBuffer; + /// List size + unsigned short listSize; + /// Frame setting, circle frame list + unsigned char circList; + /// All buffer listed is used + unsigned char allUsed; + /// Optional callback to invoke when the transfer completes. + MblTransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} DmaLlTransfer; + +//------------------------------------------------------------------------------ +/// Describes the state of an endpoint of the UDP controller. +//------------------------------------------------------------------------------ +typedef struct +{ + /// Current endpoint state. + volatile unsigned char state; + /// Current reception bank (0 or 1). + unsigned char bank; + /// Maximum packet size for the endpoint. + unsigned short size; + /// Describes an ongoing transfer (if current state is either + /// or ) + union { + Transfer singleTransfer; + MblTransfer mblTransfer; + DmaLlTransfer dmaTransfer; + } transfer; + /// Special case for send a ZLP + unsigned char sendZLP; +} Endpoint; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Holds the internal state for each endpoint of the UDP. +static Endpoint endpoints[CHIP_USB_NUMENDPOINTS]; +/// Device current state. +static unsigned char deviceState; +/// Indicates the previous device state +static unsigned char previousDeviceState; + +/// 7.1.20 Test Mode Support +/// Test codes for the USB HS test mode. +static const char test_packet_buffer[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // JKJKJKJK * 9 + 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, // JJKKJJKK * 8 + 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, // JJJJKKKK * 8 + 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // JJJJJJJKKKKKKK * 8 + 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD, // JJJJJJJK * 8 + 0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E // {JKKKKKKK * 10}, JK +}; + +// Force HS +static const unsigned char forceUsbFS = 0; + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Disables the BIAS of the USB controller +//------------------------------------------------------------------------------ +static inline void UDPHS_DisableBIAS( void ) +{ + AT91C_BASE_PMC->PMC_UCKR &= ~(unsigned int)AT91C_CKGR_BIASEN_ENABLED; +} + +//------------------------------------------------------------------------------ +/// Enables the BIAS of the USB controller +//------------------------------------------------------------------------------ +static inline void UDPHS_EnableBIAS( void ) +{ + UDPHS_DisableBIAS(); + AT91C_BASE_PMC->PMC_UCKR |= AT91C_CKGR_BIASEN_ENABLED; +} + +//------------------------------------------------------------------------------ +/// Enable UDPHS Peripheral +//------------------------------------------------------------------------------ +static inline void UDPHS_EnablePeripheral( void ) +{ +#if !defined (PMC_BY_HARD) + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDPHS); +#endif +} + +//------------------------------------------------------------------------------ +/// Enable UDPHS clock +//------------------------------------------------------------------------------ +static inline void UDPHS_EnableUsbClock( void ) +{ +#if !defined (PMC_BY_HARD) + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDPHS); + // Enable 480MHZ + //AT91C_BASE_CKGR->CKGR_UCKR |= (AT91C_CKGR_PLLCOUNT & (3 << 20)) | AT91C_CKGR_UPLLEN; + AT91C_BASE_CKGR->CKGR_UCKR |= ((0xf << 20) & (3 << 20)) | AT91C_CKGR_UPLLEN; + // Wait until UTMI PLL is locked + while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCKU) == 0); +#endif +} + +//------------------------------------------------------------------------------ +/// Disable UDPHS clock +//------------------------------------------------------------------------------ +static inline void UDPHS_DisableUsbClock( void ) +{ +#if !defined (PMC_BY_HARD) + AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_UDPHS); + // 480MHZ + AT91C_BASE_CKGR->CKGR_UCKR &= ~(unsigned int)AT91C_CKGR_UPLLEN; +#endif +} + +//------------------------------------------------------------------------------ +/// Handles a completed transfer on the given endpoint, invoking the +/// configured callback if any. +/// \param bEndpoint Number of the endpoint for which the transfer has completed. +/// \param bStatus Status code returned by the transfer operation +//------------------------------------------------------------------------------ +static void UDPHS_EndOfTransfer( unsigned char bEndpoint, char bStatus ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Check that endpoint was sending or receiving data + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) { + + TRACE_DEBUG_WP("Eo"); + if(pEndpoint->state == UDP_ENDPOINT_SENDING) { + pEndpoint->sendZLP = 0; + } + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Invoke callback is present + if (pTransfer->fCallback != 0) { + + ((TransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + bStatus, + pTransfer->transferred, + pTransfer->remaining + pTransfer->buffered); + } + else { + TRACE_DEBUG_WP("No callBack\n\r"); + } + } + else if ( (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) + || (pEndpoint->state == UDP_ENDPOINT_SENDINGM) ) { + + MblTransfer* pMblt = (MblTransfer*)&(pEndpoint->transfer); + MblTransferCallback fCallback; + void* pArg; + + TRACE_DEBUG_WP("EoMT "); + + #ifdef DMA + { DmaLlTransfer *pDmat = (DmaLlTransfer*)&(pEndpoint->transfer); + fCallback = UDP_IsDmaUsed(bEndpoint) ? + pDmat->fCallback : pMblt->fCallback; + pArg = UDP_IsDmaUsed(bEndpoint) ? + pDmat->pArgument : pMblt->pArgument; + } + #else + fCallback = pMblt->fCallback; + pArg = pMblt->pArgument; + #endif + + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + // Invoke callback + if (fCallback != 0) { + + (fCallback)(pArg, bStatus, 0); + } + else { + TRACE_DEBUG_WP("NoCB "); + } + } +} + +//------------------------------------------------------------------------------ +/// Clears the correct RX flag in endpoint status register +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDPHS_ClearRxFlag( unsigned char bEndpoint ) +{ + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_RX_BK_RDY; +} + +//------------------------------------------------------------------------------ +/// Update multi-buffer-transfer descriptors. +/// \param pTransfer Pointer to instance MblTransfer. +/// \param size Size of bytes that processed. +/// \param forceEnd Force the buffer END. +/// \return 1 if current buffer ended. +//------------------------------------------------------------------------------ +static char UDPHS_MblUpdate(MblTransfer *pTransfer, + USBDTransferBuffer * pBi, + unsigned short size, + unsigned char forceEnd) +{ + // Update transfer descriptor + pBi->remaining -= size; + // Check if current buffer ended + if (pBi->remaining == 0 || forceEnd) { + + // Process to next buffer + if ((++ pTransfer->currBuffer) == pTransfer->listSize) { + if (pTransfer->circList) { + pTransfer->currBuffer = 0; + } + else { + pTransfer->allUsed = 1; + } + } + // All buffer in the list is processed + if (pTransfer->currBuffer == pTransfer->freedBuffer) { + pTransfer->allUsed = 1; + } + // Continue transfer, prepare for next operation + if (pTransfer->allUsed == 0) { + pBi = &pTransfer->pMbl[pTransfer->currBuffer]; + pBi->buffered = 0; + pBi->transferred = 0; + pBi->remaining = pBi->size; + } + return 1; + } + return 0; +} +/* +void UDPHS_WriteFifo(volatile char* pFifo, + unsigned char* pBuffer, + unsigned int size) +{ + register unsigned int c8 = size >> 3; + register unsigned int c1 = size & 0x7; + for (; c8; c8 --) { + *pFifo = *(pBuffer ++); + *pFifo = *(pBuffer ++); + *pFifo = *(pBuffer ++); + *pFifo = *(pBuffer ++); + + *pFifo = *(pBuffer ++); + *pFifo = *(pBuffer ++); + *pFifo = *(pBuffer ++); + *pFifo = *(pBuffer ++); + } + for (; c1; c1 --) { + *pFifo = *(pBuffer ++); + } +} +*/ +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static char UDPHS_MblWriteFifo(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->currBuffer]); + volatile char *pFifo; + signed int size; + + volatile unsigned char * pBytes; + volatile char bufferEnd = 1; + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pBi->remaining) { + + size = pBi->remaining; + } + + TRACE_DEBUG_WP("w%d.%d ", pTransfer->currBuffer, size); + if (size == 0) { + + return 1; + } + + pTransfer->pLastLoaded = pBi; + pBytes = &(pBi->pBuffer[pBi->transferred + pBi->buffered]); + pBi->buffered += size; + bufferEnd = UDPHS_MblUpdate(pTransfer, pBi, size, 0); + + // Write packet in the FIFO buffer + pFifo = (char*)((unsigned int *)AT91C_BASE_UDPHS_EPTFIFO + + (EPT_VIRTUAL_SIZE * bEndpoint)); + if (size) { + signed int c8 = size >> 3; + signed int c1 = size & 0x7; + //printf("%d[%x] ", pBi->transferred, pBytes); + for (; c8; c8 --) { + *(pFifo++) = *(pBytes ++); + *(pFifo++) = *(pBytes ++); + *(pFifo++) = *(pBytes ++); + *(pFifo++) = *(pBytes ++); + + *(pFifo++) = *(pBytes ++); + *(pFifo++) = *(pBytes ++); + *(pFifo++) = *(pBytes ++); + *(pFifo++) = *(pBytes ++); + } + for (; c1; c1 --) { + *(pFifo++) = *(pBytes ++); + } + } + return bufferEnd; +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static void UDPHS_WritePayload( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + char *pFifo; + signed int size; + unsigned int dCtr; + + pFifo = (char*)((unsigned int *)AT91C_BASE_UDPHS_EPTFIFO + (EPT_VIRTUAL_SIZE * bEndpoint)); + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pTransfer->remaining) { + + size = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->buffered += size; + pTransfer->remaining -= size; + + // Write packet in the FIFO buffer + dCtr = 0; + while (size > 0) { + + pFifo[dCtr] = *(pTransfer->pData); + pTransfer->pData++; + size--; + dCtr++; + } +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from an endpoint FIFO to the current transfer buffer +/// \param bEndpoint Endpoint number. +/// \param wPacketSize Size of received data packet +//------------------------------------------------------------------------------ +static void UDPHS_ReadPayload( unsigned char bEndpoint, int wPacketSize ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + char *pFifo; + unsigned char dBytes=0; + + pFifo = (char*)((unsigned int *)AT91C_BASE_UDPHS_EPTFIFO + (EPT_VIRTUAL_SIZE * bEndpoint)); + + // Check that the requested size is not bigger than the remaining transfer + if (wPacketSize > pTransfer->remaining) { + + pTransfer->buffered += wPacketSize - pTransfer->remaining; + wPacketSize = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->remaining -= wPacketSize; + pTransfer->transferred += wPacketSize; + + // Retrieve packet + while (wPacketSize > 0) { + + *(pTransfer->pData) = pFifo[dBytes]; + pTransfer->pData++; + wPacketSize--; + dBytes++; + } +} + + +//------------------------------------------------------------------------------ +/// Received SETUP packet from endpoint 0 FIFO +/// \param pRequest Generic USB SETUP request sent over Control endpoints +//------------------------------------------------------------------------------ +static void UDPHS_ReadRequest( USBGenericRequest *pRequest ) +{ + unsigned int *pData = (unsigned int *)pRequest; + unsigned int fifo; + + fifo = (AT91C_BASE_UDPHS_EPTFIFO->UDPHS_READEPT0[0]); + *pData = fifo; + fifo = (AT91C_BASE_UDPHS_EPTFIFO->UDPHS_READEPT0[0]); + pData++; + *pData = fifo; + //TRACE_ERROR("SETUP: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n\r", pData[0],pData[1],pData[2],pData[3],pData[4],pData[5],pData[6],pData[7]); +} + +//------------------------------------------------------------------------------ +/// Reset all endpoint transfer descriptors +//------------------------------------------------------------------------------ +static void UDPHS_ResetEndpoints( void ) +{ + Endpoint *pEndpoint; + Transfer *pTransfer; + unsigned char bEndpoint; + + // Reset the transfer descriptor of every endpoint + for( bEndpoint = 0; bEndpoint < CHIP_USB_NUMENDPOINTS; bEndpoint++ ) { + + pEndpoint = &(endpoints[bEndpoint]); + pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Reset endpoint transfer descriptor + pTransfer->pData = 0; + pTransfer->transferred = -1; + pTransfer->buffered = -1; + pTransfer->remaining = -1; + pTransfer->fCallback = 0; + pTransfer->pArgument = 0; + + // Reset endpoint state + pEndpoint->bank = 0; + pEndpoint->state = UDP_ENDPOINT_DISABLED; + // Reset ZLP + pEndpoint->sendZLP = 0; + } +} + + +//------------------------------------------------------------------------------ +/// Disable all endpoints (except control endpoint 0), aborting current +/// transfers if necessary +//------------------------------------------------------------------------------ +static void UDPHS_DisableEndpoints( void ) +{ + unsigned char bEndpoint; + + // Disable each endpoint, terminating any pending transfer + // Control endpoint 0 is not disabled + for( bEndpoint = 1; bEndpoint < CHIP_USB_NUMENDPOINTS; bEndpoint++ ) { + + UDPHS_EndOfTransfer( bEndpoint, USBD_STATUS_ABORTED ); + endpoints[bEndpoint].state = UDP_ENDPOINT_DISABLED; + } +} + +//------------------------------------------------------------------------------ +/// Endpoint interrupt handler. +/// Handle IN/OUT transfers, received SETUP packets and STALLing +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDPHS_EndpointHandler( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + MblTransfer *pMblt = (MblTransfer*)&(pEndpoint->transfer); + unsigned int status = AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSTA; + unsigned short wPacketSize; + USBGenericRequest request; + + TRACE_DEBUG_WP("E%d ", bEndpoint); + TRACE_DEBUG_WP("st:0x%X ", status); + + // Handle interrupts + // IN packet sent + if( (AT91C_UDPHS_TX_PK_RDY == (AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTL & AT91C_UDPHS_TX_PK_RDY)) + && (0 == (status & AT91C_UDPHS_TX_PK_RDY )) ) { + + TRACE_DEBUG_WP("Wr "); + + // Check that endpoint was sending multi-buffer-list + if( pEndpoint->state == UDP_ENDPOINT_SENDINGM ) { + + USBDTransferBuffer * pMbli = pMblt->pLastLoaded; + + TRACE_DEBUG_WP("TxM%d.%d,%d ", + pMblt->allUsed, pMbli->buffered, pMbli->remaining); + + // End of transfer ? + if (pMblt->allUsed && pMbli->remaining == 0) { + + pMbli->transferred += pMbli->buffered; + pMbli->buffered = 0; + + // Disable interrupt + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = + AT91C_UDPHS_TX_PK_RDY; + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + else { + + // Transfer remaining data + TRACE_DEBUG_WP("%d ", pEndpoint->size); + + if (pMbli->buffered > pEndpoint->size) { + pMbli->transferred += pEndpoint->size; + pMbli->buffered -= pEndpoint->size; + } + else { + pMbli->transferred += pMbli->buffered; + pMbli->buffered = 0; + } + + // Send next packet + UDPHS_MblWriteFifo(bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = + AT91C_UDPHS_TX_PK_RDY; + + if (pMbli->remaining == 0 && pMbli->buffered == 0) { + + if (pMblt->fCallback) { + ((MblTransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + USBD_STATUS_PARTIAL_DONE, + 1); + } + } + } + } + + // Check that endpoint was in Sending state + if( pEndpoint->state == UDP_ENDPOINT_SENDING ) { + + if (pTransfer->buffered > 0) { + pTransfer->transferred += pTransfer->buffered; + pTransfer->buffered = 0; + } + + if( ((pTransfer->buffered)==0) + &&((pTransfer->transferred)==0) + &&((pTransfer->remaining)==0) + &&(pEndpoint->sendZLP == 0)) { + pEndpoint->sendZLP = 1; + } + + // End of transfer ? + if( (pTransfer->remaining > 0) + ||(pEndpoint->sendZLP == 1)) { + + pEndpoint->sendZLP = 2; + TRACE_DEBUG_WP("\n\r1pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("1pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("1pTransfer->remaining %d \n\r", pTransfer->remaining); + + // Transfer remaining data + TRACE_DEBUG_WP(" %d ", pEndpoint->size); + + // Send next packet + UDPHS_WritePayload(bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + } + else { + TRACE_DEBUG_WP("\n\r0pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("0pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("0pTransfer->remaining %d \n\r", pTransfer->remaining); + + TRACE_DEBUG_WP(" %d ", pTransfer->transferred); + + // Disable interrupt if this is not a control endpoint + if( AT91C_UDPHS_EPT_TYPE_CTL_EPT != (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG)) ) { + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = AT91C_UDPHS_TX_PK_RDY; + + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + pEndpoint->sendZLP = 0; + } + } + else { + + TRACE_DEBUG("Error Wr %d", pEndpoint->sendZLP); + } + } + + // OUT packet received + if( AT91C_UDPHS_RX_BK_RDY == (status & AT91C_UDPHS_RX_BK_RDY) ) { + + TRACE_DEBUG_WP("Rd "); + + // Check that the endpoint is in Receiving state + if (pEndpoint->state != UDP_ENDPOINT_RECEIVING) { + + // Check if an ACK has been received on a Control endpoint + if( (0 == (AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG & AT91C_UDPHS_EPT_TYPE)) + && (0 == (status & AT91C_UDPHS_BYTE_COUNT)) ) { + + // Control endpoint, 0 bytes received + // Acknowledge the data and finish the current transfer + TRACE_DEBUG_WP("Ack "); + UDPHS_ClearRxFlag(bEndpoint); + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + //todo remove endoftranfer and test + } + // Check if the data has been STALLed + else if( AT91C_UDPHS_FRCESTALL == (status & AT91C_UDPHS_FRCESTALL)) { + + // Discard STALLed data + TRACE_DEBUG_WP("Discard "); + UDPHS_ClearRxFlag(bEndpoint); + } + // NAK the data + else { + + TRACE_DEBUG_WP("Nak "); + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<>20); + + TRACE_DEBUG_WP("%d ", wPacketSize); + UDPHS_ReadPayload(bEndpoint, wPacketSize); + UDPHS_ClearRxFlag(bEndpoint); + + // Check if the transfer is finished + if ((pTransfer->remaining == 0) || (wPacketSize < pEndpoint->size)) { + + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = AT91C_UDPHS_RX_BK_RDY; + + // Disable interrupt if this is not a control endpoint + if( AT91C_UDPHS_EPT_TYPE_CTL_EPT != (AT91C_UDPHS_EPT_TYPE & (AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG)) ) { + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_STALL_SNT; + + // If the endpoint is not halted, clear the STALL condition + if (pEndpoint->state != UDP_ENDPOINT_HALTED) { + + TRACE_WARNING( "_ " ); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_FRCESTALL; + } + } + + // SETUP packet received + if( AT91C_UDPHS_RX_SETUP == (status & AT91C_UDPHS_RX_SETUP) ) { + + TRACE_DEBUG_WP("Stp "); + + // If a transfer was pending, complete it + // Handles the case where during the status phase of a control write + // transfer, the host receives the device ZLP and ack it, but the ack + // is not received by the device + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Copy the setup packet + UDPHS_ReadRequest(&request); + + // Acknowledge setup packet + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_RX_SETUP; + + // Forward the request to the upper layer + USBDCallbacks_RequestReceived(&request); + } + +} + +//------------------------------------------------------------------------------ +// Interrupt service routine +//------------------------------------------------------------------------------ +#ifdef DMA +//---------------------------------------------------------------------------- +/// Endpoint DMA interrupt handler. +/// This function (ISR) handles dma interrupts +/// \param bEndpoint Index of endpoint +//---------------------------------------------------------------------------- +static void UDPHS_DmaHandler( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + int justTransferred; + unsigned int status; + unsigned char result = USBD_STATUS_SUCCESS; + + status = AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + TRACE_DEBUG_WP("DmaE%d,%x ", bEndpoint, status); + + if (pEndpoint->state == UDP_ENDPOINT_SENDINGM) { + + DmaLlTransfer *pDmat = (DmaLlTransfer*)&(pEndpoint->transfer); + USBDDmaDescriptor *pDi = pDmat->pCurrBuffer; + USBDDmaDescriptor *pNi = (USBDDmaDescriptor*)AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMANXTDSC; + unsigned char bufCnt = 0; + + if (AT91C_UDPHS_END_BF_ST == (status & AT91C_UDPHS_END_BF_ST)) { + + // Stop loading descriptors + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL &= + ~((unsigned int)AT91C_UDPHS_LDNXT_DSC); + + //printf("%x,%x|", + // (char)status, + // (short)AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMANXTDSC); + + while( pDi->pNxtDesc != pNi ) { + + // All descriptor processed + if (pDi == 0) break; + if (pDi->used) break; + + // Current descriptor processed + /* + pDi->ctrlSettings = 0 + //|AT91C_UDPHS_CHANN_ENB + //| AT91C_UDPHS_LDNXT_DSC + //| AT91C_UDPHS_END_TR_EN + //| AT91C_UDPHS_END_TR_IT + //| AT91C_UDPHS_END_B_EN + //| AT91C_UDPHS_END_BUFFIT + //| AT91C_UDPHS_DESC_LD_IT + ; */ + pDi->used = 1; // Flag as used + bufCnt ++; + pDi = pDi->pNxtDesc; + } + pDmat->pCurrBuffer = pDi; + + if (bufCnt) { + + if (pDmat->fCallback) { + + pDmat->fCallback(pDmat->pArgument, + ((result == USBD_STATUS_SUCCESS) ? + USBD_STATUS_PARTIAL_DONE : result), + bufCnt); + } + } + + if ( (pNi == 0) + || (pNi->used && pNi) ) { + + // Disable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1 << SHIFT_DMA << bEndpoint); + + TRACE_DEBUG_WP("EoMDT "); + //printf("E:N%x,C%d ", (short)pNi, bufCnt); + + UDPHS_EndOfTransfer(bEndpoint, result); + } + else { + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL |= + (AT91C_UDPHS_LDNXT_DSC); + } + } + else { + + TRACE_ERROR("UDPHS: Dma(0x%X)\n\r", status); + result = USBD_STATUS_ABORTED; + UDPHS_EndOfTransfer(bEndpoint, result); + } + return; + } + else if (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) { + return; + } + + // Disable DMA interrupt to avoid receiving 2 interrupts (B_EN and TR_EN) + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL &= + ~(unsigned int)(AT91C_UDPHS_END_TR_EN | AT91C_UDPHS_END_B_EN); + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1 << SHIFT_DMA << bEndpoint); + + if( AT91C_UDPHS_END_BF_ST == (status & AT91C_UDPHS_END_BF_ST) ) { + + TRACE_DEBUG_WP("EndBuffer "); + + // BUFF_COUNT holds the number of untransmitted bytes. + // BUFF_COUNT is equal to zero in case of good transfer + justTransferred = pTransfer->buffered + - ((status & (unsigned int)AT91C_UDPHS_BUFF_COUNT) >> 16); + pTransfer->transferred += justTransferred; + + pTransfer->buffered = + ((status & (unsigned int)AT91C_UDPHS_BUFF_COUNT) >> 16); + + pTransfer->remaining -= justTransferred; + + TRACE_DEBUG_WP("\n\r1pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("1pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("1pTransfer->remaining %d \n\r", pTransfer->remaining); + + if( (pTransfer->remaining + pTransfer->buffered) > 0 ) { + + // Prepare an other transfer + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + pTransfer->buffered = pTransfer->remaining; + } + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = + (unsigned int)((pTransfer->pData) + (pTransfer->transferred)); + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + // DMA config for receive the good size of buffer, or an error buffer + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT) + | AT91C_UDPHS_END_TR_EN + | AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_CHANN_ENB ); + } + } + else if( AT91C_UDPHS_END_TR_ST == (status & AT91C_UDPHS_END_TR_ST) ) { + + TRACE_DEBUG_WP("EndTransf "); + + pTransfer->transferred = pTransfer->buffered + - ((status & (unsigned int)AT91C_UDPHS_BUFF_COUNT) >> 16); + pTransfer->remaining = 0; + TRACE_DEBUG_WP("\n\r0pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("0pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("0pTransfer->remaining %d \n\r", pTransfer->remaining); + } + else { + + TRACE_ERROR("UDPHS_DmaHandler: Error (0x%08X)\n\r", status); + result = USBD_STATUS_ABORTED; + } + + // Invoke callback + if( pTransfer->remaining == 0 ) { + + TRACE_DEBUG_WP("EOT "); + UDPHS_EndOfTransfer(bEndpoint, result); + } +} +#endif + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USB device interrupt handler +/// Manages device resume, suspend, end of bus reset. +/// Forwards endpoint interrupts to the appropriate handler. +//------------------------------------------------------------------------------ +void USBD_IrqHandler(void) +{ + unsigned int status; + unsigned char numIT; + + if (deviceState >= USBD_STATE_POWERED) { + + LED_Set(USBD_LEDUSB); + } + + // Get interrupts status + status = AT91C_BASE_UDPHS->UDPHS_INTSTA; + status &= AT91C_BASE_UDPHS->UDPHS_IEN; + + // Handle all UDPHS interrupts + TRACE_DEBUG_WP("H"); + while (status != 0) { + + // Start Of Frame (SOF) + if ((status & AT91C_UDPHS_IEN_SOF) != 0) { + + TRACE_DEBUG_WP("SOF "); + + // Invoke the SOF callback + //USB_StartOfFrameCallback(pUsb); + + // Acknowledge interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_IEN_SOF; + status &= ~(unsigned int)AT91C_UDPHS_IEN_SOF; + } + // Suspend + // This interrupt is always treated last (hence the '==') + else if (status == AT91C_UDPHS_DET_SUSPD) { + + TRACE_DEBUG_WP("S"); + + // The device enters the Suspended state + // MCK + UDPCK must be off + // Pull-Up must be connected + // Transceiver must be disabled + + LED_Clear(USBD_LEDUSB); + + UDPHS_DisableBIAS(); + + // Enable wakeup + AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_ENDOFRSM; + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(unsigned int)AT91C_UDPHS_DET_SUSPD; + + // Acknowledge interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_DET_SUSPD | AT91C_UDPHS_WAKE_UP; + previousDeviceState = deviceState; + deviceState = USBD_STATE_SUSPENDED; + UDPHS_DisableUsbClock(); + + // Invoke the Suspend callback + USBDCallbacks_Suspended(); + } + // Resume + else if( ((status & AT91C_UDPHS_WAKE_UP) != 0) // line activity + || ((status & AT91C_UDPHS_ENDOFRSM) != 0)) { // pc wakeup + { + // Invoke the Resume callback + USBDCallbacks_Resumed(); + + TRACE_DEBUG_WP("R"); + + UDPHS_EnableUsbClock(); + UDPHS_EnableBIAS(); + + // The device enters Configured state + // MCK + UDPCK must be on + // Pull-Up must be connected + // Transceiver must be enabled + + deviceState = previousDeviceState; + + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_ENDOFRSM | AT91C_UDPHS_DET_SUSPD; + + AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_ENDOFRSM | AT91C_UDPHS_DET_SUSPD; + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_ENDOFRSM; + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(unsigned int)AT91C_UDPHS_WAKE_UP; + } + } + // End of bus reset + else if ((status & AT91C_UDPHS_ENDRESET) == AT91C_UDPHS_ENDRESET) { + +// TRACE_DEBUG_WP("EoB "); + + // The device enters the Default state + deviceState = USBD_STATE_DEFAULT; + // MCK + UDPCK are already enabled + // Pull-Up is already connected + // Transceiver must be enabled + // Endpoint 0 must be enabled + + UDPHS_ResetEndpoints(); + UDPHS_DisableEndpoints(); + USBD_ConfigureEndpoint(0); + + // Flush and enable the Suspend interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_DET_SUSPD; + + //// Enable the Start Of Frame (SOF) interrupt if needed + //if (pCallbacks->startOfFrame != 0) + //{ + // AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_IEN_SOF; + //} + + // Invoke the Reset callback + USBDCallbacks_Reset(); + + // Acknowledge end of bus reset interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_ENDRESET; + + AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_DET_SUSPD; + } + // Handle upstream resume interrupt + else if (status & AT91C_UDPHS_UPSTR_RES) { + + TRACE_DEBUG_WP("ExtRes "); + + // - Acknowledge the IT + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_UPSTR_RES; + } + // Endpoint interrupts + else { +#ifndef DMA + // Handle endpoint interrupts + for (numIT = 0; numIT < NUM_IT_MAX; numIT++) { + + if ((status & (1 << SHIFT_INTERUPT << numIT)) != 0) { + + UDPHS_EndpointHandler(numIT); + } + } +#else + // Handle endpoint control interrupt + if ((status & (1 << SHIFT_INTERUPT << 0)) != 0) { + + UDPHS_EndpointHandler( 0 ); + } + else { + + numIT = 1; + while((status&(0x7E<UDPHS_INTSTA; + status &= AT91C_BASE_UDPHS->UDPHS_IEN; + + TRACE_DEBUG_WP("\n\r"); + if (status != 0) { + + TRACE_DEBUG_WP(" - "); + } + } + + if (deviceState >= USBD_STATE_POWERED) { + + LED_Clear(USBD_LEDUSB); + } +} + +//------------------------------------------------------------------------------ +/// Configure an endpoint with the provided endpoint descriptor +/// \param pDdescriptor Pointer to the endpoint descriptor +//------------------------------------------------------------------------------ +void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor) +{ + Endpoint *pEndpoint; + unsigned char bEndpoint; + unsigned char bType; + unsigned char bEndpointDir; + //unsigned char bInterval = 0; + unsigned char bNbTrans = 1; + unsigned char bSizeEpt = 0; + unsigned char bHs = ((AT91C_BASE_UDPHS->UDPHS_INTSTA & AT91C_UDPHS_SPEED) + > 0); + + // NULL descriptor -> Control endpoint 0 + if (pDescriptor == 0) { + + bEndpoint = 0; + pEndpoint = &(endpoints[bEndpoint]); + bType = USBEndpointDescriptor_CONTROL; + bEndpointDir = 0; + pEndpoint->size = CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0); + pEndpoint->bank = CHIP_USB_ENDPOINTS_BANKS(0); + } + else { + + // The endpoint number + bEndpoint = USBEndpointDescriptor_GetNumber(pDescriptor); + pEndpoint = &(endpoints[bEndpoint]); + // Transfer type: Control, Isochronous, Bulk, Interrupt + bType = USBEndpointDescriptor_GetType(pDescriptor); + // interval + //bInterval = USBEndpointDescriptor_GetInterval(pDescriptor); + // Direction, ignored for control endpoints + bEndpointDir = USBEndpointDescriptor_GetDirection(pDescriptor); + pEndpoint->size = USBEndpointDescriptor_GetMaxPacketSize(pDescriptor); + pEndpoint->bank = CHIP_USB_ENDPOINTS_BANKS(bEndpoint); + + // Convert descriptor value to EP configuration + if (bHs) { // HS Interval, *125us + + // MPS: Bit12,11 specify NB_TRANS + bNbTrans = ((pEndpoint->size >> 11) & 0x3); + if (bNbTrans == 3) + bNbTrans = 1; + else + bNbTrans ++; + + // Mask, bit 10..0 is the size + pEndpoint->size &= 0x3FF; + } + } + + // Abort the current transfer is the endpoint was configured and in + // Write or Read state + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) + || (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) + || (pEndpoint->state == UDP_ENDPOINT_SENDINGM) ) { + + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_RESET); + } + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Disable endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = (unsigned int)AT91C_UDPHS_SHRT_PCKT + | AT91C_UDPHS_BUSY_BANK + | AT91C_UDPHS_NAK_OUT + | AT91C_UDPHS_NAK_IN + | AT91C_UDPHS_STALL_SNT + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_TX_PK_RDY + | AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_ERR_OVFLW + | AT91C_UDPHS_MDATA_RX + | AT91C_UDPHS_DATAX_RX + | AT91C_UDPHS_NYET_DIS + | AT91C_UDPHS_INTDIS_DMA + | AT91C_UDPHS_AUTO_VALID + | AT91C_UDPHS_EPT_DISABL; + + // Reset Endpoint Fifos + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_TOGGLESQ | AT91C_UDPHS_FRCESTALL; + AT91C_BASE_UDPHS->UDPHS_EPTRST = 1<size <= 8 ) { + bSizeEpt = 0; + } + else if ( pEndpoint->size <= 16 ) { + bSizeEpt = 1; + } + else if ( pEndpoint->size <= 32 ) { + bSizeEpt = 2; + } + else if ( pEndpoint->size <= 64 ) { + bSizeEpt = 3; + } + else if ( pEndpoint->size <= 128 ) { + bSizeEpt = 4; + } + else if ( pEndpoint->size <= 256 ) { + bSizeEpt = 5; + } + else if ( pEndpoint->size <= 512 ) { + bSizeEpt = 6; + } + else if ( pEndpoint->size <= 1024 ) { + bSizeEpt = 7; + } //else { + // sizeEpt = 0; // control endpoint + //} + + // Configure endpoint + if (bType == USBEndpointDescriptor_CONTROL) { + + // Enable endpoint IT for control endpoint + AT91C_BASE_UDPHS->UDPHS_IEN |= (1<UDPHS_EPT[bEndpoint].UDPHS_EPTCFG = bSizeEpt + | ( bEndpointDir << 3) + | ( bType << 4) + | ((pEndpoint->bank) << 6) + | ( bNbTrans << 8) + ; + + while( (signed int)AT91C_UDPHS_EPT_MAPD != (signed int)((AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG) & (unsigned int)AT91C_UDPHS_EPT_MAPD) ) { + + // resolved by clearing the reset IT in good place + TRACE_ERROR("PB bEndpoint: 0x%X\n\r", bEndpoint); + TRACE_ERROR("PB bSizeEpt: 0x%X\n\r", bSizeEpt); + TRACE_ERROR("PB bEndpointDir: 0x%X\n\r", bEndpointDir); + TRACE_ERROR("PB bType: 0x%X\n\r", bType); + TRACE_ERROR("PB pEndpoint->bank: 0x%X\n\r", pEndpoint->bank); + TRACE_ERROR("PB UDPHS_EPTCFG: 0x%X\n\r", AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG); + for(;;); + } + + if (bType == USBEndpointDescriptor_CONTROL) { + + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_EPT_ENABL; + } + else { +#ifndef DMA + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_EPT_ENABL; +#else + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_AUTO_VALID + | AT91C_UDPHS_EPT_ENABL; +#endif + } + +} + +//------------------------------------------------------------------------------ +/// Sends data through an USB endpoint (IN) +/// Sets up the transfer descriptor, write one or two data payloads +/// (depending on the number of FIFO banks for the endpoint) and then +/// starts the actual transfer. The operation is complete when all +/// the data has been sent. +/// \param bEndpoint Index of endpoint +/// \param *pData Data to be written +/// \param dLength Data length to be send +/// \param fCallback Callback to be call after the success command +/// \param *pArgument Callback argument +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +char USBD_Write( unsigned char bEndpoint, + const void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength); + pEndpoint->sendZLP = 0; + // Setup the transfer descriptor + pTransfer->pData = (void *) pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Send one packet + pEndpoint->state = UDP_ENDPOINT_SENDING; + +#ifdef DMA + // Test if endpoint type control + if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) { +#endif + // Enable endpoint IT + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_TX_PK_RDY; + +#ifdef DMA + } + else { + if( CHIP_USB_ENDPOINTS_DMA(bEndpoint) == 0 ) { + TRACE_FATAL("Endpoint has no DMA\n\r"); + } + if( pTransfer->remaining == 0 ) { + // DMA not handle ZLP + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + // Enable endpoint IT + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_TX_PK_RDY; + } + else { + // Others endpoints (not control) + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + // Transfer the max + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + // Transfer the good size + pTransfer->buffered = pTransfer->remaining; + } + + TRACE_DEBUG_WP("\n\r_WR[%x]:%d ", pTransfer->pData, pTransfer->remaining ); + TRACE_DEBUG_WP("B:%d ", pTransfer->buffered ); + TRACE_DEBUG_WP("T:%d ", pTransfer->transferred ); + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData); + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + // DMA config + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT) + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_CHANN_ENB ); + } + } +#endif + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Sends data frames through a USB endpoint. Sets up the transfer descriptor +/// list, writes one or two data payloads (depending on the number of FIFO bank +/// for the endpoint) and then starts the actual transfer. The operation invoke +/// a callback with RC USBD_STATUS_PARTIAL_DONE each time when a buffer is +/// transferred and is complete when all the data has been sent. +/// +/// *If the size of the frame is greater than the size of the endpoint +/// (or twice the size if the endpoint has two FIFO banks), then the buffer +/// must be kept allocated until the frame is finished*. This means that +/// it is not possible to declare it on the stack (i.e. as a local variable +/// of a function which returns after starting a transfer). +/// +/// \param bEndpoint Endpoint number. +/// \param pMbl Pointer to a frame list with the data to send. +/// Two different list is supported, for EPs that support DMA, +/// the frame is described by USBDDmaDescriptor link list, +/// for EPs that uses FIFO the frame is described by +/// USBDTransferBuffer array. +/// For DMA transfer, the size of each frame is limited by the +/// max size of one DMA transfer. When using DMA, the frame list +/// size and size of each frame should be larger so that more +/// efficiency is achieved and CPU could have more time on +/// handler of each buffer end event. +/// \param wListSize Size of the frame list. +/// \param bCircList Circle the list. +/// \param wStartNdx Starting buffer index in the list. +/// \param fCallback Optional callback function to invoke when the transfer is +/// complete. +/// \param pArgument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer has been started; +/// otherwise, the corresponding error status code. +/// \see USBDDmaDescriptor, USBDTransferBuffer +//------------------------------------------------------------------------------ +char USBD_MblWrite( unsigned char bEndpoint, + void *pMbl, + unsigned short wListSize, + unsigned char bCircList, + unsigned short wStartNdx, + MblTransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + unsigned short i; + + // EP0 is not suitable for Mbl + if (bEndpoint == 0) { + + return USBD_STATUS_INVALID_PARAMETER; + } + + // Check that the endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + //return USBD_STATUS_LOCKED; + } + // Setup state + pEndpoint->state = UDP_ENDPOINT_SENDINGM; + + TRACE_DEBUG_WP("WriteM%d(0x%x,%d) ", bEndpoint, pMbl, wListSize); + + // Start from first if not circled list + if (!bCircList) wStartNdx = 0; + +#ifdef DMA + if (UDP_IsDmaUsed(bEndpoint)) { + + DmaLlTransfer *pDmallt = (DmaLlTransfer*)&(pEndpoint->transfer); + USBDDmaDescriptor * pDi = (USBDDmaDescriptor*)pMbl; + + // DMA config + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1 << SHIFT_DMA << bEndpoint); + + // Setup the transfer descriptor + pDmallt->pMbl = (USBDDmaDescriptor*)pMbl; + pDmallt->listSize = wListSize; + pDmallt->fCallback = fCallback; + pDmallt->pArgument = pArgument; + pDmallt->circList = bCircList; + pDmallt->allUsed = 0; + + // Check all buffers, fill settings + for (i = 0; i < wListSize - 1; i ++) { + + if (pDi->bufferLength > DMA_MAX_FIFO_SIZE) { + + TRACE_ERROR("DMA buffer size %d exceeds max %d\n\r", + pDi->bufferLength, DMA_MAX_FIFO_SIZE); + return USBD_STATUS_INVALID_PARAMETER; + } + + // Channel control value + pDi->ctrlSettings = AT91C_UDPHS_CHANN_ENB + | AT91C_UDPHS_LDNXT_DSC + | AT91C_UDPHS_END_TR_EN + //| AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_DESC_LD_IT + ; + pDi->used = 0; + pDi = pDi->pNxtDesc; + } + + // Circled list check + pDi->used = 0; + if (bCircList) { + + pDi->pNxtDesc = pDmallt->pMbl; + pDi->ctrlSettings = AT91C_UDPHS_CHANN_ENB + | AT91C_UDPHS_LDNXT_DSC + | AT91C_UDPHS_END_TR_EN + //| AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + //| AT91C_UDPHS_DESC_LD_IT + ; + } + else { + + pDi->pNxtDesc = 0; + pDi->ctrlSettings = AT91C_UDPHS_CHANN_ENB + //| AT91C_UDPHS_LDNXT_DSC + | AT91C_UDPHS_END_TR_EN + //| AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + //| AT91C_UDPHS_DESC_LD_IT + ; + } + + // Start from 'wStartNdx'th descriptor + pDi = (USBDDmaDescriptor*)pMbl; + for (;wStartNdx; wStartNdx --) { + pDi = pDi->pNxtDesc; + } + pDmallt->pCurrBuffer = (USBDDmaDescriptor*)pDi; + pDmallt->pFreedBuffer = (USBDDmaDescriptor*)pDi; + + // Disable DMA when interrupt happenned + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_INTDIS_DMA; + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMANXTDSC = (unsigned int)pMbl; + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = AT91C_UDPHS_LDNXT_DSC + //| AT91C_UDPHS_END_TR_EN + //| AT91C_UDPHS_END_TR_IT + //| AT91C_UDPHS_END_B_EN + //| AT91C_UDPHS_END_BUFFIT + //| AT91C_UDPHS_DESC_LD_IT + ; + + return USBD_STATUS_SUCCESS; + } +#endif + + // DMA is not used + { + unsigned char nbBanks = CHIP_USB_ENDPOINTS_BANKS(bEndpoint); + + // Setup the transfer descriptor + pTransfer->pMbl = (USBDTransferBuffer*)pMbl; + pTransfer->listSize = wListSize; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + pTransfer->currBuffer = wStartNdx; + pTransfer->freedBuffer = 0; + pTransfer->pLastLoaded = &(((USBDTransferBuffer*)pMbl)[wStartNdx]); + pTransfer->circList = bCircList; + pTransfer->allUsed = 0; + + // Clear all buffer + for (i = 0; i < wListSize; i ++) { + + pTransfer->pMbl[i].transferred = 0; + pTransfer->pMbl[i].buffered = 0; + pTransfer->pMbl[i].remaining = pTransfer->pMbl[i].size; + } + + // Fill data into FIFO + for (; + nbBanks && pTransfer->pMbl[pTransfer->currBuffer].remaining; + nbBanks --) { + + UDPHS_MblWriteFifo(bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = + AT91C_UDPHS_TX_PK_RDY; + } + + // Enable interrup + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = 0 + //| AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_TX_PK_RDY + ; + return USBD_STATUS_SUCCESS; + } +} + +//------------------------------------------------------------------------------ +/// Reads incoming data on an USB endpoint (OUT) +/// \param bEndpoint Index of endpoint +/// \param *pData Data to be readen +/// \param dLength Data length to be receive +/// \param fCallback Callback to be call after the success command +/// \param *pArgument Callback argument +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +char USBD_Read( unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Endpoint enters Receiving state + pEndpoint->state = UDP_ENDPOINT_RECEIVING; + + // Set the transfer descriptor + pTransfer->pData = pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + +#ifdef DMA + // Test if endpoint type control + if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) { +#endif + // Control endpoint + // Enable endpoint IT + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY; +#ifdef DMA + } + else { + if( CHIP_USB_ENDPOINTS_DMA(bEndpoint) == 0 ) { + TRACE_FATAL("Endpoint has no DMA\n\r"); + } + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Others endpoints (not control) + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + // Transfer the max + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + // Transfer the good size + pTransfer->buffered = pTransfer->remaining; + } + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData); + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + + TRACE_DEBUG_WP("\n\r_RR:%d ", pTransfer->remaining ); + TRACE_DEBUG_WP("B:%d ", pTransfer->buffered ); + TRACE_DEBUG_WP("T:%d ", pTransfer->transferred ); + + // DMA config + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT) + | AT91C_UDPHS_END_TR_EN + | AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_CHANN_ENB ); + } +#endif + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reuse first used/released buffer with new buffer address and size to be used +/// in transfer again. Only valid when frame list is ringed. Can be used for +/// both read & write. +/// \param bEndpoint Endpoint number. +/// \param pNewBuffer Pointer to new buffer with data to send (0 to keep last). +/// \param wNewSize Size of the data buffer +//------------------------------------------------------------------------------ +char USBD_MblReuse( unsigned char bEndpoint, + unsigned char *pNewBuffer, + unsigned short wNewSize ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); + USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->freedBuffer]); + + // Only for Multi-buffer-circle list + if (bEndpoint != 0 + && (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM + || pEndpoint->state == UDP_ENDPOINT_SENDINGM)) { + } + else { + + return USBD_STATUS_WRONG_STATE; + } + + TRACE_DEBUG_WP("MblReuse(%d), st%x, circ%d\n\r", + bEndpoint, pEndpoint->state, pTransfer->circList); + +#ifdef DMA + if (UDP_IsDmaUsed(bEndpoint)) { + + DmaLlTransfer *pDmallt = (DmaLlTransfer*)&(pEndpoint->transfer); + USBDDmaDescriptor *pDi = pDmallt->pFreedBuffer; + + // ONLY for circled list + if (!pDmallt->circList) { + + return USBD_STATUS_WRONG_STATE; + } + + // Check if there is freed buffer + if ((pDi == 0) || (pDi && !pDi->used) ) { + + return USBD_STATUS_LOCKED; + } + + // Update transfer information + pDmallt->pFreedBuffer = pDi->pNxtDesc; + if (pNewBuffer) { + + pDi->pDataAddr = pNewBuffer; + pDi->bufferLength = wNewSize; + } + /* + pDi->ctrlSettings = AT91C_UDPHS_CHANN_ENB + | AT91C_UDPHS_LDNXT_DSC + | AT91C_UDPHS_END_TR_EN + //| AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_DESC_LD_IT + ; */ + pDi->used = 0; + + pDmallt->allUsed = 0; + + return USBD_STATUS_SUCCESS; + } +#endif + + // DMA is not used + { + + // ONLY for circled list + if (!pTransfer->circList) { + + return USBD_STATUS_WRONG_STATE; + } + + // Check if there is freed buffer + if (pTransfer->freedBuffer == pTransfer->currBuffer + && !pTransfer->allUsed) { + + return USBD_STATUS_LOCKED; + } + + // Update transfer information + if ((++ pTransfer->freedBuffer) == pTransfer->listSize) + pTransfer->freedBuffer = 0; + if (pNewBuffer) { + + pBi->pBuffer = pNewBuffer; + pBi->size = wNewSize; + } + pBi->buffered = 0; + pBi->transferred = 0; + pBi->remaining = pBi->size; + + // At least one buffer is not processed + pTransfer->allUsed = 0; + } + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Put endpoint into Halt state +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +void USBD_Halt( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + TRACE_INFO("usbd_Halt%d ", bEndpoint); + // Check that endpoint is enabled and not already in Halt state + if( (pEndpoint->state != UDP_ENDPOINT_DISABLED) + && (pEndpoint->state != UDP_ENDPOINT_HALTED) ) { + + TRACE_INFO("Halt%d ", bEndpoint); + + // Abort the current transfer if necessary + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + + // Put endpoint into Halt state + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_FRCESTALL; + pEndpoint->state = UDP_ENDPOINT_HALTED; + +#ifdef DMA + // Test if endpoint type control + if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) { +#endif + // Enable the endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1<UDPHS_IEN |= (1<state != UDP_ENDPOINT_DISABLED) { + if (pEndpoint->state == UDP_ENDPOINT_HALTED) { + + TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); + + // Return endpoint to Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Clear FORCESTALL flag + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_TOGGLESQ | AT91C_UDPHS_FRCESTALL; + + // Reset Endpoint Fifos + AT91C_BASE_UDPHS->UDPHS_EPTRST = (1<state == UDP_ENDPOINT_HALTED) { + status = 1; + } + return( status ); +} + +//------------------------------------------------------------------------------ +/// IS High Speed device working in High Speed ? +/// \return 1 if the device is in High Speed; otherwise 0 (Full Speed) +//------------------------------------------------------------------------------ +unsigned char USBD_IsHighSpeed( void ) +{ + unsigned char status = 0; + + if( AT91C_UDPHS_SPEED == (AT91C_BASE_UDPHS->UDPHS_INTSTA & AT91C_UDPHS_SPEED) ) + { + // High Speed + TRACE_DEBUG_WP("High Speed\n\r"); + status = 1; + } + else { + TRACE_DEBUG_WP("Full Speed\n\r"); + } + return( status ); +} + + +//------------------------------------------------------------------------------ +/// Causes the endpoint to acknowledge the next received packet with a STALL +/// handshake. +/// Further packets are then handled normally. +/// \param bEndpoint Index of endpoint +/// \return Operation result code: USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char USBD_Stall( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint); + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Stall%d ", bEndpoint); + + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_FRCESTALL; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Activates a remote wakeup procedure +//------------------------------------------------------------------------------ +void USBD_RemoteWakeUp(void) +{ + TRACE_DEBUG_WP("Remote WakeUp\n\r"); + + // Device is currently suspended + if (deviceState == USBD_STATE_SUSPENDED) { + + TRACE_DEBUG_WP("RW\n\r"); + UDPHS_EnableUsbClock(); + + // Activates a remote wakeup + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_REWAKEUP; + + while ((AT91C_BASE_UDPHS->UDPHS_CTRL&AT91C_UDPHS_REWAKEUP) == AT91C_UDPHS_REWAKEUP) { + + TRACE_DEBUG_WP("W"); + } + UDPHS_EnableBIAS(); + } + // Device is NOT suspended + else { + + TRACE_WARNING("USBD_RemoteWakeUp: Device is not suspended\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// Sets the device address +/// \param address Adress to be set +//------------------------------------------------------------------------------ +void USBD_SetAddress( unsigned char address ) +{ + TRACE_DEBUG_WP("SetAddr(%d) ", address); + + // Set address + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~(unsigned int)AT91C_UDPHS_DEV_ADDR; // RAZ Address + AT91C_BASE_UDPHS->UDPHS_CTRL |= address | AT91C_UDPHS_FADDR_EN; + + // If the address is 0, the device returns to the Default state + if (address == 0) { + deviceState = USBD_STATE_DEFAULT; + } + // If the address is non-zero, the device enters the Address state + else { + deviceState = USBD_STATE_ADDRESS; + } +} + +//------------------------------------------------------------------------------ +/// Changes the device state from Address to Configured, or from Configured +/// to Address. +/// This method directly access the last received SETUP packet to decide on +/// what to do. +/// \param cfgnum configuration number +//------------------------------------------------------------------------------ +void USBD_SetConfiguration( unsigned char cfgnum ) +{ + TRACE_DEBUG_WP("SetCfg(%d) ", cfgnum); + + // Check the request + if( cfgnum != 0 ) { + + // Enter Configured state + deviceState = USBD_STATE_CONFIGURED; + } + // If the configuration number is zero, the device goes back to the Address + // state + else { + + // Go back to Address state + deviceState = USBD_STATE_ADDRESS; + + // Abort all transfers + UDPHS_DisableEndpoints(); + } +} + +//------------------------------------------------------------------------------ +/// Enables the pull-up on the D+ line to connect the device to the USB. +//------------------------------------------------------------------------------ +void USBD_Connect( void ) +{ + TRACE_DEBUG_WP("Conn "); +#if defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~(unsigned int)AT91C_UDPHS_DETACH; // Pull Up on DP + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_PULLD_DIS; // Disable Pull Down + +#elif defined(CHIP_USB_PULLUP_INTERNAL_BY_MATRIX) + TRACE_DEBUG_WP("PUON 1\n\r"); + AT91C_BASE_MATRIX->MATRIX_USBPCR |= AT91C_MATRIX_USBPCR_PUON; + +#elif defined(CHIP_USB_PULLUP_EXTERNAL) + +#ifdef PIN_USB_PULLUP + const Pin pinPullUp = PIN_USB_PULLUP; + if( pinPullUp.type == PIO_OUTPUT_0 ) { + + PIO_Set(&pinPullUp); + } + else { + + PIO_Clear(&pinPullUp); + } +#else + #error unsupported now +#endif + +#elif !defined(CHIP_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. + +#endif +} + +//------------------------------------------------------------------------------ +/// Disables the pull-up on the D+ line to disconnect the device from the bus. +//------------------------------------------------------------------------------ +void USBD_Disconnect( void ) +{ + TRACE_DEBUG_WP("Disc "); + +#if defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_DETACH; // detach + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~(unsigned int)AT91C_UDPHS_PULLD_DIS; // Enable Pull Down + +#elif defined(CHIP_USB_PULLUP_INTERNAL_BY_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; + +#elif defined(CHIP_USB_PULLUP_EXTERNAL) + +#ifdef PIN_USB_PULLUP + const Pin pinPullUp = PIN_USB_PULLUP; + if (pinPullUp.type == PIO_OUTPUT_0) { + + PIO_Clear(&pinPullUp); + } + else { + + PIO_Set(&pinPullUp); + } +#else + #error unsupported now +#endif + +#elif !defined(CHIP_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. + +#endif + + // Device returns to the Powered state + if (deviceState > USBD_STATE_POWERED) { + + deviceState = USBD_STATE_POWERED; + } + if (previousDeviceState > USBD_STATE_POWERED) { + + previousDeviceState = USBD_STATE_POWERED; + } +} + +//------------------------------------------------------------------------------ +/// Certification test for High Speed device. +/// \param bIndex Test to be done +//------------------------------------------------------------------------------ +void USBD_Test( unsigned char bIndex ) +{ + char *pFifo; + unsigned char i; + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(unsigned int)AT91C_UDPHS_DET_SUSPD; // remove suspend for TEST + AT91C_BASE_UDPHS->UDPHS_TST |= AT91C_UDPHS_SPEED_CFG_HS; // force High Speed (remove suspend) + + switch( bIndex ) { + + case USBFeatureRequest_TESTPACKET: + TRACE_DEBUG_WP("TEST_PACKET "); + + AT91C_BASE_UDPHS->UDPHS_DMA[1].UDPHS_DMACONTROL = 0; + AT91C_BASE_UDPHS->UDPHS_DMA[2].UDPHS_DMACONTROL = 0; + + // Configure endpoint 2, 64 bytes, direction IN, type BULK, 1 bank + AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTCFG = AT91C_UDPHS_EPT_SIZE_64 | AT91C_UDPHS_EPT_DIR_IN | AT91C_UDPHS_EPT_TYPE_BUL_EPT | AT91C_UDPHS_BK_NUMBER_1; + while( (signed int)(AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTCFG & (unsigned int)AT91C_UDPHS_EPT_MAPD) != (signed int)AT91C_UDPHS_EPT_MAPD ) {} + + AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTCTLENB = AT91C_UDPHS_EPT_ENABL; + + // Write FIFO + pFifo = (char*)((unsigned int *)(AT91C_BASE_UDPHS_EPTFIFO->UDPHS_READEPT0) + (EPT_VIRTUAL_SIZE * 2)); + for( i=0; iUDPHS_TST |= AT91C_UDPHS_TST_PKT; + // Send packet + AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + break; + + case USBFeatureRequest_TESTJ: + TRACE_DEBUG_WP("TEST_J "); + AT91C_BASE_UDPHS->UDPHS_TST = AT91C_UDPHS_TST_J; + break; + + case USBFeatureRequest_TESTK: + TRACE_DEBUG_WP("TEST_K "); + AT91C_BASE_UDPHS->UDPHS_TST = AT91C_UDPHS_TST_K; + break; + + case USBFeatureRequest_TESTSE0NAK: + TRACE_DEBUG_WP("TEST_SEO_NAK "); + AT91C_BASE_UDPHS->UDPHS_IEN = 0; // for test + break; + + case USBFeatureRequest_TESTSENDZLP: + //while( 0 != (AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & AT91C_UDPHS_TX_PK_RDY ) ) {} + AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + //while( 0 != (AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & AT91C_UDPHS_TX_PK_RDY ) ) {} + TRACE_DEBUG_WP("SEND_ZLP "); + break; + } + TRACE_DEBUG_WP("\n\r"); +} + + +//------------------------------------------------------------------------------ +/// Initializes the specified USB driver +/// This function initializes the current FIFO bank of endpoints, +/// configures the pull-up and VBus lines, disconnects the pull-up and +/// then trigger the Init callback. +//------------------------------------------------------------------------------ +void USBD_Init(void) +{ + unsigned char i; + + TRACE_DEBUG_WP("USBD Init()\n\r"); + + // Reset endpoint structures + UDPHS_ResetEndpoints(); + + // Enables the UDPHS peripheral + UDPHS_EnablePeripheral(); + + // XCHQ[2010.1.21]: From IP recomendation, the order for init is: + // - reset IP (EN_UDPHS), - enable PLL 480M, - enable USB Clock + + // Enables the USB Clock + // UDPHS_EnableUsbClock(); + + // Configure the pull-up on D+ and disconnect it +#if defined(CHIP_USB_PULLUP_INTERNAL) + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_DETACH; // detach + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_PULLD_DIS; // Disable Pull Down + +#elif defined(CHIP_USB_PULLUP_INTERNAL_BY_MATRIX) + TRACE_DEBUG_WP("PUON 0\n\r"); + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; + +#elif defined(CHIP_USB_PULLUP_EXTERNAL) +#ifdef PIN_USB_PULLUP + const Pin pinPullUp = PIN_USB_PULLUP; + PIO_Configure(&pinPullUp, 1); + if (pinPullUp.type == PIO_OUTPUT_0) { + + PIO_Clear(&pinPullUp); + } + else { + + PIO_Set(&pinPullUp); + } +#else + #error unsupported now +#endif +#elif !defined(CHIP_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. + +#endif + + // Reset and enable IP UDPHS + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~(unsigned int)AT91C_UDPHS_EN_UDPHS; + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_EN_UDPHS; + // Enable and disable of the transceiver is automaticaly done by the IP. + + // Enables the USB Clock + // (XCHQ[2010.1.21], IP recomendation, setup clock after reset IP) + UDPHS_EnableUsbClock(); + + // With OR without DMA !!! + // Initialization of DMA + for( i=1; i<=((AT91C_BASE_UDPHS->UDPHS_IPFEATURES & AT91C_UDPHS_DMA_CHANNEL_NBR)>>4); i++ ) { + + // RESET endpoint canal DMA: + // DMA stop channel command + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0; // STOP command + + // Disable endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLDIS = (unsigned int)AT91C_UDPHS_SHRT_PCKT + | AT91C_UDPHS_BUSY_BANK + | AT91C_UDPHS_NAK_OUT + | AT91C_UDPHS_NAK_IN + | AT91C_UDPHS_STALL_SNT + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_TX_PK_RDY + | AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_ERR_OVFLW + | AT91C_UDPHS_MDATA_RX + | AT91C_UDPHS_DATAX_RX + | AT91C_UDPHS_NYET_DIS + | AT91C_UDPHS_INTDIS_DMA + | AT91C_UDPHS_AUTO_VALID + | AT91C_UDPHS_EPT_DISABL; + + // Clear status endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCLRSTA = AT91C_UDPHS_TOGGLESQ + | AT91C_UDPHS_FRCESTALL + | AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_STALL_SNT + | AT91C_UDPHS_NAK_IN + | AT91C_UDPHS_NAK_OUT; + + // Reset endpoint config + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLENB = 0; + + // Reset DMA channel (Buff count and Control field) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = AT91C_UDPHS_LDNXT_DSC; // NON STOP command + + // Reset DMA channel 0 (STOP) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0; // STOP command + + // Clear DMA channel status (read the register for clear it) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMASTATUS = AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMASTATUS; + + } + + AT91C_BASE_UDPHS->UDPHS_TST = forceUsbFS ? AT91C_UDPHS_SPEED_CFG_FS : 0; + AT91C_BASE_UDPHS->UDPHS_IEN = 0; + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_UPSTR_RES + | AT91C_UDPHS_ENDOFRSM + | AT91C_UDPHS_WAKE_UP + | AT91C_UDPHS_ENDRESET + | AT91C_UDPHS_IEN_SOF + | AT91C_UDPHS_MICRO_SOF + | AT91C_UDPHS_DET_SUSPD; + + // Device is in the Attached state + deviceState = USBD_STATE_SUSPENDED; + previousDeviceState = USBD_STATE_POWERED; + + // Enable interrupts + AT91C_BASE_UDPHS->UDPHS_IEN = AT91C_UDPHS_ENDOFRSM + | AT91C_UDPHS_WAKE_UP + | AT91C_UDPHS_DET_SUSPD; + + // Disable USB clocks + UDPHS_DisableUsbClock(); + + // Configure interrupts + USBDCallbacks_Initialized(); +} + +//------------------------------------------------------------------------------ +/// Configure USB Speed, should be invoked before USB attachment. +/// \param forceFS Force to use FS mode. +//------------------------------------------------------------------------------ +void USBD_ConfigureSpeed(unsigned char forceFS) +{ + if (forceFS) { + AT91C_BASE_UDPHS->UDPHS_TST |= AT91C_UDPHS_SPEED_CFG_FS; + } + else { + AT91C_BASE_UDPHS->UDPHS_TST &= ~(unsigned int)AT91C_UDPHS_SPEED_CFG_FS; + } +} + + +//------------------------------------------------------------------------------ +/// Returns the current state of the USB device. +/// \return Device current state. +//------------------------------------------------------------------------------ +unsigned char USBD_GetState( void ) +{ + return deviceState; +} + + diff --git a/usb/device/core/core.dir b/usb/device/core/core.dir new file mode 100644 index 0000000..fcf0dc0 --- /dev/null +++ b/usb/device/core/core.dir @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// !Purpose +/// +/// This directory contains structures, definitions and functions for AT91 UDP. +/// They can be divided into two groups: +/// - Hardware layer: low-level operations on the USB UDP controller +/// - USBD_OTGHS.c USBD_UDP.c USBD_UDPHS.c +/// - USB Device API: other files in the directory, offers hardware-independent +/// methods and structures +//------------------------------------------------------------------------------ + diff --git a/usb/device/device.dir b/usb/device/device.dir new file mode 100644 index 0000000..2988e1e --- /dev/null +++ b/usb/device/device.dir @@ -0,0 +1,655 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for USB %device +/// applications with Atmel AT91 microcontrollers and USB %device framework. +/// +/// You can develop your own USB %device products based on the class-specific +/// driver code provided, or just take them as a refference. +/// +/// !!!Contents +/// There are two groups for the implement: +/// -# The hardware interface driver for USB peripheral on AT91 +/// microcontrollers (UDP or UDPHS), this is a part of the "AT91 USB +/// framework". +/// - "core": hardware interface driver for AT91 USB peripheral +/// -# The %device class driver to class-specific %device. +/// - "audio-speaker" +/// - "ccid" +/// - "cdc-serial" +/// - "hid-keyboard" +/// - "massstorage" +/// +/// For more information about what a particular group contains, please refer to +/// its documentation page. +/// +/// \note +/// Depending on the project, not all the subdirectories will be available +/// (i.e. the #ccid# directory will not be in projects without USB CCID +/// function). +//------------------------------------------------------------------------------ + +/** + \page "USBD API" + + See "USBD API Structures" and "USBD API Methods".\n + + !!!USBD API Structures + + Several specific structures are used by the USBD API to perform various + operations, such as invoking callbacks or accessing the USBD controller. + + There are two %main structures: + - USBDDriver: + It is the %main structure of the USB API. It should be instanciated + in class-specific USB %device driver or user application. + - USBDDriverDescriptors: + It is a list of all descriptors used by a USB %device driver. It + should be instanciated in class-specific USB %device driver or + user application and passed to USBD by USBDDriver_Initialize. + + !!!USBD API Methods + + The USB API provides serveral methods to perform the following operations: + - Changing the %device state + - USBD_Init + - USBD_Connect, USBD_Disconnect + - USBD_SetAddress + - USBD_SetConfiguration + - USBD_GetState + - "USB Device State Diagram" + - Handling events coming from the USB controller + - USBD_IrqHandler + - Modifying the behavior of an endpoint + - USBD_ConfigureEndpoint + - USBD_Stall + - USBD_Halt + - USBD_Unhalt + - USBD_IsHalted + - Transferring data + - USBD_Write + - USBD_Read + - USBD_IsoWrite + - Special functions + - USBD_RemoteWakeUp + + See "USBD API Methods" for detailed informations. + +*/ + +/** + \page "USBD API Structures" + + !!!USBD API Structures + + Several specific structures are used by the USBD API to perform various + operations, such as invoking callbacks or accessing the USBD controller. + + There are two %main structures: + - USBDDriver: + It is the %main structure of the USB API. It should be instanciated + in class-specific USB %device driver or user application. + - USBDDriverDescriptors: + It is a list of all descriptors used by a USB %device driver. It + should be instanciated in class-specific USB %device driver or + user application and passed to USBD by USBDDriver_Initialize. + + */ + +/** + \page "USBD API Methods" + + !!!USB API methods + + The USB API provides serveral methods to perform the following operations: + - Changing the %device state + - USBD_Init + - USBD_Connect, USBD_Disconnect + - USBD_SetAddress + - USBD_SetConfiguration + - USBD_GetState + - "USB Device State Diagram" + - Handling events coming from the USB controller + - USBD_IrqHandler + - Modifying the behavior of an endpoint + - USBD_ConfigureEndpoint + - USBD_Stall + - USBD_Halt + - USBD_Unhalt + - USBD_IsHalted + - Transferring data + - USBD_Write + - USBD_Read + - USBD_IsoWrite + - Special functions + - USBD_RemoteWakeUp + + !!!Controlling the Device State + + Chapter 9 of the USB specification 2.0 describes the various states a %device + can be in. Most of the methods of this API are used to change between those + states. + + !!USBD_Init + + USBD_Init is the first method to call in a user application. Technically, it + must occur just before entering the Attached state. It performs the following + actions: + - USB Device driver and endpoint state initialization + - D+ pull-up configuration and disabling + - UDP hardware initialization (Peripheral and clock init) + + A USB %device uses a pull-up on the D+ line to signal its connection to the + host. Depending on the USB controller present on the chip, either an + internal or external pull-up is used. In both cases, its configuration is + performed directly by this method. Please refer to the documentation of a + particular controller for more information about the D+ pull-up. + + The ini callback has to perform several mandatory operations at this point. + You can find the default operations in USBDCallbacks_Initialized. + + !!USBD_Connect, USBD_Disconnect + + These two methods control the state of the D+ upll-up. This makes it possible + to connect of disconnect the %device by software when needed. USBD_Connect + changes the %device state from Powered to Default, while USBD_Disconnect + changes from either Default, Address or Configured to Powered. + + !!USBD_SetAddress + + USBD_SetAddress extracts the information from the last received + SETUP packet to either change the %device state from Default to + Address or from Address to Default. The difference is made + depending on the value of the wValue field of the request. + + This method must only be called right after the SET_ADDRESS + request is received. + + !!USBD_SetConfiguration + + This function operates in a way similar to USBD_SetAddress. When the SETUP + packet containing a SET_CONFIGURATION request is received, + USBD_SetConfiguration should be called to extract the new configuration + value to adopt. If the wValue field of the request is non-zero, then the + %device must adopt the new configuration and enter the Configuration state; + otherwise, it returns (or stays) in the Address state. + + !!USBD_GetState + + As its name implies, USBD_GetState simply returns the current state of the USB + driver. See state definitions on "USB %device states". + - USBD_STATE_SUSPENDED + - USBD_STATE_ATTACHED + - USBD_STATE_POWERED + - USBD_STATE_DEFAULT + - USBD_STATE_ADDRESS + - USBD_STATE_CONFIGURED + + !!Device State Diagram + See "USB Device State Diagram" + + !!!Event Handling (USBD_IrqHandler) + Several events can occur at the USB controller level: + - End of bus reset + - Reception of a SETUP packet + - Change of bus activity (active -> idle -> active ..) + - Completin of an endpoint operation + - ... + + Whenever such an event occurs, it must be forwarded to the USBD API to be + handled in an appropriate way. The USBD_IrqHandler performs this + functionality, so the controller interrupt must be configured to call it. + + Several #callbacks# can be triggered depending on the event notified by + the controller: + - Suspend, when the bus is idle + - Resume, when the bus becomes active again + - NewRequest, when a setup packet is received on a control endpoint + - StartOfFrame, every 1 ms (for full-speed controllers) or 125us (for high- + speed controllers) + + More information about these callbacks and their expected behavior can be + found in "USBD Callback API". + + !!!Endpoint Behavior Modification + + The USBD API offers following functions to control how an endpoint operates. + - USBD_ConfigureEndpoint + - USBD_Stall + - USBD_Halt + - USBD_Unhalt + - USBD_IsHalted + + !!USBD_ConfigureEndpoint + USBD_ConfigureEndpoint is used to configure an endpoint at the USB controller + level. An appropriate endpoint descriptor must be provided to do that. The + descriptor is used to configure the endpoint type (either Control, Bulk, + Interrupt or Isochronous), direction (IN or OUT) and address. + + Control endpoint 0 is automatically configured by the USBD API when the End of + bus reset event is signalled by the USB controller. Therefore, there is no need + to do it manually. + + !!USBD_Stall + The USBD_Stall method causes and endpoint to acknowledge its next received + packet with a STALL handshake. Further packets are then handled normally. + + Most of the time, this method should be used with endpoint 0 to signal the + host that the %device cannot process a command. + + !!USBD_Halt, USBD_Unhalt, USBD_IsHalted + USBD_Halt sets the Halt status of an endpoint. When in Halt mode, every + received packet is acknowledged with a STALL handshake instead of being + handled normally. + + #}USB_Halt#} can be called either with the USB_SET_FEATURE, USB_CLEAR_FEATURE + or USB_GET_STATUS parameter to modify the endpoint Halt state. + + USBD_Unhalt clears the Halt status of an endpoint. + + USBD_IsHalted gets the Halt status of an endpoint. + + !!!Data Transfer + Data transfer (IN or OUT) on an endpoint can be performed by calling two + methods, USBD_Write and USBD_Read. + + !!USBD_Write + The USBD_Write function sends a data payload on a specific endpoint. If the + data payload equals or exceeds the maximum packet size of the endpoint, then + several IN transactions are necessary. This method should only be called on an + IN or Control endpoint. + + The write is performed #asynchronously#, i.e., the function returns immediately + without waiting for the transfer to finish. When the transfer is complete, an + optional user-provided callback function is called. This makes it possible to + create an #OS-friendly synchronous function# by locking and unlocking a + semaphore before and after each write. + + This function handles double-buffering, if it is supported by the USB + controller and if it has been enabled for the endpoint. Do not forget that + using double-buffering is mandatory for isochronous transactions. + + - #Note# + The double-buffering this function supported is only in period of each + write action. That is, when the function is invoked to start transfer + trunk of data, the data is automatically splitted to several IN + transactions and ping-pong is started on the 2nd transaction. But when + all the data of the trunk is finished the ping-pong is stopped. So it can + not process the list of buffer that should use double-buffering all the + time. See USBD_IsoWrite for such kind of operations. + + !!USBD_Read + The USBD_Read reads incoming data on an endpoint. The transfer stops either + when the provided buffer is full, or a short packet (size inferior to the + endpoint maximum packet size) is received. This method must only be called on + an OUT or Control endpoint. + + The read is performed #asynchronously#, i.e., the function returns immediately + without waiting for the transfer to finish. When the transfer is complete, an + optional user-provided callback function is called. This makes it possible to + create an #OS-friendly synchronous function# by locking and unlocking a + semaphore before and after each read. + + This function handles #double-buffering#, if it is supported by the USB + controller and if it has been enabled for the endpoint. Do not forget that + using double-buffering is mandatory for isochronous transactions. + + !!USBD_IsoWrite + The USBD_IsoWrite function sends a buffer list on a specific endpoint. The each + buffer's payload should be equals or less than the maximum packet size of the + endpoint. The transfer ends when all buffera are sent out. And the buffer is + previously sent can be filled with new data before the transfer ends. To + maitain a ring buffer for the outgoing stream. This method should only be + called on an ISO IN endpoint. + + The write is performed #asynchronously#, i.e., the function returns immediately + without waiting for the transfer to finish. When the transfer is complete, an + optional user-provided callback function is called. This makes it possible to + create an #OS-friendly synchronous function# by locking and unlocking a + semaphore before and after each write. + + This function handles double-buffering, if it is supported by the USB + controller and if it has been enabled for the endpoint. Do not forget that + using double-buffering is mandatory for isochronous transactions. + + !!!Special Functions + + - USBD_RemoteWakeUp: This method starts a remote wakeup procedure. This makes + it possible for a suspended %device to wake a host with may itself be + suspended. + +*/ + +/** + \page "USB Device State Diagram" + + \image USBDeviceStateDiagram.png "Changing the Device State" + +*/ + +/* (Image Link Backup) +USBDeviceStateDiagram.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +USBDCallbackInvocationFlowchart.png +*/ + +/** + \page "USBD Callback API" + + !!!Callback API + + The callback API is a means of communication between the user application and + the USBD API. When particular operations must be performed, the USB driver + calls serveral external functions, refferred to as #callbacks#. They can also + be invoked to notify the user application of pending events. + + Defining all callbacks is not mandatory. For example, if the %device shall not + enter low-power mode, then it is appropriate not to provide a Suspend callback. + If a callback is mandatory, this is notified in its description. + + See USBDCallbacks.h for callback definitions. + + !!!Callback Invocation + The following events can trigger a callback: + - USB initialization: USBDCallbacks_Initialized + - End of bus reset: USBDCallbacks_Reset + - Device suspend: USBDCallbacks_Suspended + - Device resume: USBDCallbacks_Resumed + - SETUP request received: USBDCallbacks_RequestReceived + - Start of a new USB frame + + \image USBDCallbackInvocationFlowchart.png "Callback Invocation Flowchart" + + !!Init + The USBDCallbacks_Initialized callback is invoked when the USBD_Init method is + called. It has to perform several mandatory steps to make it possible to use + the API: + - If an OS is used, perform any specific operation to install the driver + - Configure USB controller interrupt + - Configure Vbus monitoring PIO and interrupt ( but it's in app layer now ) + The USB controller interrupt must be configured to #call the + USBD_IrqHandler# API function when triggered. This is necessary to have + events happening at the USB controller level handled appropriately by the API. + + If a PIO pin is connected to VBus, it is possible to monitor it by configuring + the pin as an input and enabling the PIO interrupt. The interrupt service + routine should simply check the Vbus status and then %call the USBD_Connect + and USBD_Disconnect$ function to put %device into right state. + + Finally, if an OS is being used, then the driver should probably be installed + prior to use. Interrupt configuration may also be done differently. Please + refer to the documentation of the OS for more information. + + This callback is #mandatory#. + + !!Reset + When an End of bus reset has been detected, the USBDCallbacks_Reset callback + is triggered. The callback should perform #initialization# or #re- + initialization# of the user-level application. For example, a class driver + like MSD should re-initialize its internal state when a USB reset is performed. + + !!Suspend + When the USB %device enters the Suspended state, the USB API notifies this state + change by invoking the USBDCallbacks_Suspended callback. This can happen either + when the bus is idle or when the %device is disconnected from the USB. + + If the %device should enter low-power mode when suspended, then this callback + must perform the required operations to do so, e.g., switching to a slow clock, + disabling PLLs, etc. + + - }Note: The electrical specification of the USB 2.0 defines a maximum current + consumption of 500uA for suspended %device. This includes the current passing + through pull-ups and upll-downs.} + + !!Resume + The USBDCallbacks_Resumed callback is invoked when the USB %device leaves the + Suspended state and returns to its previous state (either Powered, Default, + Address or Configured). This may happen when activity is detected on the USB, + or when the %device gets connected. + + If the %device was in low-power mode because of the Suspend callback, then this + callback must perform the necessary poerations to return the %device into a + normal operation mode, e.g., switching to a fast clock. + + !!NewRequest + When a SETUP request is received on a control endpoint, the USBD API layer + triggers the USBDCallbacks_RequestReceived callback to notify the user + application. The received request can then be accessed through the + corresponding USBGenericRequest structure. + + SETUP packets are used for class-specific requests (e.g. }GetMaxLUN} in MSD) + as well as standard USB requests (e.g. }SetConfiguration}). The former are + described in }USB Device Class Documents}, such as the }Mass Storage Bulk + Only 1.0}, the latter are defined in the USB Specification 2.0. + + - }Note: that SETUP requests which are not understood by the %device should + be acknowledged with a STALL handshake. This notifies the host that the + %device cannot process the command.} + + This callback is #mandatory#. + + !!StartOfFrame + Every 1ms (for a full-speed %device) or 125us (for a high-speed %device) a + new USB frame starts. A callback can be invoked whenever this occurs. + + Because the start-of-frame interrupt %puts some stress on the processor + (since it is called a lot), it is only activated the corresponding + callback is defined (#now it's NOT defined in current framework#). + +*/ + +/** + \page "USBD Standard Request Handler" + + !!!Standard Request Handler + + Chapter 9 of the USB specification 2.0 defines a set of standard requests + which have to be implemented by all devices. Since most class drivers treat + those requests in the standard way, the USB framework provides a way to easily + do that. + + !!!USBDDriver_RequestHandler + + USBDDriver_RequestHandler handles the standard requests in an appropriate way. + It can answer the following commands: + + - GET_DESCRIPTOR + - SET_ADDRESS + - SET_CONFIGURATION + - GET_CONFIGURATION + - CLEAR_FEATURE + - SET_FEATURE + - GET_STATUS + + Simply using this standard request handler enables a %device to be enumerated + correctly. + + !!Get Descriptor + The GET_DESCRIPTOR request is used by the host to retrieve information about + the %device by means of several descriptors. + + The standard request handler simply sends the corresponding descriptor to the + host. How these descriptors are provided to the function is discussed in + Structures. + + !!Set Address + Whenever the host wants to change the %device state from Default to Address, or + vice-versa, it sends a SET_ADDRESS request. The wValue field contains the new + address of the %device; if it is null, then the %device returns to the Default + state. + + The USBD_SetAddress function is called to perform this operation. Note that a + zero-length packet must be sent prior to doing that, to acknowledge the SETUP + transfer. + + !!Set Configuration & GetConfiguration + The SET_CONFIGURATION request makes it possible for the host to select between + one or more configurations for the %device. GET_CONFIGURATION is used to + retrieve the currently selected one. + + Those two requests are handled in a very basic way by + USBDDriver_RequestHandler: it assumes that the %device has only one + configuration. Therefore, the SET_CONFIGURATION request is simply acknowledged + with a zero-length packet, and GET_CONFIGURATION is answered with either 0 + or 1. If the user application needs more than one configuration, it will be + the duty of the class driver handler to service those requests. + + In addition, when the SET_CONFIGURATION request causes the %device to enter the + Configured state, the standard request handler calls the USBD_ConfigureEndpoint + method for each endpoint used by the %device; + + !!Clear Feature, Set Feature & Get Status + Several features of a %device can either be activated or deactivated by the USB + host: + - Remote wakeup + - Endpoint Halt state + Three requests can be used to either set, clear or get the status of these two + features: SET_FEATURE, CLEAR_FEATURE and GET_STATUS. + + The USBDDriver_RequestHandler method answers a Halt state operation by calling + the USBD_Halt method on the endpoint with the request. + + !!!Structures + Several pieces of information must be known to the USBDDriver_RequestHandler + to be able to process some SETUP commands. For example, all the descriptors + (configuration, etc.) used by the %device are needed since they must be sent + to the host when a GET_DESCRIPTOR is received. + + The USBGenericRequest structure is a "standard USB class driver" object used + to hold the required information. It must be passed as an argument to the + USBDDriver_RequestHandler method. Another structure, USBDDriverDescriptors, is + used to store the descriptors list. + + !!!Usage + The NewRequest callback is used to notify the user application that a new SETUP + request has been received. SETUP request can either be class-specific or + standard. + + The correct way to handle incoming requests is to first process class-specific + requests using a class handler. For example, a Mass Storage implementation will + define the NewRequest callback to call MSDDriver_RequestHandler. This function + will handle the necessary requests, and forward the rest to + USBDDriver_RequestHandler. + + If a request cannot be processed, USBDDriver_RequestHandler will STALL control + endpoint 0. + +*/ + +/** + \page "VID, PID, SN & Strings" + + This page collects the definition for USB %device to indicate the Vendor and + Product information. + + + If you need only the functions in demo %driver, you can easily modify these + definitions to change your device's Identification and Display strings. + + They are defined in the driver c code file that suffixed with + "DriverDescriptors" under the driver directory. + + !!!VID, PID & SN in Device Descriptor + + Defined as const and used in USBDeviceDescriptor instance initialization. + Gives identivication to the USB %device by VID and PID. The INF installation + file should mach the VID & PID so that the %device can be installed. + +\code +const USBDeviceDescriptor deviceDescriptor = {...}; +\endcode + + - "audio-speaker": "Audio Speaker Device Codes" + - ccid: "CCID Device IDs" + - "cdc-serial": "CDC Serial Device IDs" + - "hid-keyboard": "HID Device Descriptor IDs" + - massstorage: "MSD Device Descriptor IDs" + + !!!Strings + + The strings gives additional information on the USB %device, normally string + description about the vendor, product and serial number. + + The strings are defined as a list to initialize the driver's + USBDDriverDescriptors instance: + + - "audio-speaker": auddSpeakerDriverDescriptors + - ccid: ccidDriverDescriptors + - "cdc-serial": cdcdSerialDriverDescriptors + - "hid-keyboard": hiddKeyboardDriverDescriptors + - massstorage: msdDriverDescriptors + +\code +// String descriptors +const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor, +}; +\endcode +*/ diff --git a/usb/device/hid-keyboard/HIDClassArch.png b/usb/device/hid-keyboard/HIDClassArch.png new file mode 100644 index 0000000..6d09580 Binary files /dev/null and b/usb/device/hid-keyboard/HIDClassArch.png differ diff --git a/usb/device/hid-keyboard/HIDDKeyboardCallbacks.h b/usb/device/hid-keyboard/HIDDKeyboardCallbacks.h new file mode 100644 index 0000000..6bbfaea --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardCallbacks.h @@ -0,0 +1,57 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit HIDDKeyboardCallbacks + + !!!Purpose + + Definitions of callbacks used by the HID keyboard device driver to + notify the application of events. + + !!!Usage + + -# Re-implement any number of these callbacks anywhere in the program; + they will be called automatically by the driver when the related + event occurs. +*/ + +#ifndef HIDDKEYBOARDCALLBACKS_H +#define HIDDKEYBOARDCALLBACKS_H + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void HIDDKeyboardCallbacks_LedsChanged(unsigned char numLockStatus, + unsigned char capsLockStatus, + unsigned char scrollLockStatus); + +#endif //#ifndef HIDDKEYBOARDCALLBACKS_H + diff --git a/usb/device/hid-keyboard/HIDDKeyboardCallbacks_LedsChanged.c b/usb/device/hid-keyboard/HIDDKeyboardCallbacks_LedsChanged.c new file mode 100644 index 0000000..be8963d --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardCallbacks_LedsChanged.c @@ -0,0 +1,59 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDKeyboardCallbacks.h" +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Indicates that the status of one or more LEDs has been changed by the +/// host. +/// \param numLockStatus Indicates the current status of the num. lock key. +/// \param capsLockStatus Indicates the current status of the caps lock key. +/// \param scrollLockStatus Indicates the current status of the scroll lock key. +//------------------------------------------------------------------------------ +void HIDDKeyboardCallbacks_LedsChanged( + unsigned char numLockStatus, + unsigned char capsLockStatus, + unsigned char scrollLockStatus) +{ + TRACE_INFO( + "LEDs status: %d, %d, %d\n\r", + numLockStatus, + capsLockStatus, + scrollLockStatus); +} + diff --git a/usb/device/hid-keyboard/HIDDKeyboardDriver.c b/usb/device/hid-keyboard/HIDDKeyboardDriver.c new file mode 100644 index 0000000..e244cb5 --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardDriver.c @@ -0,0 +1,477 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDKeyboardDriver.h" +#include "HIDDKeyboardDriverDescriptors.h" +#include "HIDDKeyboardCallbacks.h" +#include "HIDDKeyboardInputReport.h" +#include "HIDDKeyboardOutputReport.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Driver structure for an HID device implementing keyboard functionalities. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard USB device driver instance. + USBDDriver usbdDriver; + /// Idle rate (in milliseconds) of the input report. + unsigned char inputReportIdleRate; + /// Input report instance. + HIDDKeyboardInputReport inputReport; + /// Output report instance. + HIDDKeyboardOutputReport outputReport; + +} HIDDKeyboardDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Static instance of the HID keyboard device driver. +static HIDDKeyboardDriver hiddKeyboardDriver; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +/** + Returns the descriptor requested by the host. + +\param type Descriptor type. +\param length Maximum number of bytes to send. +\return 1 if the request has been handled by this function, otherwise 0. +*/ +static unsigned char HIDDKeyboardDriver_GetDescriptor(unsigned char type, + unsigned char length) +{ + const USBConfigurationDescriptor *pConfiguration; + HIDDescriptor *hidDescriptors[2]; + + switch (type) { + + case HIDGenericDescriptor_REPORT: + TRACE_INFO_WP("Report "); + + // Adjust length and send report descriptor + if (length > HIDDKeyboardDriverDescriptors_REPORTSIZE) { + + length = HIDDKeyboardDriverDescriptors_REPORTSIZE; + } + USBD_Write(0, &hiddReportDescriptor, length, 0, 0); + break; + + case HIDGenericDescriptor_HID: + TRACE_INFO_WP("HID "); + + // Configuration descriptor is different depending on configuration + if (USBD_IsHighSpeed()) { + + pConfiguration = hiddKeyboardDriver.usbdDriver.pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = hiddKeyboardDriver.usbdDriver.pDescriptors->pFsConfiguration; + } + + // Parse the device configuration to get the HID descriptor + USBConfigurationDescriptor_Parse( + pConfiguration, + 0, + 0, + (USBGenericDescriptor **) hidDescriptors); + + // Adjust length and send HID descriptor + if (length > sizeof(HIDDescriptor)) { + + length = sizeof(HIDDescriptor); + } + USBD_Write(0, hidDescriptors[0], length, 0, 0); + break; + + default: + return 0; + } + + return 1; +} + +/** + Sends the current Idle rate of the input report to the host. +*/ +static void HIDDKeyboardDriver_GetIdle() +{ + TRACE_INFO_WP("gIdle "); + + USBD_Write(0, &(hiddKeyboardDriver.inputReportIdleRate), 1, 0, 0); +} + +/** + Retrieves the new idle rate of the input report from the USB host. + + \param idleRate New input report idle rate. +*/ +static void HIDDKeyboardDriver_SetIdle(unsigned char idleRate) +{ + TRACE_INFO_WP("sIdle(%d) ", idleRate); + + hiddKeyboardDriver.inputReportIdleRate = idleRate; + USBD_Write(0, 0, 0, 0, 0); +} + +/** + Sends the requested report to the host. + +\param type Report type. +\param length Maximum number of bytes to send. +*/ +static void HIDDKeyboardDriver_GetReport(unsigned char type, + unsigned short length) +{ + TRACE_INFO_WP("gReport "); + + // Check report type + switch (type) { + + case HIDReportRequest_INPUT: + TRACE_INFO_WP("In "); + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardInputReport)) { + + length = sizeof(HIDDKeyboardInputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.inputReport), + length, + 0, // No callback + 0); + break; + + case HIDReportRequest_OUTPUT: + TRACE_INFO_WP("Out "); + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardOutputReport)) { + + length = sizeof(HIDDKeyboardOutputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + 0, // No callback + 0); + break; + + default: + USBD_Stall(0); + } +} + +/** + Callback invoked when an output report has been received from the host. + Forward the new status of the LEDs to the user program via the + HIDDKeyboardCallbacks_LedsChanged callback. +*/ +static void HIDDKeyboardDriver_ReportReceived() +{ + TRACE_INFO_WP("oReport "); + + // Trigger callback + HIDDKeyboardCallbacks_LedsChanged( + HIDDKeyboardOutputReport_GetNumLockStatus(&(hiddKeyboardDriver.outputReport)), + HIDDKeyboardOutputReport_GetCapsLockStatus(&(hiddKeyboardDriver.outputReport)), + HIDDKeyboardOutputReport_GetScrollLockStatus(&(hiddKeyboardDriver.outputReport))); + + // Restart transfer + USBD_Read(HIDDKeyboardDriverDescriptors_INTERRUPTOUT, + &(hiddKeyboardDriver.outputReport), + sizeof(HIDDKeyboardOutputReport), + (TransferCallback) HIDDKeyboardDriver_ReportReceived, + 0); // No argument for callback function +} + +/** + Retrieves the new value of a report from the host and saves it. + +\param type Report type. +\param length Report length. +*/ +static void HIDDKeyboardDriver_SetReport(unsigned char type, + unsigned short length) +{ + TRACE_INFO_WP("sReport "); + + // Check report type + switch (type) { + + case HIDReportRequest_INPUT: + // SET_REPORT requests on input reports are ignored + USBD_Stall(0); + break; + + case HIDReportRequest_OUTPUT: + // Check report length + if (length != sizeof(HIDDKeyboardOutputReport)) { + + USBD_Stall(0); + } + else { + + USBD_Read(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + (TransferCallback) HIDDKeyboardDriver_ReportReceived, + 0); // No argument to the callback function + } + break; + + default: + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +// Optional RequestReceived() callback re-implementation +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) + +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + HIDDKeyboardDriver_RequestHandler(request); +} + +#endif + +//------------------------------------------------------------------------------ +// ConfigurationChanged() callback re-implementation +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + if (cfgnum > 0) { + + // Start receiving output reports + USBD_Read(HIDDKeyboardDriverDescriptors_INTERRUPTOUT, + &(hiddKeyboardDriver.outputReport), + sizeof(HIDDKeyboardOutputReport), + (TransferCallback) HIDDKeyboardDriver_ReportReceived, + 0); // No argument for callback function + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +/** + Initializes the HID keyboard device driver. +*/ +void HIDDKeyboardDriver_Initialize() +{ + hiddKeyboardDriver.inputReportIdleRate = 0; + HIDDKeyboardInputReport_Initialize(&(hiddKeyboardDriver.inputReport)); + HIDDKeyboardOutputReport_Initialize(&(hiddKeyboardDriver.outputReport)); + USBDDriver_Initialize(&(hiddKeyboardDriver.usbdDriver), + &hiddKeyboardDriverDescriptors, + 0); // Multiple interface settings not supported + USBD_Init(); +} + +/** + Handles HID-specific SETUP request sent by the host. + + \param request Pointer to a USBGenericRequest instance. +*/ +void HIDDKeyboardDriver_RequestHandler(const USBGenericRequest *request) +{ + TRACE_INFO_WP("NewReq "); + + // Check if this is a standard request + if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // This is a standard request + switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, otherwise forward it to + // the standard driver + if (!HIDDKeyboardDriver_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(&(hiddKeyboardDriver.usbdDriver), + request); + } + break; + + default: + USBDDriver_RequestHandler(&(hiddKeyboardDriver.usbdDriver), + request); + } + } + // Check if this is a class request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + // This is a class-specific request + switch (USBGenericRequest_GetRequest(request)) { + + case HIDGenericRequest_GETIDLE: + HIDDKeyboardDriver_GetIdle(); + break; + + case HIDGenericRequest_SETIDLE: + HIDDKeyboardDriver_SetIdle(HIDIdleRequest_GetIdleRate(request)); + break; + + case HIDGenericRequest_GETREPORT: + HIDDKeyboardDriver_GetReport( + HIDReportRequest_GetReportType(request), + USBGenericRequest_GetLength(request)); + break; + + case HIDGenericRequest_SETREPORT: + HIDDKeyboardDriver_SetReport( + HIDReportRequest_GetReportType(request), + USBGenericRequest_GetLength(request)); + break; + + default: + TRACE_WARNING( + "HIDDKeyboardDriver_RequestHandler: Unknown REQ 0x%02X\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + } + else { + + // Vendor request ? + USBD_Stall(0); + } +} + +/** + Reports a change in which keys are currently pressed or release to the + host. + +\param pressedKeys Pointer to an array of key codes indicating keys that have + been pressed since the last call to HIDDKeyboardDriver_ChangeKeys. +\param pressedKeysSize Number of key codes in the pressedKeys array. +\param releasedKeys Pointer to an array of key codes indicates keys that have + been released since the last call to HIDDKeyboardDriver_ChangeKeys. +\param releasedKeysSize Number of key codes in the releasedKeys array. +\return USBD_STATUS_SUCCESS if the report has been sent to the host; + otherwise an error code. +*/ +unsigned char HIDDKeyboardDriver_ChangeKeys(unsigned char *pressedKeys, + unsigned char pressedKeysSize, + unsigned char *releasedKeys, + unsigned char releasedKeysSize) +{ + // Press keys + while (pressedKeysSize > 0) { + + // Check if this is a standard or modifier key + if (HIDKeypad_IsModifierKey(*pressedKeys)) { + + // Set the corresponding bit in the input report + HIDDKeyboardInputReport_PressModifierKey( + &(hiddKeyboardDriver.inputReport), + *pressedKeys); + } + else { + + HIDDKeyboardInputReport_PressStandardKey( + &(hiddKeyboardDriver.inputReport), + *pressedKeys); + } + + pressedKeysSize--; + pressedKeys++; + } + + // Release keys + while (releasedKeysSize > 0) { + + // Check if this is a standard or modifier key + if (HIDKeypad_IsModifierKey(*releasedKeys)) { + + // Set the corresponding bit in the input report + HIDDKeyboardInputReport_ReleaseModifierKey( + &(hiddKeyboardDriver.inputReport), + *releasedKeys); + } + else { + + HIDDKeyboardInputReport_ReleaseStandardKey( + &(hiddKeyboardDriver.inputReport), + *releasedKeys); + } + + releasedKeysSize--; + releasedKeys++; + } + + // Send input report through the interrupt IN endpoint + return USBD_Write(HIDDKeyboardDriverDescriptors_INTERRUPTIN, + &(hiddKeyboardDriver.inputReport), + sizeof(HIDDKeyboardInputReport), + 0, + 0); +} + +//------------------------------------------------------------------------------ +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//------------------------------------------------------------------------------ +void HIDDKeyboardDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&(hiddKeyboardDriver.usbdDriver))) { + + USBD_RemoteWakeUp(); + } +} + diff --git a/usb/device/hid-keyboard/HIDDKeyboardDriver.h b/usb/device/hid-keyboard/HIDDKeyboardDriver.h new file mode 100644 index 0000000..42dc0a2 --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardDriver.h @@ -0,0 +1,75 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit HIDDKeyboardDriver + + !!!Purpose + + Definition of methods for using a HID keyboard device driver. + + !!!Usage + + -# Re-implement the USBDCallbacks_RequestReceived callback to forward + requests to HIDDKeyboardDriver_RequestHandler. This is done + automatically unless the NOAUTOCALLBACK symbol is defined during + compilation. + -# Initialize the driver using HIDDKeyboardDriver_Initialize. The + USB driver is automatically initialized by this method. + -# Call the HIDDKeyboardDriver_ChangeKeys method when one or more + keys are pressed/released. +*/ + +#ifndef HIDDKEYBOARDDRIVER_H +#define HIDDKEYBOARDDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void HIDDKeyboardDriver_Initialize(); + +extern void HIDDKeyboardDriver_RequestHandler(const USBGenericRequest *request); + +extern unsigned char HIDDKeyboardDriver_ChangeKeys( + unsigned char *pressedKeys, + unsigned char pressedKeysSize, + unsigned char *releasedKeys, + unsigned char releasedKeysSize); + +extern void HIDDKeyboardDriver_RemoteWakeUp(void); + +#endif //#ifndef HIDDKEYBOARDDRIVER_H + diff --git a/usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.c b/usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.c new file mode 100644 index 0000000..51dfb48 --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.c @@ -0,0 +1,420 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: HIDDKeyboardDriverDescriptors + + About: Purpose + Declaration of the descriptors used by the HID device keyboard driver. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDKeyboardDriverDescriptors.h" +#include "HIDDKeyboardInputReport.h" +#include "HIDDKeyboardOutputReport.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Device Descriptor IDs" +/// This page lists VID, PID & Release number. +/// +/// !IDs +/// - HIDDKeyboardDriverDescriptors_PRODUCTID +/// - HIDDKeyboardDriverDescriptors_VENDORID +/// - HIDDKeyboardDriverDescriptors_RELEASE + +/// Device product ID. +#define HIDDKeyboardDriverDescriptors_PRODUCTID 0x6127 +/// Device vendor ID. +#define HIDDKeyboardDriverDescriptors_VENDORID 0x03EB +/// Device release number. +#define HIDDKeyboardDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// List of descriptors that make up the configuration descriptors of a +/// device using the HID keyboard driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Configuration descriptor. + USBConfigurationDescriptor configuration; + /// Interface descriptor. + USBInterfaceDescriptor interface; + /// HID descriptor. + HIDDescriptor hid; + /// Interrupt IN endpoint descriptor. + USBEndpointDescriptor interruptIn; + /// Interrupt OUT endpoint descriptor. + USBEndpointDescriptor interruptOut; + +} __attribute__ ((packed)) HIDDKeyboardDriverConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Device descriptor. +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDDKeyboardDriverDescriptors_VENDORID, + HIDDKeyboardDriverDescriptors_PRODUCTID, + HIDDKeyboardDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Device qualifier descriptor (high-speed only). +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // One possible configuration + 0 // Reserved +}; +#endif + +/// Configuration descriptor. +static const HIDDKeyboardDriverConfigurationDescriptors configurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HIDDKeyboardDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDKeyboardDriverDescriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDKeyboardDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDKeyboardDriverDescriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING + } +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Other-speed configuration descriptor. +static const HIDDKeyboardDriverConfigurationDescriptors otherSpeedDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(HIDDKeyboardDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDKeyboardDriverDescriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDKeyboardDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDKeyboardDriverDescriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTOUT_POLLING + } +}; +#endif + +/// Language ID string descriptor. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(23), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('H'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('K'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('Y'), + USBStringDescriptor_UNICODE('B'), + USBStringDescriptor_UNICODE('O'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('R'), + USBStringDescriptor_UNICODE('D') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(12), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3'), + USBStringDescriptor_UNICODE('4'), + USBStringDescriptor_UNICODE('5'), + USBStringDescriptor_UNICODE('6'), + USBStringDescriptor_UNICODE('7'), + USBStringDescriptor_UNICODE('8'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('F') +}; + +/// Array of pointers to string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors used by the HID keyboard driver. +USBDDriverDescriptors hiddKeyboardDriverDescriptors = { + + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptors, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptors, + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptors, + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptors, +#else + 0, // No full-speed device qualifier descriptor + 0, // No full-speed other speed configuration + 0, // No high-speed device descriptor + 0, // No high-speed configuration descriptor + 0, // No high-speed device qualifier descriptor + 0, // No high-speed other speed configuration descriptor +#endif + stringDescriptors, + 4 // Four string descriptors in list +}; + +/// Report descriptor used by the driver. +const unsigned char hiddReportDescriptor[] = { + + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_KEYBOARD, + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, + + // Input report: modifier keys + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_REPORTCOUNT + 1, 8, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDDKeyboardDriverDescriptors_LASTMODIFIERKEY, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_INPUT + 1, HIDReport_VARIABLE, + + // Input report: standard keys + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, + HIDDKeyboardDriverDescriptors_FIRSTSTANDARDKEY, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, + HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDDKeyboardDriverDescriptors_FIRSTSTANDARDKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY, + HIDReport_INPUT + 1, 0 /* Data array */, + + // Output report: LEDs + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDLeds_PAGEID, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_LOCAL_USAGEMINIMUM + 1, HIDLeds_NUMLOCK, + HIDReport_LOCAL_USAGEMAXIMUM + 1, HIDLeds_SCROLLLOCK, + HIDReport_OUTPUT + 1, HIDReport_VARIABLE, + + // Output report: padding + HIDReport_GLOBAL_REPORTCOUNT + 1, 1, + HIDReport_GLOBAL_REPORTSIZE + 1, 5, + HIDReport_OUTPUT + 1, HIDReport_CONSTANT, + + HIDReport_ENDCOLLECTION +}; + diff --git a/usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.h b/usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.h new file mode 100644 index 0000000..9a2dc2f --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.h @@ -0,0 +1,114 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit HIDDKeyboardDriverDescriptors + + !!!Purpose + + Definitions of the descriptors required by the HID device keyboard + driver. + + !!!Usage + + -# Use the hiddKeyboardDriverDescriptors variable to initialize a + USBDDriver instance. + -# Send hiddReportDescriptor to the host when a GET_DESCRIPTOR request + for the report descriptor is received. +*/ + +#ifndef HIDDKEYBOARDDRIVERDESCRIPTORS_H +#define HIDDKEYBOARDDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Endpoints" +/// This page lists endpoint addresses and polling settings. +/// +/// !Endpoints +/// - HIDDKeyboardDriverDescriptors_INTERRUPTIN +/// - HIDDKeyboardDriverDescriptors_INTERRUPTOUT +/// +/// !Polling Rates +/// - HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING +/// - HIDDKeyboardDriverDescriptors_INTERRUPTOUT_POLLING + +/// Interrupt IN endpoint number. +#define HIDDKeyboardDriverDescriptors_INTERRUPTIN 1 +/// Interrupt IN endpoint polling rate (in milliseconds). +#define HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING 10 +/// Interrupt OUT endpoint number. +#define HIDDKeyboardDriverDescriptors_INTERRUPTOUT 2 +/// Interrupt OUT endpoint polling rate (in milliseconds). +#define HIDDKeyboardDriverDescriptors_INTERRUPTOUT_POLLING 10 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Keypad keys" +/// This page lists definition for HID keypad keys. +/// +/// !Keys +/// - HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY +/// - HIDDKeyboardDriverDescriptors_LASTMODIFIERKEY +/// - HIDDKeyboardDriverDescriptors_FIRSTSTANDARDKEY +/// - HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY + +/// Key code of the first accepted modifier key. +#define HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY HIDKeypad_LEFTCONTROL +/// Key code of the last accepted modifier key. +#define HIDDKeyboardDriverDescriptors_LASTMODIFIERKEY HIDKeypad_RIGHTGUI +/// Key code of the first accepted standard key. +#define HIDDKeyboardDriverDescriptors_FIRSTSTANDARDKEY 0 +/// Key code of the last accepted standard key. +#define HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY HIDKeypad_NUMLOCK +//------------------------------------------------------------------------------ + +/// Size of the report descriptor in bytes. +#define HIDDKeyboardDriverDescriptors_REPORTSIZE 61 + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +extern USBDDriverDescriptors hiddKeyboardDriverDescriptors; + +extern const unsigned char hiddReportDescriptor[]; + +#endif //#ifndef HIDDKEYBOARDDRIVERDESCRIPTORS_H + diff --git a/usb/device/hid-keyboard/HIDDKeyboardInputReport.c b/usb/device/hid-keyboard/HIDDKeyboardInputReport.c new file mode 100644 index 0000000..3257bdb --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardInputReport.c @@ -0,0 +1,164 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: HIDDKeyboardInputReport implementation + + About: Purpose + Implementation of the HIDDKeyboardInputReport class. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDKeyboardInputReport.h" +#include "HIDDKeyboardDriverDescriptors.h" +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a keyboard input report instance. +/// \param report Pointer to a HIDDKeyboardInputReport instance. +//------------------------------------------------------------------------------ +void HIDDKeyboardInputReport_Initialize(HIDDKeyboardInputReport *report) +{ + unsigned int i; + + report->bmModifierKeys = 0; + for (i = 0; i < HIDDKeyboardInputReport_MAXKEYPRESSES; i++) { + + report->pressedKeys[i] = 0; + } +} + +//------------------------------------------------------------------------------ +/// Reports a standard key as being pressed. +/// \param report Pointer to a HIDDKeyboardInputReport instance. +/// \param key Key code of the standard key. +//------------------------------------------------------------------------------ +void HIDDKeyboardInputReport_PressStandardKey(HIDDKeyboardInputReport *report, + unsigned char key) +{ + ASSERT(key <= HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY, + "Invalid standard key code (%d)\n\r", + key); + + // Find first available slot + unsigned int i = 0; + unsigned char found = 0; + while ((i < HIDDKeyboardInputReport_MAXKEYPRESSES) && !found) { + + // Free slot: no key referenced (code = 0) or ErrorRollOver + if ((report->pressedKeys[i] == 0) + || (report->pressedKeys[i] == HIDKeypad_ERRORROLLOVER)) { + + found = 1; + report->pressedKeys[i] = key; + } + + i++; + } + + // Report ErrorRollOver in all fields if too many keys are pressed + if (!found) { + + for (i=0; i < HIDDKeyboardInputReport_MAXKEYPRESSES; i++) { + + report->pressedKeys[i] = HIDKeypad_ERRORROLLOVER; + } + } +} + +//------------------------------------------------------------------------------ +/// Reports a standard key as not being pressed anymore. +/// \param report Pointer to a HIDDKeyboardInputReport instance. +/// \param key Key code of the standard key +//------------------------------------------------------------------------------ +void HIDDKeyboardInputReport_ReleaseStandardKey(HIDDKeyboardInputReport *report, + unsigned char key) +{ + ASSERT(key <= HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY, + "Invalid standard key code (%d)\n\r", + key); + + // Look for key in array + unsigned int i = 0; + unsigned char found = 0; + while ((i < HIDDKeyboardInputReport_MAXKEYPRESSES) && !found) { + + if (report->pressedKeys[i] == key) { + + found = 1; + report->pressedKeys[i] = 0; + } + + i++; + } +} + +//------------------------------------------------------------------------------ +/// Reports a modifier key as being currently pressed. +/// \param report Pointer to a HIDDKeyboardInputReport instance. +/// \param key Key code of the modifier key. +//------------------------------------------------------------------------------ +void HIDDKeyboardInputReport_PressModifierKey(HIDDKeyboardInputReport *report, + unsigned char key) +{ + ASSERT((key >= HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY) + && (key <= HIDDKeyboardDriverDescriptors_LASTMODIFIERKEY), + "Invalid standard key code (%d)\n\r", + key); + + // Set corresponding bit + unsigned char bit = key - HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY; + report->bmModifierKeys |= 1 << bit; +} + +//------------------------------------------------------------------------------ +/// Reports a modifier key as not being pressed anymore. +/// \param report Pointer to a HIDDKeyboardInputReport instance. +/// \param key Key code of the modifier key. +//------------------------------------------------------------------------------ +void HIDDKeyboardInputReport_ReleaseModifierKey(HIDDKeyboardInputReport *report, + unsigned char key) +{ + ASSERT((key >= HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY) + && (key <= HIDDKeyboardDriverDescriptors_LASTMODIFIERKEY), + "Invalid standard key code (%d)\n\r", + key); + + // Clear corresponding bit + unsigned char bit = key - HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY; + report->bmModifierKeys &= ~(1 << bit); +} + diff --git a/usb/device/hid-keyboard/HIDDKeyboardInputReport.h b/usb/device/hid-keyboard/HIDDKeyboardInputReport.h new file mode 100644 index 0000000..7eeafd8 --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardInputReport.h @@ -0,0 +1,149 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit HIDDKeyboardInputReport.h + + !!!Purpose + + Class for manipulating HID keyboard input reports. + + !!!Usage + + -# Initialize a newly created input report with + HIDDKeyboardInputReport_Initialize. + -# Change the standard keys that are pressed and released using + HIDDKeyboardInputReport_PressStandardKey and + HIDDKeyboardInputReport_ReleaseStandardKey. + -# Change the modifier keys that are currently pressed and released + using HIDDKeyboardInputReport_PressModifierKey and + HIDDKeyboardInputReport_ReleaseModifierKey. +*/ + +#ifndef HIDDKEYBOARDINPUTREPORT_H +#define HIDDKEYBOARDINPUTREPORT_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Maximum number of simultaneous key presses. +#define HIDDKeyboardInputReport_MAXKEYPRESSES 3 + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// HID input report structure used by the keyboard driver to notify the +/// host of pressed keys. +/// +/// The first byte is used to report the state of modifier keys. The +/// other three contains the keycodes of the currently pressed keys. +//------------------------------------------------------------------------------ +typedef struct { + + /// State of modifier keys. + unsigned char bmModifierKeys:8; + /// Key codes of pressed keys. + unsigned char pressedKeys[HIDDKeyboardInputReport_MAXKEYPRESSES]; + +} __attribute__ ((packed)) HIDDKeyboardInputReport; // GCC + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +/* + Function: HIDDKeyboardInputReport_Initialize + Initializes a keyboard input report instance. + + Parameters: + report - Pointer to a HIDDKeyboardInputReport instance. +*/ +extern void HIDDKeyboardInputReport_Initialize(HIDDKeyboardInputReport *report); + +/* + Function: HIDDKeyboardInputReport_PressStandardKey + Reports a standard key as being pressed. + + Parameters: + report - Pointer to a HIDDKeyboardInputReport instance. + key - Key code of the standard key. +*/ +extern void HIDDKeyboardInputReport_PressStandardKey( + HIDDKeyboardInputReport *report, + unsigned char key); + +/* + Function: HIDDKeyboardInputReport_ReleaseStandardKey + Reports a standard key as not being pressed anymore. + + Parameters: + report - Pointer to a HIDDKeyboardInputReport instance. + key - Key code of the standard key +*/ +extern void HIDDKeyboardInputReport_ReleaseStandardKey( + HIDDKeyboardInputReport *report, + unsigned char key); + +/* + Function: HIDDKeyboardInputReport_PressModifierKey + Reports a modifier key as being currently pressed. + + Parameters: + report - Pointer to a HIDDKeyboardInputReport instance. + key - Key code of the modifier key. +*/ +extern void HIDDKeyboardInputReport_PressModifierKey( + HIDDKeyboardInputReport *report, + unsigned char key); + +/* + Function: HIDDKeyboardInputReport_ReleaseModifierKey + Reports a modifier key as not being pressed anymore. + + Parameters: + report - Pointer to a HIDDKeyboardInputReport instance. + key - Key code of the modifier key. +*/ +extern void HIDDKeyboardInputReport_ReleaseModifierKey( + HIDDKeyboardInputReport *report, + unsigned char key); + +#endif //#ifndef HIDDKEYBOARDINPUTREPORT_H + diff --git a/usb/device/hid-keyboard/HIDDKeyboardOutputReport.c b/usb/device/hid-keyboard/HIDDKeyboardOutputReport.c new file mode 100644 index 0000000..f79d7ac --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardOutputReport.c @@ -0,0 +1,94 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: HIDDKeyboardOutputReport implementation + + About: Purpose + Implementation of the HIDDKeyboardOutputReport class. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDKeyboardOutputReport.h" + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a keyboard output report. +/// \param report Pointer to a HIDDKeyboardOutputReport instance. +//------------------------------------------------------------------------------ +void HIDDKeyboardOutputReport_Initialize(HIDDKeyboardOutputReport *report) +{ + report->numLockStatus = 0; + report->capsLockStatus = 0; + report->scrollLockStatus = 0; + report->padding = 0; +} + +//------------------------------------------------------------------------------ +/// Indicates the current status of the num. lock LED according to the +/// given report. +/// \param report Pointer to a HIDDKeyboardOutputReport instance. +/// \return 1 if the num. lock LED is light on; otherwise 0. +//------------------------------------------------------------------------------ +unsigned char HIDDKeyboardOutputReport_GetNumLockStatus( + const HIDDKeyboardOutputReport *report) +{ + return report->numLockStatus; +} + +//------------------------------------------------------------------------------ +/// Indicates the current status of the caps lock LED according to the +/// given report. +/// \param report Pointer to a HIDDKeyboardOutputReport instance. +/// \return 1 if the caps lock LED is light on; otherwise 0. +//------------------------------------------------------------------------------ +unsigned char HIDDKeyboardOutputReport_GetCapsLockStatus( + const HIDDKeyboardOutputReport *report) +{ + return report->capsLockStatus; +} + +//------------------------------------------------------------------------------ +/// Indicates the current status of the scroll lock LED according to the +/// given report. +/// \param report Pointer to a HIDDKeyboardOutputReport instance. +/// \return 1 if the scroll lock LED is light on; otherwise 0. +//------------------------------------------------------------------------------ +unsigned char HIDDKeyboardOutputReport_GetScrollLockStatus( + const HIDDKeyboardOutputReport *report) +{ + return report->scrollLockStatus; +} + diff --git a/usb/device/hid-keyboard/HIDDKeyboardOutputReport.h b/usb/device/hid-keyboard/HIDDKeyboardOutputReport.h new file mode 100644 index 0000000..760df1b --- /dev/null +++ b/usb/device/hid-keyboard/HIDDKeyboardOutputReport.h @@ -0,0 +1,96 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit HIDDKeyboardOutputReport + + !!!Purpose + + Definition of a class for manipulating HID keyboard output reports. + + !!!Usage + + -# Initialize a newly-created output report instance with + HIDDKeyboardOutputReport_Initialize. + -# Retrieve the status of the three LEDs using + HIDDKeyboardOutputReport_GetNumLockStatus, + HIDDKeyboardOutputReport_GetCapsLockStatus and + HIDDKeyboardOutputReport_GetScrollLockStatus. +*/ + +#ifndef HIDKEYBOARDOUTPUTREPORT_H +#define HIDKEYBOARDOUTPUTREPORT_H + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// HID output report structure used by the host to control the state of +/// the keyboard LEDs. +/// +/// Only the first three bits are relevant, the other 5 are used as +/// padding bits. +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char numLockStatus:1, /// State of the num. lock LED. + capsLockStatus:1, /// State of the caps lock LED. + scrollLockStatus:1, /// State of the scroll lock LED. + padding:5; /// Padding bits. + +} __attribute__ ((packed)) HIDDKeyboardOutputReport; // GCC + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void HIDDKeyboardOutputReport_Initialize( + HIDDKeyboardOutputReport *report); + +extern unsigned char HIDDKeyboardOutputReport_GetNumLockStatus( + const HIDDKeyboardOutputReport *report); + +extern unsigned char HIDDKeyboardOutputReport_GetCapsLockStatus( + const HIDDKeyboardOutputReport *report); + +extern unsigned char HIDDKeyboardOutputReport_GetScrollLockStatus( + const HIDDKeyboardOutputReport *report); + +#endif //#ifndef HIDKEYBOARDOUTPUTREPORT_H + diff --git a/usb/device/hid-keyboard/hid-keyboard.dir b/usb/device/hid-keyboard/hid-keyboard.dir new file mode 100644 index 0000000..923a362 --- /dev/null +++ b/usb/device/hid-keyboard/hid-keyboard.dir @@ -0,0 +1,787 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB HID +/// %device - USB HID Keyboard driver, to implement an USB keyboard %device. +/// +/// !!!Contents +/// +/// There are three things for the implement of the USB HID Keyboard driver: +/// - Implement the USB HID driver structs and functions for the %device, +/// to initialize, to handle HID-specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// endpoints, +/// - Create the HID Keyboard device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the %device as a USB Keyboard %device. +/// - Implement methods to update the keyboard keys status, so that host can +/// get it through USB. +/// +/// For more information about what a particular group contains, please refer to +/// "USB HID Keyboard". +//------------------------------------------------------------------------------ + +/** + \page "USB HID Keyboard" + This page describes how to use the "AT91 USB device framework" to produce a USB + HID Keyboard driver, which appears as a USB keyboard on host. + + Details about the USB and the HID class can be found in the }USB specification + 2.0} and the }HID specification 1.11}, respectively. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Device Class Definition for HID 1.11 + - + HID Usage Tables 1.12 + + !!!HID Basic + See "USB HID Basic". + + !!!Architecture + See "USB Device Framework Architecture". + + !!!Descriptors + + ... + + !!Device Descriptor + The Device descriptor of an HID %device is very basic, since the HID class + code is only specified at the Interface level. Thus, it only contains + standard values, as shown below: +\code +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDDKeyboardDriverDescriptors_VENDORID, + HIDDKeyboardDriverDescriptors_PRODUCTID, + HIDDKeyboardDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; +\endcode + Note that the Vendor ID is a special value attributed by the USB-IF + organization. The product ID can be chosen freely by the vendor. + + !!Configuration Descriptor + Since one interface is required by the HID specification, this must be + specified in the Configuration descriptor. There is no other value of + interest to put here. +\code +// Configuration descriptor +{ + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HIDDKeyboardDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) +}, +\endcode + When the Configuration descriptor is requested by the host (by using the + GET_DESCRIPTOR command), the %device must also sent all the related + descriptors, i.e. Interface, Endpoint and Class-Specific descriptors. It is + convenient to create a single structure to hold all this data, for sending + everything in one chunk. In the example software, a + HIDDKeyboardDriverConfigurationDescriptors structure has been declared for + that. + + !!HID Class Interface Descriptor + Since a keyboard %device needs to transmit as well as receive data, two + Interrupt (IN & OUT) endpoints are needed. This must be indicated in the + Interface descriptor. Conversely to the mouse example, the Boot protocol is + not implemented here, since there are more constraints on a keyboard %device. +\code +// Interface descriptor +{ + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor +}, +\endcode + + !!HID Descriptor + While a HID keyboard produces two different reports, one Input and one Output, + only one Report descriptor can be used to describe them. Since having Physical + descriptors is also useless for a keyboard, there will only be one HID class + descriptor specified here. + + For a keyboard, the }bCountryCode} field can be used to specify the language + of the key caps. As this is optional, it is simply set to 00h in the example: +\code +// HID descriptor +{ + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDKeyboardDriverDescriptors_REPORTSIZE +}, +\endcode + + !!Report Descriptor + Two current reports are defined in the Report descriptor. The first one is + used to notify the host of which keys are pressed, with both modifier keys + (alt, ctrl, etc.) and alphanumeric keys. The second report is necessary for + the host to send the LED (num lock, caps lock, etc.) states. + + The Report descriptor starts with the global %device functionality, described + with a #Usage Page# and a #Usage# items: +\code +const unsigned char hiddReportDescriptor[] = { + + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_KEYBOARD, +\endcode + + As in the mouse example, the }Generic Desktop} page is used. This time, the + specific usage is the }Keyboard} one. An Application collection is then + defined to group the reports together: +\code + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, +\endcode + + The first report to be defined is the modifier keys. They are represented as a + bitmap field, indicating whether or not each key is pressed. A single byte is + used to map keys \#224-231 defined in the }HID Usage Tables} document: + LeftControl, LeftShift, LeftAlt, LeftGUI (e.g. Windows key), + RightControl, RightShift, RightAlt and RightGUI. + The }Keypad} usage page must be specified for this report, and since this is a + bitmap value, the data is flagged as }Variable}: +\code + // Input report: modifier keys + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_REPORTCOUNT + 1, 8, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDDKeyboardDriverDescriptors_FIRSTMODIFIERKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDDKeyboardDriverDescriptors_LASTMODIFIERKEY, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_INPUT + 1, HIDReport_VARIABLE, +\endcode + + Then, the actual alphanumeric key report is described. This is done by + defining several bytes of data, one for each pressed key. In the example, + up to three keys can be pressed at the same time (and detected) by the user. + Once again, the usage page is set to }Keypad}. This time however, the data + must be specified as an }Array}, since the same control (the keypad) produces + several values: +\code + // Input report: standard keys + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, + HIDDKeyboardDriverDescriptors_FIRSTSTANDARDKEY, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, + HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDDKeyboardDriverDescriptors_FIRSTSTANDARDKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDDKeyboardDriverDescriptors_LASTSTANDARDKEY, + HIDReport_INPUT + 1, 0 /* Data array */, +\endcode + + The LED array is then defined, with the associated usage page. The Report + descriptor is formatted in this order to avoid redefining unchanged }Global} + items, in order to save memory. This time again, the LED status is reported as + a bitmap field. Three LEDs are used here: Num Lock, Caps Lock and Scroll Lock + (IDs 01h to 03h). It is important to note that this is an #Output# report: +\code + // Output report: LEDs + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDLeds_PAGEID, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_LOCAL_USAGEMINIMUM + 1, HIDLeds_NUMLOCK, + HIDReport_LOCAL_USAGEMAXIMUM + 1, HIDLeds_SCROLLLOCK, + HIDReport_OUTPUT + 1, HIDReport_VARIABLE, +\endcode + + Since the previous report only contains 3 bits, the data must be padded to a + multiple of one byte. This is done by using constant Output data, as follows: +\code + // Output report: padding + HIDReport_GLOBAL_REPORTCOUNT + 1, 1, + HIDReport_GLOBAL_REPORTSIZE + 1, 5, + HIDReport_OUTPUT + 1, HIDReport_CONSTANT, +\endcode + + The last item, }End Collection}, is necessary to close the previously opened + }Application Collection}. +\code + HIDReport_ENDCOLLECTION +}; +\endcode + + The Input and Output reports defined by this descriptor can be modeled by the + following structures: +\code +// HID Input Report +typedef struct { + + // State of modifier keys. + unsigned char bmModifierKeys:8; + // Key codes of pressed keys. + unsigned char pressedKeys[HIDDKeyboardInputReport_MAXKEYPRESSES]; + +} __attribute__ ((packed)) HIDDKeyboardInputReport; // GCC +// HID Output Report +typedef struct { + + unsigned char numLockStatus:1, // State of the num. lock LED. + capsLockStatus:1, // State of the caps lock LED. + scrollLockStatus:1, // State of the scroll lock LED. + padding:5; // Padding bits. + +} __attribute__ ((packed)) HIDDKeyboardOutputReport; // GCC +\endcode + + An instance of each one of the reports is stored in a HIDDKeyboardDriver + structure, which holds the standard class driver and HID keyboard-specific + data. + + !!Physical Descriptor + A Physical descriptor is useless for a keyboard %device, so none are defined + in this example. + + !!Endpoint Descriptor + Following the Interface and HID-specific descriptors, the two necessary + endpoints are defined. +\code +// Interrupt IN endpoint descriptor +{ + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDKeyboardDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING +}, +// Interrupt OUT endpoint descriptor +{ + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDKeyboardDriverDescriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING +} +\endcode + + !!String Descriptors + Please refer to "Usage: USBD VID, PID & Strings". + + !!!Class-specific requests + A driver request handler should first differentiate between class-specific and + standard requests using the corresponding bits in the }bmRequestType} field. + In most cases, standard requests can be immediately forwarded to the standard + request handler method; class-specific methods must be decoded and treated by + the custom handler. + + !!GetDescriptor + Three values have been added by the HID specification for the #GET_DESCRIPTOR# + request. The high byte of the }wValue} field contains the type of the + requested descriptor; in addition to the standard types, the #HID + specification# adds the #HID descriptor# (21h), the #Report descriptor# + (22h) and the #Physical descriptor# (23h) types. + + There is no particular action to perform besides sending the descriptor. This + can be done by using the USBD_Write method, after the requested descriptor has + been identified: +\code +switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, + // otherwise forward it to + // the standard driver + if (!HIDDKeyboardDriver_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(&(hiddKeyboardDriver.usbdDriver), + request); + } + break; + + default: + USBDDriver_RequestHandler(&(hiddKeyboardDriver.usbdDriver), + request); +} +\endcode + A slight complexity of the GET_DESCRIPTOR and SET_DESCRIPTOR requests is that + those are standard requests, but the standard request handler + (USBDDriver_RequestHandler) must not always be called to treat them (since + they may refer to HID descriptors). The solution is to first identify + GET/SET_DESCRIPTOR requests, treat the HID-specific cases and, finally, + forward any other request to the standard handler. + + In this case, a GET_DESCRIPTOR request for the Physical descriptor is first + forwarded to the standard handler, and STALLed there because it is not + recognized. This is done because the %device does not have any Physical + descriptors, and thus, does not need to handle the associated request. + + !!SetDescriptor + This request is optional and is never issued by most hosts. It is not + implemented in this example. + + !!GetReport + Since the HID keyboard defines two different reports, the Report Type value + specified by this request (upper byte of the }wValue} field) must be examined + to decide which report to send. If the type value is 01h, then the Input + report must be returned; if it is 02h, the Output report is requested: +\code +case HIDGenericRequest_GETREPORT: +//------------------------------- + type = HIDReportRequest_GetReportType(request); + length = USBGenericRequest_GetLength(request); + switch (type) { + + case HIDReportRequest_INPUT: + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardInputReport)) { + + length = sizeof(HIDDKeyboardInputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.inputReport), + length, + 0, // No callback + 0); + break; + + case HIDReportRequest_OUTPUT: + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardOutputReport)) { + + length = sizeof(HIDDKeyboardOutputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + 0, // No callback + 0); + break; + + default: + USBD_Stall(0); + } +break; +\endcode + + !!SetReport + For an HID keyboard, the #SET_REPORT# command can be sent by the host to + change the state of the LEDs. Normally, the dedicated Interrupt OUT endpoint + will be used for this; but in some cases, using the default Control endpoint + can save some bandwidth on the host side. + + Note that the SET_REPORT request can be directed at the Input report of the + keyboard; in this case, it can be safely discarded, according to the HID + specification. Normally, most host drivers only target the Output report. The + Report Type value is stored in the upper byte of the }wValue} field. + + The length of the data phase to follow is stored in the }wLength} field of the + request. It should be equal to the total length of the Output report. If it is + different, the report status must still be updated with the received data as + best as possible. + + When the reception of the new data is completed, some processing must be done + to enable/disable the corresponding LEDs. This is done in the callback + function passed as an argument to USBD_Read: +\code +case HIDGenericRequest_SETREPORT: +//------------------------------- + type = HIDReportRequest_GetReportType(request); + length = USBGenericRequest_GetLength(request); + switch(type) { + + case HIDReportRequest_INPUT: + // SET_REPORT requests on input reports are ignored + USBD_Stall(0); + break; + + case HIDReportRequest_OUTPUT: + // Check report length + if (length != sizeof(HIDDKeyboardOutputReport)) { + + USBD_Stall(0); + } + else { + + USBD_Read(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + (TransferCallback) HIDDKeyboardDriver_ReportReceived, + 0); // No argument to the callback function + } + break; + + default: + USBD_Stall(0); + } +break; +\endcode + + !!SetIdle + In this case study, the #SET_IDLE# request is used to set a delay before a key + is repeated. This is common behavior on keyboard devices. Usually, this delay + is set to about 500 ms by the host. + + The only action here is to store the new Idle rate. The management of this + setting must be done in the main function, since Interrupt IN reports are sent + from there. + + In practice, it is not necessary to perform any action, apart from sending a + zero-length packet to acknowledge it. The main application however has to make + sure that only new reports are sent by the %device. +\code +case HIDGenericRequest_SETIDLE: +//----------------------------- + hiddKeyboardDriver.inputReportIdleRate = + HIDIdleRequest_GetIdleRate(request); + USBD_Write(0, 0, 0, 0, 0); +break; +\endcode + + !!GetIdle + The only necessary operation for this request is to send the previously saved + Idle rate. This is done by calling the USBD_Write method with the one-byte + variable as its parameter: +\code +case HIDGenericRequest_GETIDLE: +//----------------------------- + USBD_Write(0, &(hiddKeyboardDriver.inputReportIdleRate), 1, 0, 0); +break; +\endcode + + !!GetProtocol, SetProtocol + This HID keyboard example does not support the Boot protocol, so there is no + need to implement the SET_PROTOCOL and GET_PROTOCOL requests. This means they + can be safely STALLed when received. + + !!!Main Application + Like the mouse example, the main program must perform two different + operations. First, it has to monitor the physical inputs used as keys. In the + example software, the buttons present on the evaluation boards are used to + produce several modifier and alphanumeric keys. + + Also, the main program is in charge of sending reports as they are modified, + taking into account the Idle rate specified by the host. Idle rate management + can be carried out by firing/resetting a timer once a new report is sent; if + the timer expires, this means the Input report has not changed since. + According to the HID specification, a single instance of the report must be + sent in this case. + + Finally, the HID specification also defines that if too many keys are pressed + at the same time, the %device should report an }ErrorRollOver} usage value + (01h) in every byte of the key array. This has to be handled by the main + application as well. + +*/ + +/** + \page "USB HID Mouse" + This page describes how to implement a mouse %device using the HID class and + the "AT91 USB device framework". Details about the USB and the HID class can + be found in the }USB specification 2.0} and the }HID specification 1.11} + documents, respectively. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Device Class Definition for HID 1.11 + - + HID Usage Tables 1.12 + + + +*/ + +/** + \page "USB HID Basic" + + This page gives generic details on the HID class, including its purpose, + architecture and how it is supported by various operating systems. + + !!!Purpose + + The HID class has been specifically designed for Human Interface Devices, + i.e., devices which are manipulated by humans to control a computer or an + electronic %device. This includes common peripherals such as a keyboard, a + mouse or a joystick, as well as many other interfaces: remote controllers, + switches, buttons, dedicated game controls, and so on. + + It is also possible to use the HID class for devices which do not require + human interaction, but still deliver information in a similar format. For + example, devices like a thermometer or a battery indicator are supported. + + In addition, the HID class also makes it possible to not only receive data + from devices but also to send commands to them. Indeed, many devices offer + some kind of display to give back information to the user, e.g., the LEDs on a + keyboard. + + Finally, since it is quite simple to send and receive data using the HID + class, it can be used as a generic means of communication between a %device + and a host. This is made possible because of the very flexible framework + defined in the HID specification. + + In this document, three uses of the HID class will be detailed step-by-step, + each showing one particular feature of the class. The first example shows the + interaction with a simple mouse. In the second example, a keyboard is + implemented to demonstrate the possibility to send data to a peripheral. The + last example explains how to use HID as a simple two-way communication + channel. + + !!!Architecture + ... + + !!Interface + An HID %device only needs #one interface descriptor#. It should have the HID + interface class code in its bInterfaceClass field. There are special subclass + and protocol codes to specify if the HID %device is a mouse or a keyboard, and + must be supported by the BIOS. In such a case, the interface must be declared + as a Boot Interface, and the type of the %device (mouse or keyboard) must be + given in the bInterfaceProtocol field. + + !!Endpoints + Up to three endpoints can be used with an HID interface. The first two are the + default Control endpoint 0, as well as an Interrupt IN endpoint. They are + mandatory and shall always be declared. An optional Interrupt OUT endpoint can + be added as well. + + Endpoint 0 is used for class-specific requests, as well as receiving data from + the host if no Interrupt OUT endpoint has been defined. In addition, the host + can also explicitly request or send report data through this endpoint. + + The Interrupt IN and OUT pipes are used for sending asynchronous data to the + host, and to receive low-latency information. + + \image HIDClassArch.png "HID Class Driver Architecture" + + !!Class-Specific Descriptors + There are three class-specific descriptors defined in the }HID specification + 1.11}: the HID descriptor, the report descriptor and the physical descriptor. + + !HID Descriptor + The HID descriptor gives information about the HID specification revision + used, the country for which a %device is localized, and lists the number of + class-specific descriptors, including their length and type. + + HID Descriptor Format +||Field||Size(bytes)||Description +|bLength|1|Total length of the HID descriptor +|bDescriptorType|1|HID descriptor type (21h) +|bcdHID|2|HID specification release number in BCD format +|bCountryCode|1|Code of the country for which the %device is located.\n + Should be 0 if the %device is not localized. +|bNumDescriptors|1|Number of class-specific descriptors used by the %device. +|bDescriptorType|1|Type of the first class-specific descriptor. +|bDescriptorLength|1|Total length of the first class-specific descriptor. +|[bDescriptorType]|1|Type of the second class-specific descriptor. +|[bDescriptorLength]|1|Total length of the second class-specific descriptor. +|...| | + + There is always at least one Report descriptor for an HID %device, so the + corresponding fields must be present in the HID descriptor. If other + descriptors are defined, they must also be described here. + + !Report Descriptor + A HID %device must have at least one #Report descriptor#. It defines the type + of data manipulated by the %device, which is referred to as report. When the + %device wants to notify that the cursor has moved, for example, it sends the + corresponding report in the format previously defined in the Report + descriptor. + + This descriptor is quite different from others, as it does not have a fixed + table of values. Instead, it is made up of a variable number of items, which + collectively identify the information that a host can expect from or send to + the %device. + + There are five categories of items: + - #Input# items, which define the format and type of the data sent by the + %device. + - #Output# items, which define the format and type of the data expected by + the %device + - #Feature# items, which define data sent to or received from the %device, + and not intended for the end user, such as configuration parameters. +- #Collection# items, which identify a set of data as related to the same + group. +- #End Collection# items, which close other Collection items. + + Usually, a Report descriptor defines only one use (report) for a %device, e.g., + a mouse. However, it is possible to declare several reports to perform + different tasks, e.g., mouse & keyboard. This is done by assigning a different + #Report ID# to each report; this makes it possible for the %device to send + both reports through the same Interrupt endpoint, while still permitting the + host to correctly identify the data. Using only a single endpoint for several + functionalities is very powerful, as the remaining ones can then be used by + other interfaces (CDC, MSD, etc.) for an even more versatile %device. + + More details about Report descriptors will be given in the implementation + examples. For more information about the possible items, tags and values, + please refer to the }HID specification 1.11.} + + !Physical Descriptor + A #Physical descriptor# can be used to give information about which human body + part is used to activate a particular control. While this is a useless piece + of information for most devices, it can be relevant for complex devices which + provide many similar controls. In such a case, a Physical descriptor allows an + application to assign its functionalities more appropriately; for example, a + game controller often has a large number of buttons, with some of them more + accessible than the others. Those buttons would be assigned the most useful + actions. + + Since physical descriptors are not used very often, and are not useful in the + case studies described in this document, they will not be discussed further. + + !!Class-specific Requests + ... + + !GetDescriptor + While #GET_DESCRIPTOR# is a standard request (defined in the }USB + specification 2.0}), new descriptor type values have been added for the HID + class. They make it possible for the host to request the HID descriptor, + Report descriptor and Physical descriptors used by the %device. + + When requesting a HID-specific descriptor, the }wIndex} field of the request + must be set to the HID interface number. For standard requests, this field is + either set to 0 or, for String descriptors, to the index of the language ID + used. + + !SetDescriptor + Similarly, #SET_DESCRIPTOR# is a standard request with added HID-specific + values. It is used by the host to change the HID descriptors of a %device. + This is an optional request, and has few practical uses. + + !GetReport + The host can explicitly ask the %device for a report by using the #GET_REPORT# + request. However, it should be used primarily to get the state of feature + items and absolute values at initialization time, not for consistent %device + polling. + + The requested report is identified either by its Report ID (if they are used), + and/or by its type (Input, Output or Feature). + + Please note that a GET_REPORT request is different from a GET_DESCRIPTOR + request for the Report descriptor. The latter returns the whole Report + descriptor, i.e., all the items declared. The former returns the data defined + by this descriptor. + + !SetReport + #SET_REPORT# is similar to GET_REPORT, except this request is used for + changing the state of a report, instead of simply retrieving it. + + For an Input report, this request can either be considered meaningless, or can + be used to reset the current status of a control. For example, it could be + used to calibrate the neutral position of a joystick. + + !SetIdle + This request is used to specify the minimum amount of time, called #Idle + rate#, that a %device must wait before transmitting a report if its state has + not changed. This means the %device must NAK all polls on its Interrupt IN + endpoint until the report state changes, or the guarding period expires. + + The SET_IDLE command can either be issued for a particular duration, or for an + undefined period of time. The upper byte of the wValue field is used to + specify this duration. In addition, if the %device generates multiple reports, + the Report ID of the target report to affect can be specified in the lower + byte. + + In practice, this request is often used with a keyboard to put a small delay + before a key is repeated continuously. For a mouse, it must be set to 0, + meaning that the %device should never report an unchanged state. + + !GetIdle + The GET_IDLE request is issued by the host to retrieve the current Idle rate + of the %device. A particular Report can be specified with its Report ID. + + !GetProtocol + This request returns the protocol currently used by the %device. This can + either be the Report protocol (}wValue} set to 0) or the Boot protocol + (}wValue} set to 1). Since a %device supporting the Boot protocol can operate + differently depending on which mode it is in, the host system can retrieve or + change this mode with the GET_PROTOCOL and SET_PROTOCOL requests. + + This request is only need for devices supporting the Boot protocol. + + !SetProtocol + Whenever an HID %device starts up, it should use the Report protocol by + default. However, the host driver shall still use the SET_PROTOCOL + request to specify if the %device should use the Report protocol or the + Boot protocol. + + !!!Host Drivers + Most operating systems provide a generic HID driver which automatically + handles standard devices, such as keyboard, mice and joystick. In addition, + the driver can also be used by the application to easily access custom and + vendor-specific devices. + +*/ \ No newline at end of file diff --git a/usb/device/hid-mouse/HIDDMouseDriver.c b/usb/device/hid-mouse/HIDDMouseDriver.c new file mode 100644 index 0000000..dc2378c --- /dev/null +++ b/usb/device/hid-mouse/HIDDMouseDriver.c @@ -0,0 +1,392 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDMouseDriver.h" +#include "HIDDMouseDriverDescriptors.h" +#include "HIDDMouseInputReport.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Internal Defines +//------------------------------------------------------------------------------ + +/// Tag bit (Always 1) +#define HIDDMouse_TAG (1 << 3) + +/// Xsign bit +#define HIDDMouse_Xsign (1 << 4) + +/// Ysign bit +#define HIDDMouse_Ysign (1 << 5) + + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Driver structure for an HID device implementing keyboard functionalities. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard USB device driver instance. + USBDDriver usbdDriver; + /// Idle rate (in milliseconds) of the input report. + unsigned char inputReportIdleRate; + /// + unsigned char inputProtocol; + /// Input report instance. + HIDDMouseInputReport inputReport; + +} HIDDMouseDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Static instance of the HID mouse device driver. +static HIDDMouseDriver hiddMouseDriver; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns the descriptor requested by the host. +/// \param type Descriptor type. +/// \param length Maximum number of bytes to send. +/// \return 1 if the request has been handled by this function, otherwise 0. +//------------------------------------------------------------------------------ +static unsigned char HIDDMouseDriver_GetDescriptor(unsigned char type, + unsigned char length) +{ + const USBConfigurationDescriptor *pConfiguration; + HIDDescriptor *hidDescriptors[2]; + + switch (type) { + + case HIDGenericDescriptor_REPORT: + TRACE_INFO("Report "); + + // Adjust length and send report descriptor + if (length > HIDDMouseDriverDescriptors_REPORTSIZE) { + + length = HIDDMouseDriverDescriptors_REPORTSIZE; + } + USBD_Write(0, &hiddReportDescriptor, length, 0, 0); + break; + + case HIDGenericDescriptor_HID: + TRACE_INFO("HID "); + + // Configuration descriptor is different depending on configuration + if (USBD_IsHighSpeed()) { + + pConfiguration = + hiddMouseDriver.usbdDriver.pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = + hiddMouseDriver.usbdDriver.pDescriptors->pFsConfiguration; + } + + // Parse the device configuration to get the HID descriptor + USBConfigurationDescriptor_Parse(pConfiguration, 0, 0, + (USBGenericDescriptor **) &hidDescriptors[0]); + + // Adjust length and send HID descriptor + if (length > sizeof(HIDDescriptor)) { + + length = sizeof(HIDDescriptor); + } + USBD_Write(0, hidDescriptors[0], length, 0, 0); + break; + + default: + return 0; + } + + return 1; +} + +//------------------------------------------------------------------------------ +/// Sends the current Idle rate of the input report to the host. +//------------------------------------------------------------------------------ +static void HIDDMouseDriver_GetIdle() +{ + TRACE_INFO("gIdle "); + + USBD_Write(0, &(hiddMouseDriver.inputReportIdleRate), 1, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Retrieves the new idle rate of the input report from the USB host. +/// \param idleRate New input report idle rate. +//------------------------------------------------------------------------------ +static void HIDDMouseDriver_SetIdle(unsigned char idleRate) +{ + TRACE_INFO("sIdle(%d) ", idleRate); + + hiddMouseDriver.inputReportIdleRate = idleRate; + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sends the requested report to the host. +/// \param type Report type. +/// \param length Maximum number of bytes to send. +//------------------------------------------------------------------------------ +static void HIDDMouseDriver_GetReport(unsigned char type, + unsigned short length) +{ + TRACE_INFO("gReport "); + + // Check report type + switch (type) { + + case HIDReportRequest_INPUT: + TRACE_INFO("In "); + + // Adjust size and send report + if (length > sizeof(HIDDMouseInputReport)) { + + length = sizeof(HIDDMouseInputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddMouseDriver.inputReport), + length, + 0, // No callback + 0); + break; + + default: + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Retrieves the new value of a report from the host and saves it. +/// \param type Report type. +/// \param length Report length. +//------------------------------------------------------------------------------ +static void HIDDMouseDriver_SetReport(unsigned char type, + unsigned short length) +{ + TRACE_INFO("sReport "); + + // Check report type + switch (type) { + + case HIDReportRequest_INPUT: + // SET_REPORT requests on input reports are ignored + USBD_Stall(0); + break; + + default: + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +// Optional RequestReceived() callback re-implementation +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) + +//------------------------------------------------------------------------------ +/// Callback function when new request receivce from host +/// \param request Pointer to the USBGenericRequest instance +//------------------------------------------------------------------------------ +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + HIDDMouseDriver_RequestHandler(request); +} + +#endif + +//------------------------------------------------------------------------------ +// ConfigurationChanged() callback re-implementation +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback function when configureation changed +/// \param cfgnum New configuration number +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + if (cfgnum > 0) { + + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes the HID Mouse %device driver. +//------------------------------------------------------------------------------ +void HIDDMouseDriver_Initialize(void) +{ + hiddMouseDriver.inputReportIdleRate = 0; + HIDDMouseInputReport_Initialize(&(hiddMouseDriver.inputReport)); + USBDDriver_Initialize(&(hiddMouseDriver.usbdDriver), + &hiddMouseDriverDescriptors, + 0); // Multiple interface settings not supported + USBD_Init(); +} + +//------------------------------------------------------------------------------ +/// Handles HID-specific SETUP request sent by the host. +/// \param request Pointer to a USBGenericRequest instance +//------------------------------------------------------------------------------ +void HIDDMouseDriver_RequestHandler(const USBGenericRequest *request) +{ + TRACE_INFO("NewReq "); + + // Check if this is a standard request + if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // This is a standard request + switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, otherwise forward it to + // the standard driver + if (!HIDDMouseDriver_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(&(hiddMouseDriver.usbdDriver), + request); + } + break; + + default: + USBDDriver_RequestHandler(&(hiddMouseDriver.usbdDriver), + request); + } + } + // Check if this is a class request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + // This is a class-specific request + switch (USBGenericRequest_GetRequest(request)) { + + case HIDGenericRequest_GETIDLE: + HIDDMouseDriver_GetIdle(); + break; + + case HIDGenericRequest_SETIDLE: + HIDDMouseDriver_SetIdle(HIDIdleRequest_GetIdleRate(request)); + break; + + case HIDGenericRequest_GETREPORT: + HIDDMouseDriver_GetReport( + HIDReportRequest_GetReportType(request), + USBGenericRequest_GetLength(request)); + break; + + case HIDGenericRequest_SETREPORT: + HIDDMouseDriver_SetReport( + HIDReportRequest_GetReportType(request), + USBGenericRequest_GetLength(request)); + break; + + case HIDGenericRequest_GETPROTOCOL: + USBD_Write(0, &hiddMouseDriver.inputProtocol, 1, 0, 0); + break; + + case HIDGenericRequest_SETPROTOCOL: + hiddMouseDriver.inputProtocol = request->wValue; + USBD_Write(0, 0, 0, 0, 0); + break; + + default: + TRACE_WARNING( + "HIDDMouseDriver_RequestHandler: Unknown request 0x%02X\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + } + else { + + // Vendor request ? + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Update the Mouse button status and location changes via input report +/// to host +/// \param bmButtons Bit map of the button status +/// \param deltaX Movment on X direction +/// \param deltaY Movment on Y direction +//------------------------------------------------------------------------------ +unsigned char HIDDMouseDriver_ChangePoints(unsigned char bmButtons, + signed char deltaX, + signed char deltaY) +{ + hiddMouseDriver.inputReport.bmButtons = (bmButtons & 0x07) + | HIDDMouse_TAG; + hiddMouseDriver.inputReport.bX = deltaX; + hiddMouseDriver.inputReport.bY = deltaY; + // Send input report through the interrupt IN endpoint + return USBD_Write(HIDDMouseDriverDescriptors_INTERRUPTIN, + &(hiddMouseDriver.inputReport), + sizeof(HIDDMouseInputReport), + 0, + 0); +} + +//------------------------------------------------------------------------------ +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//------------------------------------------------------------------------------ +void HIDDMouseDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&(hiddMouseDriver.usbdDriver))) { + + USBD_RemoteWakeUp(); + } +} + diff --git a/usb/device/hid-mouse/HIDDMouseDriver.h b/usb/device/hid-mouse/HIDDMouseDriver.h new file mode 100644 index 0000000..94dac3a --- /dev/null +++ b/usb/device/hid-mouse/HIDDMouseDriver.h @@ -0,0 +1,107 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of methods for using a HID mouse device driver. + + !!!Usage + + -# Re-implement the USBDCallbacks_RequestReceived callback to forward + requests to HIDDMouseDriver_RequestHandler. This is done + automatically unless the NOAUTOCALLBACK symbol is defined during + compilation. + -# Initialize the driver using HIDDMouseDriver_Initialize. The + USB driver is automatically initialized by this method. + -# Call the HIDDMouseDriver_ChangePoints method when one or more + keys are pressed/released. +*/ + +#ifndef HIDDKEYBOARDDRIVER_H +#define HIDDKEYBOARDDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Mouse Button bitmaps" +/// ... +/// !Bits +/// - HIDDMouse_LEFT_BUTTON +/// - HIDDMouse_RIGHT_BUTTON +/// - HIDDMouse_MIDDLE_BUTTON + +/// Left mouse button +#define HIDDMouse_LEFT_BUTTON (1 << 0) + +/// Right mouse button +#define HIDDMouse_RIGHT_BUTTON (1 << 1) + +/// Middle mouse button +#define HIDDMouse_MIDDLE_BUTTON (1 << 2) + +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +/* + Function: HIDDMouseDriver_Initialize + Initializes the HID keyboard device driver. +*/ +extern void HIDDMouseDriver_Initialize(void); + +/* + Function: HIDDMouseDriver_RequestHandler + Handles HID-specific SETUP request sent by the host. + + Parameters: + request - Pointer to a USBGenericRequest instance. +*/ +extern void HIDDMouseDriver_RequestHandler(const USBGenericRequest *request); + +extern unsigned char HIDDMouseDriver_ChangePoints(unsigned char bmButtons, + signed char deltaX, + signed char deltaY); + +extern void HIDDMouseDriver_RemoteWakeUp(void); + +#endif //#ifndef HIDDKEYBOARDDRIVER_H + diff --git a/usb/device/hid-mouse/HIDDMouseDriverDescriptors.c b/usb/device/hid-mouse/HIDDMouseDriverDescriptors.c new file mode 100644 index 0000000..6a66d83 --- /dev/null +++ b/usb/device/hid-mouse/HIDDMouseDriverDescriptors.c @@ -0,0 +1,385 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: HIDDMouseDriverDescriptors + + About: Purpose + Declaration of the descriptors used by the HID device keyboard driver. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDMouseDriverDescriptors.h" +#include "HIDDMouseInputReport.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Mouse Device Descriptor IDs" +/// ... +/// +/// !IDs +/// - HIDDMouseDriverDescriptors_PRODUCTID +/// - HIDDMouseDriverDescriptors_VENDORID +/// - HIDDMouseDriverDescriptors_RELEASE + +/// Device product ID. +#define HIDDMouseDriverDescriptors_PRODUCTID 0x6200 +/// Device vendor ID. +#define HIDDMouseDriverDescriptors_VENDORID 0x03EB +/// Device release number. +#define HIDDMouseDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// List of descriptors that make up the configuration descriptors of a +/// %device using the HID Mouse driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Configuration descriptor. + USBConfigurationDescriptor configuration; + /// Interface descriptor. + USBInterfaceDescriptor interface; + /// HID descriptor. + HIDDescriptor hid; + /// Interrupt IN endpoint descriptor. + USBEndpointDescriptor interruptIn; + +} __attribute__ ((packed)) HIDDMouseDriverConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Device descriptor. +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDDMouseDriverDescriptors_VENDORID, + HIDDMouseDriverDescriptors_PRODUCTID, + HIDDMouseDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Device qualifier descriptor (high-speed only). +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // One possible configuration + 0 // Reserved +}; +#endif + +/// Configuration descriptor. +static const HIDDMouseDriverConfigurationDescriptors configurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HIDDMouseDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 1, // One endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_MOUSE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDMouseDriverDescriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDMouseDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDMouseInputReport), + HIDDMouseDriverDescriptors_INTERRUPTIN_POLLING + } +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Other-speed configuration descriptor. +static const HIDDMouseDriverConfigurationDescriptors otherSpeedDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(HIDDMouseDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_MOUSE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDMouseDriverDescriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDMouseDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDMouseInputReport), + HIDDMouseDriverDescriptors_INTERRUPTIN_POLLING + } +}; +#endif + +/* + Variables: String descriptors + languageIdDescriptor - Language ID string descriptor. + manufacturerDescriptor - Manufacturer name. + productDescriptor - Product name. + serialNumberDescriptor - Product serial number. + stringDescriptors - Array of pointers to string descriptors. +*/ +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L') +}; + +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(19), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('H'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('O'), + USBStringDescriptor_UNICODE('U'), + USBStringDescriptor_UNICODE('S'), + USBStringDescriptor_UNICODE('E'), +}; + +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(12), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3'), + USBStringDescriptor_UNICODE('4'), + USBStringDescriptor_UNICODE('5'), + USBStringDescriptor_UNICODE('6'), + USBStringDescriptor_UNICODE('7'), + USBStringDescriptor_UNICODE('8'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('F') +}; + +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors used by the HID keyboard driver. +USBDDriverDescriptors hiddMouseDriverDescriptors = { + + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptors, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptors, + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptors, + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptors, +#else + 0, // No full-speed device qualifier descriptor + 0, // No full-speed other speed configuration + 0, // No high-speed device descriptor + 0, // No high-speed configuration descriptor + 0, // No high-speed device qualifier descriptor + 0, // No high-speed other speed configuration descriptor +#endif + stringDescriptors, + 4 // Four string descriptors in list +}; + +/// Report descriptor used by the driver. +const unsigned char hiddReportDescriptor[] = { + + // Global Usage Page + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + // Collection: Application + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_MOUSE, + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, + // Physical collection: Pointer + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_POINTER, + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_PHYSICAL, + + // Input report: buttons + HIDReport_GLOBAL_USAGEPAGE + 1, HIDButton_PAGEID, + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_LOCAL_USAGEMINIMUM + 1, 1, + HIDReport_LOCAL_USAGEMAXIMUM + 1, 3, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_INPUT + 1, HIDReport_VARIABLE, // 3 button bits + + // Input report: padding + HIDReport_GLOBAL_REPORTCOUNT + 1, 1, + HIDReport_GLOBAL_REPORTSIZE + 1, 5, + HIDReport_INPUT + 1, HIDReport_CONSTANT, // 5 bit padding + + // Input report: pointer + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_REPORTCOUNT + 1, 2, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_X, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_Y, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, (unsigned char) -127, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 127, + HIDReport_INPUT + 1, HIDReport_VARIABLE | HIDReport_RELATIVE, + + HIDReport_ENDCOLLECTION, + HIDReport_ENDCOLLECTION +}; + diff --git a/usb/device/hid-mouse/HIDDMouseDriverDescriptors.h b/usb/device/hid-mouse/HIDDMouseDriverDescriptors.h new file mode 100644 index 0000000..4f27dec --- /dev/null +++ b/usb/device/hid-mouse/HIDDMouseDriverDescriptors.h @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definitions of the descriptors required by the HID device keyboard + driver. + + !!!Usage + -# Use the hiddMouseDriverDescriptors variable to initialize a + USBDDriver instance. + -# Send hiddReportDescriptor to the host when a GET_DESCRIPTOR request + for the report descriptor is received. +*/ + +#ifndef HIDDKEYBOARDDRIVERDESCRIPTORS_H +#define HIDDKEYBOARDDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ +/* + Constants: Endpoints + HIDDMouseDriverDescriptors_INTERRUPTIN - Interrupt IN endpoint number. + HIDDMouseDriverDescriptors_INTERRUPTIN_POLLING - Interrupt IN endpoint + polling rate (in milliseconds). +*/ +#define HIDDMouseDriverDescriptors_INTERRUPTIN 1 +#define HIDDMouseDriverDescriptors_INTERRUPTIN_POLLING 10 + +/* + Constants: Report descriptor + HIDDMouseDriverDescriptors_REPORTSIZE - Size of the report descriptor + in bytes. +*/ +#define HIDDMouseDriverDescriptors_REPORTSIZE 50 + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ +/* + Variables: HID keyboard driver descriptors + hiddMouseDriverDescriptors - List of descriptors used by the HID + keyboard driver. + hiddReportDescriptor - Report descriptor used by the driver. +*/ +extern USBDDriverDescriptors hiddMouseDriverDescriptors; +extern const unsigned char hiddReportDescriptor[]; + +#endif //#ifndef HIDDKEYBOARDDRIVERDESCRIPTORS_H + diff --git a/usb/device/hid-mouse/HIDDMouseInputReport.c b/usb/device/hid-mouse/HIDDMouseInputReport.c new file mode 100644 index 0000000..1c1c979 --- /dev/null +++ b/usb/device/hid-mouse/HIDDMouseInputReport.c @@ -0,0 +1,72 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: HIDDMouseInputReport implementation + + About: Purpose + Implementation of the HIDDMouseInputReport class. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDMouseInputReport.h" +#include "HIDDMouseDriverDescriptors.h" +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a mouse input report instance. +/// \param report Pointer to a HIDDMouseInputReport instance. +//------------------------------------------------------------------------------ +void HIDDMouseInputReport_Initialize(HIDDMouseInputReport *report) +{ + report->bmButtons = 0; + report->bX = 0; + report->bY = 0; +} + +/* +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void HIDDMouseInputReport_UpdateButtons(HIDDMouseInputReport *report) +{ +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void HIDDMouseInputReport_UpdateAxis(HIDDMouseInputReport *report) +{ +} +*/ diff --git a/usb/device/hid-mouse/HIDDMouseInputReport.h b/usb/device/hid-mouse/HIDDMouseInputReport.h new file mode 100644 index 0000000..ca074fd --- /dev/null +++ b/usb/device/hid-mouse/HIDDMouseInputReport.h @@ -0,0 +1,93 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Class for manipulating HID Mouse input reports. + + !!!Usage + + -# Initialize a newly created input report with + HIDDMouseInputReport_Initialize +*/ + +#ifndef HIDDKEYBOARDINPUTREPORT_H +#define HIDDKEYBOARDINPUTREPORT_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Mouse Keys" + +/// Mouse Left Click Button +#define HIDDMouseInputReport_LEFT_BUTTON (1 << 0) +/// Mouse Right Click Button +#define HIDDMouseInputReport_RIGHT_BUTTON (1 << 1) +/// Mouse Middle Button +#define HIDDMouseInputReport_MIDDLE_BUTTON (1 << 2) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// HID input report structure used by the Mouse driver to notify the +/// host of pressed keys. +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bmButtons; /// Bitmap state of three mouse buttons. + signed char bX; /// Pointer displacement along the X axis. + signed char bY; /// Pointer displacement along the Y axis. + +} __attribute__ ((packed)) HIDDMouseInputReport; // GCC + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void HIDDMouseInputReport_Initialize(HIDDMouseInputReport *report); + +#endif //#ifndef HIDDKEYBOARDINPUTREPORT_H + diff --git a/usb/device/hid-mouse/hid-mouse.dir b/usb/device/hid-mouse/hid-mouse.dir new file mode 100644 index 0000000..9dd733b --- /dev/null +++ b/usb/device/hid-mouse/hid-mouse.dir @@ -0,0 +1,544 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB HID +/// %device - USB HID Mouse driver, to implement an USB Mouse %device. +/// +/// !!!Contents +/// +/// There are three things for the implement of the USB HID Mouse driver: +/// - Implement the USB HID driver structs and functions for the %device, +/// to initialize, to handle HID-specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// endpoints, +/// - Create the HID Mouse device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the %device as a USB Mouse %device. +/// - Implement methods to update the Mouse keys status, so that host can +/// get it through USB. +/// +/// For more information about what a particular group contains, please refer to +/// "USB HID Mouse". +//------------------------------------------------------------------------------ + +/** + \page "USB HID Mouse" + This page describes how to use the "AT91 USB device framework" to produce a USB + HID Mouse driver, which appears as a USB Mouse on host. + + Details about the USB and the HID class can be found in the }USB specification + 2.0} and the }HID specification 1.11}, respectively. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Device Class Definition for HID 1.11 + - + HID Usage Tables 1.12 + + !!!HID Basic + See "USB HID Basic". + + !!!Architecture + See "USB Device Framework Architecture". + + !!!Descriptors + + ... + + !!Device Descriptor + The Device descriptor of an HID %device is very basic, since the HID class + code is only specified at the Interface level. Thus, it only contains + standard values, as shown below: +\code +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDDMouseDriverDescriptors_VENDORID, + HIDDMouseDriverDescriptors_PRODUCTID, + HIDDMouseDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; +\endcode + Note that the Vendor ID is a special value attributed by the USB-IF + organization. The product ID can be chosen freely by the vendor. + + !!Configuration Descriptor + Since one interface is required by the HID specification, this must be + specified in the Configuration descriptor. There is no other value of + interest to put here. +\code +// Configuration descriptor +{ + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HIDDMouseDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) +}, +\endcode + When the Configuration descriptor is requested by the host (by using the + GET_DESCRIPTOR command), the %device must also sent all the related + descriptors, i.e. Interface, Endpoint and Class-Specific descriptors. It is + convenient to create a single structure to hold all this data, for sending + everything in one chunk. In the example software, a + HIDDMouseDriverConfigurationDescriptors structure has been declared for + that. + + !!HID Class Interface Descriptor + Since a Mouse %device needs to transmit as well as receive data, two + Interrupt (IN & OUT) endpoints are needed. This must be indicated in the + Interface descriptor. Conversely to the mouse example, the Boot protocol is + not implemented here, since there are more constraints on a Mouse %device. +\code +// Interface descriptor +{ + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor +}, +\endcode + + !!HID Descriptor + While a HID Mouse produces two different reports, one Input and one Output, + only one Report descriptor can be used to describe them. Since having Physical + descriptors is also useless for a Mouse, there will only be one HID class + descriptor specified here. + + For a Mouse, the }bCountryCode} field can be used to specify the language + of the key caps. As this is optional, it is simply set to 00h in the example: +\code +// HID descriptor +{ + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDMouseDriverDescriptors_REPORTSIZE +}, +\endcode + + !!Report Descriptor + Two current reports are defined in the Report descriptor. The first one is + used to notify the host of which keys are pressed, with both modifier keys + (alt, ctrl, etc.) and alphanumeric keys. The second report is necessary for + the host to send the LED (num lock, caps lock, etc.) states. + + The Report descriptor starts with the global %device functionality, described + with a #Usage Page# and a #Usage# items: +\code +const unsigned char hiddReportDescriptor[] = { + + HIDReport_GLOBAL_USAGEPAGE + 1, HIDGenericDesktop_PAGEID, + HIDReport_LOCAL_USAGE + 1, HIDGenericDesktop_Mouse, +\endcode + + As in the mouse example, the }Generic Desktop} page is used. This time, the + specific usage is the }Mouse} one. An Application collection is then + defined to group the reports together: +\code + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, +\endcode + + The first report to be defined is the modifier keys. They are represented as a + bitmap field, indicating whether or not each key is pressed. A single byte is + used to map keys \#224-231 defined in the }HID Usage Tables} document: + LeftControl, LeftShift, LeftAlt, LeftGUI (e.g. Windows key), + RightControl, RightShift, RightAlt and RightGUI. + The }Keypad} usage page must be specified for this report, and since this is a + bitmap value, the data is flagged as }Variable}: +\code + // Input report: modifier keys + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_REPORTCOUNT + 1, 8, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDDMouseDriverDescriptors_FIRSTMODIFIERKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDDMouseDriverDescriptors_LASTMODIFIERKEY, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_INPUT + 1, HIDReport_VARIABLE, +\endcode + + Then, the actual alphanumeric key report is described. This is done by + defining several bytes of data, one for each pressed key. In the example, + up to three keys can be pressed at the same time (and detected) by the user. + Once again, the usage page is set to }Keypad}. This time however, the data + must be specified as an }Array}, since the same control (the keypad) produces + several values: +\code + // Input report: standard keys + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, + HIDDMouseDriverDescriptors_FIRSTSTANDARDKEY, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, + HIDDMouseDriverDescriptors_LASTSTANDARDKEY, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDKeypad_PAGEID, + HIDReport_LOCAL_USAGEMINIMUM + 1, + HIDDMouseDriverDescriptors_FIRSTSTANDARDKEY, + HIDReport_LOCAL_USAGEMAXIMUM + 1, + HIDDMouseDriverDescriptors_LASTSTANDARDKEY, + HIDReport_INPUT + 1, 0 /* Data array */, +\endcode + + The LED array is then defined, with the associated usage page. The Report + descriptor is formatted in this order to avoid redefining unchanged }Global} + items, in order to save memory. This time again, the LED status is reported as + a bitmap field. Three LEDs are used here: Num Lock, Caps Lock and Scroll Lock + (IDs 01h to 03h). It is important to note that this is an #Output# report: +\code + // Output report: LEDs + HIDReport_GLOBAL_REPORTCOUNT + 1, 3, + HIDReport_GLOBAL_REPORTSIZE + 1, 1, + HIDReport_GLOBAL_USAGEPAGE + 1, HIDLeds_PAGEID, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, 0, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, 1, + HIDReport_LOCAL_USAGEMINIMUM + 1, HIDLeds_NUMLOCK, + HIDReport_LOCAL_USAGEMAXIMUM + 1, HIDLeds_SCROLLLOCK, + HIDReport_OUTPUT + 1, HIDReport_VARIABLE, +\endcode + + Since the previous report only contains 3 bits, the data must be padded to a + multiple of one byte. This is done by using constant Output data, as follows: +\code + // Output report: padding + HIDReport_GLOBAL_REPORTCOUNT + 1, 1, + HIDReport_GLOBAL_REPORTSIZE + 1, 5, + HIDReport_OUTPUT + 1, HIDReport_CONSTANT, +\endcode + + The last item, }End Collection}, is necessary to close the previously opened + }Application Collection}. +\code + HIDReport_ENDCOLLECTION +}; +\endcode + + The Input and Output reports defined by this descriptor can be modeled by the + following structures: +\code +// HID Input Report +typedef struct { + + // State of modifier keys. + unsigned char bmModifierKeys:8; + // Key codes of pressed keys. + unsigned char pressedKeys[HIDDMouseInputReport_MAXKEYPRESSES]; + +} __attribute__ ((packed)) HIDDMouseInputReport; // GCC +// HID Output Report +typedef struct { + + unsigned char numLockStatus:1, // State of the num. lock LED. + capsLockStatus:1, // State of the caps lock LED. + scrollLockStatus:1, // State of the scroll lock LED. + padding:5; // Padding bits. + +} __attribute__ ((packed)) HIDDMouseOutputReport; // GCC +\endcode + + An instance of each one of the reports is stored in a HIDDMouseDriver + structure, which holds the standard class driver and HID Mouse-specific + data. + + !!Physical Descriptor + A Physical descriptor is useless for a Mouse %device, so none are defined + in this example. + + !!Endpoint Descriptor + Following the Interface and HID-specific descriptors, the two necessary + endpoints are defined. +\code +// Interrupt IN endpoint descriptor +{ + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDMouseDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDMouseInputReport), + HIDDMouseDriverDescriptors_INTERRUPTIN_POLLING +}, +// Interrupt OUT endpoint descriptor +{ + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDMouseDriverDescriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDMouseOutputReport), + HIDDMouseDriverDescriptors_INTERRUPTIN_POLLING +} +\endcode + + !!String Descriptors + Please refer to "Usage: USBD VID, PID & Strings". + + !!!Class-specific requests + A driver request handler should first differentiate between class-specific and + standard requests using the corresponding bits in the }bmRequestType} field. + In most cases, standard requests can be immediately forwarded to the standard + request handler method; class-specific methods must be decoded and treated by + the custom handler. + + !!GetDescriptor + Three values have been added by the HID specification for the #GET_DESCRIPTOR# + request. The high byte of the }wValue} field contains the type of the + requested descriptor; in addition to the standard types, the #HID + specification# adds the #HID descriptor# (21h), the #Report descriptor# + (22h) and the #Physical descriptor# (23h) types. + + There is no particular action to perform besides sending the descriptor. This + can be done by using the USBD_Write method, after the requested descriptor has + been identified: +\code +switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, + // otherwise forward it to + // the standard driver + if (!HIDDMouseDriver_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(&(hiddMouseDriver.usbdDriver), + request); + } + break; + + default: + USBDDriver_RequestHandler(&(hiddMouseDriver.usbdDriver), + request); +} +\endcode + A slight complexity of the GET_DESCRIPTOR and SET_DESCRIPTOR requests is that + those are standard requests, but the standard request handler + (USBDDriver_RequestHandler) must not always be called to treat them (since + they may refer to HID descriptors). The solution is to first identify + GET/SET_DESCRIPTOR requests, treat the HID-specific cases and, finally, + forward any other request to the standard handler. + + In this case, a GET_DESCRIPTOR request for the Physical descriptor is first + forwarded to the standard handler, and STALLed there because it is not + recognized. This is done because the %device does not have any Physical + descriptors, and thus, does not need to handle the associated request. + + !!SetDescriptor + This request is optional and is never issued by most hosts. It is not + implemented in this example. + + !!GetReport + Since the HID Mouse defines two different reports, the Report Type value + specified by this request (upper byte of the }wValue} field) must be examined + to decide which report to send. If the type value is 01h, then the Input + report must be returned; if it is 02h, the Output report is requested: +\code +case HIDGenericRequest_GETREPORT: +//------------------------------- + type = HIDReportRequest_GetReportType(request); + length = USBGenericRequest_GetLength(request); + switch (type) { + + case HIDReportRequest_INPUT: + + // Adjust size and send report + if (length > sizeof(HIDDMouseInputReport)) { + + length = sizeof(HIDDMouseInputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddMouseDriver.inputReport), + length, + 0, // No callback + 0); + break; + + case HIDReportRequest_OUTPUT: + + // Adjust size and send report + if (length > sizeof(HIDDMouseOutputReport)) { + + length = sizeof(HIDDMouseOutputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddMouseDriver.outputReport), + length, + 0, // No callback + 0); + break; + + default: + USBD_Stall(0); + } +break; +\endcode + + !!SetReport + For an HID Mouse, the #SET_REPORT# command can be sent by the host to + change the state of the LEDs. Normally, the dedicated Interrupt OUT endpoint + will be used for this; but in some cases, using the default Control endpoint + can save some bandwidth on the host side. + + Note that the SET_REPORT request can be directed at the Input report of the + Mouse; in this case, it can be safely discarded, according to the HID + specification. Normally, most host drivers only target the Output report. The + Report Type value is stored in the upper byte of the }wValue} field. + + The length of the data phase to follow is stored in the }wLength} field of the + request. It should be equal to the total length of the Output report. If it is + different, the report status must still be updated with the received data as + best as possible. + + When the reception of the new data is completed, some processing must be done + to enable/disable the corresponding LEDs. This is done in the callback + function passed as an argument to USBD_Read: +\code +case HIDGenericRequest_SETREPORT: +//------------------------------- + type = HIDReportRequest_GetReportType(request); + length = USBGenericRequest_GetLength(request); + switch(type) { + + case HIDReportRequest_INPUT: + // SET_REPORT requests on input reports are ignored + USBD_Stall(0); + break; + + case HIDReportRequest_OUTPUT: + // Check report length + if (length != sizeof(HIDDMouseOutputReport)) { + + USBD_Stall(0); + } + else { + + USBD_Read(0, // Endpoint #0 + &(hiddMouseDriver.outputReport), + length, + (TransferCallback) HIDDMouseDriver_ReportReceived, + 0); // No argument to the callback function + } + break; + + default: + USBD_Stall(0); + } +break; +\endcode + + !!SetIdle + In this case study, the #SET_IDLE# request is used to set a delay before a key + is repeated. This is common behavior on Mouse devices. Usually, this delay + is set to about 500 ms by the host. + + The only action here is to store the new Idle rate. The management of this + setting must be done in the main function, since Interrupt IN reports are sent + from there. + + In practice, it is not necessary to perform any action, apart from sending a + zero-length packet to acknowledge it. The main application however has to make + sure that only new reports are sent by the %device. +\code +case HIDGenericRequest_SETIDLE: +//----------------------------- + hiddMouseDriver.inputReportIdleRate = + HIDIdleRequest_GetIdleRate(request); + USBD_Write(0, 0, 0, 0, 0); +break; +\endcode + + !!GetIdle + The only necessary operation for this request is to send the previously saved + Idle rate. This is done by calling the USBD_Write method with the one-byte + variable as its parameter: +\code +case HIDGenericRequest_GETIDLE: +//----------------------------- + USBD_Write(0, &(hiddMouseDriver.inputReportIdleRate), 1, 0, 0); +break; +\endcode + + !!GetProtocol, SetProtocol + This HID Mouse example does not support the Boot protocol, so there is no + need to implement the SET_PROTOCOL and GET_PROTOCOL requests. This means they + can be safely STALLed when received. + + !!!Main Application + Like the mouse example, the main program must perform two different + operations. First, it has to monitor the physical inputs used as keys. In the + example software, the buttons present on the evaluation boards are used to + produce several modifier and alphanumeric keys. + + Also, the main program is in charge of sending reports as they are modified, + taking into account the Idle rate specified by the host. Idle rate management + can be carried out by firing/resetting a timer once a new report is sent; if + the timer expires, this means the Input report has not changed since. + According to the HID specification, a single instance of the report must be + sent in this case. + + Finally, the HID specification also defines that if too many keys are pressed + at the same time, the %device should report an }ErrorRollOver} usage value + (01h) in every byte of the key array. This has to be handled by the main + application as well. + +*/ \ No newline at end of file diff --git a/usb/device/hid-transfer/HIDDTransferDriver.c b/usb/device/hid-transfer/HIDDTransferDriver.c new file mode 100644 index 0000000..0810474 --- /dev/null +++ b/usb/device/hid-transfer/HIDDTransferDriver.c @@ -0,0 +1,479 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDTransferDriver.h" +#include "HIDDTransferDriverDesc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Driver structure for an HID device implementing keyboard functionalities. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard USB device driver instance. + USBDDriver usbdDriver; + + // OUT Report - block input + unsigned short iReportLen; + unsigned char iReportBuf[HIDDTransferDriver_REPORTSIZE]; + + // IN Report - block output + unsigned short oReportLen; + unsigned char oReportBuf[HIDDTransferDriver_REPORTSIZE]; + + // Interrupt OUT Data - input + /// Input data length + unsigned short iLen; + /// Input (report) Buffer + unsigned char iBuf[HIDDTransferDriver_REPORTSIZE]; + + // Nothing more now... + +} HIDDTransferDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Static instance of the HID Transfer device driver. +static HIDDTransferDriver hiddTransferDriver; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns the descriptor requested by the host. +/// \param type Descriptor type. +/// \param length Maximum number of bytes to send. +/// \return 1 if the request has been handled by this function, otherwise 0. +//------------------------------------------------------------------------------ +static unsigned char HIDDTransferDriver_GetDescriptor(unsigned char type, + unsigned char length) +{ + const USBConfigurationDescriptor *pConfiguration; + HIDDescriptor *hidDescriptors[2]; + + switch (type) { + + case HIDGenericDescriptor_REPORT: + TRACE_INFO("Report "); + + // Adjust length and send report descriptor + if (length > HIDDTransferDriverDescriptors_REPORTSIZE) { + + length = HIDDTransferDriverDescriptors_REPORTSIZE; + } + USBD_Write(0, &hiddReportDescriptor, length, 0, 0); + break; + + case HIDGenericDescriptor_HID: + TRACE_INFO("HID "); + + // Configuration descriptor is different depending on configuration + if (USBD_IsHighSpeed()) { + + pConfiguration = + hiddTransferDriver.usbdDriver.pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = + hiddTransferDriver.usbdDriver.pDescriptors->pFsConfiguration; + } + + // Parse the device configuration to get the HID descriptor + USBConfigurationDescriptor_Parse(pConfiguration, 0, 0, + (USBGenericDescriptor **) &hidDescriptors[0]); + + // Adjust length and send HID descriptor + if (length > sizeof(HIDDescriptor)) { + + length = sizeof(HIDDescriptor); + } + USBD_Write(0, hidDescriptors[0], length, 0, 0); + break; + + default: + return 0; + } + + return 1; +} + +//------------------------------------------------------------------------------ +/// Callback function when SetReport request data received from host +/// \param pArg Pointer to additional argument struct +/// \param status Result status +/// \param transferred Number of bytes transferred +/// \param remaining Number of bytes that are not transferred yet +//------------------------------------------------------------------------------ +static void HIDDTransferDriver_ReportReceived(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining) +{ + hiddTransferDriver.iReportLen = transferred; + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Callback function when GetReport request data sent to host +/// \param pArg Pointer to additional argument struct +/// \param status Result status +/// \param transferred Number of bytes transferred +/// \param remaining Number of bytes that are not transferred yet +//------------------------------------------------------------------------------ +static void HIDDTransferDriver_ReportSent(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining) +{ + USBD_Read(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Callback function when interrupt OUT data received from host +/// \param pArg Pointer to additional argument +/// \param status Result status +/// \param transferred Number of bytes transferred +/// \param remaining Number of bytes that are not transferred yet +//------------------------------------------------------------------------------ +static void HIDDTransferDriver_DataReceived(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining) +{ + hiddTransferDriver.iLen = transferred; + + USBD_Read(HIDDTransferDriverDescriptors_INTERRUPTOUT, + hiddTransferDriver.iBuf, + HIDDTransferDriver_REPORTSIZE, + (TransferCallback)HIDDTransferDriver_DataReceived, + 0); +} + +//------------------------------------------------------------------------------ +// Optional RequestReceived() callback re-implementation +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) + +//------------------------------------------------------------------------------ +/// Callback function when new request receivce from host +/// \param request Pointer to the USBGenericRequest instance +//------------------------------------------------------------------------------ +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + HIDDTransferDriver_RequestHandler(request); +} + +#endif + +//------------------------------------------------------------------------------ +// ConfigurationChanged() callback re-implementation +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback function when configureation changed +/// \param cfgnum New configuration number +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + if (cfgnum > 0) { + + hiddTransferDriver.iLen = 0; + USBD_Read(HIDDTransferDriverDescriptors_INTERRUPTOUT, + hiddTransferDriver.iBuf, + HIDDTransferDriver_REPORTSIZE, + HIDDTransferDriver_DataReceived, + 0); + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +/// Initializes the HID Transfer %device driver. +//------------------------------------------------------------------------------ +void HIDDTransferDriver_Initialize() +{ + hiddTransferDriver.iReportLen = 0; + + USBDDriver_Initialize(&(hiddTransferDriver.usbdDriver), + &hiddTransferDriverDescriptors, + 0); // Multiple interface settings not supported + USBD_Init(); +} + +//------------------------------------------------------------------------------ +/// Handles HID-specific SETUP request sent by the host. +/// \param request Pointer to a USBGenericRequest instance +//------------------------------------------------------------------------------ +void HIDDTransferDriver_RequestHandler(const USBGenericRequest *request) +{ + TRACE_INFO("NewReq "); + + // Check if this is a standard request + if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // This is a standard request + switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, otherwise forward it to + // the standard driver + if (!HIDDTransferDriver_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(&(hiddTransferDriver.usbdDriver), + request); + } + break; + + case USBGenericRequest_CLEARFEATURE: + /* Check which is the requested feature */ + switch (USBFeatureRequest_GetFeatureSelector(request)) { + case USBFeatureRequest_ENDPOINTHALT: + { unsigned char ep = + USBGenericRequest_GetEndpointNumber(request); + if (USBD_IsHalted(ep)) { + /* Unhalt endpoint restart OUT EP + */ + USBD_Unhalt(ep); + if (ep == HIDDTransferDriverDescriptors_INTERRUPTOUT) { + hiddTransferDriver.iLen = 0; + USBD_Read(HIDDTransferDriverDescriptors_INTERRUPTOUT, + hiddTransferDriver.iBuf, + HIDDTransferDriver_REPORTSIZE, + HIDDTransferDriver_DataReceived, + 0); + } + } + /* and send a zero-length packet */ + USBD_Write(0, 0, 0, 0, 0); + return; /* Done, no others */ + } + } + + default: + USBDDriver_RequestHandler(&(hiddTransferDriver.usbdDriver), + request); + } + } + // Check if this is a class request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + unsigned short length = USBGenericRequest_GetLength(request); + unsigned char type = HIDReportRequest_GetReportType(request); + + switch (USBGenericRequest_GetRequest(request)) { + + case HIDGenericRequest_SETREPORT: + + if (length <= HIDDTransferDriver_REPORTSIZE && + type == HIDReportRequest_OUTPUT) { + + USBD_Read(0, + hiddTransferDriver.iReportBuf, + length, + HIDDTransferDriver_ReportReceived, + 0); // No argument to the callback function + } + else { + + USBD_Stall(0); + } + break; + + case HIDGenericRequest_GETREPORT: + + if (length <= HIDDTransferDriver_REPORTSIZE && + type == HIDReportRequest_INPUT) { + + USBD_Write(0, + hiddTransferDriver.oReportBuf, + length, + HIDDTransferDriver_ReportSent, + 0); + } + else { + + USBD_Stall(0); + } + break; + + default: + TRACE_WARNING( + "HIDDTransferDriver_RequestHandler: request 0x%02X\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + } + else { + + // Vendor request ? + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Try to read request buffer of SetReport. +/// Set pData to 0 to get current data length only. +/// \param pData Pointer to data buffer +/// \param dLength Data buffer length +/// \return Number of bytes read +//------------------------------------------------------------------------------ +unsigned short HIDDTransferDriver_ReadReport(void *pData, + unsigned int dLength) +{ + if (pData == 0) { + + return hiddTransferDriver.iReportLen; + } + + if (dLength > HIDDTransferDriver_REPORTSIZE) { + + dLength = HIDDTransferDriver_REPORTSIZE; + } + if (dLength > hiddTransferDriver.iReportLen) { + + dLength = hiddTransferDriver.iReportLen; + } + hiddTransferDriver.iReportLen = 0; + memcpy(pData, hiddTransferDriver.iReportBuf, dLength); + + return dLength; +} + +//------------------------------------------------------------------------------ +/// Try to read request buffer of interrupt OUT EP. +/// Set pData to 0 to get current data length only. +/// \param pData Pointer to data buffer +/// \param dLength Data buffer length +/// \return Number of bytes read +//------------------------------------------------------------------------------ +unsigned short HIDDTransferDriver_Read(void *pData, + unsigned int dLength) +{ + if (pData == 0) { + + return hiddTransferDriver.iLen; + } + + if (dLength > HIDDTransferDriver_REPORTSIZE) { + + dLength = HIDDTransferDriver_REPORTSIZE; + } + if (dLength > hiddTransferDriver.iLen) { + + dLength = hiddTransferDriver.iLen; + } + hiddTransferDriver.iLen = 0; + memcpy(pData, hiddTransferDriver.iBuf, dLength); + + return dLength; +} + +//------------------------------------------------------------------------------ +/// Set data in IN report buffer, which will be sent when GetReport request +/// issued. +/// \param pData Pointer to the data sent. +/// \param dLength The data length. +//------------------------------------------------------------------------------ +void HIDDTransferDriver_SetReport(const void *pData, + unsigned int dLength) +{ + if (pData == 0 || dLength == 0) + return; + if (dLength != HIDDTransferDriver_REPORTSIZE) { + dLength = HIDDTransferDriver_REPORTSIZE; + } + if (hiddTransferDriver.oReportLen) { + TRACE_INFO("Changing IN report!\n\r"); + } + memcpy(hiddTransferDriver.oReportBuf, pData, dLength); + hiddTransferDriver.oReportLen = dLength; +} + +//------------------------------------------------------------------------------ +/// Write data through USB interrupt IN EP. +/// \param pData Pointer to the data sent. +/// \param dLength The data length. +/// \param fCallback Callback function invoked when transferring done. +/// \param pArg Pointer to additional arguments. +//------------------------------------------------------------------------------ +unsigned char HIDDTransferDriver_Write(const void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArg) +{ + if (dLength != HIDDTransferDriver_REPORTSIZE) { + + dLength = HIDDTransferDriver_REPORTSIZE; + } + return USBD_Write(HIDDTransferDriverDescriptors_INTERRUPTIN, + pData, dLength, + fCallback, pArg); +} + +//------------------------------------------------------------------------------ +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//------------------------------------------------------------------------------ +void HIDDTransferDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&(hiddTransferDriver.usbdDriver))) { + + USBD_RemoteWakeUp(); + } +} + diff --git a/usb/device/hid-transfer/HIDDTransferDriver.h b/usb/device/hid-transfer/HIDDTransferDriver.h new file mode 100644 index 0000000..f374ae0 --- /dev/null +++ b/usb/device/hid-transfer/HIDDTransferDriver.h @@ -0,0 +1,87 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of methods for using a HID transfer %device driver. + + !!!Usage + + -# Re-implement the USBDCallbacks_RequestReceived callback to forward + requests to HIDDTransferDriver_RequestHandler. This is done + automatically unless the NOAUTOCALLBACK symbol is defined during + compilation. + -# Initialize the driver using HIDDTransferDriver_Initialize. The + USB driver is automatically initialized by this method. + -# Call the HIDDTransferDriver_Write method when sendint data to host. + -# Call the HIDDTransferRead, HIDDTransferReadReport when checking and getting + received data from host. +*/ + +#ifndef HIDDKEYBOARDDRIVER_H +#define HIDDKEYBOARDDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void HIDDTransferDriver_Initialize(); + +extern void HIDDTransferDriver_RequestHandler(const USBGenericRequest *request); + +extern unsigned short HIDDTransferDriver_Read(void *pData, + unsigned int dLength); + +extern unsigned short HIDDTransferDriver_ReadReport(void *pData, + unsigned int dLength); + +extern unsigned char HIDDTransferDriver_Write(const void *pData, + unsigned int size, + TransferCallback callback, + void *pArg); + + +extern void HIDDTransferDriver_RemoteWakeUp(void); + +#endif //#ifndef HIDDKEYBOARDDRIVER_H + diff --git a/usb/device/hid-transfer/HIDDTransferDriverDesc.c b/usb/device/hid-transfer/HIDDTransferDriverDesc.c new file mode 100644 index 0000000..5cd6363 --- /dev/null +++ b/usb/device/hid-transfer/HIDDTransferDriverDesc.c @@ -0,0 +1,417 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/* + Title: HIDDTransferDriverDescriptors + + About: Purpose + Declaration of the descriptors used by the HID device Transfer driver. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "HIDDTransferDriverDesc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "HID Transfer Device Descriptor IDs" +/// ... +/// +/// !IDs +/// - HIDDTransferDriverDescriptors_PRODUCTID +/// - HIDDTransferDriverDescriptors_VENDORID +/// - HIDDTransferDriverDescriptors_RELEASE + +/// Device product ID. +#define HIDDTransferDriverDescriptors_PRODUCTID 0x6201 +/// Device vendor ID. +#define HIDDTransferDriverDescriptors_VENDORID 0x03EB +/// Device release number. +#define HIDDTransferDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +/// List of descriptors that make up the configuration descriptors of a +/// device using the HID Transfer driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Configuration descriptor. + USBConfigurationDescriptor configuration; + /// Interface descriptor. + USBInterfaceDescriptor interface; + /// HID descriptor. + HIDDescriptor hid; + /// Interrupt IN endpoint descriptor. + USBEndpointDescriptor interruptIn; + /// Interrupt OUT endpoint descriptor. + USBEndpointDescriptor interruptOut; + +} __attribute__ ((packed)) HIDDTransferDriverConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Device descriptor. +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDDTransferDriverDescriptors_VENDORID, + HIDDTransferDriverDescriptors_PRODUCTID, + HIDDTransferDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Device qualifier descriptor (high-speed only). +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // One possible configuration + 0 // Reserved +}; +#endif + +/// Configuration descriptor. +static const HIDDTransferDriverConfigurationDescriptors configurationDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HIDDTransferDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDTransferDriverDescriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDTransferDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE( + HIDDTransferDriverDescriptors_INTERRUPTIN), + MIN(USBEndpointDescriptor_MAXINTERRUPTSIZE_FS, + HIDDTransferDriver_REPORTSIZE)), + HIDDTransferDriverDescriptors_INTERRUPTIN_POLLING + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDTransferDriverDescriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE( + HIDDTransferDriverDescriptors_INTERRUPTOUT), + MIN(USBEndpointDescriptor_MAXINTERRUPTSIZE_FS, + HIDDTransferDriver_REPORTSIZE)), + HIDDTransferDriverDescriptors_INTERRUPTOUT_POLLING + } +}; + +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Other-speed configuration descriptor. +static const HIDDTransferDriverConfigurationDescriptors otherSpeedDescriptors = { + + // Configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(HIDDTransferDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor + }, + // HID descriptor + { + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDTransferDriverDescriptors_REPORTSIZE + }, + // Interrupt IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDTransferDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE( + HIDDTransferDriverDescriptors_INTERRUPTIN), + MIN(USBEndpointDescriptor_MAXINTERRUPTSIZE_HS, + HIDDTransferDriver_REPORTSIZE)), + HIDDTransferDriverDescriptors_INTERRUPTIN_POLLING + }, + // Interrupt OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDTransferDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE( + HIDDTransferDriverDescriptors_INTERRUPTOUT), + MIN(USBEndpointDescriptor_MAXINTERRUPTSIZE_HS, + HIDDTransferDriver_REPORTSIZE)), + HIDDTransferDriverDescriptors_INTERRUPTIN_POLLING + } +}; +#endif + +/* + Variables: String descriptors + languageIdDescriptor - Language ID string descriptor. + manufacturerDescriptor - Manufacturer name. + productDescriptor - Product name. + serialNumberDescriptor - Product serial number. + stringDescriptors - Array of pointers to string descriptors. +*/ +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L') +}; + +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(22), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('H'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('R'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('N'), + USBStringDescriptor_UNICODE('S'), + USBStringDescriptor_UNICODE('F'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('R'), +}; + +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(12), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3'), + USBStringDescriptor_UNICODE('4'), + USBStringDescriptor_UNICODE('5'), + USBStringDescriptor_UNICODE('6'), + USBStringDescriptor_UNICODE('7'), + USBStringDescriptor_UNICODE('8'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('F') +}; + +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor +}; + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +/// List of descriptors used by the HID Transfer driver. +USBDDriverDescriptors hiddTransferDriverDescriptors = { + + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptors, +#if defined (CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptors, + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptors, + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptors, +#else + 0, // No full-speed device qualifier descriptor + 0, // No full-speed other speed configuration + 0, // No high-speed device descriptor + 0, // No high-speed configuration descriptor + 0, // No high-speed device qualifier descriptor + 0, // No high-speed other speed configuration descriptor +#endif + stringDescriptors, + 4 // Four string descriptors in list +}; + +/// Report descriptor used by the driver. +const unsigned char hiddReportDescriptor[] = { + + // Global Usage Page + HIDReport_GLOBAL_USAGEPAGE + 2, 0xFF, 0xFF, // Vendor-defined + // Collection: Application + HIDReport_LOCAL_USAGE + 1, 0xFF, // Vendor-defined + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, + // Input report: Vendor-defined + HIDReport_LOCAL_USAGE + 1, 0xFF, // Vendor-defined usage + HIDReport_GLOBAL_REPORTCOUNT + 1, HIDDTransferDriver_REPORTSIZE, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, (unsigned char) -128, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, (unsigned char) 127, + HIDReport_INPUT + 1, 0, // No Modifiers + + // Output report: vendor-defined + HIDReport_LOCAL_USAGE + 1, 0xFF, // Vendor-defined usage + HIDReport_GLOBAL_REPORTCOUNT + 1, HIDDTransferDriver_REPORTSIZE, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, (unsigned char) -128, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, (unsigned char) 127, + HIDReport_OUTPUT + 1, 0, // No Modifiers + + HIDReport_ENDCOLLECTION +}; + diff --git a/usb/device/hid-transfer/HIDDTransferDriverDesc.h b/usb/device/hid-transfer/HIDDTransferDriverDesc.h new file mode 100644 index 0000000..6b9beb7 --- /dev/null +++ b/usb/device/hid-transfer/HIDDTransferDriverDesc.h @@ -0,0 +1,87 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definitions of the descriptors required by the HID transfer %device driver. + + !!!Usage + + -# Use the hiddTransferDriverDescriptors variable to initialize a + USBDDriver instance. + -# Send hiddReportDescriptor to the host when a GET_DESCRIPTOR request + for the report descriptor is received. +*/ + +#ifndef HIDDKEYBOARDDRIVERDESCRIPTORS_H +#define HIDDKEYBOARDDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Interrupt IN endpoint number. +#define HIDDTransferDriverDescriptors_INTERRUPTIN 1 +/// Polling rate in ms +#define HIDDTransferDriverDescriptors_INTERRUPTIN_POLLING 50 +/// Interrupt IN endpoint polling rate (in milliseconds). +#define HIDDTransferDriverDescriptors_INTERRUPTOUT 2 +/// Polling rate in ms +#define HIDDTransferDriverDescriptors_INTERRUPTOUT_POLLING 50 + +/// Size of the report descriptor in bytes. +#define HIDDTransferDriverDescriptors_REPORTSIZE 32 + +/// Size of the input and output report, in bytes +#define HIDDTransferDriver_REPORTSIZE 32 + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ +/* + Variables: HID keyboard driver descriptors + hiddTransferDriverDescriptors - List of descriptors used by the HID + keyboard driver. + hiddReportDescriptor - Report descriptor used by the driver. +*/ +extern USBDDriverDescriptors hiddTransferDriverDescriptors; +extern const unsigned char hiddReportDescriptor[]; + +#endif //#ifndef HIDDKEYBOARDDRIVERDESCRIPTORS_H + diff --git a/usb/device/hid-transfer/hid-transfer.dir b/usb/device/hid-transfer/hid-transfer.dir new file mode 100644 index 0000000..a478e50 --- /dev/null +++ b/usb/device/hid-transfer/hid-transfer.dir @@ -0,0 +1,473 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB HID +/// %device - USB HID Transfer driver, to implement an USB HID compatible +/// %device for customized data transmitting. +/// +/// !!!Contents +/// +/// There are three things for the implement of the USB HID Transfer driver: +/// - Implement the USB HID driver structs and functions for the %device, +/// to initialize, to handle HID-specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// endpoints, +/// - Create the HID Transfer device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the %device as a USB Transfer %device. +/// - Implement methods to read/write data through interrupt endpoints, so that +/// host and device can exchange data. +/// +/// For more information about what a particular group contains, please refer to +/// "USB HID Transfer". +//------------------------------------------------------------------------------ + +/** + \page "USB HID Transfer" + This page describes how to use the "AT91 USB device framework" to produce a USB + HID Transfer driver, which appears as a USB HID complient device on host. + + Details about the USB and the HID class can be found in the }USB specification + 2.0} and the }HID specification 1.11}, respectively. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Device Class Definition for HID 1.11 + - + HID Usage Tables 1.12 + + !!!HID Basic + See "USB HID Basic". + + !!!Architecture + See "USB Device Framework Architecture". + + !!!Descriptors + + ... + + !!Device Descriptor + The Device descriptor of an HID %device is very basic, since the HID class + code is only specified at the Interface level. Thus, it only contains + standard values, as shown below: +\code +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + HIDDeviceDescriptor_CLASS, + HIDDeviceDescriptor_SUBCLASS, + HIDDeviceDescriptor_PROTOCOL, + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), + HIDDKeyboardDriverDescriptors_VENDORID, + HIDDKeyboardDriverDescriptors_PRODUCTID, + HIDDKeyboardDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; +\endcode + Note that the Vendor ID is a special value attributed by the USB-IF + organization. The product ID can be chosen freely by the vendor. + + !!Configuration Descriptor + Since one interface is required by the HID specification, this must be + specified in the Configuration descriptor. There is no other value of + interest to put here. +\code +// Configuration descriptor +{ + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(HIDDKeyboardDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) +}, +\endcode + When the Configuration descriptor is requested by the host (by using the + GET_DESCRIPTOR command), the %device must also sent all the related + descriptors, i.e. Interface, Endpoint and Class-Specific descriptors. It is + convenient to create a single structure to hold all this data, for sending + everything in one chunk. In the example software, a + HIDDKeyboardDriverConfigurationDescriptors structure has been declared for + that. + + !!HID Class Interface Descriptor + Since a keyboard %device needs to transmit as well as receive data, two + Interrupt (IN & OUT) endpoints are needed. This must be indicated in the + Interface descriptor. Conversely to the mouse example, the Boot protocol is + not implemented here, since there are more constraints on a keyboard %device. +\code +// Interface descriptor +{ + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0 + 0, // This is alternate setting #0 + 2, // Two endpoints used + HIDInterfaceDescriptor_CLASS, + HIDInterfaceDescriptor_SUBCLASS_NONE, + HIDInterfaceDescriptor_PROTOCOL_NONE, + 0 // No associated string descriptor +}, +\endcode + + !!HID Descriptor + While a HID keyboard produces two different reports, one Input and one Output, + only one Report descriptor can be used to describe them. Since having Physical + descriptors is also useless for a keyboard, there will only be one HID class + descriptor specified here. + + For a keyboard, the }bCountryCode} field can be used to specify the language + of the key caps. As this is optional, it is simply set to 00h in the example: +\code +// HID descriptor +{ + sizeof(HIDDescriptor), + HIDGenericDescriptor_HID, + HIDDescriptor_HID1_11, + 0, // Device is not localized, no country code + 1, // One HID-specific descriptor (apart from this one) + HIDGenericDescriptor_REPORT, + HIDDKeyboardDriverDescriptors_REPORTSIZE +}, +\endcode + + !!Report Descriptor + Two current reports are defined in the Report descriptor. The first one is + used to notify the host of which keys are pressed, with both modifier keys + (alt, ctrl, etc.) and alphanumeric keys. The second report is necessary for + the host to send the LED (num lock, caps lock, etc.) states. + + The Report descriptor starts with the global %device functionality, described + with a #Usage Page# and a #Usage# items: +\code +const unsigned char hiddReportDescriptor[] = { + + HIDReport_GLOBAL_USAGEPAGE + 2, 0xFF, 0xFF, // Vendor-defined + HIDReport_LOCAL_USAGE + 1, 0xFF, // Vendor-defined +\endcode + + An Application collection is then defined to group the reports together: +\code + HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION, +\endcode + + The first report to be defined is the input report, all data in the buffer + is vendor defined: +\code + // Input report: Vendor-defined + HIDReport_LOCAL_USAGE + 1, 0xFF, // Vendor-defined usage + HIDReport_GLOBAL_REPORTCOUNT + 1, HIDDTransferDriver_REPORTSIZE, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, (unsigned char) -128, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, (unsigned char) 127, + HIDReport_INPUT + 1, 0, // No Modifiers +\endcode + + The output report is then defined, data is for the user to decode: +\code + // Output report: vendor-defined + HIDReport_LOCAL_USAGE + 1, 0xFF, // Vendor-defined usage + HIDReport_GLOBAL_REPORTCOUNT + 1, HIDDTransferDriver_REPORTSIZE, + HIDReport_GLOBAL_REPORTSIZE + 1, 8, + HIDReport_GLOBAL_LOGICALMINIMUM + 1, (unsigned char) -128, + HIDReport_GLOBAL_LOGICALMAXIMUM + 1, (unsigned char) 127, + HIDReport_OUTPUT + 1, 0, // No Modifiers +\endcode + + The last item, }End Collection}, is necessary to close the previously opened + }Application Collection}. +\code + HIDReport_ENDCOLLECTION +}; +\endcode + + The input report and output report are all user defined. We define the first + byte as bit map of push buttons and LEDs, remaining bytes as data. + + !!Physical Descriptor + A Physical descriptor is useless for a general transfer %device, so none is + defined in this example. + + !!Endpoint Descriptor + Following the Interface and HID-specific descriptors, the two necessary + endpoints are defined. +\code +// Interrupt IN endpoint descriptor +{ + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + HIDDKeyboardDriverDescriptors_INTERRUPTIN), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardInputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING +}, +// Interrupt OUT endpoint descriptor +{ + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + HIDDKeyboardDriverDescriptors_INTERRUPTOUT), + USBEndpointDescriptor_INTERRUPT, + sizeof(HIDDKeyboardOutputReport), + HIDDKeyboardDriverDescriptors_INTERRUPTIN_POLLING +} +\endcode + + !!String Descriptors + Please refer to "Usage: USBD VID, PID & Strings". + + !!!Class-specific requests + A driver request handler should first differentiate between class-specific and + standard requests using the corresponding bits in the }bmRequestType} field. + In most cases, standard requests can be immediately forwarded to the standard + request handler method; class-specific methods must be decoded and treated by + the custom handler. + + !!GetDescriptor + Three values have been added by the HID specification for the #GET_DESCRIPTOR# + request. The high byte of the }wValue} field contains the type of the + requested descriptor; in addition to the standard types, the #HID + specification# adds the #HID descriptor# (21h), the #Report descriptor# + (22h) and the #Physical descriptor# (23h) types. + + There is no particular action to perform besides sending the descriptor. This + can be done by using the USBD_Write method, after the requested descriptor has + been identified: +\code +switch (USBGenericRequest_GetRequest(request)) { + + case USBGenericRequest_GETDESCRIPTOR: + // Check if this is a HID descriptor, + // otherwise forward it to + // the standard driver + if (!HIDDKeyboardDriver_GetDescriptor( + USBGetDescriptorRequest_GetDescriptorType(request), + USBGenericRequest_GetLength(request))) { + + USBDDriver_RequestHandler(&(hiddKeyboardDriver.usbdDriver), + request); + } + break; + + default: + USBDDriver_RequestHandler(&(hiddKeyboardDriver.usbdDriver), + request); +} +\endcode + A slight complexity of the GET_DESCRIPTOR and SET_DESCRIPTOR requests is that + those are standard requests, but the standard request handler + (USBDDriver_RequestHandler) must not always be called to treat them (since + they may refer to HID descriptors). The solution is to first identify + GET/SET_DESCRIPTOR requests, treat the HID-specific cases and, finally, + forward any other request to the standard handler. + + In this case, a GET_DESCRIPTOR request for the Physical descriptor is first + forwarded to the standard handler, and STALLed there because it is not + recognized. This is done because the %device does not have any Physical + descriptors, and thus, does not need to handle the associated request. + + !!SetDescriptor + This request is optional and is never issued by most hosts. It is not + implemented in this example. + + !!GetReport + Since the HID keyboard defines two different reports, the Report Type value + specified by this request (upper byte of the }wValue} field) must be examined + to decide which report to send. If the type value is 01h, then the Input + report must be returned; if it is 02h, the Output report is requested: +\code +case HIDGenericRequest_GETREPORT: +//------------------------------- + type = HIDReportRequest_GetReportType(request); + length = USBGenericRequest_GetLength(request); + switch (type) { + + case HIDReportRequest_INPUT: + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardInputReport)) { + + length = sizeof(HIDDKeyboardInputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.inputReport), + length, + 0, // No callback + 0); + break; + + case HIDReportRequest_OUTPUT: + + // Adjust size and send report + if (length > sizeof(HIDDKeyboardOutputReport)) { + + length = sizeof(HIDDKeyboardOutputReport); + } + USBD_Write(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + 0, // No callback + 0); + break; + + default: + USBD_Stall(0); + } +break; +\endcode + + !!SetReport + For an HID keyboard, the #SET_REPORT# command can be sent by the host to + change the state of the LEDs. Normally, the dedicated Interrupt OUT endpoint + will be used for this; but in some cases, using the default Control endpoint + can save some bandwidth on the host side. + + Note that the SET_REPORT request can be directed at the Input report of the + keyboard; in this case, it can be safely discarded, according to the HID + specification. Normally, most host drivers only target the Output report. The + Report Type value is stored in the upper byte of the }wValue} field. + + The length of the data phase to follow is stored in the }wLength} field of the + request. It should be equal to the total length of the Output report. If it is + different, the report status must still be updated with the received data as + best as possible. + + When the reception of the new data is completed, some processing must be done + to enable/disable the corresponding LEDs. This is done in the callback + function passed as an argument to USBD_Read: +\code +case HIDGenericRequest_SETREPORT: +//------------------------------- + type = HIDReportRequest_GetReportType(request); + length = USBGenericRequest_GetLength(request); + switch(type) { + + case HIDReportRequest_INPUT: + // SET_REPORT requests on input reports are ignored + USBD_Stall(0); + break; + + case HIDReportRequest_OUTPUT: + // Check report length + if (length != sizeof(HIDDKeyboardOutputReport)) { + + USBD_Stall(0); + } + else { + + USBD_Read(0, // Endpoint #0 + &(hiddKeyboardDriver.outputReport), + length, + (TransferCallback) HIDDKeyboardDriver_ReportReceived, + 0); // No argument to the callback function + } + break; + + default: + USBD_Stall(0); + } +break; +\endcode + + !!SetIdle + In this case study, the #SET_IDLE# request is used to set a delay before a key + is repeated. This is common behavior on keyboard devices. Usually, this delay + is set to about 500 ms by the host. + + The only action here is to store the new Idle rate. The management of this + setting must be done in the main function, since Interrupt IN reports are sent + from there. + + In practice, it is not necessary to perform any action, apart from sending a + zero-length packet to acknowledge it. The main application however has to make + sure that only new reports are sent by the %device. +\code +case HIDGenericRequest_SETIDLE: +//----------------------------- + hiddKeyboardDriver.inputReportIdleRate = + HIDIdleRequest_GetIdleRate(request); + USBD_Write(0, 0, 0, 0, 0); +break; +\endcode + + !!GetIdle + The only necessary operation for this request is to send the previously saved + Idle rate. This is done by calling the USBD_Write method with the one-byte + variable as its parameter: +\code +case HIDGenericRequest_GETIDLE: +//----------------------------- + USBD_Write(0, &(hiddKeyboardDriver.inputReportIdleRate), 1, 0, 0); +break; +\endcode + + !!GetProtocol, SetProtocol + This HID keyboard example does not support the Boot protocol, so there is no + need to implement the SET_PROTOCOL and GET_PROTOCOL requests. This means they + can be safely STALLed when received. + + !!!Main Application + Like the mouse example, the main program must perform two different + operations. First, it has to monitor the physical inputs used as keys. In the + example software, the buttons present on the evaluation boards are used to + produce several modifier and alphanumeric keys. + + Also, the main program is in charge of sending reports as they are modified, + taking into account the Idle rate specified by the host. Idle rate management + can be carried out by firing/resetting a timer once a new report is sent; if + the timer expires, this means the Input report has not changed since. + According to the HID specification, a single instance of the report must be + sent in this case. + + Finally, the HID specification also defines that if too many keys are pressed + at the same time, the %device should report an }ErrorRollOver} usage value + (01h) in every byte of the key array. This has to be handled by the main + application as well. + +*/ \ No newline at end of file diff --git a/usb/device/massstorage/MSD.h b/usb/device/massstorage/MSD.h new file mode 100644 index 0000000..decab4a --- /dev/null +++ b/usb/device/massstorage/MSD.h @@ -0,0 +1,233 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Mass Storage class definitions. +/// +/// See +/// - +/// USB Mass Storage Class Spec. Overview +/// - +/// USB Mass Storage Class Bulk-Only Transport +/// +/// !Usage +/// +/// -# Uses "MSD Requests" to check incoming requests from USB Host. +/// -# Uses "MSD Subclass Codes" and "MSD Protocol Codes" to fill %device +/// interface descriptors for a MSD %device. +/// -# Handle the incoming Bulk data with "MSD CBW Definitions" and MSCbw +/// structure. +/// -# Prepare the outgoing Bulk data with "MSD CSW Definitions" and MSCsw +/// structure. +//------------------------------------------------------------------------------ + +#ifndef MSD_H +#define MSD_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Requests" +/// This page lists MSD-specific requests ( Actually for Bulk-only protocol ). +/// +/// !Requests +/// - MSD_BULK_ONLY_RESET +/// - MSD_GET_MAX_LUN + +/// Reset the mass storage %device and its associated interface. +#define MSD_BULK_ONLY_RESET 0xFF +/// Return the maximum LUN number supported by the %device. +#define MSD_GET_MAX_LUN 0xFE +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Subclass Codes" +/// This page lists the Subclass Codes for bInterfaceSubClass field. +/// (Table 2.1, USB Mass Storage Class Spec. Overview) +/// +/// !SubClasses +/// - MSD_SUBCLASS_RBC +/// - MSD_SUBCLASS_SFF_MCC +/// - MSD_SUBCLASS_QIC +/// - MSD_SUBCLASS_UFI +/// - MSD_SUBCLASS_SFF +/// - MSD_SUBCLASS_SCSI + +/// Reduced Block Commands (RBC) T10 +#define MSD_SUBCLASS_RBC 0x01 +/// C/DVD devices +#define MSD_SUBCLASS_SFF_MCC 0x02 +/// Tape device +#define MSD_SUBCLASS_QIC 0x03 +/// Floppy disk drive (FDD) device +#define MSD_SUBCLASS_UFI 0x04 +/// Floppy disk drive (FDD) device +#define MSD_SUBCLASS_SFF 0x05 +/// SCSI transparent command set +#define MSD_SUBCLASS_SCSI 0x06 +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +/// \page "MSD Protocol Codes" +/// This page lists the Transport Protocol codes for MSD. +/// (Table 3.1, USB Mass Storage Class Spec. Overview) +/// +/// !Protocols +/// - MSD_PROTOCOL_CBI_COMPLETION +/// - MSD_PROTOCOL_CBI +/// - MSD_PROTOCOL_BULK_ONLY + +/// Control/Bulk/Interrupt (CBI) Transport (with command complete interrupt) +#define MSD_PROTOCOL_CBI_COMPLETION 0x00 +/// Control/Bulk/Interrupt (CBI) Transport (no command complete interrupt) +#define MSD_PROTOCOL_CBI 0x01 +/// Bulk-Only Transport +#define MSD_PROTOCOL_BULK_ONLY 0x50 +//------------------------------------------------------------------------------ + +/// Test unit control: +#define CTRL_NOT_READY 0x00 +#define CTRL_GOOD 0x01 +#define CTRL_BUSY 0x02 + +//------------------------------------------------------------------------------ +/// \page "MSD CBW Definitions" +/// This page lists the Command Block Wrapper (CBW) definitions. +/// +/// !Constants +/// - MSD_CBW_SIZE +/// - MSD_CBW_SIGNATURE +/// +/// !Fields +/// - MSD_CBW_DEVICE_TO_HOST + +/// Command Block Wrapper Size +#define MSD_CBW_SIZE 31 +/// 'USBC' 0x43425355 +#define MSD_CBW_SIGNATURE 0x43425355 + +/// CBW bmCBWFlags field +#define MSD_CBW_DEVICE_TO_HOST (1 << 7) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD CSW Definitions" +/// This page lists the Command Status Wrapper (CSW) definitions. +/// +/// !Constants +/// - MSD_CSW_SIZE +/// - MSD_CSW_SIGNATURE +/// +/// !Command Block Status Values +/// (Table 5.3 , USB Mass Storage Class Bulk-Only Transport) +/// - MSD_CSW_COMMAND_PASSED +/// - MSD_CSW_COMMAND_FAILED +/// - MSD_CSW_PHASE_ERROR + +/// Command Status Wrapper Size +#define MSD_CSW_SIZE 13 +/// 'USBS' 0x53425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +/// Command Passed (good status) +#define MSD_CSW_COMMAND_PASSED 0 +/// Command Failed +#define MSD_CSW_COMMAND_FAILED 1 +/// Phase Error +#define MSD_CSW_PHASE_ERROR 2 +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Command Block Wrapper (CBW), +/// See Table 5.1, USB Mass Storage Class Bulk-Only Transport. +/// +/// The CBW shall start on a packet boundary and shall end as a +/// short packet with exactly 31 (1Fh) bytes transferred. +//------------------------------------------------------------------------------ +typedef struct { + + /// 'USBC' 0x43425355 (little endian) + unsigned int dCBWSignature; + /// Must be the same as dCSWTag + unsigned int dCBWTag; + /// Number of bytes transfer + unsigned int dCBWDataTransferLength; + /// Indicates the directin of the transfer: + /// 0x80=IN=device-to-host, + /// 0x00=OUT=host-to-device + unsigned char bmCBWFlags; + /// bits 0->3: bCBWLUN + unsigned char bCBWLUN :4, + bReserved1:4; /// reserved + /// bits 0->4: bCBWCBLength + unsigned char bCBWCBLength:5, + bReserved2 :3; /// reserved + /// Command block + unsigned char pCommand[16]; + +} MSCbw; + +//------------------------------------------------------------------------------ +/// Command Status Wrapper (CSW), +/// See Table 5.2, USB Mass Storage Class Bulk-Only Transport. +//------------------------------------------------------------------------------ +typedef struct +{ + /// 'USBS' 0x53425355 (little endian) + unsigned int dCSWSignature; + /// Must be the same as dCBWTag + unsigned int dCSWTag; + /// For Data-Out the device shall report in the dCSWDataResidue the + /// difference between the amount of data expected as stated in the + /// dCBWDataTransferLength, and the actual amount of data processed by + /// the device. For Data-In the device shall report in the dCSWDataResidue + /// the difference between the amount of data expected as stated in the + /// dCBWDataTransferLength and the actual amount of relevant data sent by + /// the device. The dCSWDataResidue shall not exceed the value sent in the + /// dCBWDataTransferLength. + unsigned int dCSWDataResidue; + /// Indicates the success or failure of the command. + unsigned char bCSWStatus; + +} MSCsw; + +#endif //#ifndef MSD_H + diff --git a/usb/device/massstorage/MSDAppArch.png b/usb/device/massstorage/MSDAppArch.png new file mode 100644 index 0000000..eec0f15 Binary files /dev/null and b/usb/device/massstorage/MSDAppArch.png differ diff --git a/usb/device/massstorage/MSDDStateMachine.c b/usb/device/massstorage/MSDDStateMachine.c new file mode 100644 index 0000000..bc69a49 --- /dev/null +++ b/usb/device/massstorage/MSDDStateMachine.c @@ -0,0 +1,607 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- + +#include "SBCMethods.h" +#include "MSDDStateMachine.h" + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/// Returns the expected transfer length and direction (IN, OUT or don't care) +/// from the host point-of-view. +/// \param cbw Pointer to the CBW to examinate +/// \param pLength Expected length of command +/// \param pType Expected direction of command +//----------------------------------------------------------------------------- +static void MSDD_GetCommandInformation(MSCbw *cbw, + unsigned int *length, + unsigned char *type) +{ + // Expected host transfer direction and length + (*length) = cbw->dCBWDataTransferLength; + + if (*length == 0) { + + (*type) = MSDD_NO_TRANSFER; + } + else if ((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) != 0) { + + (*type) = MSDD_DEVICE_TO_HOST; + } + else { + + (*type) = MSDD_HOST_TO_DEVICE; + } +} + +//----------------------------------------------------------------------------- +/// Pre-processes a command by checking the differences between the host and +/// device expectations in term of transfer type and length. +/// Once one of the thirteen cases is identified, the actions to do during the +/// post-processing phase are stored in the dCase variable of the command +/// state. +/// \param pMsdDriver Pointer to a MSDDriver instance +/// \return 1 if the command is supported, false otherwise +//----------------------------------------------------------------------------- +static unsigned char MSDD_PreProcessCommand(MSDDriver *pMsdDriver) +{ + unsigned int hostLength = 0; + unsigned int deviceLength = 0; + unsigned char hostType; + unsigned char deviceType; + unsigned char isCommandSupported; + MSDCommandState *commandState = &(pMsdDriver->commandState); + MSCsw *csw = &(commandState->csw); + MSCbw *cbw = &(commandState->cbw); + MSDLun *lun = &(pMsdDriver->luns[(unsigned char) cbw->bCBWLUN]); + + // Get information about the command + // Host-side + MSDD_GetCommandInformation(cbw, &hostLength, &hostType); + + // Device-side + isCommandSupported = SBC_GetCommandInformation(cbw->pCommand, + &deviceLength, + &deviceType, + lun); + + // Initialize data residue and result status + csw->dCSWDataResidue = 0; + csw->bCSWStatus = MSD_CSW_COMMAND_PASSED; + + // Check if the command is supported + if (isCommandSupported) { + + // Identify the command case + if(hostType == MSDD_NO_TRANSFER) { + + // Case 1 (Hn = Dn) + if(deviceType == MSDD_NO_TRANSFER) { + + //TRACE_WARNING("Case 1\n\r"); + commandState->postprocess = 0; + commandState->length = 0; + } + else if(deviceType == MSDD_DEVICE_TO_HOST) { + + // Case 2 (Hn < Di) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 2\n\r"); + commandState->postprocess = MSDD_CASE_PHASE_ERROR; + commandState->length = 0; + } + else { //if(deviceType == MSDD_HOST_TO_DEVICE) { + + // Case 3 (Hn < Do) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 3\n\r"); + commandState->postprocess = MSDD_CASE_PHASE_ERROR; + commandState->length = 0; + } + } + + // Case 4 (Hi > Dn) + else if(hostType == MSDD_DEVICE_TO_HOST) { + + if(deviceType == MSDD_NO_TRANSFER) { + + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 4\n\r"); + commandState->postprocess = MSDD_CASE_STALL_IN; + commandState->length = 0; + csw->dCSWDataResidue = hostLength; + } + else if(deviceType == MSDD_DEVICE_TO_HOST) { + + if(hostLength > deviceLength) { + + // Case 5 (Hi > Di) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 5\n\r"); + commandState->postprocess = MSDD_CASE_STALL_IN; + commandState->length = deviceLength; + csw->dCSWDataResidue = hostLength - deviceLength; + } + else if(hostLength == deviceLength) { + + // Case 6 (Hi = Di) + commandState->postprocess = 0; + commandState->length = deviceLength; + } + else { //if(hostLength < deviceLength) { + + // Case 7 (Hi < Di) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 7\n\r"); + commandState->postprocess = MSDD_CASE_PHASE_ERROR; + commandState->length = hostLength; + } + } + else { //if(deviceType == MSDD_HOST_TO_DEVICE) { + + // Case 8 (Hi <> Do) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 8\n\r"); + commandState->postprocess = + MSDD_CASE_STALL_IN | MSDD_CASE_PHASE_ERROR; + commandState->length = 0; + } + } + else if(hostType == MSDD_HOST_TO_DEVICE) { + + if(deviceType == MSDD_NO_TRANSFER) { + + // Case 9 (Ho > Dn) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 9\n\r"); + commandState->postprocess = MSDD_CASE_STALL_OUT; + commandState->length = 0; + csw->dCSWDataResidue = hostLength; + } + else if(deviceType == MSDD_DEVICE_TO_HOST) { + + // Case 10 (Ho <> Di) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 10\n\r"); + commandState->postprocess = + MSDD_CASE_STALL_OUT | MSDD_CASE_PHASE_ERROR; + commandState->length = 0; + } + else { //if(deviceType == MSDD_HOST_TO_DEVICE) { + + if(hostLength > deviceLength) { + + // Case 11 (Ho > Do) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 11\n\r"); + commandState->postprocess = MSDD_CASE_STALL_OUT; +// commandState->length = deviceLength; +// csw->dCSWDataResidue = hostLength - deviceLength; + commandState->length = 0; + csw->dCSWDataResidue = deviceLength; + } + else if(hostLength == deviceLength) { + + // Case 12 (Ho = Do) + //TRACE_WARNING( + // "MSDD_PreProcessCommand: Case 12\n\r"); + commandState->postprocess = 0; + commandState->length = deviceLength; + } + else { //if(hostLength < deviceLength) { + + // Case 13 (Ho < Do) + TRACE_WARNING( + "MSDD_PreProcessCommand: Case 13\n\r"); + commandState->postprocess = MSDD_CASE_PHASE_ERROR; + commandState->length = hostLength; + } + } + } + } + + return isCommandSupported; +} + +//----------------------------------------------------------------------------- +/// Post-processes a command given the case identified during the +/// pre-processing step. +/// Depending on the case, one of the following actions can be done: +/// - Bulk IN endpoint is stalled +/// - Bulk OUT endpoint is stalled +/// - CSW status set to phase error +/// \param pMsdDriver Pointer to a MSDDriver instance +/// \return If the device is halted +//----------------------------------------------------------------------------- +static unsigned char MSDD_PostProcessCommand(MSDDriver *pMsdDriver) +{ + MSDCommandState *commandState = &(pMsdDriver->commandState); + MSCsw *csw = &(commandState->csw); + unsigned char haltStatus = 0; + + // STALL Bulk IN endpoint ? + if ((commandState->postprocess & MSDD_CASE_STALL_IN) != 0) { + + TRACE_INFO_WP("StallIn "); + MSDD_Halt(MSDD_CASE_STALL_IN); + haltStatus = 1; + } + + // STALL Bulk OUT endpoint ? + if ((commandState->postprocess & MSDD_CASE_STALL_OUT) != 0) { + + TRACE_INFO_WP("StallOut "); + MSDD_Halt(MSDD_CASE_STALL_OUT); + haltStatus = 1; + } + + // Set CSW status code to phase error ? + if ((commandState->postprocess & MSDD_CASE_PHASE_ERROR) != 0) { + + TRACE_INFO_WP("PhaseErr "); + csw->bCSWStatus = MSD_CSW_PHASE_ERROR; + } + + return haltStatus; +} + +//----------------------------------------------------------------------------- +/// Processes the latest command received by the %device. +/// \param pMsdDriver Pointer to a MSDDriver instance +/// \return 1 if the command has been completed, false otherwise. +//----------------------------------------------------------------------------- +static unsigned char MSDD_ProcessCommand(MSDDriver * pMsdDriver) +{ + unsigned char status; + MSDCommandState *commandState = &(pMsdDriver->commandState); + MSCbw *cbw = &(commandState->cbw); + MSCsw *csw = &(commandState->csw); + MSDLun *lun = &(pMsdDriver->luns[(unsigned char) cbw->bCBWLUN]); + unsigned char isCommandComplete = 0; + + // Check if LUN is valid + if (cbw->bCBWLUN > pMsdDriver->maxLun) { + + TRACE_WARNING( + "MSDD_ProcessCommand: LUN %d not exist\n\r", cbw->bCBWLUN); + status = MSDD_STATUS_ERROR; + } + else { + + // Process command + if (pMsdDriver->maxLun > 0) { + + TRACE_INFO_WP("LUN%d ", cbw->bCBWLUN); + } + + status = SBC_ProcessCommand(lun, commandState); + } + + // Check command result code + if (status == MSDD_STATUS_PARAMETER) { + + TRACE_WARNING( + "MSDD_ProcessCommand: Unknown cmd 0x%02X\n\r", + cbw->pCommand[0]); + + // Update sense data + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_ILLEGAL_REQUEST, + SBC_ASC_INVALID_FIELD_IN_CDB, + 0); + + // Result codes + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + isCommandComplete = 1; + + // stall the request, IN or OUT + if (((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) == 0) + && (cbw->dCBWDataTransferLength > 0)) { + + // Stall the OUT endpoint : host to device + // MSDD_Halt(MSDD_CASE_STALL_OUT); + commandState->postprocess = MSDD_CASE_STALL_OUT; + TRACE_INFO_WP("StaOUT "); + } + else { + + // Stall the IN endpoint : device to host + // MSDD_Halt(MSDD_CASE_STALL_IN); + commandState->postprocess = MSDD_CASE_STALL_IN; + TRACE_INFO_WP("StaIN "); + } + } + else if (status == MSDD_STATUS_ERROR) { + + TRACE_WARNING("MSD_ProcessCommand: Cmd %x fail\n\r", + ((SBCCommand*)commandState->cbw.pCommand)->bOperationCode); + + // Update sense data + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_MEDIUM_ERROR, + SBC_ASC_INVALID_FIELD_IN_CDB, + 0); + + // Result codes + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + isCommandComplete = 1; + } + else if (status == MSDD_STATUS_RW) { + + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + isCommandComplete = 1; + } + else { + + // Update sense data + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_NO_SENSE, + 0, + 0); + + // Is command complete ? + if (status == MSDD_STATUS_SUCCESS) { + + isCommandComplete = 1; + } + } + + // Check if command has been completed + if (isCommandComplete) { + + TRACE_INFO_WP("Cplt "); + + // Adjust data residue + if (commandState->length != 0) { + + csw->dCSWDataResidue += commandState->length; + + // STALL the endpoint waiting for data + if ((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) == 0) { + + // Stall the OUT endpoint : host to device + // MSDD_Halt(MSDD_CASE_STALL_OUT); + commandState->postprocess = MSDD_CASE_STALL_OUT; + TRACE_INFO_WP("StaOUT "); + } + else { + + // Stall the IN endpoint : device to host + // MSDD_Halt(MSDD_CASE_STALL_IN); + commandState->postprocess = MSDD_CASE_STALL_IN; + TRACE_INFO_WP("StaIN "); + } + } + + // Reset command state + commandState->state = 0; + } + + return isCommandComplete; +} + +//----------------------------------------------------------------------------- +/// State machine for the MSD %device driver +/// \param pMsdDriver Pointer to a MSDDriver instance +//----------------------------------------------------------------------------- +void MSDD_StateMachine(MSDDriver * pMsdDriver) +{ + MSDCommandState *commandState = &(pMsdDriver->commandState); + MSCbw *cbw = &(commandState->cbw); + MSCsw *csw = &(commandState->csw); + MSDTransfer *transfer = &(commandState->transfer); + unsigned char status; + + // Identify current driver state + switch (pMsdDriver->state) { + //---------------------- + case MSDD_STATE_READ_CBW: + //---------------------- + // Start the CBW read operation + transfer->semaphore = 0; + status = MSDD_Read(cbw, + MSD_CBW_SIZE, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status == USBD_STATUS_SUCCESS) { + + // If the command was successful, wait for transfer + pMsdDriver->state = MSDD_STATE_WAIT_CBW; + } + break; + + //---------------------- + case MSDD_STATE_WAIT_CBW: + //---------------------- + // Check transfer semaphore + if (transfer->semaphore > 0) { + + // Take semaphore and terminate transfer + transfer->semaphore--; + + // Check if transfer was successful + if (transfer->status == USBD_STATUS_SUCCESS) { + + TRACE_INFO_WP("------------------------------\n\r"); + + // Process received command + pMsdDriver->state = MSDD_STATE_PROCESS_CBW; + } + else if (transfer->status == USBD_STATUS_RESET) { + + TRACE_INFO("MSDD_StateMachine: EP resetted\n\r"); + pMsdDriver->state = MSDD_STATE_READ_CBW; + } + else { + + TRACE_WARNING( + "MSDD_StateMachine: Failed to read CBW\n\r"); + pMsdDriver->state = MSDD_STATE_READ_CBW; + } + } + break; + + //------------------------- + case MSDD_STATE_PROCESS_CBW: + //------------------------- + // Check if this is a new command + if (commandState->state == 0) { + + // Copy the CBW tag + csw->dCSWTag = cbw->dCBWTag; + + // Check that the CBW is 31 bytes long + if ((transfer->transferred != MSD_CBW_SIZE) || + (transfer->remaining != 0)) { + + TRACE_WARNING( + "MSDD_StateMachine: Invalid CBW (len %d)\n\r", + (int)transfer->transferred); + + // Wait for a reset recovery + pMsdDriver->waitResetRecovery = 1; + + // Halt the Bulk-IN and Bulk-OUT pipes + MSDD_Halt(MSDD_CASE_STALL_OUT | MSDD_CASE_STALL_IN); + + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + pMsdDriver->state = MSDD_STATE_READ_CBW; + + } + // Check the CBW Signature + else if (cbw->dCBWSignature != MSD_CBW_SIGNATURE) { + + TRACE_WARNING( + "MSD_BOTStateMachine: Invalid CBW (Bad signature)\n\r"); + + // Wait for a reset recovery + pMsdDriver->waitResetRecovery = 1; + + // Halt the Bulk-IN and Bulk-OUT pipes + MSDD_Halt(MSDD_CASE_STALL_OUT | MSDD_CASE_STALL_IN); + + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + pMsdDriver->state = MSDD_STATE_READ_CBW; + } + else { + + // Pre-process command + MSDD_PreProcessCommand(pMsdDriver); + } + } + + // Process command + if (csw->bCSWStatus == MSDD_STATUS_SUCCESS) { + + if (MSDD_ProcessCommand(pMsdDriver)) { + + // Post-process command if it is finished + if (MSDD_PostProcessCommand(pMsdDriver)) { + + TRACE_INFO_WP("WaitHALT "); + pMsdDriver->state = MSDD_STATE_WAIT_HALT; + } + else { + + pMsdDriver->state = MSDD_STATE_SEND_CSW; + } + } + TRACE_INFO_WP("\n\r"); + } + + break; + + //---------------------- + case MSDD_STATE_SEND_CSW: + //---------------------- + // Set signature + csw->dCSWSignature = MSD_CSW_SIGNATURE; + + // Start the CSW write operation + status = MSDD_Write(csw, + MSD_CSW_SIZE, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status == USBD_STATUS_SUCCESS) { + + TRACE_INFO_WP("SendCSW "); + + // Wait for end of transfer + pMsdDriver->state = MSDD_STATE_WAIT_CSW; + } + break; + + //---------------------- + case MSDD_STATE_WAIT_CSW: + //---------------------- + // Check transfer semaphore + if (transfer->semaphore > 0) { + + // Take semaphore and terminate transfer + transfer->semaphore--; + + // Check if transfer was successful + if (transfer->status == USBD_STATUS_RESET) { + + TRACE_INFO("MSDD_StateMachine: EP resetted\n\r"); + } + else if (transfer->status == USBD_STATUS_ABORTED) { + + TRACE_WARNING( + "MSDD_StateMachine: Failed to send CSW\n\r"); + } + else { + + TRACE_INFO_WP("ok"); + } + + // Read new CBW + pMsdDriver->state = MSDD_STATE_READ_CBW; + } + break; + + //---------------------- + case MSDD_STATE_WAIT_HALT: + //---------------------- + if (MSDD_IsHalted() == 0) { + + pMsdDriver->state = MSDD_STATE_SEND_CSW; + } + break; + } +} diff --git a/usb/device/massstorage/MSDDStateMachine.h b/usb/device/massstorage/MSDDStateMachine.h new file mode 100644 index 0000000..77e5cc8 --- /dev/null +++ b/usb/device/massstorage/MSDDStateMachine.h @@ -0,0 +1,264 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +/// \unit +/// !Purpose +/// +/// Definitions, structs, functions required by a Mass Storage device driver +/// state machine.. +/// +/// !Usage +/// +/// - For a USB device: +/// -# MSDD_Write, MSDD_Read, MSDD_Halt should be defined for +/// usage in the state machine procedure. +/// +/// -# MSDD_StateMachine is invoked to run the MSD state machine. +/// +//----------------------------------------------------------------------------- + +#ifndef MSDDSTATEMACHINE_H +#define MSDDSTATEMACHINE_H + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include "MSD.h" +#include "MSDLun.h" +#include + +//----------------------------------------------------------------------------- +// Definitions +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +/// \page "MSD Driver Possible states" +/// ... +/// +/// !States +/// - MSDD_STATE_READ_CBW +/// - MSDD_STATE_WAIT_CBW +/// - MSDD_STATE_PROCESS_CBW +/// - MSDD_STATE_WAIT_HALT +/// - MSDD_STATE_SEND_CSW +/// - MSDD_STATE_WAIT_CSW +/// - MSDD_STATE_WAIT_RESET + +//! \brief Driver is expecting a command block wrapper +#define MSDD_STATE_READ_CBW (1 << 0) + +//! \brief Driver is waiting for the transfer to finish +#define MSDD_STATE_WAIT_CBW (1 << 1) + +//! \brief Driver is processing the received command +#define MSDD_STATE_PROCESS_CBW (1 << 2) + +//! \brief Driver is halted because pipe halt or wait reset +#define MSDD_STATE_WAIT_HALT (1 << 3) + +//! \brief Driver is starting the transmission of a command status wrapper +#define MSDD_STATE_SEND_CSW (1 << 4) + +//! \brief Driver is waiting for the CSW transmission to finish +#define MSDD_STATE_WAIT_CSW (1 << 5) + +//! \brief Driver is waiting for the MassStorageReset +#define MSDD_STATE_WAIT_RESET (1 << 6) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Driver Result Codes" +/// This page lists result codes for MSD functions. +/// +/// !Codes +/// - MSDD_STATUS_SUCCESS +/// - MSDD_STATUS_ERROR +/// - MSDD_STATUS_INCOMPLETE +/// - MSDD_STATUS_PARAMETER +/// - MSDD_STATUS_RW + +//! \brief Method was successful +#define MSDD_STATUS_SUCCESS 0x00 + +//! \brief There was an error when trying to perform a method +#define MSDD_STATUS_ERROR 0x01 + +//! \brief No error was encountered but the application should call the +//! method again to continue the operation +#define MSDD_STATUS_INCOMPLETE 0x02 + +//! \brief A wrong parameter has been passed to the method +#define MSDD_STATUS_PARAMETER 0x03 + +//! \brief An error when reading/writing disk (protected or not present) +#define MSDD_STATUS_RW 0x04 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Driver Action Cases" +/// This page lists actions to perform during the post-processing phase of a +/// command. +/// +/// !Actions +/// - MSDD_CASE_PHASE_ERROR +/// - MSDD_CASE_STALL_IN +/// - MSDD_CASE_STALL_OUT + +//! \brief Indicates that the CSW should report a phase error +#define MSDD_CASE_PHASE_ERROR (1 << 0) + +//! \brief The driver should halt the Bulk IN pipe after the transfer +#define MSDD_CASE_STALL_IN (1 << 1) + +//! \brief The driver should halt the Bulk OUT pipe after the transfer +#define MSDD_CASE_STALL_OUT (1 << 2) + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Driver Xfr Directions" +/// This page lists possible direction values for a data transfer +/// +/// !Directions +/// - MSDD_DEVICE_TO_HOST +/// - MSDD_HOST_TO_DEVICE +/// - MSDD_NO_TRANSFER + +#define MSDD_DEVICE_TO_HOST 0 +#define MSDD_HOST_TO_DEVICE 1 +#define MSDD_NO_TRANSFER 2 +//------------------------------------------------------------------------------ + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +//! \brief Structure for holding the result of a USB transfer +//! \see MSDDriver_Callback +//------------------------------------------------------------------------------ +typedef struct { + + volatile unsigned int transferred; /// Number of bytes transferred + volatile unsigned int remaining; /// Number of bytes not transferred + volatile unsigned short semaphore; /// Semaphore to indicate transfer completion + volatile unsigned short status; /// Operation result code + +} MSDTransfer; + +//------------------------------------------------------------------------------ +//! \brief Status of an executing command +//! \see MSDCbw +//! \see MSDCsw +//! \see MSDTransfer +//------------------------------------------------------------------------------ +typedef struct { + + MSDTransfer transfer; /// Current transfer status (USB) + MSDTransfer disktransfer;/// Current transfer status (Disk) + unsigned int length; /// Remaining length of command + MSCbw cbw; /// Received CBW (31 bytes) + unsigned char state; /// Current command state + MSCsw csw; /// CSW to send (13 bytes) + unsigned char postprocess; /// Actions to perform when command is complete + +} MSDCommandState; + +//------------------------------------------------------------------------------ +/// \brief MSD driver state variables +/// \see MSDCommandState +/// \see MSDLun +//------------------------------------------------------------------------------ +typedef struct { + + /// LUN list for the %device. + MSDLun *luns; + /// State of the currently executing command + MSDCommandState commandState; + /// Maximum LUN index + unsigned char maxLun; + /// Current state of the driver + unsigned char state; + /// Indicates if the driver is waiting for a reset recovery + unsigned char waitResetRecovery; + +} MSDDriver; + +//----------------------------------------------------------------------------- +// Inline functions +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/// This function is to be used as a callback for USB or LUN transfers. +/// \param transfer Pointer to the transfer structure to update +/// \param status Operation result code +/// \param transferred Number of bytes transferred by the command +/// \param remaining Number of bytes not transferred +//----------------------------------------------------------------------------- +static inline void MSDDriver_Callback(MSDTransfer *transfer, + unsigned char status, + unsigned int transferred, + unsigned int remaining) +{ + TRACE_DEBUG("Cbk "); + transfer->semaphore++; + transfer->status = status; + transfer->transferred = transferred; + transfer->remaining = remaining; +} + +//----------------------------------------------------------------------------- +// Driver functions +//----------------------------------------------------------------------------- +//- MSD General support function +extern char MSDD_Read( + void* pData, + unsigned int dLength, + TransferCallback fCallback, + void* pArgument); + +extern char MSDD_Write( + void* pData, + unsigned int dLength, + TransferCallback fCallback, + void* pArgument); + +extern void MSDD_Halt(unsigned int stallCase); + +extern unsigned int MSDD_IsHalted(void); + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +extern void MSDD_StateMachine(MSDDriver * pMsdDriver); + +#endif // #define MSDDSTATEMACHINE_H + diff --git a/usb/device/massstorage/MSDDriver.c b/usb/device/massstorage/MSDDriver.c new file mode 100644 index 0000000..fbaebb0 --- /dev/null +++ b/usb/device/massstorage/MSDDriver.c @@ -0,0 +1,343 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Includes +//------------------------------------------------------------------------------ + +#include +#include "MSDDriver.h" +#include "MSDDriverDescriptors.h" +#include "SBCMethods.h" +#include +#include +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// Internal variables +//----------------------------------------------------------------------------- + +/// Mass storage device driver instance. +static MSDDriver msdDriver; + +/// Standard device driver instance. +static USBDDriver usbdDriver; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Resets the state of the MSD driver +//----------------------------------------------------------------------------- +static void MSDDriver_Reset(void) +{ + TRACE_INFO_WP("MSDReset "); + + msdDriver.state = MSDD_STATE_READ_CBW; + msdDriver.waitResetRecovery = 0; + msdDriver.commandState.state = 0; +} + +//----------------------------------------------------------------------------- +// Callback re-implementation +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/// Invoked when a new SETUP request is received from the host. Forwards the +/// request to the Mass Storage device driver handler function. +/// \param request Pointer to a USBGenericRequest instance. +//----------------------------------------------------------------------------- +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + MSDDriver_RequestHandler(request); +} + +//----------------------------------------------------------------------------- +/// Invoked when the configuration of the device changes. Resets the mass +/// storage driver. +/// \param cfgnum New configuration number. +//----------------------------------------------------------------------------- +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + if (cfgnum > 0) { + + MSDDriver_Reset(); + } +} + +//----------------------------------------------------------------------------- +// Driver functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Reads from host through MSD defined bulk OUT pipe. Act as USBD_Read but by +/// a fixed OUT endpoint. +/// \param data Pointer to the data buffer that contains data read from host. +/// \param size The number of bytes should be read (buffer size). +/// \param callback Pointer to the function invoked on end of reading. +/// \param argument Pointer to additional argument data struct. +//----------------------------------------------------------------------------- +char MSDD_Read(void *data, + unsigned int size, + TransferCallback callback, + void *argument) + +{ + return USBD_Read(MSDDriverDescriptors_BULKOUT, + data, + size, + callback, + argument); +} + +//----------------------------------------------------------------------------- +/// Writes to host through MSD defined bulk IN pipe. Act as USBD_Write but by +/// a fixed IN endpoint. +/// \param data Pointer to the data that writes to the host. +/// \param size The number of bytes should be write. +/// \param callback Pointer to the function invoked on end of writing. +/// \param argument Pointer to additional argument data struct. +//----------------------------------------------------------------------------- +char MSDD_Write(void *data, + unsigned int size, + TransferCallback callback, + void *argument) +{ + return USBD_Write(MSDDriverDescriptors_BULKIN, + data, + size, + callback, + argument); +} + +//----------------------------------------------------------------------------- +/// HALT Specified USB pipe. +/// \param stallCASE Case of the stall condition (Bulk In/Out/Both). +//----------------------------------------------------------------------------- +void MSDD_Halt(unsigned int stallCASE) +{ + if (stallCASE & MSDD_CASE_STALL_OUT) { + + USBD_Halt(MSDDriverDescriptors_BULKOUT); + } + + if (stallCASE & MSDD_CASE_STALL_IN) { + + USBD_Halt(MSDDriverDescriptors_BULKIN); + } +} + +//----------------------------------------------------------------------------- +/// Return halted status +/// \return stallCASE bitmap, case of the stall condition +/// (bit: MSDD_CASE_STALL_OUT or MSDD_CASE_STALL_IN) +//----------------------------------------------------------------------------- +unsigned int MSDD_IsHalted(void) +{ + unsigned int stallCASE = 0; + if (USBD_IsHalted(MSDDriverDescriptors_BULKOUT)) { + + stallCASE |= MSDD_CASE_STALL_OUT; + } + if (USBD_IsHalted(MSDDriverDescriptors_BULKIN)) { + + stallCASE |= MSDD_CASE_STALL_IN; + } + return stallCASE; +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Initializes the MSD driver and the associated USB driver. +/// \param luns Pointer to a list of LUNs +/// \param numLuns Number of LUN in list +/// \see MSDLun +//----------------------------------------------------------------------------- +void MSDDriver_Initialize(MSDLun *defLuns, unsigned char numLuns) +{ + + TRACE_INFO("MSD init\n\r"); + + // Command state initialization + msdDriver.commandState.state = 0; + msdDriver.commandState.postprocess = 0; + msdDriver.commandState.length = 0; + msdDriver.commandState.transfer.semaphore = 0; + + // LUNs + msdDriver.luns = defLuns; + msdDriver.maxLun = (unsigned char) (numLuns - 1); + + // Reset BOT driver + MSDDriver_Reset(); + + // Init the USB driver + USBDDriver_Initialize(&usbdDriver, &msdDriverDescriptors, 0); + USBD_Init(); +} + +//----------------------------------------------------------------------------- +/// Handler for incoming SETUP requests on default Control endpoint 0. +/// +/// Standard requests are forwarded to the USBDDriver_RequestHandler +/// method. +/// \param request Pointer to a USBGenericRequest instance +//----------------------------------------------------------------------------- +void MSDDriver_RequestHandler(const USBGenericRequest *request) +{ + TRACE_INFO_WP("NewReq "); + + // Handle requests + switch (USBGenericRequest_GetRequest(request)) { + //--------------------- + case USBGenericRequest_CLEARFEATURE: + //--------------------- + TRACE_INFO_WP("ClrFeat "); + + switch (USBFeatureRequest_GetFeatureSelector(request)) { + + //--------------------- + case USBFeatureRequest_ENDPOINTHALT: + //--------------------- + TRACE_INFO_WP("Hlt "); + + // Do not clear the endpoint halt status if the device is waiting + // for a reset recovery sequence + if (!msdDriver.waitResetRecovery) { + + // Forward the request to the standard handler + USBDDriver_RequestHandler(&usbdDriver, request); + } + else { + + TRACE_INFO_WP("No "); + } + + USBD_Write(0, 0, 0, 0, 0); + break; + + //------ + default: + //------ + // Forward the request to the standard handler + USBDDriver_RequestHandler(&usbdDriver, request); + } + break; + + //------------------- + case MSD_GET_MAX_LUN: + //------------------- + TRACE_INFO_WP("gMaxLun "); + + // Check request parameters + if ((request->wValue == 0) + && (request->wIndex == 0) + && (request->wLength == 1)) { + + USBD_Write(0, &(msdDriver.maxLun), 1, 0, 0); + + } + else { + + TRACE_WARNING( + "MSDDriver_RequestHandler: GetMaxLUN(%d,%d,%d)\n\r", + request->wValue, request->wIndex, request->wLength); + USBD_Stall(0); + } + break; + + //----------------------- + case MSD_BULK_ONLY_RESET: + //----------------------- + TRACE_INFO_WP("Rst "); + + // Check parameters + if ((request->wValue == 0) + && (request->wIndex == 0) + && (request->wLength == 0)) { + + // Reset the MSD driver + MSDDriver_Reset(); + USBD_Write(0, 0, 0, 0, 0); + } + else { + + TRACE_WARNING( + "MSDDriver_RequestHandler: Reset(%d,%d,%d)\n\r", + request->wValue, request->wIndex, request->wLength); + USBD_Stall(0); + } + break; + + //------ + default: + //------ + // Forward request to standard handler + USBDDriver_RequestHandler(&usbdDriver, request); + + break; + } +} + +//----------------------------------------------------------------------------- +/// State machine for the MSD driver +//----------------------------------------------------------------------------- +void MSDDriver_StateMachine(void) +{ + if (USBD_GetState() < USBD_STATE_CONFIGURED){} + else MSDD_StateMachine(&msdDriver); + +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void MSDDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) { + + USBD_RemoteWakeUp(); + } + // Remote wake-up NOT enabled + else { + + TRACE_WARNING( + "MSD..RemoteWakeUp: Host has not enabled remote wake-up\n\r"); + } +} + diff --git a/usb/device/massstorage/MSDDriver.h b/usb/device/massstorage/MSDDriver.h new file mode 100644 index 0000000..0a8738a --- /dev/null +++ b/usb/device/massstorage/MSDDriver.h @@ -0,0 +1,75 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Mass storage %device driver implementation. +/// +/// !Usage +/// +/// -# Enable and setup USB related pins (see pio & board.h). +/// -# Configure the memory interfaces used for Mass Storage LUNs +/// (see memories, MSDLun.h). +/// -# Configure the USB MSD %driver using MSDDriver_Initialize. +/// -# Invoke MSDDriver_StateMachine in main loop to handle all Mass Storage +/// operations. +//------------------------------------------------------------------------------ + +#ifndef MSDDRIVER_H +#define MSDDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MSD.h" +#include "MSDLun.h" +#include + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +extern void MSDDriver_Initialize(MSDLun *luns, unsigned char numLuns); + +extern void MSDDriver_RequestHandler(const USBGenericRequest *request); + +extern void MSDDriver_StateMachine(void); + +extern void MSDDriver_RemoteWakeUp(void); + +extern void MSDDriver_DataCallback(unsigned char isRead, + unsigned int dataLength, + unsigned int nullCnt, + unsigned int fullCnt); + +#endif // #ifndef MSDDRIVER_H + diff --git a/usb/device/massstorage/MSDDriverArch.png b/usb/device/massstorage/MSDDriverArch.png new file mode 100644 index 0000000..4063440 Binary files /dev/null and b/usb/device/massstorage/MSDDriverArch.png differ diff --git a/usb/device/massstorage/MSDDriverClasses.png b/usb/device/massstorage/MSDDriverClasses.png new file mode 100644 index 0000000..3fd68d1 Binary files /dev/null and b/usb/device/massstorage/MSDDriverClasses.png differ diff --git a/usb/device/massstorage/MSDDriverDescriptors.c b/usb/device/massstorage/MSDDriverDescriptors.c new file mode 100644 index 0000000..1d8a073 --- /dev/null +++ b/usb/device/massstorage/MSDDriverDescriptors.c @@ -0,0 +1,475 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MSDDriverDescriptors.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Device Descriptor IDs" +/// ... +/// !IDs +/// - MSDDriverDescriptors_VENDORID +/// - MSDDriverDescriptors_PRODUCTID +/// - MSDDriverDescriptors_RELEASE + +/// Vendor ID for the Mass Storage device driver. +#define MSDDriverDescriptors_VENDORID 0x03EB +/// Product ID for the Mass Storage device driver. +#define MSDDriverDescriptors_PRODUCTID 0x6129 +/// Device release number for the Mass Storage device driver. +#define MSDDriverDescriptors_RELEASE 0x0100 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// List of configuration descriptors used by a Mass Storage device driver. +//------------------------------------------------------------------------------ +typedef struct { + + /// Standard configuration descriptor. + USBConfigurationDescriptor configuration; +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + USBOtgDescriptor otgDescriptor; +#endif + /// Mass storage interface descriptor. + USBInterfaceDescriptor interface; + /// Bulk-out endpoint descriptor. + USBEndpointDescriptor bulkOut; + /// Bulk-in endpoint descriptor. + USBEndpointDescriptor bulkIn; + +} __attribute__ ((packed)) MSDConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// Mass storage driver device descriptor. +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + MSDeviceDescriptor_CLASS, + MSDeviceDescriptor_SUBCLASS, + MSDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + MSDDriverDescriptors_VENDORID, + MSDDriverDescriptors_PRODUCTID, + MSDDriverDescriptors_RELEASE, + 1, // Manufacturer string descriptor index. + 2, // Product string descriptor index. + 3, // Serial number string descriptor index. + 1 // Device has one possible configuration. +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Device qualifier descriptor. +static const USBDeviceQualifierDescriptor qualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), + USBGenericDescriptor_DEVICEQUALIFIER, + USBDeviceDescriptor_USB2_00, + MSDeviceDescriptor_CLASS, + MSDeviceDescriptor_SUBCLASS, + MSDeviceDescriptor_PROTOCOL, + CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0), + 1, // Device has one possible configuration. + 0x00 +}; +#endif + +/// Full-speed configuration descriptor. +static const MSDConfigurationDescriptors configurationDescriptorsFS = { + + // Standard configuration descriptor. + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(MSDConfigurationDescriptors), + 1, // Configuration has one interface. + 1, // This is configuration #1. + 0, // No string descriptor for configuration. + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0. + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed Bulk endpoints. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // Must be 0 for full-speed Bulk endpoints. + } +}; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +/// Full-speed other speed configuration descriptor. +static const MSDConfigurationDescriptors otherSpeedDescriptorsFS = { + + // Standard configuration descriptor. + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(MSDConfigurationDescriptors), + 1, // Configuration has one interface. + 1, // This is configuration #1. + 0, // No string descriptor for configuration. + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0. + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + } +}; + +/// High-speed configuration descriptor. +static const MSDConfigurationDescriptors configurationDescriptorsHS = { + + // Standard configuration descriptor. + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(MSDConfigurationDescriptors), + 1, // Configuration has one interface. + 1, // This is configuration #1. + 0, // No string descriptor for configuration. + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0. + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0 // No string descriptor for endpoint. + } +}; + +/// High-speed other speed configuration descriptor. +static const MSDConfigurationDescriptors otherSpeedDescriptorsHS = { + + // Standard configuration descriptor. + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(MSDConfigurationDescriptors), + 1, // Configuration has one interface. + 1, // This is configuration #1. + 0, // No string descriptor for configuration. + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, +#if defined(CHIP_USB_OTGHS) + // OTG descriptor + { + sizeof(USBOtgDescriptor), + USBGenericDescriptor_OTG, + USBOTGDescriptor_HNP_SRP + }, +#endif + // Mass Storage interface descriptor. + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // This is interface #0. + 0, // This is alternate setting #0. + 2, // Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, + MSInterfaceDescriptor_SCSI, + MSInterfaceDescriptor_BULKONLY, + 0 // No string descriptor for interface. + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_BULK, + MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0 // No string descriptor for endpoint. + } +}; +#endif + +/// Language ID string descriptor. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer string descriptor. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L') +}; + +/// Product string descriptor. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(14), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('S'), + USBStringDescriptor_UNICODE('D') +}; + +/// Serial number string descriptor. The serial number must be at least 12 +/// characters long and made up of only letters & numbers to be compliant with +/// the MSD specification. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(12), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3'), + USBStringDescriptor_UNICODE('4'), + USBStringDescriptor_UNICODE('5'), + USBStringDescriptor_UNICODE('6'), + USBStringDescriptor_UNICODE('7'), + USBStringDescriptor_UNICODE('8'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('B') +}; + +/// List of all string descriptors used. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor +}; + +//------------------------------------------------------------------------------ +// Global variables +//------------------------------------------------------------------------------ + +/// List of the standard descriptors used by the Mass Storage driver. +const USBDDriverDescriptors msdDriverDescriptors = { + + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptorsFS, +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptorsFS, + &deviceDescriptor, + (USBConfigurationDescriptor *) &configurationDescriptorsHS, + &qualifierDescriptor, + (USBConfigurationDescriptor *) &otherSpeedDescriptorsHS, +#else + 0, // No full-speed device qualifier descriptor + 0, // No full-speed other speed configuration + 0, // No high-speed device descriptor + 0, // No high-speed configuration descriptor + 0, // No high-speed device qualifier descriptor + 0, // No high-speed other speed configuration descriptor +#endif + stringDescriptors, + 4 // Four string descriptors in array +}; + diff --git a/usb/device/massstorage/MSDDriverDescriptors.h b/usb/device/massstorage/MSDDriverDescriptors.h new file mode 100644 index 0000000..be046a9 --- /dev/null +++ b/usb/device/massstorage/MSDDriverDescriptors.h @@ -0,0 +1,83 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Definitions of the descriptors required by a Mass Storage device driver. +/// +/// !Usage +/// +/// - For a USB %device: +/// -# When initializing a USBDDriver instance, use msdDriverDescriptors as +/// the list of standard descriptors. +//------------------------------------------------------------------------------ + +#ifndef MSDDRIVERDESCRIPTORS_H +#define MSDDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD endpoint addresses" +/// This page lists the addresses used by the Mass Storage driver %endpoints. +/// +/// !Addresses +/// +/// - MSDDriverDescriptors_BULKOUT +/// - MSDDriverDescriptors_BULKIN + +/// Address of the Mass Storage bulk-out endpoint. +#define MSDDriverDescriptors_BULKOUT 1 + +/// Address of the Mass Storage bulk-in endpoint. +#if defined(at91sam9m10) + #define MSDDriverDescriptors_BULKIN 6 +#else + #define MSDDriverDescriptors_BULKIN 2 +#endif +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported variables +//------------------------------------------------------------------------------ + +extern const USBDDriverDescriptors msdDriverDescriptors; + +#endif //#ifndef MSDDRIVERDESCRIPTORS_H + diff --git a/usb/device/massstorage/MSDDriverStates.png b/usb/device/massstorage/MSDDriverStates.png new file mode 100644 index 0000000..a0c55c8 Binary files /dev/null and b/usb/device/massstorage/MSDDriverStates.png differ diff --git a/usb/device/massstorage/MSDIOFifo.c b/usb/device/massstorage/MSDIOFifo.c new file mode 100644 index 0000000..03f09a6 --- /dev/null +++ b/usb/device/massstorage/MSDIOFifo.c @@ -0,0 +1,68 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "MSDIOFifo.h" + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//! \brief Initializes a MSDIOFifo instance. +//! \param pFifo Pointer to the MSDIOFifo instance to initialize +//! \param pBuffer Pointer to a buffer used for read/write operation and +//! which must be blockSize bytes aligned. +//! \param bufferSize Total size of the buffer in bytes +//------------------------------------------------------------------------------ +void MSDIOFifo_Init(MSDIOFifo *pFifo, + void * pBuffer, unsigned short bufferSize) +{ + pFifo->pBuffer = pBuffer; + pFifo->bufferSize = bufferSize; + + pFifo->inputNdx = 0; + pFifo->outputNdx = 0; + pFifo->inputTotal = 0; + pFifo->outputTotal = 0; + + pFifo->inputState = MSDIO_IDLE; + pFifo->outputState = MSDIO_IDLE; + + pFifo->fullCnt = 0; + pFifo->nullCnt = 0; +} diff --git a/usb/device/massstorage/MSDIOFifo.h b/usb/device/massstorage/MSDIOFifo.h new file mode 100644 index 0000000..efe204d --- /dev/null +++ b/usb/device/massstorage/MSDIOFifo.h @@ -0,0 +1,133 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +#ifndef _MSDIOFIFO_H +#define _MSDIOFIFO_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Idle state, do nothing +#define MSDIO_IDLE 0 +/// Start, to start IO operation +#define MSDIO_START 1 +/// Wait, waiting for IO operation done +#define MSDIO_WAIT 2 +/// Next, to check if the next block can be performed +#define MSDIO_NEXT 3 +/// Pause, to pause the process for buffer full or null +#define MSDIO_PAUSE 4 +/// Abort, to abort the process +#define MSDIO_ABORT 5 +/// Done, finish without error +#define MSDIO_DONE 6 +/// Error, any error happens +#define MSDIO_ERROR 7 + +/// FIFO offset before USB transmit start +//#define MSDIO_FIFO_OFFSET (4*512) + +/// FIFO trunk size (in each transfer, large amount of data) +#define MSDIO_READ10_CHUNK_SIZE (4*512) +#define MSDIO_WRITE10_CHUNK_SIZE (4*512) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// FIFO buffer for READ/WRITE (disk) operation of a mass storage device +typedef struct _MSDIOFifo { + + /// Pointer to the ring buffer allocated for read/write + unsigned char * pBuffer; + /// The size of the buffer allocated + unsigned int bufferSize; +#ifdef MSDIO_FIFO_OFFSET + /// The offset to start USB transfer (READ10) + unsigned int bufferOffset; +#endif + /// The index of input data (loaded to fifo buffer) + unsigned int inputNdx; + /// The total size of the loaded data + unsigned int inputTotal; + /// The index of output data (sent from the fifo buffer) + unsigned int outputNdx; + /// The total size of the output data + unsigned int outputTotal; + + /// The total size of the data + unsigned int dataTotal; + /// The size of the block in bytes + unsigned short blockSize; +#if defined(MSDIO_READ10_CHUNK_SIZE) || defined(MSDIO_WRITE10_CHUNK_SIZE) + /// The size of one chunk + /// (1 block, or several blocks for large amount data R/W) + unsigned int chunkSize; +#endif + /// State of input & output + unsigned char inputState; + unsigned char outputState; + + //- Statistics + /// Times when fifo has no data to send + unsigned short nullCnt; + /// Times when fifo can not load more input data + unsigned short fullCnt; +} MSDIOFifo, *PMSDIOFifo; + +//------------------------------------------------------------------------------ +// MACROS +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Increase the index, by defined block size, in the ring buffer +/// \param ndx The index to be increased +/// \param sectSize The defined block size +/// \param bufSize The ring buffer size +//------------------------------------------------------------------------------ +#define MSDIOFifo_IncNdx(ndx, sectSize, bufSize) \ + if ((ndx) >= (bufSize) - (sectSize)) (ndx) = 0; \ + else (ndx) += (sectSize) + + +//------------------------------------------------------------------------------ +// Exported Functions +//------------------------------------------------------------------------------ + + +extern void MSDIOFifo_Init(MSDIOFifo *pFifo, + void * pBuffer, unsigned short bufferSize); + +#endif // _MSDIOFIFO_H + diff --git a/usb/device/massstorage/MSDLun.c b/usb/device/massstorage/MSDLun.c new file mode 100644 index 0000000..42999d7 --- /dev/null +++ b/usb/device/massstorage/MSDLun.c @@ -0,0 +1,366 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + + +#include "MSDLun.h" +#include +#include + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + +/// Default LUN block size in bytes +#define DEFAULT_LUN_BLOCK_SIZE 512 + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Inquiry data to return to the host for the Lun. +static SBCInquiryData inquiryData = { + + SBC_DIRECT_ACCESS_BLOCK_DEVICE, // Direct-access block device + SBC_PERIPHERAL_DEVICE_CONNECTED,// Peripheral device is connected + 0x00, // Reserved bits + 0x01, // Media is removable + SBC_SPC_VERSION_4, // SPC-4 supported + 0x2, // Response data format, must be 0x2 + 0, // Hierarchical addressing not supported + 0, // ACA not supported + 0x0, // Obsolete bits + sizeof(SBCInquiryData) - 5, // Additional length + 0, // No embedded SCC + 0, // No access control coordinator + SBC_TPGS_NONE, // No target port support group + 0, // Third-party copy not supported + 0x0, // Reserved bits + 0, // Protection information not supported + 0x0, // Obsolete bit + 0, // No embedded enclosure service component + 0x0, // ??? + 0, // Device is not multi-port + 0x0, // Obsolete bits + 0x0, // Unused feature + 0x0, // Unused features + 0, // Task management model not supported + 0x0, // ??? + {'A','T','M','E','L',' ',' ',' '}, + {'M','a','s','s',' ', + 'S','t','o','r','a','g','e',' ', + 'M','S','D'}, + {'0','.','0','1'}, + {'M','a','s','s',' ', + 'S','t','o','r','a','g','e',' ', + 'E','x','a','m','p','l','e'}, + 0x00, // Unused features + 0x00, // Reserved bits + {SBC_VERSION_DESCRIPTOR_SBC_3}, // SBC-3 compliant device + {0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0} // Reserved +}; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//! \brief Initializes a LUN instance. Must be invoked at least once even no +//! Media is linked. +//! \param lun Pointer to the MSDLun instance to initialize +//! \param media Media on which the LUN is constructed, set to 0 to +//! disconnect the Media or initialize an ejected LUN. +//! \param ioBuffer Pointer to a buffer used for read/write operation and +//! which must be blockSize bytes long. +//! \param ioBufferSize Size of the allocated IO buffer. +//! \param baseAddress Base address of the LUN in number of media blocks +//! \param size Total size of the LUN in number of media blocks +//! \param blockSize One block of the LUN in number of media blocks +//! \param protected The LUN area is forced to readonly even the media +//! is writable +//! \param dataMonitor Pointer to a Monitor Function to analyze the flow of +//! this LUN. +//------------------------------------------------------------------------------ +void LUN_Init(MSDLun *lun, + Media *media, + unsigned char *ioBuffer, + unsigned int ioBufferSize, + unsigned int baseAddress, + unsigned int size, + unsigned short blockSize, + unsigned char protected, + void (*dataMonitor)(unsigned char flowDirection, + unsigned int dataLength, + unsigned int fifoNullCount, + unsigned int fifoFullCount)) +{ + unsigned int logicalBlockAddress; + TRACE_INFO("LUN init\n\r"); + + // Initialize inquiry data + lun->inquiryData = &inquiryData; + + // Initialize request sense data + lun->requestSenseData.bResponseCode = SBC_SENSE_DATA_FIXED_CURRENT; + lun->requestSenseData.isValid = 1; + lun->requestSenseData.bObsolete1 = 0; + lun->requestSenseData.bSenseKey = SBC_SENSE_KEY_NO_SENSE; + lun->requestSenseData.bReserved1 = 0; + lun->requestSenseData.isILI = 0; + lun->requestSenseData.isEOM = 0; + lun->requestSenseData.isFilemark = 0; + lun->requestSenseData.pInformation[0] = 0; + lun->requestSenseData.pInformation[1] = 0; + lun->requestSenseData.pInformation[2] = 0; + lun->requestSenseData.pInformation[3] = 0; + lun->requestSenseData.bAdditionalSenseLength + = sizeof(SBCRequestSenseData) - 8; + lun->requestSenseData.bAdditionalSenseCode = 0; + lun->requestSenseData.bAdditionalSenseCodeQualifier = 0; + lun->requestSenseData.bFieldReplaceableUnitCode = 0; + lun->requestSenseData.bSenseKeySpecific = 0; + lun->requestSenseData.pSenseKeySpecific[0] = 0; + lun->requestSenseData.pSenseKeySpecific[0] = 0; + lun->requestSenseData.isSKSV = 0; + + STORE_DWORDB(0, lun->readCapacityData.pLogicalBlockAddress); + STORE_DWORDB(0, lun->readCapacityData.pLogicalBlockLength); + + // Initialize LUN + lun->media = media; + if (media == 0) { + lun->status = LUN_NOT_PRESENT; + return; + } + + lun->baseAddress = baseAddress; + + // Customized block size + if (blockSize) { + lun->blockSize = blockSize; + } + else { + if (media->blockSize < DEFAULT_LUN_BLOCK_SIZE) + lun->blockSize = DEFAULT_LUN_BLOCK_SIZE / media->blockSize; + else + lun->blockSize = 1; + } + + if (size) { + lun->size = size; + } + else { + lun->size = media->size; + //if (blockSize) + // lun->size = media->size / blockSize; + //else { + // if (media->blockSize < DEFAULT_LUN_BLOCK_SIZE) + // lun->size = media->size / lun->blockSize; + // else + // lun->size = media->size; + //} + } + + TRACE_INFO("LUN: blkSize %d, size %d\n\r", (int)lun->blockSize, (int)lun->size); + if (protected) lun->protected = 1; + else lun->protected = media->protected; + + lun->ioFifo.pBuffer = ioBuffer; + lun->ioFifo.bufferSize = ioBufferSize; + + lun->dataMonitor = dataMonitor; + + // Initialize read capacity data + logicalBlockAddress = lun->size / lun->blockSize - 1; + STORE_DWORDB(logicalBlockAddress, + lun->readCapacityData.pLogicalBlockAddress); + STORE_DWORDB(lun->blockSize * media->blockSize, + lun->readCapacityData.pLogicalBlockLength); + + // Indicate media change + lun->status = LUN_CHANGED; +} + +//------------------------------------------------------------------------------ +//! \brief Eject the media from a LUN +//! \param lun Pointer to the MSDLun instance to initialize +//! \return Operation result code +//------------------------------------------------------------------------------ +unsigned char LUN_Eject(MSDLun *lun) +{ + if (lun->media) { + + // Avoid any LUN R/W in progress + if (lun->media->state == MED_STATE_BUSY) { + + return USBD_STATUS_LOCKED; + } + + // Remove the link of the media + lun->media = 0; + } + // LUN is removed + lun->status = LUN_NOT_PRESENT; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +//! \brief Writes data on the a LUN starting at the specified block address. +//! \param lun Pointer to a MSDLun instance +//! \param blockAddress First block address to write +//! \param data Pointer to the data to write +//! \param length Number of blocks to write +//! \param callback Optional callback to invoke when the write finishes +//! \return Operation result code +//------------------------------------------------------------------------------ +unsigned char LUN_Write(MSDLun *lun, + unsigned int blockAddress, + void *data, + unsigned int length, + TransferCallback callback, + void *argument) +{ + unsigned int medBlk, medLen; + unsigned char status; + + TRACE_INFO_WP("LUNWrite(%u) ", blockAddress); + + // Check that the data is not too big + if ((length + blockAddress) * lun->blockSize > lun->size) { + + TRACE_WARNING("LUN_Write: Data too big\n\r"); + status = USBD_STATUS_ABORTED; + } + else if (lun->media == 0 || lun->status != LUN_READY) { + + TRACE_WARNING("LUN_Write: Media not ready\n\r"); + status = USBD_STATUS_ABORTED; + } + else if (lun->protected) { + TRACE_WARNING("LUN_Write: LUN is readonly\n\r"); + status = USBD_STATUS_ABORTED; + } + else { + + // Compute write start address + medBlk = lun->baseAddress + blockAddress * lun->blockSize; + medLen = length * lun->blockSize; + + // Start write operation + status = MED_Write(lun->media, + medBlk, + data, + medLen, + (MediaCallback) callback, + argument); + + // Check operation result code + if (status == MED_STATUS_SUCCESS) { + + status = USBD_STATUS_SUCCESS; + } + else { + + TRACE_WARNING("LUN_Write: Cannot write media\n\r"); + status = USBD_STATUS_ABORTED; + } + } + + return status; +} + +//------------------------------------------------------------------------------ +//! \brief Reads data from a LUN, starting at the specified block address. +//! \param lun Pointer to a MSDLun instance +//! \param blockAddress First block address to read +//! \param data Pointer to a data buffer in which to store the data +//! \param length Number of blocks to read +//! \param callback Optional callback to invoke when the read finishes +//! \return Operation result code +//------------------------------------------------------------------------------ +unsigned char LUN_Read(MSDLun *lun, + unsigned int blockAddress, + void *data, + unsigned int length, + TransferCallback callback, + void *argument) +{ + unsigned int medBlk, medLen; + unsigned char status; + + // Check that the data is not too big + if ((length + blockAddress) * lun->blockSize > lun->size) { + + TRACE_WARNING("LUN_Read: Area: (%d + %d)*%d > %d\n\r", + (int)length, (int)blockAddress, (int)lun->blockSize, (int)lun->size); + status = USBD_STATUS_ABORTED; + } + else if (lun->media == 0 || lun->status != LUN_READY) { + + TRACE_WARNING("LUN_Read: Media not present\n\r"); + status = USBD_STATUS_ABORTED; + } + else { + + TRACE_INFO_WP("LUNRead(%u) ", blockAddress); + + // Compute read start address + medBlk = lun->baseAddress + (blockAddress * lun->blockSize); + medLen = length * lun->blockSize; + + // Start write operation + status = MED_Read(lun->media, + medBlk, + data, + medLen, + (MediaCallback) callback, + argument); + + // Check result code + if (status == MED_STATUS_SUCCESS) { + + status = USBD_STATUS_SUCCESS; + } + else { + + TRACE_WARNING("LUN_Read: Cannot read media\n\r"); + status = USBD_STATUS_ABORTED; + } + } + + return status; +} diff --git a/usb/device/massstorage/MSDLun.h b/usb/device/massstorage/MSDLun.h new file mode 100644 index 0000000..52eea4c --- /dev/null +++ b/usb/device/massstorage/MSDLun.h @@ -0,0 +1,152 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// Logical Unit Number (LUN) used by the Mass Storage driver and the SCSI +/// protocol. Represents a logical hard-drive. +/// +/// !Usage +/// -# Initialize Memory related pins (see pio & board.h). +/// -# Initialize a Media instance for the LUN (see memories). +/// -# Initlalize the LUN with LUN_Init, and link to the initialized Media. +/// -# To read data from the LUN linked media, uses LUN_Read. +/// -# To write data to the LUN linked media, uses LUN_Write. +/// -# To unlink the media, uses LUN_Eject. +//------------------------------------------------------------------------------ + +#ifndef MSDLUN_H +#define MSDLUN_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "SBC.h" +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// LUN RC: success +#define LUN_STATUS_SUCCESS 0x00 +/// LUN RC: error +#define LUN_STATUS_ERROR 0x02 + +/// Media of LUN is removed +#define LUN_NOT_PRESENT 0x00 +/// LUN is ejected by host +#define LUN_EJECTED 0x01 +/// Media of LUN is changed +#define LUN_CHANGED 0x10 +/// LUN Not Ready to Ready transition +#define LUN_TRANS_READY LUN_CHANGED +/// Media of LUN is ready +#define LUN_READY 0x11 + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +/// LUN structure +typedef struct { + + /// Pointer to a SBCInquiryData instance. + SBCInquiryData *inquiryData; + /// Fifo for USB transfer, must be assigned. + MSDIOFifo ioFifo; + /// Pointer to Media instance for the LUN. + Media *media; + /// Pointer to a Monitor Function to analyze the flow of LUN. + /// \param flowDirection 1 - device to host (READ10) + /// 0 - host to device (WRITE10) + /// \param dataLength Length of data transferred in bytes. + /// \param fifoNullCount Times that FIFO is NULL to wait + /// \param fifoFullCount Times that FIFO is filled to wait + void (*dataMonitor)(unsigned char flowDirection, + unsigned int dataLength, + unsigned int fifoNullCount, + unsigned int fifoFullCount); + /// The start position of the media (blocks) allocated to the LUN. + unsigned int baseAddress; + /// The size of the media (blocks) allocated to the LUN. + unsigned int size; + /// Sector size of the media in number of media blocks + unsigned short blockSize; + /// The LUN can be readonly even the media is writable + unsigned char protected; + /// The LUN status (Ejected/Changed/) + unsigned char status; + + /// Data for the RequestSense command. + SBCRequestSenseData requestSenseData; + /// Data for the ReadCapacity command. + SBCReadCapacity10Data readCapacityData; + +} MSDLun; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +extern void LUN_Init(MSDLun *lun, + Media *media, + unsigned char *ioBuffer, + unsigned int ioBufferSize, + unsigned int baseAddress, + unsigned int size, + unsigned short blockSize, + unsigned char protected, + void (*dataMonitor)(unsigned char flowDirection, + unsigned int dataLength, + unsigned int fifoNullCount, + unsigned int fifoFullCount)); + +extern unsigned char LUN_Eject(MSDLun *lun); + +extern unsigned char LUN_Write(MSDLun *lun, + unsigned int blockAddress, + void *data, + unsigned int length, + TransferCallback callback, + void *argument); + +extern unsigned char LUN_Read(MSDLun *lun, + unsigned int blockAddress, + void *data, + unsigned int length, + TransferCallback callback, + void *argument); + +#endif //#ifndef MSDLUN_H + diff --git a/usb/device/massstorage/MSDMediaArch.png b/usb/device/massstorage/MSDMediaArch.png new file mode 100644 index 0000000..b641e97 Binary files /dev/null and b/usb/device/massstorage/MSDMediaArch.png differ diff --git a/usb/device/massstorage/SBC.h b/usb/device/massstorage/SBC.h new file mode 100644 index 0000000..7c64844 --- /dev/null +++ b/usb/device/massstorage/SBC.h @@ -0,0 +1,653 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// SCSI definitions. +/// +/// !Usage +/// +/// -# After command block received, Access and decode the SCSI command block +/// with SBCCommand structure. +//------------------------------------------------------------------------------ + +#ifndef SBC_H +#define SBC_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Operation Codes" +/// This page lists operation codes of commands described in the SBC-3 +/// standard. +/// +/// \note That most commands are actually defined in other standards, +/// like SPC-4. Optional commands are not included here. +/// +/// \see sbc3r07.pdf - Section 5.1 - Table 12 +/// \see spc4r06.pdf +/// \see SBCCommand +/// +/// !Codes +/// - SBC_INQUIRY +/// - SBC_READ_10 +/// - SBC_READ_CAPACITY_10 +/// - SBC_REQUEST_SENSE +/// - SBC_TEST_UNIT_READY +/// - SBC_WRITE_10 +/// +/// !Optional Codes but required by Windows +/// - SBC_PREVENT_ALLOW_MEDIUM_REMOVAL +/// - SBC_MODE_SENSE_6 +/// - SBC_VERIFY_10 +/// - SBC_READ_FORMAT_CAPACITIES + +/// Request information regarding parameters of the target and Logical Unit. +#define SBC_INQUIRY 0x12 +/// Request the transfer data to the host. +#define SBC_READ_10 0x28 +/// Request capacities of the currently installed medium. +#define SBC_READ_CAPACITY_10 0x25 +/// Request that the device server transfer sense data. +#define SBC_REQUEST_SENSE 0x03 +/// Check if the LUN is ready +#define SBC_TEST_UNIT_READY 0x00 +/// Request that the device write the data transferred by the host. +#define SBC_WRITE_10 0x2A + +/// Request that the target enable or disable the removal of the medium in +/// the Logical Unit. +#define SBC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E +/// Report parameters. +#define SBC_MODE_SENSE_6 0x1A +/// Request that the %device verify the data on the medium. +#define SBC_VERIFY_10 0x2F +/// Request a list of the possible capacities that can be formatted on medium +#define SBC_READ_FORMAT_CAPACITIES 0x23 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Periph. Qualifiers" +/// This page lists the peripheral qualifier values specified in the INQUIRY +/// data +/// \see spc4r06.pdf - Section 6.4.2 - Table 83 +/// \see SBCInquiryData +/// +/// !Qualifiers +/// - SBC_PERIPHERAL_DEVICE_CONNECTED +/// - SBC_PERIPHERAL_DEVICE_NOT_CONNECTED +/// - SBC_PERIPHERAL_DEVICE_NOT_SUPPORTED + +#define SBC_PERIPHERAL_DEVICE_CONNECTED 0x00 +#define SBC_PERIPHERAL_DEVICE_NOT_CONNECTED 0x01 +#define SBC_PERIPHERAL_DEVICE_NOT_SUPPORTED 0x03 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Periph. Types" +/// This page lists peripheral device types specified in the INQUIRY data +/// \see spc4r06.pdf - Section 6.4.2 - Table 84 +/// \see SBCInquiryData +/// +/// !Types +/// - SBC_DIRECT_ACCESS_BLOCK_DEVICE +/// - SBC_SEQUENTIAL_ACCESS_DEVICE +/// - SBC_PRINTER_DEVICE +/// - SBC_PROCESSOR_DEVICE +/// - SBC_WRITE_ONCE_DEVICE +/// - SBC_CD_DVD_DEVICE +/// - SBC_SCANNER_DEVICE +/// - SBC_OPTICAL_MEMORY_DEVICE +/// - SBC_MEDIA_CHANGER_DEVICE +/// - SBC_COMMUNICATION_DEVICE +/// - SBC_STORAGE_ARRAY_CONTROLLER_DEVICE +/// - SBC_ENCLOSURE_SERVICES_DEVICE +/// - SBC_SIMPLIFIED_DIRECT_ACCESS_DEVICE +/// - SBC_OPTICAL_CARD_READER_WRITER_DEVICE +/// - SBC_BRIDGE_CONTROLLER_COMMANDS +/// - SBC_OBJECT_BASED_STORAGE_DEVICE + +#define SBC_DIRECT_ACCESS_BLOCK_DEVICE 0x00 +#define SBC_SEQUENTIAL_ACCESS_DEVICE 0x01 +#define SBC_PRINTER_DEVICE 0x02 +#define SBC_PROCESSOR_DEVICE 0x03 +#define SBC_WRITE_ONCE_DEVICE 0x04 +#define SBC_CD_DVD_DEVICE 0x05 +#define SBC_SCANNER_DEVICE 0x06 +#define SBC_OPTICAL_MEMORY_DEVICE 0x07 +#define SBC_MEDIA_CHANGER_DEVICE 0x08 +#define SBC_COMMUNICATION_DEVICE 0x09 +#define SBC_STORAGE_ARRAY_CONTROLLER_DEVICE 0x0C +#define SBC_ENCLOSURE_SERVICES_DEVICE 0x0D +#define SBC_SIMPLIFIED_DIRECT_ACCESS_DEVICE 0x0E +#define SBC_OPTICAL_CARD_READER_WRITER_DEVICE 0x0F +#define SBC_BRIDGE_CONTROLLER_COMMANDS 0x10 +#define SBC_OBJECT_BASED_STORAGE_DEVICE 0x11 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \brief Version value for the SBC-3 specification +/// \see spc4r06.pdf - Section 6.4.2 - Table 85 +#define SBC_SPC_VERSION_4 0x06 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \brief Values for the TPGS field returned in INQUIRY data +/// \see spc4r06.pdf - Section 6.4.2 - Table 86 +#define SBC_TPGS_NONE 0x0 +#define SBC_TPGS_ASYMMETRIC 0x1 +#define SBC_TPGS_SYMMETRIC 0x2 +#define SBC_TPGS_BOTH 0x3 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \brief Version descriptor value for the SBC-3 specification +/// \see spc4r06.pdf - Section 6.4.2 - Table 87 +#define SBC_VERSION_DESCRIPTOR_SBC_3 0x04C0 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Sense Response Codes" +/// This page lists sense data response codes returned in REQUEST SENSE data +/// \see spc4r06.pdf - Section 4.5.1 - Table 12 +/// +/// !Codes +/// - SBC_SENSE_DATA_FIXED_CURRENT +/// - SBC_SENSE_DATA_FIXED_DEFERRED +/// - SBC_SENSE_DATA_DESCRIPTOR_CURRENT +/// - SBC_SENSE_DATA_DESCRIPTOR_DEFERRED + +#define SBC_SENSE_DATA_FIXED_CURRENT 0x70 +#define SBC_SENSE_DATA_FIXED_DEFERRED 0x71 +#define SBC_SENSE_DATA_DESCRIPTOR_CURRENT 0x72 +#define SBC_SENSE_DATA_DESCRIPTOR_DEFERRED 0x73 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Sense Keys" +/// This page lists sense key values returned in the REQUEST SENSE data +/// \see spc4r06.pdf - Section 4.5.6 - Table 27 +/// +/// !Keys +/// - SBC_SENSE_KEY_NO_SENSE +/// - SBC_SENSE_KEY_RECOVERED_ERROR +/// - SBC_SENSE_KEY_NOT_READY +/// - SBC_SENSE_KEY_MEDIUM_ERROR +/// - SBC_SENSE_KEY_HARDWARE_ERROR +/// - SBC_SENSE_KEY_ILLEGAL_REQUEST +/// - SBC_SENSE_KEY_UNIT_ATTENTION +/// - SBC_SENSE_KEY_DATA_PROTECT +/// - SBC_SENSE_KEY_BLANK_CHECK +/// - SBC_SENSE_KEY_VENDOR_SPECIFIC +/// - SBC_SENSE_KEY_COPY_ABORTED +/// - SBC_SENSE_KEY_ABORTED_COMMAND +/// - SBC_SENSE_KEY_VOLUME_OVERFLOW +/// - SBC_SENSE_KEY_MISCOMPARE + +/// No specific sense key. Successful command. +#define SBC_SENSE_KEY_NO_SENSE 0x00 +/// Command completed succesfully with some recovery action by the %device. +#define SBC_SENSE_KEY_RECOVERED_ERROR 0x01 +/// The device can not be accessed. +#define SBC_SENSE_KEY_NOT_READY 0x02 +/// Command terminated with a error condition that was probably caused by a +/// flaw in the medium or an error in the recorded data. +#define SBC_SENSE_KEY_MEDIUM_ERROR 0x03 +/// Hardware failure while performing the command or during a self test. +#define SBC_SENSE_KEY_HARDWARE_ERROR 0x04 +/// Illegal parameter found in the command or additional parameters. +#define SBC_SENSE_KEY_ILLEGAL_REQUEST 0x05 +/// Removable medium may have been changed or the %device has been reset. +#define SBC_SENSE_KEY_UNIT_ATTENTION 0x06 +/// Write on a block that is protected. +#define SBC_SENSE_KEY_DATA_PROTECT 0x07 +/// Indicates that a write-once device or a sequential-access device +/// encountered blank medium or format-defined end-of-data indication while +/// reading or a write-once device encountered a non-blank medium while writing. +#define SBC_SENSE_KEY_BLANK_CHECK 0x08 +/// Reporting vendor specific conditions. +#define SBC_SENSE_KEY_VENDOR_SPECIFIC 0x09 +/// EXTENDED COPY command was aborted. +#define SBC_SENSE_KEY_COPY_ABORTED 0x0A +/// Device aborted the command. +#define SBC_SENSE_KEY_ABORTED_COMMAND 0x0B +/// A buffered peripheral device is overflow. +#define SBC_SENSE_KEY_VOLUME_OVERFLOW 0x0D +/// The source data did not match the data read from the medium. +#define SBC_SENSE_KEY_MISCOMPARE 0x0E +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Sense Additionals" +/// This page lists additional sense code values returned in REQUEST SENSE data +/// \see spc4r06.pdf - Section 4.5.6 - Table 28 +/// +/// !Additional Codes +/// - SBC_ASC_LOGICAL_UNIT_NOT_READY +/// - SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE +/// - SBC_ASC_INVALID_FIELD_IN_CDB +/// - SBC_ASC_WRITE_PROTECTED +/// - SBC_ASC_FORMAT_CORRUPTED +/// - SBC_ASC_INVALID_COMMAND_OPERATION_CODE +/// - SBC_ASC_TOO_MUCH_WRITE_DATA +/// - SBC_ASC_NOT_READY_TO_READY_CHANGE +/// - SBC_ASC_MEDIUM_NOT_PRESENT + +#define SBC_ASC_LOGICAL_UNIT_NOT_READY 0x04 +#define SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21 +#define SBC_ASC_INVALID_FIELD_IN_CDB 0x24 +#define SBC_ASC_WRITE_PROTECTED 0x27 +#define SBC_ASC_FORMAT_CORRUPTED 0x31 +#define SBC_ASC_INVALID_COMMAND_OPERATION_CODE 0x20 +#define SBC_ASC_TOO_MUCH_WRITE_DATA 0x26 +#define SBC_ASC_NOT_READY_TO_READY_CHANGE 0x28 +#define SBC_ASC_MEDIUM_NOT_PRESENT 0x3A +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \brief MEDIUM TYPE field value for direct-access block devices +/// \see sbc3r06.pdf - Section 6.3.1 +#define SBC_MEDIUM_TYPE_DIRECT_ACCESS_BLOCK_DEVICE 0x00 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \brief MRIE field values +/// \see sbc3r06.pdf - Section 7.4.11 - Table 286 +#define SBC_MRIE_NO_REPORTING 0x00 +#define SBC_MRIE_ASYNCHRONOUS 0x01 +#define SBC_MRIE_GENERATE_UNIT_ATTENTION 0x02 +#define SBC_MRIE_COND_GENERATE_RECOVERED_ERROR 0x03 +#define SBC_MRIE_UNCOND_GENERATE_RECOVERED_ERROR 0x04 +#define SBC_MRIE_GENERATE_NO_SENSE 0x05 +#define SBC_MRIE_ON_REQUEST 0x06 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \brief Supported mode pages +/// \see sbc3r06.pdf - Section 6.3.1 - Table 115 +#define SBC_PAGE_READ_WRITE_ERROR_RECOVERY 0x01 +#define SBC_PAGE_INFORMATIONAL_EXCEPTIONS_CONTROL 0x1C +#define SBC_PAGE_RETURN_ALL 0x3F +#define SBC_PAGE_VENDOR_SPECIFIC 0x00 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "MSD Endian Macros" +/// This page lists the macros for endianness conversion. +/// +/// !Macros +/// - WORDB +/// - DWORDB +/// - STORE_DWORDB +/// - STORE_WORDB +/// \brief Converts a byte array to a word value using the big endian format +#define WORDB(bytes) ((unsigned short) ((bytes[0] << 8) | bytes[1])) + +/// \brief Converts a byte array to a dword value using the big endian format +#define DWORDB(bytes) ((unsigned int) ((bytes[0] << 24) | (bytes[1] << 16) \ + | (bytes[2] << 8) | bytes[3])) + +/// \brief Stores a dword value in a byte array, in big endian format +#define STORE_DWORDB(dword, bytes) \ + bytes[0] = (unsigned char) (((dword) >> 24) & 0xFF); \ + bytes[1] = (unsigned char) (((dword) >> 16) & 0xFF); \ + bytes[2] = (unsigned char) (((dword) >> 8) & 0xFF); \ + bytes[3] = (unsigned char) ((dword) & 0xFF); + +/// \brief Stores a word value in a byte array, in big endian format +#define STORE_WORDB(word, bytes) \ + bytes[0] = (unsigned char) (((word) >> 8) & 0xFF); \ + bytes[1] = (unsigned char) ((word) & 0xFF); +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +#ifdef __ICCARM__ // IAR +#pragma pack(1) // IAR +#define __attribute__(...) // IAR +#endif // IAR + +//------------------------------------------------------------------------------ +/// \brief Structure for the INQUIRY command +/// \see spc4r06.pdf - Section 6.4.1 - Table 81 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x12 : SBC_INQUIRY + unsigned char isEVPD:1, //!< Type of requested data + bReserved1:7; //!< Reserved bits + unsigned char bPageCode; //!< Specifies the VPD to return + unsigned char pAllocationLength[2]; //!< Size of host buffer + unsigned char bControl; //!< 0x00 + +} __attribute__ ((packed)) SBCInquiry; // GCC + +//------------------------------------------------------------------------------ +/// \brief Standard INQUIRY data format returned by the device +/// \see spc4r06.pdf - Section 6.4.2 - Table 82 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bPeripheralDeviceType:5, //!< Peripheral device type + bPeripheralQualifier :3; //!< Peripheral qualifier + unsigned char bReserved1:7, //!< Reserved bits + isRMB :1; //!< Is media removable ? + unsigned char bVersion; //!< SPC version used + unsigned char bResponseDataFormat:4, //!< Must be 0x2 + isHIGHSUP :1, //!< Hierarchical addressing used ? + isNORMACA :1, //!< ACA attribute supported ? + bObsolete1 :2; //!< Obsolete bits + unsigned char bAdditionalLength; //!< Length of remaining INQUIRY data + unsigned char isSCCS :1, //!< Embedded SCC ? + isACC :1, //!< Access control coordinator ? + bTPGS :2, //!< Target port support group + is3PC :1, //!< Third-party copy supported ? + bReserved2:2, //!< Reserved bits + isProtect :1; //!< Protection info supported ? + unsigned char bObsolete2:1, //!< Obsolete bit + isEncServ :1, //!< Embedded enclosure service comp? + isVS :1, //!< ??? + isMultiP :1, //!< Multi-port device ? + bObsolete3:3, //!< Obsolete bits + bUnused1 :1; //!< Unused feature + unsigned char bUnused2:6, //!< Unused features + isCmdQue:1, //!< Task management model supported ? + isVS2 :1; //!< ??? + unsigned char pVendorID[8]; //!< T10 vendor identification + unsigned char pProductID[16]; //!< Vendor-defined product ID + unsigned char pProductRevisionLevel[4];//!< Vendor-defined product revision + unsigned char pVendorSpecific[20]; //!< Vendor-specific data + unsigned char bUnused3; //!< Unused features + unsigned char bReserved3; //!< Reserved bits + unsigned short pVersionDescriptors[8]; //!< Standards the device complies to + unsigned char pReserved4[22]; //!< Reserved bytes + +} __attribute__ ((packed)) SBCInquiryData; // GCC + +//------------------------------------------------------------------------------ +/// \brief Data structure for the READ (10) command +/// \see sbc3r07.pdf - Section 5.7 - Table 34 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x28 : SBC_READ_10 + unsigned char bObsolete1:1, //!< Obsolete bit + isFUA_NV:1, //!< Cache control bit + bReserved1:1, //!< Reserved bit + isFUA:1, //!< Cache control bit + isDPO:1, //!< Cache control bit + bRdProtect:3; //!< Protection information to send + unsigned char pLogicalBlockAddress[4]; //!< Index of first block to read + unsigned char bGroupNumber:5, //!< Information grouping + bReserved2:3; //!< Reserved bits + unsigned char pTransferLength[2]; //!< Number of blocks to transmit + unsigned char bControl; //!< 0x00 + +} __attribute__ ((packed)) SBCRead10; // GCC + +//------------------------------------------------------------------------------ +/// \brief Structure for the READ CAPACITY (10) command +/// \see sbc3r07.pdf - Section 5.11.1 - Table 40 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x25 : RBC_READ_CAPACITY + unsigned char bObsolete1:1, //!< Obsolete bit + bReserved1:7; //!< Reserved bits + unsigned char pLogicalBlockAddress[4]; //!< Block to evaluate if PMI is set + unsigned char pReserved2[2]; //!< Reserved bytes + unsigned char isPMI:1, //!< Partial medium indicator bit + bReserved3:7; //!< Reserved bits + unsigned char bControl; //!< 0x00 + +} SBCReadCapacity10; + +//------------------------------------------------------------------------------ +/// \brief Data returned by the device after a READ CAPACITY (10) command +/// \see sbc3r07.pdf - Section 5.11.2 - Table 41 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char pLogicalBlockAddress[4]; //!< Address of last logical block + unsigned char pLogicalBlockLength[4]; //!< Length of each logical block + +} SBCReadCapacity10Data; + +//------------------------------------------------------------------------------ +/// \brief Structure for the REQUEST SENSE command +/// \see spc4r06.pdf - Section 6.26 - Table 170 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x03 : SBC_REQUEST_SENSE + unsigned char isDesc :1, //!< Type of information expected + bReserved1:7; //!< Reserved bits + unsigned char pReserved2[2]; //!< Reserved bytes + unsigned char bAllocationLength; //!< Size of host buffer + unsigned char bControl; //!< 0x00 + +} SBCRequestSense; + +//------------------------------------------------------------------------------ +/// \brief Fixed format sense data returned after a REQUEST SENSE command has +/// been received with a DESC bit cleared. +/// \see spc4r06.pdf - Section 4.5.3 - Table 26 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bResponseCode:7, //!< Sense data format + isValid :1; //!< Information field is standard + unsigned char bObsolete1; //!< Obsolete byte + unsigned char bSenseKey :4, //!< Generic error information + bReserved1:1, //!< Reserved bit + isILI :1, //!< SSC + isEOM :1, //!< SSC + isFilemark:1; //!< SSC + unsigned char pInformation[4]; //!< Command-specific + unsigned char bAdditionalSenseLength; //!< sizeof(SBCRequestSense_data)-8 + unsigned char pCommandSpecificInformation[4]; //!< Command-specific + unsigned char bAdditionalSenseCode; //!< Additional error information + unsigned char bAdditionalSenseCodeQualifier; //!< Further error information + unsigned char bFieldReplaceableUnitCode; //!< Specific component code + unsigned char bSenseKeySpecific:7, //!< Additional exception info + isSKSV :1; //!< Is sense key specific valid? + unsigned char pSenseKeySpecific[2]; //!< Additional exception info + +} SBCRequestSenseData; + +//------------------------------------------------------------------------------ +/// \brief Data structure for the TEST UNIT READY command +/// \see spc4r06.pdf - Section 6.34 - Table 192 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x00 : SBC_TEST_UNIT_READY + unsigned char pReserved1[4]; //!< Reserved bits + unsigned char bControl; //!< 0x00 + +} __attribute__ ((packed)) SBCTestUnitReady; // GCC + +//------------------------------------------------------------------------------ +/// \brief Structure for the WRITE (10) command +/// \see sbc3r07.pdf - Section 5.26 - Table 70 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x2A : SBC_WRITE_10 + unsigned char bObsolete1:1, //!< Obsolete bit + isFUA_NV:1, //!< Cache control bit + bReserved1:1, //!< Reserved bit + isFUA:1, //!< Cache control bit + isDPO:1, //!< Cache control bit + bWrProtect:3; //!< Protection information to send + unsigned char pLogicalBlockAddress[4]; //!< First block to write + unsigned char bGroupNumber:5, //!< Information grouping + bReserved2:3; //!< Reserved bits + unsigned char pTransferLength[2]; //!< Number of blocks to write + unsigned char bControl; //!< 0x00 + +} SBCWrite10; + +//------------------------------------------------------------------------------ +/// \brief Structure for the PREVENT/ALLOW MEDIUM REMOVAL command +/// \see sbc3r07.pdf - Section 5.5 - Table 30 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x1E : SBC_PREVENT_ALLOW_MEDIUM_REMOVAL + unsigned char pReserved1[3]; //!< Reserved bytes + unsigned char bPrevent:2, //!< Accept/prohibit removal + bReserved2:6; //!< Reserved bits + unsigned char bControl; //!< 0x00 + +} __attribute__ ((packed)) SBCMediumRemoval; // GCC + +//------------------------------------------------------------------------------ +/// \brief Structure for the MODE SENSE (6) command +/// \see spc4r06 - Section 6.9.1 - Table 98 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bOperationCode; //!< 0x1A : SBC_MODE_SENSE_6 + unsigned char bReserved1:3, //!< Reserved bits + isDBD:1, //!< Disable block descriptors bit + bReserved2:4; //!< Reserved bits + unsigned char bPageCode:6, //!< Mode page to return + bPC:2; //!< Type of parameter values to return + unsigned char bSubpageCode; //!< Mode subpage to return + unsigned char bAllocationLength; //!< Host buffer allocated size + unsigned char bControl; //!< 0x00 + +} __attribute__ ((packed)) SBCModeSense6; // GCC + +//------------------------------------------------------------------------------ +/// \brief Header for the data returned after a MODE SENSE (6) command +/// \see spc4r06.pdf - Section 7.4.3 - Table 268 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bModeDataLength; //!< Length of mode data to follow + unsigned char bMediumType; //!< Type of medium (SBC_MEDIUM_TYPE_DIRECT_ACCESS_BLOCK_DEVICE) + unsigned char bReserved1:4, //!< Reserved bits + isDPOFUA:1, //!< DPO/FUA bits supported ? + bReserved2:2, //!< Reserved bits + isWP:1; //!< Is medium write-protected ? + unsigned char bBlockDescriptorLength; //!< Length of all block descriptors + +} __attribute__ ((packed)) SBCModeParameterHeader6; // GCC + +//------------------------------------------------------------------------------ +/// \brief Informational exceptions control mode page +/// \see spc4r06.pdf - Section 7.4.11 - Table 285 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bPageCode:6, //!< 0x1C : SBC_PAGE_INFORMATIONAL_EXCEPTIONS_CONTROL + isSPF:1, //!< Page or subpage data format + isPS:1; //!< Parameters saveable ? + unsigned char bPageLength; //!< Length of page data (0x0A) + unsigned char isLogErr:1, //!< Should informational exceptions be logged ? + isEBackErr:1, //!< Enable background error bit + isTest:1, //!< Create a device test failure ? + isDExcpt:1, //!< Disable exception control bit + isEWasc:1, //!< Report warnings ? + isEBF:1, //!< Enable background function bit + bReserved1:1, //!< Reserved bit + isPerf:1; //!< Delay acceptable when treating exceptions ? + unsigned char bMRIE:4, //!< Method of reporting informational exceptions + bReserved2:4; //!< Reserved bits + unsigned char pIntervalTimer[4]; //!< Error reporting period + unsigned char pReportCount[4]; //!< Maximum number of time a report can be issued + +} __attribute__ ((packed)) SBCInformationalExceptionsControl; // GCC + +//------------------------------------------------------------------------------ +/// \brief Read/write error recovery mode page +/// \see sbc3r07.pdf - Section 6.3.5 - Table 122 +//------------------------------------------------------------------------------ +typedef struct { + + unsigned char bPageCode:6, //!< 0x01 : SBC_PAGE_READ_WRITE_ERROR_RECOVERY + isSPF:1, //!< Page or subpage data format + isPS:1; //!< Parameters saveable ? + unsigned char bPageLength; //!< Length of page data (0x0A) + unsigned char isDCR:1, //!< Disable correction bit + isDTE:1, //!< Data terminate on error bit + isPER:1, //!< Post error bit + isEER:1, //!< Enable early recovery bit + isRC:1, //!< Read continuous bit + isTB:1, //!< Transfer block bit + isARRE:1, //!< Automatic read reallocation enabled bit + isAWRE:1; //!< Automatic write reallocation enabled bit + unsigned char bReadRetryCount; //!< Number of retries when reading + unsigned char pObsolete1[3]; //!< Obsolete bytes + unsigned char bReserved1; //!< Reserved byte + unsigned char bWriteRetryCount; //!< Number of retries when writing + unsigned char bReserved2; //!< Reserved byte + unsigned char pRecoveryTimeLimit[2]; //!< Maximum time duration for error recovery + +} __attribute__ ((packed)) SBCReadWriteErrorRecovery; // GCC + +//------------------------------------------------------------------------------ +/// \brief Generic structure for holding information about SBC commands +/// \see SBCInquiry +/// \see SBCRead10 +/// \see SBCReadCapacity10 +/// \see SBCRequestSense +/// \see SBCTestUnitReady +/// \see SBCWrite10 +/// \see SBCMediumRemoval +/// \see SBCModeSense6 +//------------------------------------------------------------------------------ +typedef union { + + unsigned char bOperationCode; //!< Operation code of the command + SBCInquiry inquiry; //!< INQUIRY command + SBCRead10 read10; //!< READ (10) command + SBCReadCapacity10 readCapacity10; //!< READ CAPACITY (10) command + SBCRequestSense requestSense; //!< REQUEST SENSE command + SBCTestUnitReady testUnitReady; //!< TEST UNIT READY command + SBCWrite10 write10; //!< WRITE (10) command + SBCMediumRemoval mediumRemoval; //!< PREVENT/ALLOW MEDIUM REMOVAL command + SBCModeSense6 modeSense6; //!< MODE SENSE (6) command + +} SBCCommand; + +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +#endif //#ifndef SBC_H + diff --git a/usb/device/massstorage/SBCMethods.c b/usb/device/massstorage/SBCMethods.c new file mode 100644 index 0000000..cd7a87a --- /dev/null +++ b/usb/device/massstorage/SBCMethods.c @@ -0,0 +1,1611 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "SBCMethods.h" +#include "MSDDStateMachine.h" +#include + +#include "MSDIOFifo.h" + +//------------------------------------------------------------------------------ +// Global variables +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +#ifdef MSDIO_READ10_CHUNK_SIZE +/// READ10 - Read data from specific LUN to FIFO +#define SBC_READ_CHUNK(pLun, lba, pFifo, pCb, pArg) \ + LUN_Read((pLun), (lba), \ + &(pFifo)->pBuffer[(pFifo)->inputNdx], \ + ((pFifo)->chunkSize/(pFifo)->blockSize), \ + (TransferCallback)(pCb), (void*)pArg) +/// READ10 - Transfer data from FIFO to USB +#define SBC_TX_CHUNK(pFifo, pCb, pArg) \ + MSDD_Write(&(pFifo)->pBuffer[(pFifo)->outputNdx], \ + (pFifo)->chunkSize, \ + (TransferCallback)(pCb), (void*)(pArg)) +#endif + +#ifdef MSDIO_WRITE10_CHUNK_SIZE +/// WRITE10 - Read data from USB to FIFO +#define SBC_RX_CHUNK(pFifo,pCb,pArg) \ + MSDD_Read(&(pFifo)->pBuffer[(pFifo)->inputNdx], \ + (pFifo)->chunkSize, \ + (TransferCallback)(pCb), (void*)(pArg)) +/// WRITE10 - Write data from FIFO to LUN +#define SBC_WRITE_CHUNK(pLun, lba, pFifo, pCb, pArg) \ + LUN_Write((pLun), (lba), \ + &(pFifo)->pBuffer[(pFifo)->outputNdx], \ + ((pFifo)->chunkSize/(pFifo)->blockSize), \ + (TransferCallback)(pCb), (void*)(pArg)) +#endif + + +//------------------------------------------------------------------------------ +//! \brief Header for the mode pages data +//! \see SBCModeParameterHeader6 +//------------------------------------------------------------------------------ +static const SBCModeParameterHeader6 modeParameterHeader6 = { + + sizeof(SBCModeParameterHeader6) - 1, //! Length is 0x03 + SBC_MEDIUM_TYPE_DIRECT_ACCESS_BLOCK_DEVICE, //! Direct-access block device + 0, //! Reserved bits + 0, //! DPO/FUA not supported + 0, //! Reserved bits + 0, //! not write-protected + 0 //! No block descriptor +}; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//! \brief Check if the LUN is ready. +//! \param lun Pointer to the LUN affected by the command +//! \return 1 if the LUN is ready to be written +//! \see MSDLun +//------------------------------------------------------------------------------ +static unsigned char SBCLunIsReady(MSDLun *lun) +{ + unsigned char lunIsReady = 0; + + if (lun->media == 0 || lun->status < LUN_CHANGED) { + TRACE_INFO("SBCLunIsReady: Not Present!\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_NOT_READY, + SBC_ASC_MEDIUM_NOT_PRESENT, + 0); + + } + else if (lun->status < LUN_READY) { + TRACE_INFO("SBCLunIsReady: Changing!\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_UNIT_ATTENTION, + SBC_ASC_NOT_READY_TO_READY_CHANGE, + 0); + lun->status = LUN_READY; + } + else { + + lunIsReady = 1; + } + + return lunIsReady; +} +//------------------------------------------------------------------------------ +//! \brief Check if the LUN can write. +//! \param lun Pointer to the LUN affected by the command +//! \return 1 if the LUN is ready to be written +//! \see MSDLun +//------------------------------------------------------------------------------ +static unsigned char SBCLunCanBeWritten(MSDLun *lun) +{ + unsigned char canBeWritten = 0; + + if (!SBCLunIsReady(lun)) { + + TRACE_WARNING("SBCLunCanBeWritten: Not Ready!\n\r"); + } + else if (lun->protected) { + + TRACE_WARNING("SBCLunCanBeWritten: Protected!\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_DATA_PROTECT, + SBC_ASC_WRITE_PROTECTED, + 0); + } + else { + + canBeWritten = 1; + } + + return canBeWritten; +} + +//------------------------------------------------------------------------------ +//! \brief Performs a WRITE (10) command on the specified LUN. +//! +//! The data to write is first received from the USB host and then +//! actually written on the media. +//! This function operates asynchronously and must be called multiple +//! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE +//! indicates that at least another call of the method is necessary. +//! \param lun Pointer to the LUN affected by the command +//! \param commandState Current state of the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//! \see MSDCommandState +//------------------------------------------------------------------------------ +static unsigned char SBC_Write10(MSDLun *lun, + MSDCommandState *commandState) +{ + unsigned char status; + unsigned char result = MSDD_STATUS_INCOMPLETE; + SBCRead10 *command = (SBCRead10 *) commandState->cbw.pCommand; + MSDTransfer *transfer = &(commandState->transfer); + MSDTransfer *disktransfer = &(commandState->disktransfer); + MSDIOFifo *fifo = &lun->ioFifo; + + // Init command state + if (commandState->state == 0) { + + commandState->state = SBC_STATE_WRITE; + + // The command should not be proceeded if READONLY + if (!SBCLunCanBeWritten(lun)) { + + return MSDD_STATUS_RW; + } + else { + + + // Initialize FIFO + fifo->dataTotal = commandState->length; + fifo->blockSize = lun->blockSize * lun->media->blockSize; + #ifdef MSDIO_WRITE10_CHUNK_SIZE + if ( fifo->dataTotal >= 64 * 1024 + && fifo->blockSize < MSDIO_WRITE10_CHUNK_SIZE) + fifo->chunkSize = MSDIO_WRITE10_CHUNK_SIZE; + else + fifo->chunkSize = fifo->blockSize; + #endif + fifo->fullCnt = 0; + fifo->nullCnt = 0; + + // Initialize FIFO output (Disk) + fifo->outputNdx = 0; + fifo->outputTotal = 0; + fifo->outputState = MSDIO_IDLE; + transfer->semaphore = 0; + + // Initialize FIFO input (USB) + fifo->inputNdx = 0; + fifo->inputTotal = 0; + fifo->inputState = MSDIO_START; + disktransfer->semaphore = 0; + } + + } + + if (commandState->length == 0) { + + // Perform the callback! + if (lun->dataMonitor) { + + lun->dataMonitor(0, fifo->dataTotal, fifo->nullCnt, fifo->fullCnt); + } + return MSDD_STATUS_SUCCESS; + } + + // USB receive task + switch(fifo->inputState) { + + //------------------ + case MSDIO_IDLE: + //------------------ + if (fifo->inputTotal < fifo->dataTotal && + fifo->inputTotal - fifo->outputTotal < fifo->bufferSize) { + + fifo->inputState = MSDIO_START; + } + break; + + //------------------ + case MSDIO_START: + //------------------ + // Should not start if there is any disk error + if (fifo->outputState == MSDIO_ERROR) { + + TRACE_INFO_WP("udErr "); + fifo->inputState = MSDIO_ERROR; + break; + } + + // Read one block of data sent by the host + if (lun->media->mappedWR) { + + // Directly read to memory + status = MSDD_Read((void*) + ((lun->media->baseAddress + + (lun->baseAddress + + DWORDB(command->pLogicalBlockAddress) + * lun->blockSize + ) + ) * lun->media->blockSize + ), + fifo->dataTotal, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + } + else { + #ifdef MSDIO_WRITE10_CHUNK_SIZE + status = SBC_RX_CHUNK(fifo, MSDDriver_Callback, transfer); + #else + // Read block to buffer + status = MSDD_Read((void*)&fifo->pBuffer[fifo->inputNdx], + fifo->blockSize, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + #endif + } + + // Check operation result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Write10: Failed to start receiving\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_HARDWARE_ERROR, + 0, + 0); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("uRx "); + + // Prepare next device state + fifo->inputState = MSDIO_WAIT; + } + break; // MSDIO_START + + //------------------ + case MSDIO_WAIT: + //------------------ + TRACE_INFO_WP("uWait "); + + // Check semaphore + if (transfer->semaphore > 0) { + + transfer->semaphore--; + fifo->inputState = MSDIO_NEXT; + } + break; + + //------------------ + case MSDIO_NEXT: + //------------------ + // Check the result code of the read operation + if (transfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Write10: Failed to received\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_HARDWARE_ERROR, + 0, + 0); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("uNxt "); + + // Mapped read, all data done + if (lun->media->mappedWR) { + + fifo->inputTotal = fifo->dataTotal; + fifo->inputState = MSDIO_IDLE; + } + else { + + // Update input index + #ifdef MSDIO_WRITE10_CHUNK_SIZE + MSDIOFifo_IncNdx(fifo->inputNdx, + fifo->chunkSize, + fifo->bufferSize); + fifo->inputTotal += fifo->chunkSize; + #else + MSDIOFifo_IncNdx(fifo->inputNdx, + fifo->blockSize, + fifo->bufferSize); + fifo->inputTotal += fifo->blockSize; + #endif + + // Start Next block + // - All Data done? + if (fifo->inputTotal >= fifo->dataTotal) { + + fifo->inputState = MSDIO_IDLE; + } + // - Buffer full? + else if (fifo->inputNdx == fifo->outputNdx) { + fifo->inputState = MSDIO_IDLE; + fifo->fullCnt ++; + + TRACE_DEBUG_WP("ufFull%d ", fifo->inputNdx); + } + // - More data to transfer? + else if (fifo->inputTotal < fifo->dataTotal) { + fifo->inputState = MSDIO_START; + + TRACE_INFO_WP("uStart "); + } + // never executed ! + //else { + // fifo->inputState = MSDIO_IDLE; + // TRACE_INFO_WP("uDone "); + //} + } + + } + break; // MSDIO_NEXT + + //------------------ + case MSDIO_ERROR: + //------------------ + + TRACE_WARNING_WP("uErr "); + commandState->length -= fifo->inputTotal; + return MSDD_STATUS_RW; + + } + + // Disk write task + switch(fifo->outputState) { + + //------------------ + case MSDIO_IDLE: + //------------------ + if (fifo->outputTotal < fifo->inputTotal) { + + fifo->outputState = MSDIO_START; + } + break; + + //------------------ + case MSDIO_START: + //------------------ + + // Write the block to the media + if (lun->media->mappedWR) { + + MSDDriver_Callback(disktransfer, MED_STATUS_SUCCESS, 0, 0); + status = LUN_STATUS_SUCCESS; + } + else { + #ifdef MSDIO_WRITE10_CHUNK_SIZE + status = SBC_WRITE_CHUNK(lun, DWORDB(command->pLogicalBlockAddress), + fifo, MSDDriver_Callback, disktransfer); + #else + status = LUN_Write(lun, + DWORDB(command->pLogicalBlockAddress), + &fifo->pBuffer[fifo->outputNdx], + 1, + (TransferCallback) MSDDriver_Callback, + (void *) disktransfer); + #endif + } + + // Check operation result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Write10: Failed to start write - "); + + if (!SBCLunCanBeWritten(lun)) { + + TRACE_WARNING("?\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_NOT_READY, + 0, + 0); + } + + fifo->outputState = MSDIO_ERROR; + } + else { + + // Prepare next state + fifo->outputState = MSDIO_WAIT; + } + break; // MSDIO_START + + //------------------ + case MSDIO_WAIT: + //------------------ + TRACE_INFO_WP("dWait "); + + // Check semaphore value + if (disktransfer->semaphore > 0) { + + // Take semaphore and move to next state + disktransfer->semaphore--; + fifo->outputState = MSDIO_NEXT; + } + break; + + //------------------ + case MSDIO_NEXT: + //------------------ + // Check operation result code + if (transfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Write10: Failed to write\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_RECOVERED_ERROR, + SBC_ASC_TOO_MUCH_WRITE_DATA, + 0); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("dNxt "); + + // Update transfer length and block address + + // Mapped memory, done + if (lun->media->mappedWR) { + + commandState->length = 0; + fifo->outputState = MSDIO_IDLE; + } + else { + + // Update output index + #ifdef MSDIO_WRITE10_CHUNK_SIZE + STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + + fifo->chunkSize/fifo->blockSize, + command->pLogicalBlockAddress); + MSDIOFifo_IncNdx(fifo->outputNdx, + fifo->chunkSize, + fifo->bufferSize); + fifo->outputTotal += fifo->chunkSize; + #else + STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1, + command->pLogicalBlockAddress); + MSDIOFifo_IncNdx(fifo->outputNdx, + fifo->blockSize, + fifo->bufferSize); + fifo->outputTotal += fifo->blockSize; + #endif + + // Start Next block + // - All data done? + if (fifo->outputTotal >= fifo->dataTotal) { + + fifo->outputState = MSDIO_IDLE; + commandState->length = 0; + TRACE_INFO_WP("dDone "); + } + // - Send next? + else if (fifo->outputTotal < fifo->inputTotal) { + + fifo->outputState = MSDIO_START; + TRACE_INFO_WP("dStart "); + } + // - Buffer Null? + else { + fifo->outputState = MSDIO_IDLE; + fifo->nullCnt ++; + + TRACE_DEBUG_WP("dfNull%d ", fifo->outputNdx); + } + } + } + break; // MSDIO_NEXT + + //------------------ + case MSDIO_ERROR: + //------------------ + break; + } + + return result; +} + +//------------------------------------------------------------------------------ +//! \brief Performs a READ (10) command on specified LUN. +//! +//! The data is first read from the media and then sent to the USB host. +//! This function operates asynchronously and must be called multiple +//! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE +//! indicates that at least another call of the method is necessary. +//! \param lun Pointer to the LUN affected by the command +//! \param commandState Current state of the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//! \see MSDCommandState +//------------------------------------------------------------------------------ +static unsigned char SBC_Read10(MSDLun *lun, + MSDCommandState *commandState) +{ + unsigned char status; + unsigned char result = MSDD_STATUS_INCOMPLETE; + SBCRead10 *command = (SBCRead10 *) commandState->cbw.pCommand; + MSDTransfer *transfer = &(commandState->transfer); + MSDTransfer *disktransfer = &(commandState->disktransfer); + MSDIOFifo *fifo = &lun->ioFifo; + + // Init command state + if (commandState->state == 0) { + + commandState->state = SBC_STATE_READ; + + if (!SBCLunIsReady(lun)) { + + return MSDD_STATUS_RW; + } + else { + + // Initialize FIFO + fifo->dataTotal = commandState->length; + fifo->blockSize = lun->blockSize * lun->media->blockSize; + #ifdef MSDIO_READ10_CHUNK_SIZE + if ( fifo->dataTotal >= 64*1024 + && fifo->blockSize < MSDIO_READ10_CHUNK_SIZE) + fifo->chunkSize = MSDIO_READ10_CHUNK_SIZE; + else + fifo->chunkSize = fifo->blockSize; + #endif + fifo->fullCnt = 0; + fifo->nullCnt = 0; + + #ifdef MSDIO_FIFO_OFFSET + // Enable offset if total size >= 2*bufferSize + if (fifo->dataTotal / fifo->bufferSize >= 2) + fifo->bufferOffset = MSDIO_FIFO_OFFSET; + else + fifo->bufferOffset = 0; + #endif + + // Initialize FIFO output (USB) + fifo->outputNdx = 0; + fifo->outputTotal = 0; + fifo->outputState = MSDIO_IDLE; + transfer->semaphore = 0; + + // Initialize FIFO input (Disk) + fifo->inputNdx = 0; + fifo->inputTotal = 0; + fifo->inputState = MSDIO_START; + disktransfer->semaphore = 0; + } + } + + // Check length + if (commandState->length == 0) { + + // Perform the callback! + if (lun->dataMonitor) { + + lun->dataMonitor(1, fifo->dataTotal, fifo->nullCnt, fifo->fullCnt); + } + return MSDD_STATUS_SUCCESS; + } + + // Disk reading task + switch(fifo->inputState) { + + //------------------ + case MSDIO_IDLE: + //------------------ + if (fifo->inputTotal < fifo->dataTotal && + fifo->inputTotal - fifo->outputTotal < fifo->bufferSize) { + + fifo->inputState = MSDIO_START; + } + break; + + //------------------ + case MSDIO_START: + //------------------ + // Read one block of data from the media + if (lun->media->mappedRD) { + + // Directly write, no read needed + MSDDriver_Callback(disktransfer, MED_STATUS_SUCCESS, 0, 0); + status = LUN_STATUS_SUCCESS; + } + else { + #ifdef MSDIO_READ10_CHUNK_SIZE + status = SBC_READ_CHUNK(lun, DWORDB(command->pLogicalBlockAddress), + fifo, MSDDriver_Callback, disktransfer); + #else + status = LUN_Read(lun, + DWORDB(command->pLogicalBlockAddress), + &fifo->pBuffer[fifo->inputNdx], + 1, + (TransferCallback) MSDDriver_Callback, + (void *)disktransfer); + #endif + } + + // Check operation result code + if (status != LUN_STATUS_SUCCESS) { + + TRACE_WARNING("RBC_Read10: Failed to start reading\n\r"); + + if (SBCLunIsReady(lun)) { + + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_NOT_READY, + SBC_ASC_LOGICAL_UNIT_NOT_READY, + 0); + } + + fifo->inputState = MSDIO_ERROR; + } + else { + + TRACE_INFO_WP("dRd "); + + // Move to next command state + fifo->inputState = MSDIO_WAIT; + } + break; // MSDIO_START + + //------------------ + case MSDIO_WAIT: + //------------------ + // Check semaphore value + if (disktransfer->semaphore > 0) { + + TRACE_INFO_WP("dOk "); + + // Take semaphore and move to next state + disktransfer->semaphore--; + fifo->inputState = MSDIO_NEXT; + } + break; + + //------------------ + case MSDIO_NEXT: + //------------------ + // Check the operation result code + if (disktransfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Read10: Failed to read media\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_RECOVERED_ERROR, + SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + 0); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("dNxt "); + + if (lun->media->mappedRD) { + + // All data is ready + fifo->inputState = MSDIO_IDLE; + fifo->inputTotal = fifo->dataTotal; + } + else { + + // Update block address + #ifdef MSDIO_READ10_CHUNK_SIZE + STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + + fifo->chunkSize/fifo->blockSize, + command->pLogicalBlockAddress); + + // Update input index + MSDIOFifo_IncNdx(fifo->inputNdx, + fifo->chunkSize, + fifo->bufferSize); + fifo->inputTotal += fifo->chunkSize; + #else + // Update block address + STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1, + command->pLogicalBlockAddress); + + // Update input index + MSDIOFifo_IncNdx(fifo->inputNdx, + fifo->blockSize, + fifo->bufferSize); + fifo->inputTotal += fifo->blockSize; + #endif + + // Start Next block + // - All Data done? + if (fifo->inputTotal >= fifo->dataTotal) { + + TRACE_INFO_WP("dDone "); + fifo->inputState = MSDIO_IDLE; + } + // - Buffer full? + else if (fifo->inputNdx == fifo->outputNdx) { + + TRACE_INFO_WP("dfFull%d ", (int)fifo->inputNdx); + fifo->inputState = MSDIO_IDLE; + fifo->fullCnt ++; + } + // - More data to transfer? + else if (fifo->inputTotal < fifo->dataTotal) { + + TRACE_DEBUG_WP("dStart "); + fifo->inputState = MSDIO_START; + } + } + + } + + break; + + //------------------ + case MSDIO_ERROR: + //------------------ + break; + } + + // USB sending task + switch(fifo->outputState) { + + //------------------ + case MSDIO_IDLE: + //------------------ + if (fifo->outputTotal < fifo->inputTotal) { + + #ifdef MSDIO_FIFO_OFFSET + // Offset buffer the input data + if (fifo->bufferOffset) { + if (fifo->inputTotal < fifo->bufferOffset) { + break; + } + fifo->bufferOffset = 0; + } + #endif + fifo->outputState = MSDIO_START; + } + break; + + //------------------ + case MSDIO_START: + //------------------ + // Should not start if there is any disk error + if (fifo->outputState == MSDIO_ERROR) { + + fifo->inputState = MSDIO_ERROR; + break; + } + + // Send the block to the host + if (lun->media->mappedRD) { + + status = MSDD_Write((void*) + ((lun->media->baseAddress + + (lun->baseAddress + + DWORDB(command->pLogicalBlockAddress) + * lun->blockSize + ) + ) * lun->media->blockSize + ), + commandState->length, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + } + else { + #ifdef MSDIO_READ10_CHUNK_SIZE + status = SBC_TX_CHUNK(fifo, MSDDriver_Callback, transfer); + #else + status = MSDD_Write(&fifo->pBuffer[fifo->outputNdx], + fifo->blockSize, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + #endif + } + // Check operation result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Read10: Failed to start to send\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_HARDWARE_ERROR, + 0, + 0); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("uTx "); + + // Move to next command state + fifo->outputState = MSDIO_WAIT; + } + break; // MSDIO_START + + //------------------ + case MSDIO_WAIT: + //------------------ + // Check semaphore value + if (transfer->semaphore > 0) { + + TRACE_INFO_WP("uOk "); + + // Take semaphore and move to next state + transfer->semaphore--; + fifo->outputState = MSDIO_NEXT; + } + break; + + //------------------ + case MSDIO_NEXT: + //------------------ + // Check operation result code + if (transfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_Read10: Failed to send data\n\r"); + SBC_UpdateSenseData(&(lun->requestSenseData), + SBC_SENSE_KEY_HARDWARE_ERROR, + 0, + 0); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("uNxt "); + + if (lun->media->mappedRD) { + + commandState->length = 0; + } + else { + + // Update output index + #ifdef MSDIO_READ10_CHUNK_SIZE + MSDIOFifo_IncNdx(fifo->outputNdx, + fifo->chunkSize, + fifo->bufferSize); + fifo->outputTotal += fifo->chunkSize; + #else + MSDIOFifo_IncNdx(fifo->outputNdx, + fifo->blockSize, + fifo->bufferSize); + fifo->outputTotal += fifo->blockSize; + #endif + + // Start Next block + // - All data done? + if (fifo->outputTotal >= fifo->dataTotal) { + + fifo->outputState = MSDIO_IDLE; + commandState->length = 0; + TRACE_INFO_WP("uDone "); + } + // - Buffer Null? + else if (fifo->inputNdx == fifo->outputNdx) { + + TRACE_INFO_WP("ufNull%d ", (int)fifo->outputNdx); + fifo->outputState = MSDIO_IDLE; + fifo->nullCnt ++; + } + // - Send next? + else if (fifo->outputTotal < fifo->inputTotal) { + + TRACE_DEBUG_WP("uStart "); + fifo->outputState = MSDIO_START; + } + } + + } + break; + + //------------------ + case MSDIO_ERROR: + //------------------ + break; + } + + return result; +} + +//------------------------------------------------------------------------------ +//! \brief Performs a READ CAPACITY (10) command. +//! +//! This function operates asynchronously and must be called multiple +//! times to complete. A result code of MSDD_STATUS_INCOMPLETE +//! indicates that at least another call of the method is necessary. +//! \param lun Pointer to the LUN affected by the command +//! \param commandState Current state of the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//! \see MSDCommandState +//------------------------------------------------------------------------------ +static unsigned char SBC_ReadCapacity10(MSDLun *lun, + MSDCommandState *commandState) +{ + unsigned char result = MSDD_STATUS_INCOMPLETE; + unsigned char status; + MSDTransfer *transfer = &(commandState->transfer); + + if (!SBCLunIsReady(lun)) { + + TRACE_INFO("SBC_ReadCapacity10: Not Ready!\n\r"); + return MSDD_STATUS_RW; + } + + // Initialize command state if needed + if (commandState->state == 0) { + + commandState->state = SBC_STATE_WRITE; + } + + // Identify current command state + switch (commandState->state) { + //------------------- + case SBC_STATE_WRITE: + //------------------- + // Start the write operation + status = MSDD_Write(&(lun->readCapacityData), + commandState->length, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_ReadCapacity: Cannot start sending data\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + // Proceed to next command state + TRACE_INFO_WP("Sending "); + commandState->state = SBC_STATE_WAIT_WRITE; + } + break; + + //------------------------ + case SBC_STATE_WAIT_WRITE: + //------------------------ + // Check semaphore value + if (transfer->semaphore > 0) { + + // Take semaphore and terminate command + transfer->semaphore--; + + if (transfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING("RBC_ReadCapacity: Cannot send data\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("Sent "); + result = MSDD_STATUS_SUCCESS; + } + commandState->length -= transfer->transferred; + } + break; + } + + return result; +} + +//------------------------------------------------------------------------------ +//! \brief Handles an INQUIRY command. +//! +//! This function operates asynchronously and must be called multiple +//! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE +//! indicates that at least another call of the method is necessary. +//! \param lun Pointer to the LUN affected by the command +//! \param commandState Current state of the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//! \see MSDCommandState +//------------------------------------------------------------------------------ +static unsigned char SBC_Inquiry(MSDLun *lun, + MSDCommandState *commandState) +{ + unsigned char result = MSDD_STATUS_INCOMPLETE; + unsigned char status; + MSDTransfer *transfer = &(commandState->transfer); + + // Check if required length is 0 + if (commandState->length == 0) { + + // Nothing to do + result = MSDD_STATUS_SUCCESS; + } + // Initialize command state if needed + else if (commandState->state == 0) { + + commandState->state = SBC_STATE_WRITE; + + // Change additional length field of inquiry data + lun->inquiryData->bAdditionalLength + = (unsigned char) (commandState->length - 5); + } + + // Identify current command state + switch (commandState->state) { + //------------------- + case SBC_STATE_WRITE: + //------------------- + // Start write operation + status = MSDD_Write((void *) lun->inquiryData, + commandState->length, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "SPC_Inquiry: Cannot start sending data\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + // Proceed to next state + TRACE_INFO_WP("Sending "); + commandState->state = SBC_STATE_WAIT_WRITE; + } + break; + + //------------------------ + case SBC_STATE_WAIT_WRITE: + //------------------------ + // Check the semaphore value + if (transfer->semaphore > 0) { + + // Take semaphore and terminate command + transfer->semaphore--; + + if (transfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "SPC_Inquiry: Data transfer failed\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + TRACE_INFO_WP("Sent "); + result = MSDD_STATUS_SUCCESS; + } + + // Update length field + commandState->length -= transfer->transferred; + } + break; + } + + return result; +} + +//------------------------------------------------------------------------------ +//! \brief Performs a REQUEST SENSE command. +//! +//! This function operates asynchronously and must be called multiple +//! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE +//! indicates that at least another call of the method is necessary. +//! \param lun Pointer to the LUN affected by the command +//! \param commandState Current state of the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//! \see MSDCommandState +//------------------------------------------------------------------------------ +static unsigned char SBC_RequestSense(MSDLun *lun, + MSDCommandState *commandState) +{ + unsigned char result = MSDD_STATUS_INCOMPLETE; + unsigned char status; + MSDTransfer *transfer = &(commandState->transfer); + + // Check if requested length is zero + if (commandState->length == 0) { + + // Nothing to do + result = MSDD_STATUS_SUCCESS; + } + // Initialize command state if needed + else if (commandState->state == 0) { + + commandState->state = SBC_STATE_WRITE; + } + + // Identify current command state + switch (commandState->state) { + //------------------- + case SBC_STATE_WRITE: + //------------------- + // Start transfer + status = MSDD_Write(&(lun->requestSenseData), + commandState->length, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "RBC_RequestSense: Cannot start sending data\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + // Change state + commandState->state = SBC_STATE_WAIT_WRITE; + } + break; + + //------------------------ + case SBC_STATE_WAIT_WRITE: + //------------------------ + // Check the transfer semaphore + if (transfer->semaphore > 0) { + + // Take semaphore and finish command + transfer->semaphore--; + + if (transfer->status != USBD_STATUS_SUCCESS) { + + result = MSDD_STATUS_ERROR; + } + else { + + result = MSDD_STATUS_SUCCESS; + } + + // Update length + commandState->length -= transfer->transferred; + } + break; + } + + return result; +} + +//------------------------------------------------------------------------------ +//! \brief Performs a MODE SENSE (6) command. +//! +//! This function operates asynchronously and must be called multiple +//! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE +//! indicates that at least another call of the method is necessary. +//! \param lun Pointer to the LUN affected by the command +//! \param commandState Current state of the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//! \see MSDCommandState +//------------------------------------------------------------------------------ +static unsigned char SBC_ModeSense6(MSDLun *lun, MSDCommandState *commandState) +{ + unsigned char result = MSDD_STATUS_INCOMPLETE; + unsigned char status; + MSDTransfer *transfer = &(commandState->transfer); + + if (!SBCLunIsReady(lun)) { + + TRACE_INFO("SBC_ModeSense6: Not Ready!\n\r"); + return MSDD_STATUS_RW; + } + + // Check if mode page is supported + if (((SBCCommand *) commandState->cbw.pCommand)->modeSense6.bPageCode + != SBC_PAGE_RETURN_ALL) { + + return MSDD_STATUS_PARAMETER; + } + + // Initialize command state if needed + if (commandState->state == 0) { + + commandState->state = SBC_STATE_WRITE; + } + + // Check current command state + switch (commandState->state) { + //------------------- + case SBC_STATE_WRITE: + //------------------- + // Start transfer + status = MSDD_Write((void *) &modeParameterHeader6, + commandState->length, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "SPC_ModeSense6: Cannot start data transfer\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + // Proceed to next state + commandState->state = SBC_STATE_WAIT_WRITE; + } + break; + + //------------------------ + case SBC_STATE_WAIT_WRITE: + //------------------------ + TRACE_INFO_WP("Wait "); + + // Check semaphore value + if (transfer->semaphore > 0) { + + // Take semaphore and terminate command + transfer->semaphore--; + + if (transfer->status != USBD_STATUS_SUCCESS) { + + TRACE_WARNING( + "SPC_ModeSense6: Data transfer failed\n\r"); + result = MSDD_STATUS_ERROR; + } + else { + + result = MSDD_STATUS_SUCCESS; + } + + // Update length field + commandState->length -= transfer->transferred; + + } + break; + } + + return result; +} + +//------------------------------------------------------------------------------ +//! \brief Performs a TEST UNIT READY COMMAND command. +//! \param lun Pointer to the LUN affected by the command +//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) +//! \see MSDLun +//------------------------------------------------------------------------------ +static unsigned char SBC_TestUnitReady(MSDLun *lun) +{ + unsigned char result = MSDD_STATUS_RW; + unsigned char senseKey = SBC_SENSE_KEY_NO_SENSE, + addSenseCode = 0, + addSenseCodeQual = 0; + + // Check current media state + if (lun->status < LUN_CHANGED) { + + TRACE_INFO_WP("Ejc "); + senseKey = SBC_SENSE_KEY_NOT_READY; + addSenseCode = SBC_ASC_MEDIUM_NOT_PRESENT; + } + else if (lun->status == LUN_CHANGED) { + + TRACE_INFO_WP("Chg "); + senseKey = SBC_SENSE_KEY_UNIT_ATTENTION; + addSenseCode = SBC_ASC_NOT_READY_TO_READY_CHANGE; + lun->status = LUN_READY; + } + else { + + switch(lun->media->state) { + //------------------- + case MED_STATE_READY: + //------------------- + // Nothing to do + TRACE_INFO_WP("Rdy "); + result = MSDD_STATUS_SUCCESS; + break; + + //------------------ + case MED_STATE_BUSY: + //------------------ + TRACE_INFO_WP("Bsy "); + senseKey = SBC_SENSE_KEY_NOT_READY; + break; + + //------ + default: + //------ + TRACE_INFO_WP("? "); + senseKey = SBC_SENSE_KEY_NOT_READY; + addSenseCode = SBC_ASC_MEDIUM_NOT_PRESENT; + break; + } + } + SBC_UpdateSenseData(&(lun->requestSenseData), + senseKey, + addSenseCode, + addSenseCodeQual); + + return result; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//! \brief Updates the sense data of a LUN with the given key and codes +//! \param requestSenseData Pointer to the sense data to update +//! \param senseKey Sense key +//! \param additionalSenseCode Additional sense code +//! \param additionalSenseCodeQualifier Additional sense code qualifier +//------------------------------------------------------------------------------ +void SBC_UpdateSenseData(SBCRequestSenseData *requestSenseData, + unsigned char senseKey, + unsigned char additionalSenseCode, + unsigned char additionalSenseCodeQualifier) +{ + requestSenseData->bSenseKey = senseKey; + requestSenseData->bAdditionalSenseCode = additionalSenseCode; + requestSenseData->bAdditionalSenseCodeQualifier + = additionalSenseCodeQualifier; +} + +//------------------------------------------------------------------------------ +//! \brief Return information about the transfer length and direction expected +//! by the device for a particular command. +//! \param command Pointer to a buffer holding the command to evaluate +//! \param length Expected length of the data transfer +//! \param type Expected direction of data transfer +//! \param lun Pointer to the LUN affected by the command +//------------------------------------------------------------------------------ +unsigned char SBC_GetCommandInformation(void *command, + unsigned int *length, + unsigned char *type, + MSDLun *lun) +{ + SBCCommand *sbcCommand = (SBCCommand *) command; + unsigned char isCommandSupported = 1; + + // Identify command + switch (sbcCommand->bOperationCode) { + //--------------- + case SBC_INQUIRY: + //--------------- + (*type) = MSDD_DEVICE_TO_HOST; + + // Allocation length is stored in big-endian format + (*length) = WORDB(sbcCommand->inquiry.pAllocationLength); + break; + + //-------------------- + case SBC_MODE_SENSE_6: + //-------------------- + (*type) = MSDD_DEVICE_TO_HOST; + if (sbcCommand->modeSense6.bAllocationLength > + sizeof(SBCModeParameterHeader6)) { + + *length = sizeof(SBCModeParameterHeader6); + } + else { + + *length = sbcCommand->modeSense6.bAllocationLength; + } + break; + + //------------------------------------ + case SBC_PREVENT_ALLOW_MEDIUM_REMOVAL: + //------------------------------------ + (*type) = MSDD_NO_TRANSFER; + break; + + //--------------------- + case SBC_REQUEST_SENSE: + //--------------------- + (*type) = MSDD_DEVICE_TO_HOST; + (*length) = sbcCommand->requestSense.bAllocationLength; + break; + + //----------------------- + case SBC_TEST_UNIT_READY: + //----------------------- + (*type) = MSDD_NO_TRANSFER; + break; + + //--------------------- + case SBC_READ_CAPACITY_10: + //--------------------- + (*type) = MSDD_DEVICE_TO_HOST; + (*length) = sizeof(SBCReadCapacity10Data); + break; + + //--------------- + case SBC_READ_10: + //--------------- + (*type) = MSDD_DEVICE_TO_HOST; + (*length) = WORDB(sbcCommand->read10.pTransferLength) + * lun->blockSize * lun->media->blockSize; + break; + + //---------------- + case SBC_WRITE_10: + //---------------- + (*type) = MSDD_HOST_TO_DEVICE; + (*length) = WORDB(sbcCommand->write10.pTransferLength) + * lun->blockSize * lun->media->blockSize; + break; + + //----------------- + case SBC_VERIFY_10: + //----------------- + (*type) = MSDD_NO_TRANSFER; + break; + #if 0 + //--------------------- + case SBC_READ_FORMAT_CAPACITIES: + //--------------------- + (*type) = MSDD_DEVICE_TO_HOST; + (*length) = 1; + break; + #endif + //------ + default: + //------ + isCommandSupported = 0; + } + + // If length is 0, no transfer is expected + if ((*length) == 0) { + + (*type) = MSDD_NO_TRANSFER; + } + + return isCommandSupported; +} + +//------------------------------------------------------------------------------ +//! \brief Processes a SBC command by dispatching it to a subfunction. +//! \param lun Pointer to the affected LUN +//! \param commandState Pointer to the current command state +//! \return Operation result code +//------------------------------------------------------------------------------ +unsigned char SBC_ProcessCommand(MSDLun *lun, + MSDCommandState *commandState) +{ + unsigned char result = MSDD_STATUS_INCOMPLETE; + SBCCommand *command = (SBCCommand *) commandState->cbw.pCommand; + + // Identify command + switch (command->bOperationCode) { + //--------------- + case SBC_READ_10: + //--------------- + TRACE_DEBUG_WP("Read(10) "); + + // Perform the Read10 command + result = SBC_Read10(lun, commandState); + break; + + //---------------- + case SBC_WRITE_10: + //---------------- + TRACE_DEBUG_WP("Write(10) "); + + // Perform the Write10 command + result = SBC_Write10(lun, commandState); + break; + + //--------------------- + case SBC_READ_CAPACITY_10: + //--------------------- + TRACE_INFO_WP("RdCapacity(10) "); + + // Perform the ReadCapacity command + result = SBC_ReadCapacity10(lun, commandState); + break; + + //--------------------- + case SBC_VERIFY_10: + //--------------------- + TRACE_INFO_WP("Verify(10) "); + + // Flush media + MED_Flush(lun->media); + result = MSDD_STATUS_SUCCESS; + break; + + //--------------- + case SBC_INQUIRY: + //--------------- + TRACE_INFO_WP("Inquiry "); + + // Process Inquiry command + result = SBC_Inquiry(lun, commandState); + break; + + //-------------------- + case SBC_MODE_SENSE_6: + //-------------------- + TRACE_INFO_WP("ModeSense(6) "); + + // Process ModeSense6 command + result = SBC_ModeSense6(lun, commandState); + break; + + //----------------------- + case SBC_TEST_UNIT_READY: + //----------------------- + TRACE_INFO_WP("TstUnitRdy "); + + // Process TestUnitReady command + //MED_Flush(lun->media); + result = SBC_TestUnitReady(lun); + break; + + //--------------------- + case SBC_REQUEST_SENSE: + //--------------------- + TRACE_INFO_WP("ReqSense "); + + // Perform the RequestSense command + result = SBC_RequestSense(lun, commandState); + break; + + //------------------------------------ + case SBC_PREVENT_ALLOW_MEDIUM_REMOVAL: + //------------------------------------ + TRACE_INFO_WP("PrevAllowRem "); + + // Check parameter + result = command->mediumRemoval.bPrevent ? + MSDD_STATUS_PARAMETER : MSDD_STATUS_SUCCESS; + break; + #if 0 + //------------------------------------ + case SBC_READ_FORMAT_CAPACITIES: + //------------------------------------ + TRACE_INFO_WP("RdFmtCap "); + if (!SBCLunIsReady(lun)) { + + result = MSDD_STATUS_RW; + break; + } + #endif + //------ + default: + //------ + result = MSDD_STATUS_PARAMETER; + } + + return result; +} diff --git a/usb/device/massstorage/SBCMethods.h b/usb/device/massstorage/SBCMethods.h new file mode 100644 index 0000000..1ef199e --- /dev/null +++ b/usb/device/massstorage/SBCMethods.h @@ -0,0 +1,111 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// !Purpose +/// +/// SCSI commands implementation. +/// +/// !Usage +/// +/// -# After a CBW is received from host, use SBC_GetCommandInformation to check +/// if the command is supported, and get the command length and type +/// information before processing it. +/// -# Then SBC_ProcessCommand can be used to handle a valid command, to +/// perform the command operations. +/// -# SBC_UpdateSenseData is used to update the sense data that will be sent +/// to host. +//------------------------------------------------------------------------------ + +#ifndef SBCMETHODS_H +#define SBCMETHODS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "SBC.h" +#include "MSDLun.h" +#include "MSDDStateMachine.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "SBC Command States" +/// This page lists the possible states of a SBC command. +/// +/// !States +/// - SBC_STATE_READ +/// - SBC_STATE_WAIT_READ +/// - SBC_STATE_WRITE +/// - SBC_STATE_WAIT_WRITE +/// - SBC_STATE_NEXT_BLOCK + +/// Start of reading bulk data +#define SBC_STATE_READ 0x01 +/// Waiting for the bulk data reading complete +#define SBC_STATE_WAIT_READ 0x02 +/// Read error state +#define SBC_STATE_READ_ERROR 0x03 +/// Start next read block +#define SBC_STATE_NEXT_READ 0x04 +/// Start writing bulk data to host +#define SBC_STATE_WRITE 0x05 +/// Waiting for the bulk data sending complete +#define SBC_STATE_WAIT_WRITE 0x06 +/// Write error state +#define SBC_STATE_WRITE_ERROR 0x07 +/// Start next write block +#define SBC_STATE_NEXT_WRITE 0x08 +/// Start next command block +#define SBC_STATE_NEXT_BLOCK 0x09 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +void SBC_UpdateSenseData(SBCRequestSenseData *requestSenseData, + unsigned char senseKey, + unsigned char additionalSenseCode, + unsigned char additionalSenseCodeQualifier); + +unsigned char SBC_GetCommandInformation(void *command, + unsigned int *length, + unsigned char *type, + MSDLun *lun); + +unsigned char SBC_ProcessCommand(MSDLun *lun, + MSDCommandState *commandState); + +#endif //#ifndef SBCMETHODS_H + diff --git a/usb/device/massstorage/massstorage.dir b/usb/device/massstorage/massstorage.dir new file mode 100644 index 0000000..bc793c5 --- /dev/null +++ b/usb/device/massstorage/massstorage.dir @@ -0,0 +1,1005 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \dir +/// +/// !!!Purpose +/// +/// This directory provides definitions, structs and functions for a USB Mass +/// Storage %device (MSD) - USB Mass Storage demo. +/// +/// !!!Contents +/// +/// There are four things for the implement of the USB MSD driver: +/// - Implement the MSD driver structs and functions for the %device, +/// to initialize, to handle MSD-specific requests and dispach +/// standard requests in USBD callbacks, to read/write through assigned USB +/// %endpoints, +/// - Create the MSD device's descriptors that should be passed to +/// the USBDDriver instance on initialization, so that the host can +/// recognize the %device as a USB Mass Storage %device. +/// - Implement state machine for MSD command/data/status handling. +/// - Implement storage media interface for MSD disk accessing. +/// +/// For more information about what a particular group contains, please refer to +/// "USB MSD Driver". +//------------------------------------------------------------------------------ + +/** + \page "USB MSD Driver" + This page describes how to use the USB framework to produce a USB MSD driver, + which appears as a USB Disk on host. + + !!!References + - "AT91 USB device framework" + - "USB Device Enumeration" + - + Universal Serial Bus Revision 2.0 specification + (.zip file format, size 9.80 MB) + - + Mass Storage Overview 1.2 + - + Mass Storage Bulk Only 1.0 + - SCSI Standards + - SCSI Block Commands - 3 (SBC-3) + - SCSI Primary Commands - 4 (SPC-4) + + !!!Mass Storage Class Basic + This section gives generic details on the MSD class. + + See "USB MSD Basic". + + !!!Mass Storage SCSI Disk + + This section describes how to implement a USB disk by using the MSD class with + the SCSI transparent command set and the AT91 USB Framework. For more + information about the framework, please refer to the "AT91 USB device + framework" application note; details about the USB and the Mass Storage class + can be found in the USB specification 2.0 and the MSC Bulk-Only Transport + specification 1.0 documents, respectively. + + The software example provided with this document uses the ram disk of the chip + as its storage medium, but has been designed in a modular way to allow easy + modification for any medium, e.g. internal flash, DataFlash, SD card, external + Flash chip. + + !!Architecture + The MSD driver is based on framework, See "USB Device Framework Architecture". + + The internal architecture of the Application layer is extended for the + following factors: + - The Command/Data/Status flow described in "USB MSD Basic" requires the use + of a #state machine# for non-blocking operation. + - The example software has been designed to be easily extended with support + for other media. + - The example software has been designed to support multiple LUNs on one or + more media. + + \image MSDAppArch.png "Application Layer Architecture" + + !!Descriptors + There are no class-specific descriptors for a device using the MSD class with + the Bulk-only transport protocol. This section thus only details the values + which must be set in the standard descriptors. + + !Device Descriptor +\code +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), // bLength: Size of descriptor (18 bytes) + USBGenericDescriptor_DEVICE, // bDescriptorType: Device descriptor + USBDeviceDescriptor_USB2_00, // bcdUSB: USB 2.00 + MSDeviceDescriptor_CLASS, // bDeviceClass: 0 + MSDeviceDescriptor_SUBCLASS, // bDeviceSubClass: 0 + MSDeviceDescriptor_PROTOCOL, // bDeviceProtocol: 0 + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), // bMaxPacketSize0: Max Size EP0 + MSDDriverDescriptors_VENDORID, // idVendor: Vendor ID ATMEL (0x03eb) + MSDDriverDescriptors_PRODUCTID,// idProduct: Product ID (0x6202) + MSDDriverDescriptors_RELEASE, // bcdDevice: 0x0001, Version 0.01 + 1, // iManufacturer: Manufacturer string (manufacturerDescriptor) index. + 2, // iProduct: Product string (productDescriptor) index. + 3, // iSerialNumber: Serial number string (serialNumberDescriptor) index. + 1 // bNumConfigurations: Device has one possible configuration. +}; +\endcode + Note that the Vendor ID is a special value attributed by the USB-IF + organization. The product ID can be chosen freely by the vendor. + + !Configuration Descriptor + The descriptors are defined as: +\code +const MSDConfigurationDescriptors configurationDescriptorsFS; +\endcode + + Configuration descriptor +\code +// Standard configuration descriptor. +{ + sizeof(USBConfigurationDescriptor), // bLength: 9 bytes + USBGenericDescriptor_CONFIGURATION, // bDescriptorType: Configuration + sizeof(MSDConfigurationDescriptors),// wTotalLength: Length of all + 1, // bNumInterface: Configuration has one interface. + 1, // bConfigurationValue: This is configuration #1. + 0, // iConfiguration: No string descriptor for configuration. + BOARD_USB_BMATTRIBUTES, // bmAttributes: Power and remote wakeup + USBConfigurationDescriptor_POWER(100) // 100mA max power +}, +\endcode + + !Interface Descriptor + The interface descriptor must indicate several features: + - #Mass Storage Device# class code (08h) in the }bInterfaceClass} field + - #Data Transport Protocol# code in the }bInterfaceSubclass} field + - #Bulk-Only Transport# protocol code (50h) in the }bInterfaceProtocol} field + This example uses the SCSI transparent command set (code 06h). This is the + most appropriate setting for a Flash %device, given that the RBC command set + is not supported by Microsoft Windows. +\code +// Mass Storage interface descriptor. +{ + sizeof(USBInterfaceDescriptor), // bLength: Size of descriptor(9 bytes) + USBGenericDescriptor_INTERFACE, // bDescriptorType: Interface descriptor + 0, // bInterfaceNumber: This is interface #0. + 0, // bAlternateSetting: This is alternate setting #0. + 2, // bNumEndpoints: Interface uses two endpoints. + MSInterfaceDescriptor_CLASS, // bInterfaceClass: Mass Storage Device Class + MSInterfaceDescriptor_SCSI, // bInterfaceSubClass: SCSI transparent command + MSInterfaceDescriptor_BULKONLY,// bInterfaceProtocol: Bulk-Only transport + 0 // iInterface: No string descriptor for interface. +}, +\endcode + + !Endpoint Descriptors + No special requirements on these apart from being Bulk-IN and Bulk-OUT. +\code +// Bulk-OUT endpoint descriptor +{ + sizeof(USBEndpointDescriptor), // bLength: 7 bytes + USBGenericDescriptor_ENDPOINT, // bDescriptorType: Endpoint descriptor + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_OUT, + MSDDriverDescriptors_BULKOUT), // bEndpointAddress: OUT 0x01 + USBEndpointDescriptor_BULK, // bmAttributes: Bulk endpoint + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), // wMaxPacketSize: 64 bytes + 0 // bInterval: Must be 0 for full-speed Bulk endpoints. +}, +// Bulk-IN endpoint descriptor +{ + sizeof(USBEndpointDescriptor), // bLength: 7 bytes + USBGenericDescriptor_ENDPOINT, // bDescriptorType: Endpoint descriptor + USBEndpointDescriptor_ADDRESS( + USBEndpointDescriptor_IN, + MSDDriverDescriptors_BULKIN), // bEndpointAddress: IN 0x82 + USBEndpointDescriptor_BULK, // bmAttributes: Bulk endpoint + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN), + USBEndpointDescriptor_MAXBULKSIZE_FS), // wMaxPacketSize: 64 + 0 // bInterval: Must be 0 for full-speed Bulk endpoints. +} +\endcode + + !String descriptors + Several descriptors can be commented with a String descriptor. The latter are + completely optional and do not influence the detection of the device by the + operating system. Whether or not to include them is entirely up to the + programmer. + + There is one exception to this rule when using the MSD class. According to the + specification, there must be a Serial Number string. It must contains at least + 12 characters, and these characters must only be either letters (a-z, A-Z) or + numbers (0-9). This cause no problem for the driver in practice, but this is a + strict requirement for certification. Also remember that string descriptors + use the Unicode format. + + See manufacturerDescriptor, productDescriptor, serialNumberDescriptor. + + !!Class-specific Requests + There are two Mass Storage-specific requests: + - GetMaxLUN + - Bulk-Only Mass Storage Reset + + Standard requests can be forwarded to the USBDDriver_RequestHandler, with one + exception: #CLEAR_FEATURE#. This is necessary for Reset Recovery sequence. + + !ClearFeature + As previously stated, the CLEAR_FEATURE request must be handled in a + particular way, depending on whether or not the device is waiting for a Reset + Recovery sequence. If it is, then CLEAR_FEATURE requests to unhalt a Bulk + endpoint must be discarded. + + In the example software, this behavior is indicated by a boolean field in the + driver structure, named waitResetRecovery. The handler only has to check this + field value to decide whether to forward the request to the standard handler + or to discard it. +\code +// Handle requests +switch (USBGenericRequest_GetRequest(request)) { +//--------------------- +case USBGenericRequest_CLEARFEATURE: +//--------------------- + + switch (USBFeatureRequest_GetFeatureSelector(request)) { + + //--------------------- + case USBFeatureRequest_ENDPOINTHALT: + //--------------------- + + // Do not clear the endpoint halt status if the device is waiting + // for a reset recovery sequence + if (!msdDriver.waitResetRecovery) { + + // Forward the request to the standard handler + USBDDriver_RequestHandler(&usbdDriver, request); + } + + USBD_Write(0, 0, 0, 0, 0); + break; + + //------ + default: + //------ + // Forward the request to the standard handler + USBDDriver_RequestHandler(&usbdDriver, request); + } + break; +} +\endcode + + !GetMaxLUN + Usually, the first request issued by the host right after the enumeration + phase will be a GET_MAX_LUN request. It enables it to discover how many + different logical units the device has; each of these LUNs can then be queried + in turn by the host when needed. + + After the request is received by the device, it should return one byte of data + indicating the maximum Logical Unit Number (LUN). It is equal to the number of + LUNs used by the device minus one. For example, a device with three LUNs shall + return a GET_MAX_LUN value of two. + + Sending this byte is done by calling the USBD_Write method on Control endpoint + 0. Note that the data must be held in a permanent buffer (since the transfer + is asynchronous); in the software provided with this application note, a + dedicated field is used in the driver structure (MSDDriver) to store this + value. + + In addition due to the }Mass Storage Bulk-Only Transport} specification the + }wValue} should be 0, }wLength} should be 1, }wIndex} should be the interface + number also 0. A request which does not comply to these requirements must be + STALLed. +\code +//------------------- +case MSD_GET_MAX_LUN: +//------------------- + // Check request parameters + if ((request->wValue == 0) + && (request->wIndex == 0) + && (request->wLength == 1)) { + USBD_Write(0, &(msdDriver.maxLun), 1, 0, 0); + } + else { + USBD_Stall(0); + } + break; +\endcode + + !Bulk-Only Mass Storage Reset + The host issues #RESET# requests to return the MSD driver of the device to its + initial state, i.e., ready to receive a new command. However, this request + does not impact the USB controller state; in particular, endpoints must not be + reset. This means the data toggle bit must not be altered, and Halted endpoint + must not be returned to a normal state. After processing the reset, the device + must return a Zero-Length Packet (ZLP) to acknowledge the SETUP transfer. + + Like GET_MAX_LUN, this request must be issued with specific parameters - + wValue, wIndex and wLength should be zero. A request which does not have valid + values in its field must be acknowledged with a STALL handshake from the + %device. + + The handler for this request must return the state machine to its initial state. +\code +//----------------------- +case MSD_BULK_ONLY_RESET: +//----------------------- + // Check parameters + if ((request->wValue == 0) + && (request->wIndex == 0) + && (request->wLength == 0)) { + + // Reset the MSD driver + MSDDriver_Reset(); + USBD_Write(0, 0, 0, 0, 0); + } + else { + USBD_Stall(0); + } + break; +\endcode + + !!State Machine + ... + + !Rationale + A state machine is necessary for #non-blocking# operation of the driver. As + previously stated, there are three steps when processing a command: + - Reception of the CBW + - Processing of the command (with data transfers if required) + - Emission of the CSW + + Without a state machine, the program execution would be stopped at each step + to wait for transfers completion or command processing. For example, reception + of a CBW does not always happen immediately (the host does not have to issue + commands regularly) and can block the system for a long time. + + Developing an asynchronous design based on a state machine is made easier when + using Atmel "AT91 USB device framework", as most methods are asynchronous. For + example, a write operation (using the USBD_Write function) returns + immediately; a callback function can then be invoked when the transfer + actually completes. + + !States + Apart from the three states corresponding to the command processing flow (CBW, + command processing and CSW), two more can be identified. The + reception/emission of CBW/CSW will be broken down into two different states: + the first state is used to issue the read/write operation, while the second + one waits for the transfer to finish. This can be done by monitoring a + "transfer complete" flag which is set using a callback function. + + In addition, some commands can be quite complicated to process: they may + require several consecutive data transfers mixed with media access. Each + command thus has its own second-tier state machine. During execution of a + command, the main state machine remains in the "processing" state, and + proceeds to the next one (CSW emission) only when the command is complete. + + Here is the states list: + - MSDDriver_STATE_READ_CBW: Start of CBW reception + (initial state after reset) + - MSDDriver_STATE_WAIT_CBW: Waiting for CBW reception + - MSDDriver_STATE_PROCESS_CBW: Command processing + - MSDDriver_STATE_SEND_CSW: Start of CSW emission + - MSDDriver_STATE_WAIT_CSW: Waiting for CSW emission + + A single function, named MSDDriver_StateMachine, is provided by the driver. It + must be called regularly during the program execution. The following + subsections describe the actions that must be performed during each state. + + \image MSDDriverStates.png "MSD Driver State Machine" + + #MSDDriver_STATE_READ_CBW# + + As said previously, this state is used to start the reception of a new Command + Block Wrapper. This is done using the USB_Read method of the USB framework. + The result code of the function is checked for any error; the + USB_STATUS_SUCCESS code indicates that the transfer has been successfully + started. +\code +//---------------------- +case MSDDriver_STATE_READ_CBW: +//---------------------- + // Start the CBW read operation + transfer->semaphore = 0; + status = USBD_Read(MSDDriverDescriptors_BULKOUT, + cbw, + MSD_CBW_SIZE, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status == USBD_STATUS_SUCCESS) { + + // If the command was successful, wait for transfer + msdDriver.state = MSDDriver_STATE_WAIT_CBW; + } + break; +\endcode + A callback function to invoke when the transfer is complete is provided to the + USBD_Read method, to update a MSDTransfer structure. This structure + indicates the transfer completion, the returned result code and the number of + transferred and remaining bytes. +\code +typedef struct { + unsigned int transferred; //!< Number of bytes transferred + unsigned int remaining; //!< Number of bytes not transferred + unsigned char semaphore; //!< Semaphore to indicate transfer completion + unsigned char status; //!< Operation result code +} MSDTransfer; +\endcode + The callback function is trivial and thus not listed here. + + #MSDDriver_STATE_WAIT_CBW# + + The first step here is to monitor the }semaphore} field of the MSDTransfer + structure (see above); this will enable detection of the transfer end. Please + note that this field must be declared as volatile in C, or accesses to it + might get optimized by the compiler; this can result in endless loops. + + If the transfer is complete, then the result code must be checked to see if + there was an error. If the operation is successful, the state machine can + proceed to command processing. Otherwise, it returns to the READ_CBW state. +\code +//---------------------- +case MSDDriver_STATE_WAIT_CBW: +//---------------------- + // Check transfer semaphore + if (transfer->semaphore > 0) { + + // Take semaphore and terminate transfer + transfer->semaphore--; + + // Check if transfer was successful + if (transfer->status == USBD_STATUS_SUCCESS) { + + // Process received command + msdDriver.state = MSDDriver_STATE_PROCESS_CBW; + } + else if (transfer->status == USBD_STATUS_RESET) { + + msdDriver.state = MSDDriver_STATE_READ_CBW; + } + else { + + msdDriver.state = MSDDriver_STATE_READ_CBW; + } + } + break; +\endcode + + #MSDDriver_STATE_PROCESS_CBW# + + Once the CBW has been received, its validity must be checked. A CBW is not + valid if: + - it has not been received right after a CSW was sent or a reset occured or + - it is not exactly 31 bytes long or + - its signature field is not equal to 43425355h + + The state machine prevents the first case from happening, so only the two + other cases have to be verified. + + The number of bytes transferred during a USBD_Read operation is passed as an + argument to the callback function, if one has been specified. As stated + previously, such a function is used to fill a MSDTransfer structure. + Therefore, it is trivial to check that the CBW is indeed 31 bytes by verifying + that the number of bytes transferred is 31, and that there are no remaining + bytes. The following table illustrates the three cases which may happen: +||Number of bytes transferred||Number of bytes remaining||Meaning +|transferred<31|remaining==0|CBW is too short +|transferred==31|remaining>0|CBW is too long +|transferred==31|remaining==0|CBW length is correct + + Checking the signature is simply done by comparing the dCBWSignature field + with the expected value (43425355h). + + If the CBW is not valid, then the device must immediately halt both Bulk + endpoints, to STALL further traffic from the host. In addition, it should stay + in this state until a Reset Recovery is performed by the host. This is done by + setting the waitResetRecovery flag in the MSDDriver structure. Finally, the + CSW status is set to report an error, and the state machine is returned to + MSDDriver_STATE_READ_CBW. + + Otherwise, if the CBW is correct, then the command can be processed. Remember + the CBW tag must be copied regardless of the validity of the CBW. + + Note that these steps are only necessary for a new command (remember commands + are asynchronous and are carried out in several calls, so a check can be + performed to avoid useless processing. A value of zero for the internal + command state indicates a new command. +\code +//------------------------- +case MSDDriver_STATE_PROCESS_CBW: +//------------------------- + // Check if this is a new command + if (commandState->state == 0) { + + // Copy the CBW tag + csw->dCSWTag = cbw->dCBWTag; + + // Check that the CBW is 31 bytes long + if ((transfer->transferred != MSD_CBW_SIZE) || + (transfer->remaining != 0)) { + + // Wait for a reset recovery + msdDriver.waitResetRecovery = 1; + + // Halt the Bulk-IN and Bulk-OUT pipes + USBD_Halt(MSDDriverDescriptors_BULKOUT); + USBD_Halt(MSDDriverDescriptors_BULKIN); + + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + msdDriver.state = MSDDriver_STATE_READ_CBW; + + } + // Check the CBW Signature + else if (cbw->dCBWSignature != MSD_CBW_SIGNATURE) { + + // Wait for a reset recovery + msdDriver.waitResetRecovery = 1; + + // Halt the Bulk-IN and Bulk-OUT pipes + USBD_Halt(MSDDriverDescriptors_BULKOUT); + USBD_Halt(MSDDriverDescriptors_BULKIN); + + csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; + msdDriver.state = MSDDriver_STATE_READ_CBW; + } + else { + + // Pre-process command + MSDDriver_PreProcessCommand(); + } + } + + // Process command + if (csw->bCSWStatus == MSDDriver_STATUS_SUCCESS) { + + if (MSDDriver_ProcessCommand()) { + + // Post-process command if it is finished + MSDDriver_PostProcessCommand(); + msdDriver.state = MSDDriver_STATE_SEND_CSW; + } + } + + break; +\endcode + + #MSDDriver_STATE_SEND_CSW# + + This state is similar to MSDDriver_STATE_READ_CBW, except that a write + operation is performed instead of a read and the CSW is sent, not the CBW. The + same callback function is used to fill the transfer structure, which is + checked in the next state: +\code +//---------------------- +case MSDDriver_STATE_SEND_CSW: +//---------------------- + // Set signature + csw->dCSWSignature = MSD_CSW_SIGNATURE; + + // Start the CSW write operation + status = USBD_Write(MSDDriverDescriptors_BULKIN, + csw, + MSD_CSW_SIZE, + (TransferCallback) MSDDriver_Callback, + (void *) transfer); + + // Check operation result code + if (status == USBD_STATUS_SUCCESS) { + + // Wait for end of transfer + msdDriver.state = MSDDriver_STATE_WAIT_CSW; + } + break; +\endcode + + #MSDDriver_STATE_WAIT_CSW# + + Again, this state is very similar to MSDDriver_STATE_WAIT_CBW. The only + difference is that the state machine is set to MSDDriver_STATE_READ_CBW + regardless of the operation result code: +\code +//---------------------- +case MSDDriver_STATE_WAIT_CSW: +//---------------------- + // Check transfer semaphore + if (transfer->semaphore > 0) { + + // Take semaphore and terminate transfer + transfer->semaphore--; + + // Read new CBW + msdDriver.state = MSDDriver_STATE_READ_CBW; + } + break; +\endcode + + !!Media + + USB MSD Media access is three-level abstraction. + + \image MSDMediaArch.png "Media Architecture" + + The bottom level is the specific driver for each media type (See memories). + + In the middle, a structure Media is used to hide which specific driver a media + instance is using. This enables transparent use of any media driver once it + has been initialized (See _Media). + + Finally, a LUN abstraction is made over the media structure to allow multiple + partitions over one media. This also makes it possible to place the LUN at any + address and use any block size. When performing a write or read operation on a + LUN, it forwards the operation to the underlying media while translating it to + the correct address and length. + + !Media Drivers + A media driver must provide several functions for: + - Reading data from the media + - Writing data on the media + - Handling interrupts on the media + The last function may be empty if the media does not require interrupts for + asynchronous operation, or if synchronous operation produces an acceptable + delay. + + In addition, it should also define a function for initializing a Media + structure with the correct values, as well as perform the necessary step for + the media to be useable. + + For the drivers see: + - MEDSdram.h: }Internal Flash Driver} + - MEDFlash.h: }SDRAM disk driver} + + !!SCSI Commands + + The example software described in this application note uses SCSI commands + with the MSD class, since this is the most appropriate setting for a Flash + device. This section details how SCSI commands are processed. + + !Documents + + There are several documents covering SCSI commands. In this application note, + the reference document used is SCSI Block Commands - 3 (SBC-3). However, it + makes many references to another SCSI document, SCSI Primary Commands - 4 + (SPC-4). Both are needed for full details on required commands. + + !Endianness + + SCSI commands use the big-endian format for storing word- and double word- + sized data. This means the Most Significant Bit (MSB) is stored at the + lowest address, and the Least Significant Bit (LSB) at the highest one. + + On ARM Thumb microcontrollers, the endianness of the core is selectable. + However, the little-endian mode is most often used. Therefore, SCSI command + data must be converted before being usable. This is done by declaring + word- and dword-sized fields as byte arrays, and then using a macro for + loading or storing data. Several of them are available in the provided + software: + - Load + - WORDB: Converts a big-endian word value to little-endian + - DWORDB: Converts a big-endian double-word value to little-endian + - Store + - STORE_WORDB: Stores a little-endian word value in big-endian format + - STORE_DWORDB: Stores a little-endian double-word value in big-endian format + + !Sense Data + + When an error happens during the execution of a command, it is recorded by the + device. The host may then issue a Request Sense command to retrieve + #Sense Data#, i.e., information about previous errors. + + While the sense data structure has many fields, only three are really + important. The first one is the Sense Key. It indicates the result of the last + command performed: success, media not ready, hardware error, etc. Two other + fields can then be specified to give a more accurate description of the + problem. They are named }Additional Sense Code} and }Additional Sense Code + Qualifier}. + + In the example application, each LUN has its own sense data. It is updated + during command execution if there is any error. + + !Commands + + The SBC-3 specification gives a list of mandatory and optional commands that + are relevant for a block device (like a Flash drive). In practice, only a + subset of the mandatory commands is effectively used by operating systems; + conversely, several commands which are supposed to be optional are required. + The software provided with this application note implements the following list + of commands: + - SBC-3 + - Prevent/Allow Medium Removal + - Read (10) + - Read Capacity (10) + - Verify (10) + - Write (10) + - SPC-4 + - Inquiry + - Mode Sense (6) + - Request Sense + - Test Unit Ready + The commands are actually processed in SBC_ProcessCommand. + + }Internal State Machine} + + As previously stated, most commands have an internal state machine to prevent + blocking the whole system during a data transfer (on the USB or when accessing + a media). A result code is used to indicate that the corresponding function + must be called again for the command to complete (MSDDriver_STATUS_SUCCESS). + + A command state structure is used by the driver to record several parameters + during command processing: +\code +typedef struct { + + MSDTransfer transfer; //!< Current transfer status + MSCbw cbw; //!< Received CBW + MSCsw csw; //!< CSW to send + unsigned char state; //!< Current command state + unsigned char postprocess; //!< Actions to perform when command is complete + unsigned int length; //!< Remaining length of command + +} MSDCommandState; +\endcode + + Note that the }state} field must be initialized when the command is first + called. A value of 0 means that no command is currently being executed. + + For the commands descriptions and implementation, please reffer to the SCSI + spec. and source code. + + Functions to handle SCSI commands: + - SBC_Inquiry + - SBC_Read10 + - SBC_ReadCapacity10 + - SBC_RequestSense + - SBC_TestUnitReady + - SBC_Write10 + - SBC_ModeSense6 + + !Command Processing + + }Flow} + + Command processing is actually divided into three phases in the example + software: + - Pre-processing: MSDDriver_PreProcessCommand + - Processing: MSDDriver_ProcessCommand + - Post-processing: MSDDriver_PostProcessCommand + + }The Thirteen Cases} + + There are basically three actions that should be performed depending on the + case: + - STALL the Bulk-IN endpoint + - STALL the Bulk-OUT endpoint + - Report a Phase Error in the CSW + + The table below lists all cases along with the actions which must be taken + after the command, including the correct length/direction of the transfer. The + following notation is used to characterize host and %device expectations: + + Data %Transfer Characterization +||Notation||Meaning||Notation||Meaning +|Hn|Host expects no data transfer|Dn|Device expects no data transfer +|Hi|Host expects to #receive# data|Di|Device expects to #send# data +|Ho|Host expects to #send# data|Do|Device expects to #receive# data +|Lh|Length of data expected by the host|Ld|Length of data expected by the %device + +|Hx=Dx|Host and %device agree on transfer length and direction (x is either n, i or o) +|Hx>Dx|Host and %device agree on transfer direction, host expects a larger transfer than %device +|HxDx|Host and %device disagree on transfer direction + + The Thirteen Cases +||\#||Case||Length||Residue||Direction||STALL IN?||STALL OUT?||Phase Error? +|1|Hn = Dn|0|0|Irrelevant| | | +|2|Hn < Di|0|Ld - Lh|Irrelevant| | |X +|3|Hn < Do|0|Ld - Lh|Irrelevant| | |X +|4|Hi > Dn|0|Lh|Irrelevant|X| | +|5|Hi > Di|Ld|Lh - Ld|In|X| | +|6|Hi = Di|Ld|0|In| | | +|7|Hi < Di|Lh|Ld - Lh|In| | |X +|8|Hi <> Do|0|0|Irrelevant|X| |X +|9|Ho > Dn|0|Lh|Irrelevant| |X| +|10|Ho <> Di|0|0|Irrelevant| |X|X +|11|Ho > Do|Ld|Lh - Ld|Out| |X| +|12|Ho = Do|Ld|0|Out| | | +|13|Ho < Do|Lh|Lh - Ld|Out| | |X + + !!Main Application + After the MSD driver and the media have been initialized using the + corresponding functions, the only requirement for the main application is to + regularly call the state machine function. This is necessary for processing + received commands in a fully asynchronous way. + + The application is otherwise free of doing any other task; for example, it + could implement a filesystem and a serial port interface to be accessed with a + standard terminal. An MP3 player could also continue playing a song while its + memory is accessed like an external hard disk. + + \image MSDDriverClasses.png "Driver Class Diagram" + +*/ +/** + \page "USB MSD Basic" + + This page gives generic details on the MSD class. + + !!!Purpose + + The MSD class defines how devices such as a hard disk, a USB floppy disk drive + or a disk-on-key shall operate on the USB. These devices are referred to as + mass storage devices, since they usually offer a high storage capacity. When + plugged to a PC, a %device complying to the MSD specification is accessed like + any other disk on the system. + + In practice, the specification only defines a way to wrap existing data + transfer protocols, such as SCSI or the Reduced Block Commands (RBC) set. A + list of the supported protocols and their uses will be given in the following + section. + + !!!Data Transfer Protocols + + The }Mass Storagae Class Specification Overview 1.2} supports the following + set of %devices: + + Protocols for MSD %devices +||Subclass Code||Command Block Spec.||Used by +|01h|Reduced Block Commands(RBC)|Flash %devices +|02h|SFF-8020i, MMC-2|CD & DVD %devices +|03h|QIC-157|Tape %devices +|04h|UFI|Floppy disk drives +|05h|SFF-8070i|Floppy disk drives +|06h|SCSI transparent command set|Any + + The SCSI transparent command set comprises all SCSI-related specifications, + such as SCSI Primary Commands (SPC), SCSI Block Commands (SBC), and so on. A + command will be issued by the host to determine exactly with which standard + the %device is compliant. + + The protocol used by the %device is specified in its Interface descriptor, in + the }bInterfaceSubclass} field. + + !!!Transfer Protocols + + There are actually two different transport protocols for the MSD class: + - Control/Bulk/Interface (CBI) transport + - Bulk-Only Transport (BOT) + + These two methods are described in two separate stand-alone documents. CBI can + be considered obsolete and is being completely replaced by BOT. It was + originally targeted at full-speed floppy disk drives. Therefore, the rest of + this document will talk about Bulk-Only Transport exclusively. + + Transport Protocol Codes +||bInterfaceProtocol||Protocol Implementation +|00h|Control/Bulk/Interrupt protocol (with command completion interrupt) +|01h|Control/Bulk/Interrupt protocol (without command completion interrupt) +|50h|Bulk-only transport + + !!!Architecture + ... + + !!Interfaces & Endpoints + + An MSD %device only needs one single interface. The bInterfaceClass field of + the interface descriptor should be set to MSD class code (0x08), the + corresponding data transfer protocol code in the }bInterfaceSubclass} field + and transport protocol code in the }bInterfaceProtocol} field can be found in + the tables on above. + + Exactly three %endpoints (when using the Bulk-Only Transport protocol) are + necessary for MSD %devices. + + The first one is the Control endpoint 0, and is used for class-specific + requests and for clearing Halt conditions on the other two %endpoints. + Endpoints are halted in response to errors and host bad behavior during data + transfers, and the CLEAR_FEATURE request is consequently used to return them + to a functional state. + + The other two %endpoints, which are of type Bulk, are used for transferring + commands and data over the bus. There must be one Bulk-IN and one Bulk-OUT + endpoint. + + \image MSDDriverArch.png "Mass Storage Device Driver Architecture" + + !!Class-Specific Descriptors + No class-specific descriptors for an MSD %device using the Bulk-only transfport + protocol. + + !!Class-Specific Requests + Two class specific requests should be handled. + + !GetMaxLUN + A %device can feature one or more Logical Unit (LU). Each of these units will + be treated as a separate disk when plugged to a computer. A %device can have up + to 15 logical units. + + The GET_MAX_LUN request is issued by the host to determine the maximum Logical + Unit Number (LUN) supported by the %device. This is not equivalent to the + number of LU on the %device; since units are numbered starting from 0, a %device + with 5 LUs should report a value of 4, which will be the index of the fifth + unit. + + Optionally, a %device with only one LUN may STALL this request instead of + returning a value of zero. + + !Bulk-Only Mass Storage Reset + This request is used to reset the state of the %device and prepare it to + receive commands and data. Note that the data toggle bits must not be altered + by a RESET command; same for the Halt state of %endpoints, i.e., halted + %endpoints must not be reset to a normal state. + + !!Command/Data/Status + Each MSD transaction is divided into three steps: + - Command stage + - Data stage (optional) + - Status stage + + During the command stage, a Command Block Wrapper (CBW) is transmitted by the + host to the %device. The CBW describes several parameters of the transaction + (direction, length, LUN) and carries a variable-length command block. The + command block contains data in the format defined by the transfer protocol + used by the %device. + + Command Block Wrapper Data Format +||Offset||Field Name||Length||Comment +|0|dCBWSignature|4 bytes|Signature to identify CBW, must be 43425355h +|4|dCBWTag|4 bytes|Tag sent by the host, echoed in the CSW +|8|dCBWTransferLength|4 bytes|Length of transfer during the data stage +|12|bmCBWFlags|1 byte|Bits 0-6: Reserved/obsolete\n + Bit 7: Transfer direction (0 = OUT, 1 = IN) +|13|bCBWLUN|1 byte|Bits 0-3: LUN to which the command is sent\n + Bits 4-7: Reserved +|14|bCBWCBLength|1 byte|Bits 0-5: Length of command block in bytes\n + Bits 6-7: Reserved +|15|CBWCB|0-16 bytes|Command block to be executed by the %device + + After the %device has received and interpreted the command, an optional data + stage may take place if the command requires it. During this step, data is + transferred either to or from the %device depending on the command, in several + IN/OUT transfers. + + Once the data stage is complete, the host issues a final IN request on the + Bulk-IN endpoint of the %device to request the Command Status Wrapper (CSW). + The CSW is used to report correct or incorrect execution of the command, as + well as indicating the length of remaining data that has not been transferred. + + Command Status Wrapper +||Offset||Field Name||Length||Comment +|0|dCSWSignature|4 bytes|Signature to identify CSW, must be 53425355h +|4|dCSWTag|4 bytes|Copy of previous CBW tag +|8|dCSWDataResidue|4 bytes|Difference between expected and real transfer length +|12|bCSWStatus|1 byte|Indicates the success or failure of the command + + These steps are all performed on the two Bulk %endpoints, and do not involve + Control endpoint 0 at all. + + !!Reset Recovery + When severe errors occur during command or data transfers (as defined in the + }Mass Storage Bulk-only Transport 1.0} document), the %device must halt both + Bulk %endpoints and wait for a #Reset Recovery# procedure. The Reset Recovery + sequence goes as follows: + - The host issues a Bulk-Only Mass Storage Reset request + - The host issues two #CLEAR_FEATURE# requests to unhalt each endpoint + + A %device waiting for a Reset Recovery must not carry out CLEAR_FEATURE + requests trying to unhalt either Bulk endpoint until after a Reset request has + been received. This enables the host to distinguish between severe and minor + errors. + + The only major error defined by the Bulk-Only Transport standard is when a CBW + is not valid. This means one or more of the following: + - The CBW is not received after a CSW has been sent or a reset. + - The CBW is not exactly 31 bytes in length. + - The dCBWSignature field of the CBW is not equal to 43425355h. + + !!!Host Drivers + Almost all operating systems now provide a generic driver for the MSD class. + However, the set of supported data transfer protocols may vary. For example, + Microsoft Windows does not currently support the Reduced Block Command set. + +*/ -- cgit v1.2.3