1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074 |
- /**
- * \file
- *
- * \brief USB Device Driver for UOTGHS. Compliant with common UDD driver.
- *
- * Copyright (c) 2012-2015 Atmel Corporation. All rights reserved.
- *
- * \asf_license_start
- *
- * \page License
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. The name of Atmel may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * 4. This software may only be redistributed and used in connection with an
- * Atmel microcontroller product.
- *
- * 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
- * EXPRESSLY AND SPECIFICALLY 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.
- *
- * \asf_license_stop
- *
- */
-
- /*
- * Support and FAQ: visit <a href="https://www.atmel.com/design-support/">Atmel Support</a>
- */
-
- #ifdef ARDUINO_ARCH_SAM
-
- #include "compiler.h"
- #include "uotghs_device_due.h"
-
- #include "conf_usb.h"
- #include "sysclk.h"
- #include "udd.h"
- #include "uotghs_otg.h"
- #include <string.h>
-
- #ifndef UDD_NO_SLEEP_MGR
- # include "sleep.h"
- # include "sleepmgr.h"
- #endif
-
- #if !(SAM3XA)
- # error The current UOTGHS Device Driver supports only SAM3X and SAM3A.
- #endif
- #ifndef UDD_USB_INT_FUN
- # define UDD_USB_INT_FUN UOTGHS_Handler
- #endif
-
- #ifndef UDD_USB_INT_LEVEL
- # define UDD_USB_INT_LEVEL 5 // By default USB interrupt have low priority
- #endif
-
- #define UDD_EP_USED(ep) (USB_DEVICE_MAX_EP >= ep)
-
- #if ( (UDD_EP_USED( 1) && Is_udd_endpoint_dma_supported( 1)) \
- ||(UDD_EP_USED( 2) && Is_udd_endpoint_dma_supported( 2)) \
- ||(UDD_EP_USED( 3) && Is_udd_endpoint_dma_supported( 3)) \
- ||(UDD_EP_USED( 4) && Is_udd_endpoint_dma_supported( 4)) \
- ||(UDD_EP_USED( 5) && Is_udd_endpoint_dma_supported( 5)) \
- ||(UDD_EP_USED( 6) && Is_udd_endpoint_dma_supported( 6)) \
- ||(UDD_EP_USED( 7) && Is_udd_endpoint_dma_supported( 7)) \
- ||(UDD_EP_USED( 8) && Is_udd_endpoint_dma_supported( 8)) \
- ||(UDD_EP_USED( 9) && Is_udd_endpoint_dma_supported( 9)) \
- ||(UDD_EP_USED(10) && Is_udd_endpoint_dma_supported(10)) \
- ||(UDD_EP_USED(11) && Is_udd_endpoint_dma_supported(11)) \
- ||(UDD_EP_USED(12) && Is_udd_endpoint_dma_supported(12)) \
- ||(UDD_EP_USED(13) && Is_udd_endpoint_dma_supported(13)) \
- ||(UDD_EP_USED(14) && Is_udd_endpoint_dma_supported(14)) \
- ||(UDD_EP_USED(15) && Is_udd_endpoint_dma_supported(15)) \
- )
- # define UDD_EP_DMA_SUPPORTED
- #endif
-
- #if ( (UDD_EP_USED( 1) && !Is_udd_endpoint_dma_supported( 1)) \
- ||(UDD_EP_USED( 2) && !Is_udd_endpoint_dma_supported( 2)) \
- ||(UDD_EP_USED( 3) && !Is_udd_endpoint_dma_supported( 3)) \
- ||(UDD_EP_USED( 4) && !Is_udd_endpoint_dma_supported( 4)) \
- ||(UDD_EP_USED( 5) && !Is_udd_endpoint_dma_supported( 5)) \
- ||(UDD_EP_USED( 6) && !Is_udd_endpoint_dma_supported( 6)) \
- ||(UDD_EP_USED( 7) && !Is_udd_endpoint_dma_supported( 7)) \
- ||(UDD_EP_USED( 8) && !Is_udd_endpoint_dma_supported( 8)) \
- ||(UDD_EP_USED( 9) && !Is_udd_endpoint_dma_supported( 9)) \
- ||(UDD_EP_USED(10) && !Is_udd_endpoint_dma_supported(10)) \
- ||(UDD_EP_USED(11) && !Is_udd_endpoint_dma_supported(11)) \
- ||(UDD_EP_USED(12) && !Is_udd_endpoint_dma_supported(12)) \
- ||(UDD_EP_USED(13) && !Is_udd_endpoint_dma_supported(13)) \
- ||(UDD_EP_USED(14) && !Is_udd_endpoint_dma_supported(14)) \
- ||(UDD_EP_USED(15) && !Is_udd_endpoint_dma_supported(15)) \
- )
- # define UDD_EP_FIFO_SUPPORTED
- #endif
-
- // for debug text
- //#define dbg_print printf
- #define dbg_print(...)
-
- /**
- * \ingroup udd_group
- * \defgroup udd_udphs_group USB On-The-Go High-Speed Port for device mode (UOTGHS)
- *
- * \section UOTGHS_CONF UOTGHS Custom configuration
- * The following UOTGHS driver configuration must be included in the conf_usb.h
- * file of the application.
- *
- * UDD_USB_INT_LEVEL<br>
- * Option to change the interrupt priority (0 to 15) by default 5 (recommended).
- *
- * UDD_USB_INT_FUN<br>
- * Option to fit interrupt function to what defined in exception table.
- *
- * UDD_ISOCHRONOUS_NB_BANK(ep)<br>
- * Feature to reduce or increase isochronous endpoints buffering (1 to 3).
- * Default value 2.
- *
- * UDD_BULK_NB_BANK(ep)<br>
- * Feature to reduce or increase bulk endpoints buffering (1 to 2).
- * Default value 2.
- *
- * UDD_INTERRUPT_NB_BANK(ep)<br>
- * Feature to reduce or increase interrupt endpoints buffering (1 to 2).
- * Default value 1.
- *
- * \section Callbacks management
- * The USB driver is fully managed by interrupt and does not request periodique
- * task. Thereby, the USB events use callbacks to transfer the information.
- * The callbacks are declared in static during compilation or in variable during
- * code execution.
- *
- * Static declarations defined in conf_usb.h:
- * - UDC_VBUS_EVENT(bool b_present)<br>
- * To signal Vbus level change
- * - UDC_SUSPEND_EVENT()<br>
- * Called when USB bus enter in suspend mode
- * - UDC_RESUME_EVENT()<br>
- * Called when USB bus is wakeup
- * - UDC_SOF_EVENT()<br>
- * Called for each received SOF, Note: Each 1ms in HS/FS mode only.
- *
- * Dynamic callbacks, called "endpoint job" , are registered
- * in udd_ep_job_t structure via the following functions:
- * - udd_ep_run()<br>
- * To call it when a transfer is finish
- * - udd_ep_wait_stall_clear()<br>
- * To call it when a endpoint halt is disabled
- *
- * \section Power mode management
- * The Sleep modes authorized :
- * - in USB IDLE state, the UOTGHS needs of USB clock and authorizes up to sleep mode WFI.
- * - in USB SUSPEND state, the UOTGHS no needs USB clock and authorizes up to sleep mode WAIT.
- * @{
- */
-
- // Check USB Device configuration
- #ifndef USB_DEVICE_EP_CTRL_SIZE
- # error USB_DEVICE_EP_CTRL_SIZE not defined
- #endif
- #ifndef USB_DEVICE_MAX_EP
- # error USB_DEVICE_MAX_EP not defined
- #endif
-
- // Note: USB_DEVICE_MAX_EP does not include control endpoint
- #if USB_DEVICE_MAX_EP > (UDD_MAX_PEP_NB-1)
- # error USB_DEVICE_MAX_EP is too high and not supported by this part
- #endif
-
- #define UDD_EP_ISO_NBANK_ERROR(ep) \
- ( (UDD_ISOCHRONOUS_NB_BANK(ep) < 1) \
- || (UDD_ISOCHRONOUS_NB_BANK(ep) > 3) )
- #define UDD_EP_BULK_NBANK_ERROR(ep) \
- ( (UDD_BULK_NB_BANK(ep) < 1) || (UDD_BULK_NB_BANK(ep) > 2) )
- #define UDD_EP_INT_NBANK_ERROR(ep) \
- ( (UDD_INTERRUPT_NB_BANK(ep) < 1) || (UDD_INTERRUPT_NB_BANK(ep) > 2) )
-
- #define UDD_EP_ISO_NB_BANK_ERROR(ep) \
- (UDD_EP_USED(ep) && UDD_EP_ISO_NBANK_ERROR(ep))
- #define UDD_EP_BULK_NB_BANK_ERROR(ep) \
- (UDD_EP_USED(ep) && UDD_EP_ISO_NBANK_ERROR(ep))
- #define UDD_EP_INT_NB_BANK_ERROR(ep) \
- (UDD_EP_USED(ep) && UDD_EP_ISO_NBANK_ERROR(ep))
-
- #define UDD_EP_NB_BANK_ERROR(ep, type) \
- (ATPASTE3(UDD_EP_, type, _NB_BANK_ERROR(ep)))
-
- #define UDD_ISO_NB_BANK_ERROR \
- ( UDD_EP_NB_BANK_ERROR( 1, ISO) \
- || UDD_EP_NB_BANK_ERROR( 2, ISO) \
- || UDD_EP_NB_BANK_ERROR( 3, ISO) \
- || UDD_EP_NB_BANK_ERROR( 4, ISO) \
- || UDD_EP_NB_BANK_ERROR( 5, ISO) \
- || UDD_EP_NB_BANK_ERROR( 6, ISO) \
- || UDD_EP_NB_BANK_ERROR( 7, ISO) \
- || UDD_EP_NB_BANK_ERROR( 8, ISO) \
- || UDD_EP_NB_BANK_ERROR( 9, ISO) \
- || UDD_EP_NB_BANK_ERROR(10, ISO) \
- || UDD_EP_NB_BANK_ERROR(11, ISO) \
- || UDD_EP_NB_BANK_ERROR(12, ISO) \
- || UDD_EP_NB_BANK_ERROR(13, ISO) \
- || UDD_EP_NB_BANK_ERROR(14, ISO) \
- || UDD_EP_NB_BANK_ERROR(15, ISO) )
- #define UDD_BULK_NB_BANK_ERROR \
- ( UDD_EP_NB_BANK_ERROR( 1, BULK) \
- || UDD_EP_NB_BANK_ERROR( 2, BULK) \
- || UDD_EP_NB_BANK_ERROR( 3, BULK) \
- || UDD_EP_NB_BANK_ERROR( 4, BULK) \
- || UDD_EP_NB_BANK_ERROR( 5, BULK) \
- || UDD_EP_NB_BANK_ERROR( 6, BULK) \
- || UDD_EP_NB_BANK_ERROR( 7, BULK) \
- || UDD_EP_NB_BANK_ERROR( 8, BULK) \
- || UDD_EP_NB_BANK_ERROR( 9, BULK) \
- || UDD_EP_NB_BANK_ERROR(10, BULK) \
- || UDD_EP_NB_BANK_ERROR(11, BULK) \
- || UDD_EP_NB_BANK_ERROR(12, BULK) \
- || UDD_EP_NB_BANK_ERROR(13, BULK) \
- || UDD_EP_NB_BANK_ERROR(14, BULK) \
- || UDD_EP_NB_BANK_ERROR(15, BULK) )
- #define UDD_INTERRUPT_NB_BANK_ERROR \
- ( UDD_EP_NB_BANK_ERROR( 1, INT) \
- || UDD_EP_NB_BANK_ERROR( 2, INT) \
- || UDD_EP_NB_BANK_ERROR( 3, INT) \
- || UDD_EP_NB_BANK_ERROR( 4, INT) \
- || UDD_EP_NB_BANK_ERROR( 5, INT) \
- || UDD_EP_NB_BANK_ERROR( 6, INT) \
- || UDD_EP_NB_BANK_ERROR( 7, INT) \
- || UDD_EP_NB_BANK_ERROR( 8, INT) \
- || UDD_EP_NB_BANK_ERROR( 9, INT) \
- || UDD_EP_NB_BANK_ERROR(10, INT) \
- || UDD_EP_NB_BANK_ERROR(11, INT) \
- || UDD_EP_NB_BANK_ERROR(12, INT) \
- || UDD_EP_NB_BANK_ERROR(13, INT) \
- || UDD_EP_NB_BANK_ERROR(14, INT) \
- || UDD_EP_NB_BANK_ERROR(15, INT) )
-
- #ifndef UDD_ISOCHRONOUS_NB_BANK
- # define UDD_ISOCHRONOUS_NB_BANK(ep) 2
- #else
- # if UDD_ISO_NB_BANK_ERROR
- # error UDD_ISOCHRONOUS_NB_BANK(ep) must be define within 1 to 3.
- # endif
- #endif
-
- #ifndef UDD_BULK_NB_BANK
- # define UDD_BULK_NB_BANK(ep) 2
- #else
- # if UDD_BULK_NB_BANK_ERROR
- # error UDD_BULK_NB_BANK must be define with 1 or 2.
- # endif
- #endif
-
- #ifndef UDD_INTERRUPT_NB_BANK
- # define UDD_INTERRUPT_NB_BANK(ep) 1
- #else
- # if UDD_INTERRUPT_NB_BANK_ERROR
- # error UDD_INTERRUPT_NB_BANK must be define with 1 or 2.
- # endif
- #endif
-
-
- /**
- * \name Power management routine.
- */
- //@{
-
- #ifndef UDD_NO_SLEEP_MGR
-
- //! Definition of sleep levels
- #define UOTGHS_SLEEP_MODE_USB_SUSPEND SLEEPMGR_WAIT_FAST
- #define UOTGHS_SLEEP_MODE_USB_IDLE SLEEPMGR_SLEEP_WFI
-
- //! State of USB line
- static bool udd_b_idle;
- //! State of sleep manager
- static bool udd_b_sleep_initialized = false;
-
-
- /*! \brief Authorize or not the CPU powerdown mode
- *
- * \param b_enable true to authorize idle mode
- */
- static void udd_sleep_mode(bool b_idle)
- {
- if (!b_idle && udd_b_idle) {
- dbg_print("_S ");
- sleepmgr_unlock_mode(UOTGHS_SLEEP_MODE_USB_IDLE);
- }
- if (b_idle && !udd_b_idle) {
- dbg_print("_W ");
- sleepmgr_lock_mode(UOTGHS_SLEEP_MODE_USB_IDLE);
- }
- udd_b_idle = b_idle;
- }
- #else
-
- static void udd_sleep_mode(bool b_idle)
- {
- b_idle = b_idle;
- }
-
- #endif // UDD_NO_SLEEP_MGR
-
- //@}
-
-
- /**
- * \name Control endpoint low level management routine.
- *
- * This function performs control endpoint mangement.
- * It handle the SETUP/DATA/HANDSHAKE phases of a control transaction.
- */
- //@{
-
- //! Global variable to give and record information about setup request management
- COMPILER_WORD_ALIGNED udd_ctrl_request_t udd_g_ctrlreq;
-
- //! Bit definitions about endpoint control state machine for udd_ep_control_state
- typedef enum {
- UDD_EPCTRL_SETUP = 0, //!< Wait a SETUP packet
- UDD_EPCTRL_DATA_OUT = 1, //!< Wait a OUT data packet
- UDD_EPCTRL_DATA_IN = 2, //!< Wait a IN data packet
- UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP = 3, //!< Wait a IN ZLP packet
- UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP = 4, //!< Wait a OUT ZLP packet
- UDD_EPCTRL_STALL_REQ = 5, //!< STALL enabled on IN & OUT packet
- } udd_ctrl_ep_state_t;
-
- //! State of the endpoint control management
- static udd_ctrl_ep_state_t udd_ep_control_state;
-
- //! Total number of data received/sent during data packet phase with previous payload buffers
- static uint16_t udd_ctrl_prev_payload_buf_cnt;
-
- //! Number of data received/sent to/from udd_g_ctrlreq.payload buffer
- static uint16_t udd_ctrl_payload_buf_cnt;
-
- /**
- * \brief Reset control endpoint
- *
- * Called after a USB line reset or when UDD is enabled
- */
- static void udd_reset_ep_ctrl(void);
-
- /**
- * \brief Reset control endpoint management
- *
- * Called after a USB line reset or at the end of SETUP request (after ZLP)
- */
- static void udd_ctrl_init(void);
-
- //! \brief Managed reception of SETUP packet on control endpoint
- static void udd_ctrl_setup_received(void);
-
- //! \brief Managed reception of IN packet on control endpoint
- static void udd_ctrl_in_sent(void);
-
- //! \brief Managed reception of OUT packet on control endpoint
- static void udd_ctrl_out_received(void);
-
- //! \brief Managed underflow event of IN packet on control endpoint
- static void udd_ctrl_underflow(void);
-
- //! \brief Managed overflow event of OUT packet on control endpoint
- static void udd_ctrl_overflow(void);
-
- //! \brief Managed stall event of IN/OUT packet on control endpoint
- static void udd_ctrl_stall_data(void);
-
- //! \brief Send a ZLP IN on control endpoint
- static void udd_ctrl_send_zlp_in(void);
-
- //! \brief Send a ZLP OUT on control endpoint
- static void udd_ctrl_send_zlp_out(void);
-
- //! \brief Call callback associated to setup request
- static void udd_ctrl_endofrequest(void);
-
-
- /**
- * \brief Main interrupt routine for control endpoint
- *
- * This switchs control endpoint events to correct sub function.
- *
- * \return \c 1 if an event about control endpoint is occured, otherwise \c 0.
- */
- static bool udd_ctrl_interrupt(void);
-
- //@}
-
-
- /**
- * \name Management of bulk/interrupt/isochronous endpoints
- *
- * The UDD manages the data transfer on endpoints:
- * - Start data tranfer on endpoint with USB Device DMA
- * - Send a ZLP packet if requested
- * - Call callback registered to signal end of transfer
- * The transfer abort and stall feature are supported.
- */
- //@{
- #if (0!=USB_DEVICE_MAX_EP)
-
- //! Structure definition about job registered on an endpoint
- typedef struct {
- union {
- //! Callback to call at the end of transfer
- udd_callback_trans_t call_trans;
-
- //! Callback to call when the endpoint halt is cleared
- udd_callback_halt_cleared_t call_nohalt;
- };
- //! Buffer located in internal RAM to send or fill during job
- uint8_t *buf;
- //! Size of buffer to send or fill
- iram_size_t buf_size;
- //!< Size of data transfered
- iram_size_t buf_cnt;
- //!< Size of data loaded (or prepared for DMA) last time
- iram_size_t buf_load;
- //! A job is registered on this endpoint
- uint8_t busy:1;
- //! A short packet is requested for this job on endpoint IN
- uint8_t b_shortpacket:1;
- //! A stall has been requested but not executed
- uint8_t stall_requested:1;
- } udd_ep_job_t;
-
-
- //! Array to register a job on bulk/interrupt/isochronous endpoint
- static udd_ep_job_t udd_ep_job[USB_DEVICE_MAX_EP];
-
- //! \brief Reset all job table
- static void udd_ep_job_table_reset(void);
-
- //! \brief Abort all endpoint jobs on going
- static void udd_ep_job_table_kill(void);
-
- #ifdef UDD_EP_FIFO_SUPPORTED
- /**
- * \brief Fill banks and send them
- *
- * \param ep endpoint number of job to abort
- */
- static void udd_ep_in_sent(udd_ep_id_t ep);
-
- /**
- * \brief Store received banks
- *
- * \param ep endpoint number of job to abort
- */
- static void udd_ep_out_received(udd_ep_id_t ep);
- #endif
-
- /**
- * \brief Abort endpoint job on going
- *
- * \param ep endpoint number of job to abort
- */
- static void udd_ep_abort_job(udd_ep_id_t ep);
-
- /**
- * \brief Call the callback associated to the job which is finished
- *
- * \param ptr_job job to complete
- * \param b_abort if true then the job has been aborted
- */
- static void udd_ep_finish_job(udd_ep_job_t * ptr_job, bool b_abort, uint8_t ep_num);
-
- #ifdef UDD_EP_DMA_SUPPORTED
- /**
- * \brief Start the next transfer if necessary or complet the job associated.
- *
- * \param ep endpoint number without direction flag
- */
- static void udd_ep_trans_done(udd_ep_id_t ep);
- #endif
-
- /**
- * \brief Main interrupt routine for bulk/interrupt/isochronous endpoints
- *
- * This switchs endpoint events to correct sub function.
- *
- * \return \c 1 if an event about bulk/interrupt/isochronous endpoints has occured, otherwise \c 0.
- */
- static bool udd_ep_interrupt(void);
-
- #endif // (0!=USB_DEVICE_MAX_EP)
- //@}
-
-
- // ------------------------
- //--- INTERNAL ROUTINES TO MANAGED GLOBAL EVENTS
-
- /**
- * \internal
- * \brief Function called by UOTGHS interrupt to manage USB Device interrupts
- *
- * USB Device interrupt events are splited in three parts:
- * - USB line events (SOF, reset, suspend, resume, wakeup)
- * - control endpoint events (setup reception, end of data transfer, underflow, overflow, stall)
- * - bulk/interrupt/isochronous endpoints events (end of data transfer)
- *
- * Note:
- * Here, the global interrupt mask is not clear when an USB interrupt is enabled
- * because this one can not be occured during the USB ISR (=during INTX is masked).
- * See Technical reference $3.8.3 Masking interrupt requests in peripheral modules.
- */
- #ifdef UHD_ENABLE
- void udd_interrupt(void);
- void udd_interrupt(void)
- #else
- ISR(UDD_USB_INT_FUN)
- #endif
- {
- /* For fast wakeup clocks restore
- * In WAIT mode, clocks are switched to FASTRC.
- * After wakeup clocks should be restored, before that ISR should not
- * be served.
- */
- if (!pmc_is_wakeup_clocks_restored() && !Is_udd_suspend()) {
- cpu_irq_disable();
- return;
- }
-
- if (Is_udd_sof()) {
- udd_ack_sof();
- if (Is_udd_full_speed_mode()) {
- udc_sof_notify();
- }
- #ifdef UDC_SOF_EVENT
- UDC_SOF_EVENT();
- #endif
- goto udd_interrupt_sof_end;
- }
-
- if (Is_udd_msof()) {
- udd_ack_msof();
- udc_sof_notify();
- goto udd_interrupt_sof_end;
- }
-
- dbg_print("%c ", udd_is_high_speed() ? 'H' : 'F');
-
- if (udd_ctrl_interrupt()) {
- goto udd_interrupt_end; // Interrupt acked by control endpoint managed
- }
-
- #if (0 != USB_DEVICE_MAX_EP)
- if (udd_ep_interrupt()) {
- goto udd_interrupt_end; // Interrupt acked by bulk/interrupt/isochronous endpoint managed
- }
- #endif
-
- // USB bus reset detection
- if (Is_udd_reset()) {
- udd_ack_reset();
- dbg_print("RST ");
- // Abort all jobs on-going
- #if (USB_DEVICE_MAX_EP != 0)
- udd_ep_job_table_kill();
- #endif
- // Reset USB Device Stack Core
- udc_reset();
- // Reset endpoint control
- udd_reset_ep_ctrl();
- // Reset endpoint control management
- udd_ctrl_init();
- goto udd_interrupt_end;
- }
-
- if (Is_udd_suspend_interrupt_enabled() && Is_udd_suspend()) {
- otg_unfreeze_clock();
- // The suspend interrupt is automatic acked when a wakeup occur
- udd_disable_suspend_interrupt();
- udd_enable_wake_up_interrupt();
- otg_freeze_clock(); // Mandatory to exit of sleep mode after a wakeup event
- udd_sleep_mode(false); // Enter in SUSPEND mode
- #ifdef UDC_SUSPEND_EVENT
- UDC_SUSPEND_EVENT();
- #endif
- goto udd_interrupt_end;
- }
-
- if (Is_udd_wake_up_interrupt_enabled() && Is_udd_wake_up()) {
- // Ack wakeup interrupt and enable suspend interrupt
- otg_unfreeze_clock();
- // Check USB clock ready after suspend and eventually sleep USB clock
- while (!Is_otg_clock_usable()) {
- if (Is_udd_suspend()) {
- break; // In case of USB state change in HS
- }
- };
- // The wakeup interrupt is automatic acked when a suspend occur
- udd_disable_wake_up_interrupt();
- udd_enable_suspend_interrupt();
- udd_sleep_mode(true); // Enter in IDLE mode
- #ifdef UDC_RESUME_EVENT
- UDC_RESUME_EVENT();
- #endif
- goto udd_interrupt_end;
- }
-
- if (Is_otg_vbus_transition()) {
- dbg_print("VBus ");
- // Ack Vbus transition and send status to high level
- otg_unfreeze_clock();
- otg_ack_vbus_transition();
- otg_freeze_clock();
- #ifndef USB_DEVICE_ATTACH_AUTO_DISABLE
- if (Is_otg_vbus_high()) {
- udd_attach();
- } else {
- udd_detach();
- }
- #endif
- #ifdef UDC_VBUS_EVENT
- UDC_VBUS_EVENT(Is_otg_vbus_high());
- #endif
- goto udd_interrupt_end;
- }
- udd_interrupt_end:
- dbg_print("\n\r");
- udd_interrupt_sof_end:
- return;
- }
-
-
- bool udd_include_vbus_monitoring(void)
- {
- return true;
- }
-
-
- void udd_enable(void)
- {
- irqflags_t flags;
-
- flags = cpu_irq_save();
-
- #ifdef UHD_ENABLE
- // DUAL ROLE INITIALIZATION
- if (otg_dual_enable()) {
- // The current mode has been started by otg_dual_enable()
- cpu_irq_restore(flags);
- return;
- }
- #else
- // SINGLE DEVICE MODE INITIALIZATION
- pmc_enable_periph_clk(ID_UOTGHS);
- sysclk_enable_usb();
-
- // Here, only the device mode is possible, then link UOTGHS interrupt to UDD interrupt
- NVIC_SetPriority((IRQn_Type) ID_UOTGHS, UDD_USB_INT_LEVEL);
- NVIC_EnableIRQ((IRQn_Type) ID_UOTGHS);
-
- // Always authorize asynchrone USB interrupts to exit of sleep mode
- // For SAM USB wake up device except BACKUP mode
- pmc_set_fast_startup_input(PMC_FSMR_USBAL);
- #endif
-
- #if (defined USB_ID_GPIO) && (defined UHD_ENABLE)
- // Check that the device mode is selected by ID pin
- if (!Is_otg_id_device()) {
- cpu_irq_restore(flags);
- return; // Device is not the current mode
- }
- #else
- // ID pin not used then force device mode
- otg_disable_id_pin();
- otg_force_device_mode();
- #endif
- // Enable USB hardware
- otg_enable_pad();
- otg_enable();
-
- // Set the USB speed requested by configuration file
- #ifdef USB_DEVICE_LOW_SPEED
- udd_low_speed_enable();
- #else
- udd_low_speed_disable();
- # ifdef USB_DEVICE_HS_SUPPORT
- udd_high_speed_enable();
- # else
- udd_high_speed_disable();
- # endif
- #endif // USB_DEVICE_LOW_SPEED
-
- // Check USB clock
- otg_unfreeze_clock();
- while (!Is_otg_clock_usable());
-
- // Reset internal variables
- #if (0!=USB_DEVICE_MAX_EP)
- udd_ep_job_table_reset();
- #endif
-
- otg_ack_vbus_transition();
- // Force Vbus interrupt in case of Vbus always with a high level
- // This is possible with a short timing between a Host mode stop/start.
- if (Is_otg_vbus_high()) {
- otg_raise_vbus_transition();
- }
- otg_enable_vbus_interrupt();
- otg_freeze_clock();
-
- #ifndef UDD_NO_SLEEP_MGR
- if (!udd_b_sleep_initialized) {
- udd_b_sleep_initialized = true;
- // Initialize the sleep mode authorized for the USB suspend mode
- udd_b_idle = false;
- sleepmgr_lock_mode(UOTGHS_SLEEP_MODE_USB_SUSPEND);
- } else {
- udd_sleep_mode(false); // Enter idle mode
- }
- #endif
-
- cpu_irq_restore(flags);
- }
-
-
- void udd_disable(void)
- {
- irqflags_t flags;
-
- #ifdef UHD_ENABLE
- # ifdef USB_ID_GPIO
- if (Is_otg_id_host()) {
- // Freeze clock to switch mode
- otg_freeze_clock();
- udd_detach();
- otg_disable();
- return; // Host mode running, ignore UDD disable
- }
- # else
- if (Is_otg_host_mode_forced()) {
- return; // Host mode running, ignore UDD disable
- }
- # endif
- #endif
-
- flags = cpu_irq_save();
- otg_unfreeze_clock();
- udd_detach();
- #ifndef UDD_NO_SLEEP_MGR
- if (udd_b_sleep_initialized) {
- udd_b_sleep_initialized = false;
- sleepmgr_unlock_mode(UOTGHS_SLEEP_MODE_USB_SUSPEND);
- }
- #endif
-
- #ifndef UHD_ENABLE
- otg_disable();
- otg_disable_pad();
- sysclk_disable_usb();
- pmc_disable_periph_clk(ID_UOTGHS);
- // Else the USB clock disable is done by UHC which manage USB dual role
- #endif
- cpu_irq_restore(flags);
- }
-
-
- void udd_attach(void)
- {
- irqflags_t flags;
- flags = cpu_irq_save();
-
- // At startup the USB bus state is unknown,
- // therefore the state is considered IDLE to not miss any USB event
- udd_sleep_mode(true);
- otg_unfreeze_clock();
-
- // This section of clock check can be improved with a chek of
- // USB clock source via sysclk()
- // Check USB clock because the source can be a PLL
- while (!Is_otg_clock_usable());
-
- // Authorize attach if Vbus is present
- udd_attach_device();
-
- // Enable USB line events
- udd_enable_reset_interrupt();
- udd_enable_suspend_interrupt();
- udd_enable_wake_up_interrupt();
- udd_enable_sof_interrupt();
- #ifdef USB_DEVICE_HS_SUPPORT
- udd_enable_msof_interrupt();
- #endif
- // Reset following interupts flag
- udd_ack_reset();
- udd_ack_sof();
- udd_ack_msof();
-
- // The first suspend interrupt must be forced
- // The first suspend interrupt is not detected else raise it
- udd_raise_suspend();
-
- udd_ack_wake_up();
- otg_freeze_clock();
- cpu_irq_restore(flags);
- }
-
-
- void udd_detach(void)
- {
- otg_unfreeze_clock();
-
- // Detach device from the bus
- udd_detach_device();
- otg_freeze_clock();
- udd_sleep_mode(false);
- }
-
-
- bool udd_is_high_speed(void)
- {
- #ifdef USB_DEVICE_HS_SUPPORT
- return !Is_udd_full_speed_mode();
- #else
- return false;
- #endif
- }
-
-
- void udd_set_address(uint8_t address)
- {
- udd_disable_address();
- udd_configure_address(address);
- udd_enable_address();
- }
-
-
- uint8_t udd_getaddress(void)
- {
- return udd_get_configured_address();
- }
-
-
- uint16_t udd_get_frame_number(void)
- {
- return udd_frame_number();
- }
-
- uint16_t udd_get_micro_frame_number(void)
- {
- return udd_micro_frame_number();
- }
-
- void udd_send_remotewakeup(void)
- {
- #ifndef UDD_NO_SLEEP_MGR
- if (!udd_b_idle)
- #endif
- {
- udd_sleep_mode(true); // Enter in IDLE mode
- otg_unfreeze_clock();
- udd_initiate_remote_wake_up();
- }
- }
-
-
- void udd_set_setup_payload(uint8_t *payload, uint16_t payload_size)
- {
- udd_g_ctrlreq.payload = payload;
- udd_g_ctrlreq.payload_size = payload_size;
- }
-
-
- #if (0 != USB_DEVICE_MAX_EP)
- bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes,
- uint16_t MaxEndpointSize)
- {
- bool b_dir_in;
- uint16_t ep_allocated;
- uint8_t nb_bank, bank, i;
-
- b_dir_in = ep & USB_EP_DIR_IN;
- ep = ep & USB_EP_ADDR_MASK;
-
- if (ep > USB_DEVICE_MAX_EP) {
- return false;
- }
- if (Is_udd_endpoint_enabled(ep)) {
- return false;
- }
- dbg_print("alloc(%x, %d) ", ep, MaxEndpointSize);
-
- // Bank choise
- switch (bmAttributes & USB_EP_TYPE_MASK) {
- case USB_EP_TYPE_ISOCHRONOUS:
- nb_bank = UDD_ISOCHRONOUS_NB_BANK(ep);
- break;
- case USB_EP_TYPE_INTERRUPT:
- nb_bank = UDD_INTERRUPT_NB_BANK(ep);
- break;
- case USB_EP_TYPE_BULK:
- nb_bank = UDD_BULK_NB_BANK(ep);
- break;
- default:
- Assert(false);
- return false;
- }
- switch (nb_bank) {
- case 1:
- bank = UOTGHS_DEVEPTCFG_EPBK_1_BANK >>
- UOTGHS_DEVEPTCFG_EPBK_Pos;
- break;
- case 2:
- bank = UOTGHS_DEVEPTCFG_EPBK_2_BANK >>
- UOTGHS_DEVEPTCFG_EPBK_Pos;
- break;
- case 3:
- bank = UOTGHS_DEVEPTCFG_EPBK_3_BANK >>
- UOTGHS_DEVEPTCFG_EPBK_Pos;
- break;
- default:
- Assert(false);
- return false;
- }
-
- // Check if endpoint size is 8,16,32,64,128,256,512 or 1023
- Assert(MaxEndpointSize < 1024);
- Assert((MaxEndpointSize == 1023)
- || !(MaxEndpointSize & (MaxEndpointSize - 1)));
- Assert(MaxEndpointSize >= 8);
-
- // Set configuration of new endpoint
- udd_configure_endpoint(ep, bmAttributes, (b_dir_in ? 1 : 0),
- MaxEndpointSize, bank);
- ep_allocated = 1 << ep;
-
- // Unalloc endpoints superior
- for (i = USB_DEVICE_MAX_EP; i > ep; i--) {
- if (Is_udd_endpoint_enabled(i)) {
- ep_allocated |= 1 << i;
- udd_disable_endpoint(i);
- udd_unallocate_memory(i);
- }
- }
-
- // Realloc/Enable endpoints
- for (i = ep; i <= USB_DEVICE_MAX_EP; i++) {
- if (ep_allocated & (1 << i)) {
- udd_ep_job_t *ptr_job = &udd_ep_job[i - 1];
- bool b_restart = ptr_job->busy;
- // Restart running job because
- // memory window slides up and its data is lost
- ptr_job->busy = false;
- // Re-allocate memory
- udd_allocate_memory(i);
- udd_enable_endpoint(i);
- if (!Is_udd_endpoint_configured(i)) {
- dbg_print("ErrRealloc%d ", i);
- if (NULL == ptr_job->call_trans) {
- return false;
- }
- if (Is_udd_endpoint_in(i)) {
- i |= USB_EP_DIR_IN;
- }
- ptr_job->call_trans(UDD_EP_TRANSFER_ABORT,
- ptr_job->buf_cnt, i);
- return false;
- }
- udd_enable_endpoint_bank_autoswitch(i);
- if (b_restart) {
- // Re-run the job remaining part
- # ifdef UDD_EP_FIFO_SUPPORTED
- if (!Is_udd_endpoint_dma_supported(i)
- && !Is_udd_endpoint_in(i)) {
- ptr_job->buf_cnt -= ptr_job->buf_load;
- }
- # else
- ptr_job->buf_cnt -= ptr_job->buf_load;
- # endif
- b_restart = udd_ep_run(Is_udd_endpoint_in(i) ?
- (i | USB_EP_DIR_IN) : i,
- ptr_job->b_shortpacket,
- &ptr_job->buf[ptr_job->buf_cnt],
- ptr_job->buf_size
- - ptr_job->buf_cnt,
- ptr_job->call_trans);
- if (!b_restart) {
- dbg_print("ErrReRun%d ", i);
- return false;
- }
- }
- }
- }
- return true;
- }
-
-
- void udd_ep_free(udd_ep_id_t ep)
- {
- uint8_t ep_index = ep & USB_EP_ADDR_MASK;
- if (USB_DEVICE_MAX_EP < ep_index) {
- return;
- }
- udd_disable_endpoint(ep_index);
- udd_unallocate_memory(ep_index);
- udd_ep_abort_job(ep);
- udd_ep_job[ep_index - 1].stall_requested = false;
- }
-
-
- bool udd_ep_is_halted(udd_ep_id_t ep)
- {
- uint8_t ep_index = ep & USB_EP_ADDR_MASK;
- return Is_udd_endpoint_stall_requested(ep_index);
- }
-
-
- bool udd_ep_set_halt(udd_ep_id_t ep)
- {
- uint8_t ep_index = ep & USB_EP_ADDR_MASK;
- udd_ep_job_t *ptr_job = &udd_ep_job[ep_index - 1];
- irqflags_t flags;
-
- if (USB_DEVICE_MAX_EP < ep_index) {
- return false;
- }
-
- if (Is_udd_endpoint_stall_requested(ep_index) // Endpoint stalled
- || ptr_job->stall_requested) { // Endpoint stall is requested
- return true; // Already STALL
- }
-
- if (ptr_job->busy == true) {
- return false; // Job on going, stall impossible
- }
-
- flags = cpu_irq_save();
- if ((ep & USB_EP_DIR_IN) && (0 != udd_nb_busy_bank(ep_index))) {
- // Delay the stall after the end of IN transfer on USB line
- ptr_job->stall_requested = true;
- #ifdef UDD_EP_FIFO_SUPPORTED
- udd_disable_in_send_interrupt(ep_index);
- udd_enable_endpoint_bank_autoswitch(ep_index);
- #endif
- udd_enable_bank_interrupt(ep_index);
- udd_enable_endpoint_interrupt(ep_index);
- cpu_irq_restore(flags);
- return true;
- }
- // Stall endpoint immediately
- udd_disable_endpoint_bank_autoswitch(ep_index);
- udd_ack_stall(ep_index);
- udd_enable_stall_handshake(ep_index);
- cpu_irq_restore(flags);
- return true;
- }
-
-
- bool udd_ep_clear_halt(udd_ep_id_t ep)
- {
- uint8_t ep_index = ep & USB_EP_ADDR_MASK;
- udd_ep_job_t *ptr_job = &udd_ep_job[ep_index - 1];
- bool b_stall_cleared = false;
-
- if (USB_DEVICE_MAX_EP < ep_index)
- return false;
-
- if (ptr_job->stall_requested) {
- // Endpoint stall has been requested but not done
- // Remove stall request
- ptr_job->stall_requested = false;
- udd_disable_bank_interrupt(ep_index);
- udd_disable_endpoint_interrupt(ep_index);
- b_stall_cleared = true;
- }
- if (Is_udd_endpoint_stall_requested(ep_index)) {
- if (Is_udd_stall(ep_index)) {
- udd_ack_stall(ep_index);
- // A packet has been stalled
- // then reset datatoggle
- udd_reset_data_toggle(ep_index);
- }
- // Disable stall
- udd_disable_stall_handshake(ep_index);
- udd_enable_endpoint_bank_autoswitch(ep_index);
- b_stall_cleared = true;
- }
- if (b_stall_cleared) {
- // If a job is register on clear halt action
- // then execute callback
- if (ptr_job->busy == true) {
- ptr_job->busy = false;
- ptr_job->call_nohalt();
- }
- }
- return true;
- }
-
-
- bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket,
- uint8_t * buf, iram_size_t buf_size,
- udd_callback_trans_t callback)
- {
- #ifdef UDD_EP_FIFO_SUPPORTED
- bool b_dir_in = Is_udd_endpoint_in(ep & USB_EP_ADDR_MASK);
- #endif
- udd_ep_job_t *ptr_job;
- irqflags_t flags;
-
- ep &= USB_EP_ADDR_MASK;
- if (USB_DEVICE_MAX_EP < ep) {
- return false;
- }
-
- // Get job about endpoint
- ptr_job = &udd_ep_job[ep - 1];
-
- if ((!Is_udd_endpoint_enabled(ep))
- || Is_udd_endpoint_stall_requested(ep)
- || ptr_job->stall_requested) {
- return false; // Endpoint is halted
- }
-
- flags = cpu_irq_save();
- if (ptr_job->busy == true) {
- cpu_irq_restore(flags);
- return false; // Job already on going
- }
- ptr_job->busy = true;
- cpu_irq_restore(flags);
-
- // No job running. Let's setup a new one.
- ptr_job->buf = buf;
- ptr_job->buf_size = buf_size;
- ptr_job->buf_cnt = 0;
- ptr_job->buf_load = 0;
- ptr_job->call_trans = callback;
- ptr_job->b_shortpacket = b_shortpacket || (buf_size == 0);
-
- #ifdef UDD_EP_FIFO_SUPPORTED
- // No DMA support
- if (!Is_udd_endpoint_dma_supported(ep)) {
- dbg_print("ex%x.%c%d\n\r", ep, b_dir_in ? 'i':'o', buf_size);
- flags = cpu_irq_save();
- udd_enable_endpoint_interrupt(ep);
- if (b_dir_in) {
- udd_disable_endpoint_bank_autoswitch(ep);
- udd_enable_in_send_interrupt(ep);
- } else {
- udd_disable_endpoint_bank_autoswitch(ep);
- udd_enable_out_received_interrupt(ep);
- }
- cpu_irq_restore(flags);
- return true;
- }
- #endif // UDD_EP_FIFO_SUPPORTED
-
- #ifdef UDD_EP_DMA_SUPPORTED
- // Request first DMA transfer
- dbg_print("(exDMA%x) ", ep);
- udd_ep_trans_done(ep);
- return true;
- #endif
- }
-
-
- void udd_ep_abort(udd_ep_id_t ep)
- {
- uint8_t ep_index = ep & USB_EP_ADDR_MASK;
-
- #ifdef UDD_EP_FIFO_SUPPORTED
- if (!Is_udd_endpoint_dma_supported(ep_index)) {
- // Disable interrupts
- udd_disable_endpoint_interrupt(ep_index);
- udd_disable_out_received_interrupt(ep_index);
- udd_disable_in_send_interrupt(ep_index);
- } else
- #endif
- {
- // Stop DMA transfer
- udd_disable_endpoint_dma_interrupt(ep_index);
- udd_endpoint_dma_set_control(ep_index, 0);
- }
- udd_disable_endpoint_interrupt(ep_index);
- // Kill IN banks
- if (ep & USB_EP_DIR_IN) {
- while(udd_nb_busy_bank(ep_index)) {
- udd_kill_last_in_bank(ep_index);
- while(Is_udd_kill_last(ep_index));
- }
- }
- udd_ep_abort_job(ep);
- }
-
-
- bool udd_ep_wait_stall_clear(udd_ep_id_t ep,
- udd_callback_halt_cleared_t callback)
- {
- udd_ep_job_t *ptr_job;
-
- ep &= USB_EP_ADDR_MASK;
- if (USB_DEVICE_MAX_EP < ep) {
- return false;
- }
-
- ptr_job = &udd_ep_job[ep - 1];
-
- if (!Is_udd_endpoint_enabled(ep)) {
- return false; // Endpoint not enabled
- }
-
- // Wait clear halt endpoint
- if (ptr_job->busy == true) {
- return false; // Job already on going
- }
-
- if (Is_udd_endpoint_stall_requested(ep)
- || ptr_job->stall_requested) {
- // Endpoint halted then registes the callback
- ptr_job->busy = true;
- ptr_job->call_nohalt = callback;
- } else {
- // endpoint not halted then call directly callback
- callback();
- }
- return true;
- }
- #endif // (0 != USB_DEVICE_MAX_EP)
-
-
- #ifdef USB_DEVICE_HS_SUPPORT
-
- void udd_test_mode_j(void)
- {
- udd_enable_hs_test_mode();
- udd_enable_hs_test_mode_j();
- }
-
-
- void udd_test_mode_k(void)
- {
- udd_enable_hs_test_mode();
- udd_enable_hs_test_mode_k();
- }
-
-
- void udd_test_mode_se0_nak(void)
- {
- udd_enable_hs_test_mode();
- }
-
-
- void udd_test_mode_packet(void)
- {
- uint8_t i;
- uint8_t *ptr_dest;
- const uint8_t *ptr_src;
-
- const uint8_t test_packet[] = {
- // 00000000 * 9
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 01010101 * 8
- 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
- // 01110111 * 8
- 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
- // 0, {111111S * 15}, 111111
- 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF,
- // S, 111111S, {0111111S * 7}
- 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,
- // 00111111, {S0111111 * 9}, S0
- 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E
- };
-
- // Reconfigure control endpoint to bulk IN endpoint
- udd_disable_endpoint(0);
- udd_configure_endpoint(0, USB_EP_TYPE_BULK, 1,
- 64, UOTGHS_DEVEPTCFG_EPBK_1_BANK);
- udd_allocate_memory(0);
- udd_enable_endpoint(0);
-
- udd_enable_hs_test_mode();
- udd_enable_hs_test_mode_packet();
-
- // Send packet on endpoint 0
- ptr_dest = (uint8_t *) & udd_get_endpoint_fifo_access(0, 8);
- ptr_src = test_packet;
-
- for (i = 0; i < sizeof(test_packet); i++) {
- *ptr_dest++ = *ptr_src++;
- }
- udd_ack_fifocon(0);
- }
- #endif // USB_DEVICE_HS_SUPPORT
-
-
-
- // ------------------------
- //--- INTERNAL ROUTINES TO MANAGED THE CONTROL ENDPOINT
-
- static void udd_reset_ep_ctrl(void)
- {
- irqflags_t flags;
-
- // Reset USB address to 0
- udd_configure_address(0);
- udd_enable_address();
-
- // Alloc and configure control endpoint
- udd_configure_endpoint(0,
- USB_EP_TYPE_CONTROL,
- 0,
- USB_DEVICE_EP_CTRL_SIZE,
- UOTGHS_DEVEPTCFG_EPBK_1_BANK);
-
- udd_allocate_memory(0);
- udd_enable_endpoint(0);
- flags = cpu_irq_save();
- udd_enable_setup_received_interrupt(0);
- udd_enable_out_received_interrupt(0);
- udd_enable_endpoint_interrupt(0);
- cpu_irq_restore(flags);
- }
-
- static void udd_ctrl_init(void)
- {
- irqflags_t flags;
- flags = cpu_irq_save();
-
- // In case of abort of IN Data Phase:
- // No need to abort IN transfer (rise TXINI),
- // because it is automatically done by hardware when a Setup packet is received.
- // But the interrupt must be disabled to don't generate interrupt TXINI
- // after SETUP reception.
- udd_disable_in_send_interrupt(0);
- cpu_irq_restore(flags);
-
- // In case of OUT ZLP event is no processed before Setup event occurs
- udd_ack_out_received(0);
-
- udd_g_ctrlreq.callback = NULL;
- udd_g_ctrlreq.over_under_run = NULL;
- udd_g_ctrlreq.payload_size = 0;
- udd_ep_control_state = UDD_EPCTRL_SETUP;
- }
-
-
- static void udd_ctrl_setup_received(void)
- {
- irqflags_t flags;
- uint8_t i;
-
- if (UDD_EPCTRL_SETUP != udd_ep_control_state) {
- // May be a hidden DATA or ZLP phase or protocol abort
- udd_ctrl_endofrequest();
-
- // Reinitializes control endpoint management
- udd_ctrl_init();
- }
- // Fill setup request structure
- if (8 != udd_byte_count(0)) {
- udd_ctrl_stall_data();
- udd_ack_setup_received(0);
- return; // Error data number doesn't correspond to SETUP packet
- }
- uint8_t *ptr = (uint8_t *) & udd_get_endpoint_fifo_access(0,8);
- for (i = 0; i < 8; i++) {
- ((uint8_t*) &udd_g_ctrlreq.req)[i] = *ptr++;
- }
- // Manage LSB/MSB to fit with CPU usage
- udd_g_ctrlreq.req.wValue = le16_to_cpu(udd_g_ctrlreq.req.wValue);
- udd_g_ctrlreq.req.wIndex = le16_to_cpu(udd_g_ctrlreq.req.wIndex);
- udd_g_ctrlreq.req.wLength = le16_to_cpu(udd_g_ctrlreq.req.wLength);
-
- // Decode setup request
- if (udc_process_setup() == false) {
- // Setup request unknow then stall it
- udd_ctrl_stall_data();
- udd_ack_setup_received(0);
- return;
- }
- udd_ack_setup_received(0);
-
- if (Udd_setup_is_in()) {
- // IN data phase requested
- udd_ctrl_prev_payload_buf_cnt = 0;
- udd_ctrl_payload_buf_cnt = 0;
- udd_ep_control_state = UDD_EPCTRL_DATA_IN;
- udd_ctrl_in_sent(); // Send first data transfer
- } else {
- if (0 == udd_g_ctrlreq.req.wLength) {
- // No data phase requested
- // Send IN ZLP to ACK setup request
- udd_ctrl_send_zlp_in();
- return;
- }
- // OUT data phase requested
- udd_ctrl_prev_payload_buf_cnt = 0;
- udd_ctrl_payload_buf_cnt = 0;
- udd_ep_control_state = UDD_EPCTRL_DATA_OUT;
- // To detect a protocol error, enable nak interrupt on data IN phase
- udd_ack_nak_in(0);
- flags = cpu_irq_save();
- udd_enable_nak_in_interrupt(0);
- cpu_irq_restore(flags);
- }
- }
-
-
- static void udd_ctrl_in_sent(void)
- {
- static bool b_shortpacket = false;
- uint16_t nb_remain;
- uint8_t i;
- uint8_t *ptr_dest, *ptr_src;
- irqflags_t flags;
-
- flags = cpu_irq_save();
- udd_disable_in_send_interrupt(0);
- cpu_irq_restore(flags);
-
- if (UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP == udd_ep_control_state) {
- // ZLP on IN is sent, then valid end of setup request
- udd_ctrl_endofrequest();
- // Reinitializes control endpoint management
- udd_ctrl_init();
- return;
- }
- Assert(udd_ep_control_state == UDD_EPCTRL_DATA_IN);
-
- nb_remain = udd_g_ctrlreq.payload_size - udd_ctrl_payload_buf_cnt;
- if (0 == nb_remain) {
- // All content of current buffer payload are sent
- // Update number of total data sending by previous playlaod buffer
- udd_ctrl_prev_payload_buf_cnt += udd_ctrl_payload_buf_cnt;
- if ((udd_g_ctrlreq.req.wLength == udd_ctrl_prev_payload_buf_cnt)
- || b_shortpacket) {
- // All data requested are transfered or a short packet has been sent
- // then it is the end of data phase.
- // Generate an OUT ZLP for handshake phase.
- udd_ctrl_send_zlp_out();
- return;
- }
- // Need of new buffer because the data phase is not complete
- if ((!udd_g_ctrlreq.over_under_run)
- || (!udd_g_ctrlreq.over_under_run())) {
- // Underrun then send zlp on IN
- // Here nb_remain=0 and allows to send a IN ZLP
- } else {
- // A new payload buffer is given
- udd_ctrl_payload_buf_cnt = 0;
- nb_remain = udd_g_ctrlreq.payload_size;
- }
- }
- // Continue transfer and send next data
- if (nb_remain >= USB_DEVICE_EP_CTRL_SIZE) {
- nb_remain = USB_DEVICE_EP_CTRL_SIZE;
- b_shortpacket = false;
- } else {
- b_shortpacket = true;
- }
- // Fill buffer of endpoint control
- ptr_dest = (uint8_t *) & udd_get_endpoint_fifo_access(0, 8);
- ptr_src = udd_g_ctrlreq.payload + udd_ctrl_payload_buf_cnt;
- // Critical section
- // Only in case of DATA IN phase abort without USB Reset signal after.
- // The IN data don't must be written in endpoint 0 DPRAM during
- // a next setup reception in same endpoint 0 DPRAM.
- // Thereby, an OUT ZLP reception must check before IN data write
- // and if no OUT ZLP is received the data must be written quickly (800µs)
- // before an eventually ZLP OUT and SETUP reception
- flags = cpu_irq_save();
- if (Is_udd_out_received(0)) {
- // IN DATA phase aborted by OUT ZLP
- cpu_irq_restore(flags);
- udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP;
- return; // Exit of IN DATA phase
- }
- // Write quickly the IN data
- for (i = 0; i < nb_remain; i++) {
- *ptr_dest++ = *ptr_src++;
- }
- udd_ctrl_payload_buf_cnt += nb_remain;
-
- // Validate and send the data available in the control endpoint buffer
- udd_ack_in_send(0);
- udd_enable_in_send_interrupt(0);
- // In case of abort of DATA IN phase, no need to enable nak OUT interrupt
- // because OUT endpoint is already free and ZLP OUT accepted.
- cpu_irq_restore(flags);
- }
-
-
- static void udd_ctrl_out_received(void)
- {
- irqflags_t flags;
- uint8_t i;
- uint16_t nb_data;
-
- if (UDD_EPCTRL_DATA_OUT != udd_ep_control_state) {
- if ((UDD_EPCTRL_DATA_IN == udd_ep_control_state)
- || (UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP ==
- udd_ep_control_state)) {
- // End of SETUP request:
- // - Data IN Phase aborted,
- // - or last Data IN Phase hidden by ZLP OUT sending quiclky,
- // - or ZLP OUT received normaly.
- udd_ctrl_endofrequest();
- } else {
- // Protocol error during SETUP request
- udd_ctrl_stall_data();
- }
- // Reinitializes control endpoint management
- udd_ctrl_init();
- return;
- }
- // Read data received during OUT phase
- nb_data = udd_byte_count(0);
- if (udd_g_ctrlreq.payload_size < (udd_ctrl_payload_buf_cnt + nb_data)) {
- // Payload buffer too small
- nb_data = udd_g_ctrlreq.payload_size - udd_ctrl_payload_buf_cnt;
- }
- uint8_t *ptr_src = (uint8_t *) & udd_get_endpoint_fifo_access(0, 8);
- uint8_t *ptr_dest = udd_g_ctrlreq.payload + udd_ctrl_payload_buf_cnt;
- for (i = 0; i < nb_data; i++) {
- *ptr_dest++ = *ptr_src++;
- }
- udd_ctrl_payload_buf_cnt += nb_data;
-
- if ((USB_DEVICE_EP_CTRL_SIZE != nb_data)
- || (udd_g_ctrlreq.req.wLength <=
- (udd_ctrl_prev_payload_buf_cnt +
- udd_ctrl_payload_buf_cnt))) {
- // End of reception because it is a short packet
- // Before send ZLP, call intermediat calback
- // in case of data receiv generate a stall
- udd_g_ctrlreq.payload_size = udd_ctrl_payload_buf_cnt;
- if (NULL != udd_g_ctrlreq.over_under_run) {
- if (!udd_g_ctrlreq.over_under_run()) {
- // Stall ZLP
- udd_ctrl_stall_data();
- // Ack reception of OUT to replace NAK by a STALL
- udd_ack_out_received(0);
- return;
- }
- }
- // Send IN ZLP to ACK setup request
- udd_ack_out_received(0);
- udd_ctrl_send_zlp_in();
- return;
- }
-
- if (udd_g_ctrlreq.payload_size == udd_ctrl_payload_buf_cnt) {
- // Overrun then request a new payload buffer
- if (!udd_g_ctrlreq.over_under_run) {
- // No callback availabled to request a new payload buffer
- udd_ctrl_stall_data();
- // Ack reception of OUT to replace NAK by a STALL
- udd_ack_out_received(0);
- return;
- }
- if (!udd_g_ctrlreq.over_under_run()) {
- // No new payload buffer delivered
- udd_ctrl_stall_data();
- // Ack reception of OUT to replace NAK by a STALL
- udd_ack_out_received(0);
- return;
- }
- // New payload buffer available
- // Update number of total data received
- udd_ctrl_prev_payload_buf_cnt += udd_ctrl_payload_buf_cnt;
- // Reinit reception on payload buffer
- udd_ctrl_payload_buf_cnt = 0;
- }
- // Free buffer of control endpoint to authorize next reception
- udd_ack_out_received(0);
- // To detect a protocol error, enable nak interrupt on data IN phase
- udd_ack_nak_in(0);
- flags = cpu_irq_save();
- udd_enable_nak_in_interrupt(0);
- cpu_irq_restore(flags);
- }
-
-
- static void udd_ctrl_underflow(void)
- {
- if (Is_udd_out_received(0))
- return; // Underflow ignored if OUT data is received
-
- if (UDD_EPCTRL_DATA_OUT == udd_ep_control_state) {
- // Host want to stop OUT transaction
- // then stop to wait OUT data phase and wait IN ZLP handshake
- udd_ctrl_send_zlp_in();
- } else if (UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP == udd_ep_control_state) {
- // A OUT handshake is waiting by device,
- // but host want extra IN data then stall extra IN data
- udd_enable_stall_handshake(0);
- }
- }
-
-
- static void udd_ctrl_overflow(void)
- {
- if (Is_udd_in_send(0))
- return; // Overflow ignored if IN data is received
-
- // The case of UDD_EPCTRL_DATA_IN is not managed
- // because the OUT endpoint is already free and OUT ZLP accepted
-
- if (UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP == udd_ep_control_state) {
- // A IN handshake is waiting by device,
- // but host want extra OUT data then stall extra OUT data
- udd_enable_stall_handshake(0);
- }
- }
-
-
- static void udd_ctrl_stall_data(void)
- {
- // Stall all packets on IN & OUT control endpoint
- udd_ep_control_state = UDD_EPCTRL_STALL_REQ;
- udd_enable_stall_handshake(0);
- }
-
-
- static void udd_ctrl_send_zlp_in(void)
- {
- irqflags_t flags;
-
- udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP;
-
- // Validate and send empty IN packet on control endpoint
- flags = cpu_irq_save();
- // Send ZLP on IN endpoint
- udd_ack_in_send(0);
- udd_enable_in_send_interrupt(0);
- // To detect a protocol error, enable nak interrupt on data OUT phase
- udd_ack_nak_out(0);
- udd_enable_nak_out_interrupt(0);
- cpu_irq_restore(flags);
- }
-
-
- static void udd_ctrl_send_zlp_out(void)
- {
- irqflags_t flags;
-
- udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP;
- // No action is necessary to accept OUT ZLP
- // because the buffer of control endpoint is already free
-
- // To detect a protocol error, enable nak interrupt on data IN phase
- flags = cpu_irq_save();
- udd_ack_nak_in(0);
- udd_enable_nak_in_interrupt(0);
- cpu_irq_restore(flags);
- }
-
-
- static void udd_ctrl_endofrequest(void)
- {
- // If a callback is registered then call it
- if (udd_g_ctrlreq.callback) {
- udd_g_ctrlreq.callback();
- }
- }
-
-
- static bool udd_ctrl_interrupt(void)
- {
-
- if (!Is_udd_endpoint_interrupt(0)) {
- return false; // No interrupt events on control endpoint
- }
-
- dbg_print("0: ");
-
- // By default disable overflow and underflow interrupt
- udd_disable_nak_in_interrupt(0);
- udd_disable_nak_out_interrupt(0);
-
- // Search event on control endpoint
- if (Is_udd_setup_received(0)) {
- dbg_print("stup ");
- // SETUP packet received
- udd_ctrl_setup_received();
- return true;
- }
- if (Is_udd_in_send(0) && Is_udd_in_send_interrupt_enabled(0)) {
- dbg_print("in ");
- // IN packet sent
- udd_ctrl_in_sent();
- return true;
- }
- if (Is_udd_out_received(0)) {
- dbg_print("out ");
- // OUT packet received
- udd_ctrl_out_received();
- return true;
- }
- if (Is_udd_nak_out(0)) {
- dbg_print("nako ");
- // Overflow on OUT packet
- udd_ack_nak_out(0);
- udd_ctrl_overflow();
- return true;
- }
- if (Is_udd_nak_in(0)) {
- dbg_print("naki ");
- // Underflow on IN packet
- udd_ack_nak_in(0);
- udd_ctrl_underflow();
- return true;
- }
- dbg_print("n%x ", UOTGHS_ARRAY(UOTGHS_DEVEPTISR[0], 0));
- return false;
- }
-
-
- // ------------------------
- //--- INTERNAL ROUTINES TO MANAGED THE BULK/INTERRUPT/ISOCHRONOUS ENDPOINTS
-
- #if (0 != USB_DEVICE_MAX_EP)
-
- static void udd_ep_job_table_reset(void)
- {
- uint8_t i;
- for (i = 0; i < USB_DEVICE_MAX_EP; i++) {
- udd_ep_job[i].busy = false;
- udd_ep_job[i].stall_requested = false;
- }
- }
-
-
- static void udd_ep_job_table_kill(void)
- {
- uint8_t i;
-
- // For each endpoint, kill job
- for (i = 0; i < USB_DEVICE_MAX_EP; i++) {
- udd_ep_finish_job(&udd_ep_job[i], true, i + 1);
- }
- }
-
-
- static void udd_ep_abort_job(udd_ep_id_t ep)
- {
- ep &= USB_EP_ADDR_MASK;
-
- // Abort job on endpoint
- udd_ep_finish_job(&udd_ep_job[ep - 1], true, ep);
- }
-
-
- static void udd_ep_finish_job(udd_ep_job_t * ptr_job, bool b_abort, uint8_t ep_num)
- {
- if (ptr_job->busy == false) {
- return; // No on-going job
- }
- dbg_print("(JobE%x:%d) ", (ptr_job-udd_ep_job)+1, b_abort);
- ptr_job->busy = false;
- if (NULL == ptr_job->call_trans) {
- return; // No callback linked to job
- }
- if (Is_udd_endpoint_in(ep_num)) {
- ep_num |= USB_EP_DIR_IN;
- }
- ptr_job->call_trans((b_abort) ? UDD_EP_TRANSFER_ABORT :
- UDD_EP_TRANSFER_OK, ptr_job->buf_size, ep_num);
- }
-
- #ifdef UDD_EP_DMA_SUPPORTED
- static void udd_ep_trans_done(udd_ep_id_t ep)
- {
- uint32_t udd_dma_ctrl = 0;
- udd_ep_job_t *ptr_job;
- iram_size_t next_trans;
- irqflags_t flags;
-
- // Get job corresponding at endpoint
- ptr_job = &udd_ep_job[ep - 1];
-
- if (!ptr_job->busy) {
- return; // No job is running, then ignore it (system error)
- }
-
- if (ptr_job->buf_cnt != ptr_job->buf_size) {
- // Need to send or receiv other data
- next_trans = ptr_job->buf_size - ptr_job->buf_cnt;
-
- if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
- // The USB hardware support a maximum
- // transfer size of UDD_ENDPOINT_MAX_TRANS Bytes
- next_trans = UDD_ENDPOINT_MAX_TRANS;
-
- // Set 0 to tranfer the maximum
- udd_dma_ctrl = UOTGHS_DEVDMACONTROL_BUFF_LENGTH(0);
- } else {
- udd_dma_ctrl = UOTGHS_DEVDMACONTROL_BUFF_LENGTH(next_trans);
- }
- if (Is_udd_endpoint_in(ep)) {
- if (0 != (next_trans % udd_get_endpoint_size(ep))) {
- // Enable short packet option
- // else the DMA transfer is accepted
- // and interrupt DMA valid but nothing is sent.
- udd_dma_ctrl |= UOTGHS_DEVDMACONTROL_END_B_EN;
- // No need to request another ZLP
- ptr_job->b_shortpacket = false;
- }
- } else {
- if ((USB_EP_TYPE_ISOCHRONOUS != udd_get_endpoint_type(ep))
- || (next_trans <= (iram_size_t) udd_get_endpoint_size(ep))) {
-
- // Enable short packet reception
- udd_dma_ctrl |= UOTGHS_DEVDMACONTROL_END_TR_IT
- | UOTGHS_DEVDMACONTROL_END_TR_EN;
- }
- }
-
- // Start USB DMA to fill or read fifo of the selected endpoint
- udd_endpoint_dma_set_addr(ep, (uint32_t) & ptr_job->buf[ptr_job->buf_cnt]);
- udd_dma_ctrl |= UOTGHS_DEVDMACONTROL_END_BUFFIT |
- UOTGHS_DEVDMACONTROL_CHANN_ENB;
-
-
- // Disable IRQs to have a short sequence
- // between read of EOT_STA and DMA enable
- flags = cpu_irq_save();
- if (!(udd_endpoint_dma_get_status(ep)
- & UOTGHS_DEVDMASTATUS_END_TR_ST)) {
- dbg_print("dmaS%x ", ep);
- udd_endpoint_dma_set_control(ep, udd_dma_ctrl);
- ptr_job->buf_cnt += next_trans;
- ptr_job->buf_load = next_trans;
- udd_enable_endpoint_dma_interrupt(ep);
- cpu_irq_restore(flags);
- return;
- }
- cpu_irq_restore(flags);
-
- // Here a ZLP has been recieved
- // and the DMA transfer must be not started.
- // It is the end of transfer
- ptr_job->buf_size = ptr_job->buf_cnt;
- }
- if (Is_udd_endpoint_in(ep)) {
- if (ptr_job->b_shortpacket) {
- dbg_print("zlpS%x ", ep);
- // Need to send a ZLP (No possible with USB DMA)
- // enable interrupt to wait a free bank to sent ZLP
- udd_ack_in_send(ep);
- if (Is_udd_write_enabled(ep)) {
- // Force interrupt in case of ep already free
- udd_raise_in_send(ep);
- }
- udd_enable_in_send_interrupt(ep);
- udd_enable_endpoint_interrupt(ep);
- return;
- }
- }
- dbg_print("dmaE ");
- // Call callback to signal end of transfer
- udd_ep_finish_job(ptr_job, false, ep);
- }
- #endif
-
- #ifdef UDD_EP_FIFO_SUPPORTED
- static void udd_ep_in_sent(udd_ep_id_t ep)
- {
- udd_ep_job_t *ptr_job = &udd_ep_job[ep - 1];
- uint8_t *ptr_src = &ptr_job->buf[ptr_job->buf_cnt];
- uint8_t *ptr_dst = (uint8_t *) & udd_get_endpoint_fifo_access(ep, 8);
- uint32_t pkt_size = udd_get_endpoint_size(ep);
- uint32_t nb_data = 0, i;
- uint32_t nb_remain;
- irqflags_t flags;
-
- // All transfer done, including ZLP, Finish Job
- if (ptr_job->buf_cnt >= ptr_job->buf_size && !ptr_job->b_shortpacket) {
- flags = cpu_irq_save();
- udd_disable_in_send_interrupt(ep);
- udd_disable_endpoint_interrupt(ep);
- cpu_irq_restore(flags);
-
- ptr_job->buf_size = ptr_job->buf_cnt; // buf_size is passed to callback as XFR count
- udd_ep_finish_job(ptr_job, false, ep);
- return;
- } else {
- // ACK TXINI
- udd_ack_in_send(ep);
- // Fill FIFO
- ptr_dst = (uint8_t *) & udd_get_endpoint_fifo_access(ep, 8);
- ptr_src = &ptr_job->buf[ptr_job->buf_cnt];
- nb_remain = ptr_job->buf_size - ptr_job->buf_cnt;
- // Fill a bank even if no data (ZLP)
- nb_data = min(nb_remain, pkt_size);
- // Modify job information
- ptr_job->buf_cnt += nb_data;
- ptr_job->buf_load = nb_data;
-
- // Copy buffer to FIFO
- for (i = 0; i < nb_data; i++) {
- *ptr_dst++ = *ptr_src++;
- }
- // Switch to next bank
- udd_ack_fifocon(ep);
- // ZLP?
- if (nb_data < pkt_size) {
- ptr_job->b_shortpacket = false;
- }
- }
- }
-
- static void udd_ep_out_received(udd_ep_id_t ep)
- {
- udd_ep_job_t *ptr_job = &udd_ep_job[ep - 1];
- uint32_t nb_data = 0, i;
- uint32_t nb_remain = ptr_job->buf_size - ptr_job->buf_cnt;
- uint32_t pkt_size = udd_get_endpoint_size(ep);
- uint8_t *ptr_src = (uint8_t *) & udd_get_endpoint_fifo_access(ep, 8);
- uint8_t *ptr_dst = &ptr_job->buf[ptr_job->buf_cnt];
- bool b_full = false, b_short = false;
-
- // Clear RX OUT
- udd_ack_out_received(ep);
-
- // Read byte count
- nb_data = udd_byte_count(ep);
- if (nb_data < pkt_size) {
- b_short = true;
- }
- //dbg_print("o%d ", ep);
- //dbg_print("%d ", nb_data);
- // Copy data if there is
- if (nb_data > 0) {
- if (nb_data >= nb_remain) {
- nb_data = nb_remain;
- b_full = true;
- }
- // Modify job information
- ptr_job->buf_cnt += nb_data;
- ptr_job->buf_load = nb_data;
- // Copy FIFO to buffer
- for (i = 0; i < nb_data; i++) {
- *ptr_dst++ = *ptr_src++;
- }
- }
- // Clear FIFO Status
- udd_ack_fifocon(ep);
- // Finish job on error or short packet
- if (b_full || b_short) {
- //dbg_print("EoO%d\n\r", ep);
- udd_disable_out_received_interrupt(ep);
- udd_disable_endpoint_interrupt(ep);
- ptr_job->buf_size = ptr_job->buf_cnt; // buf_size is passed to callback as XFR count
- udd_ep_finish_job(ptr_job, false, ep);
- }
- }
- #endif // #ifdef UDD_EP_FIFO_SUPPORTED
-
- static bool udd_ep_interrupt(void)
- {
- udd_ep_id_t ep;
- udd_ep_job_t *ptr_job;
-
- // For each endpoint different of control endpoint (0)
- for (ep = 1; ep <= USB_DEVICE_MAX_EP; ep++) {
- // Get job corresponding at endpoint
- ptr_job = &udd_ep_job[ep - 1];
-
- #ifdef UDD_EP_DMA_SUPPORTED
- // Check DMA event
- if (Is_udd_endpoint_dma_interrupt_enabled(ep)
- && Is_udd_endpoint_dma_interrupt(ep)) {
- uint32_t nb_remaining;
- if (udd_endpoint_dma_get_status(ep)
- & UOTGHS_DEVDMASTATUS_CHANN_ENB) {
- return true; // Ignore EOT_STA interrupt
- }
- dbg_print("dma%x: ", ep);
- udd_disable_endpoint_dma_interrupt(ep);
- // Save number of data no transfered
- nb_remaining = (udd_endpoint_dma_get_status(ep) &
- UOTGHS_DEVDMASTATUS_BUFF_COUNT_Msk)
- >> UOTGHS_DEVDMASTATUS_BUFF_COUNT_Pos;
- if (nb_remaining) {
- // Transfer no complete (short packet or ZLP) then:
- // Update number of data transfered
- ptr_job->buf_cnt -= nb_remaining;
- // Set transfer complete to stop the transfer
- ptr_job->buf_size = ptr_job->buf_cnt;
- }
- udd_ep_trans_done(ep);
- return true;
- }
- #endif
- #ifdef UDD_EP_FIFO_SUPPORTED
- // Check RXRDY and TXEMPTY event for none DMA endpoints
- if (!Is_udd_endpoint_dma_supported(ep)
- && Is_udd_endpoint_interrupt_enabled(ep)) {
- dbg_print("ep%x: ", ep);
- // RXOUT: Full packet received
- if (Is_udd_out_received(ep)
- && Is_udd_out_received_interrupt_enabled(ep)) {
- dbg_print("Out ");
- udd_ep_out_received(ep);
- return true;
- }
- // TXIN: packet sent
- if (Is_udd_in_send(ep)
- && Is_udd_in_send_interrupt_enabled(ep)) {
- dbg_print("In ");
- udd_ep_in_sent(ep);
- return true;
- }
- // Errors: Abort?
- if (Is_udd_overflow(ep)
- || Is_udd_underflow(ep)
- || Is_udd_crc_error(ep)) {
- dbg_print("Err ");
- udd_ep_abort(ep);
- return true;
- }
- }
- #endif // UDD_EP_FIFO_SUPPORTED
- // Check empty bank interrupt event
- if (Is_udd_endpoint_interrupt_enabled(ep)) {
- dbg_print("bg%x: ", ep);
- if (Is_udd_in_send_interrupt_enabled(ep)
- && Is_udd_in_send(ep)) {
- dbg_print("I ");
- udd_disable_in_send_interrupt(ep);
- // One bank is free then send a ZLP
- udd_ack_in_send(ep);
- udd_ack_fifocon(ep);
- udd_ep_finish_job(ptr_job, false, ep);
- return true;
- }
- if (Is_udd_bank_interrupt_enabled(ep)
- && (0 == udd_nb_busy_bank(ep))) {
- dbg_print("EoT ");
- // End of background transfer on IN endpoint
- udd_disable_bank_interrupt(ep);
- udd_disable_endpoint_interrupt(ep);
-
- Assert(ptr_job->stall_requested);
- // A stall has been requested during backgound transfer
- ptr_job->stall_requested = false;
- udd_disable_endpoint_bank_autoswitch(ep);
- udd_enable_stall_handshake(ep);
- udd_reset_data_toggle(ep);
- return true;
- }
- }
- }
- return false;
- }
- #endif // (0 != USB_DEVICE_MAX_EP)
-
- //@}
-
- #endif // ARDUINO_ARCH_SAM
|