//========================================================================== // // usbs_at91.c // // Driver for the AT91 USB device // //========================================================================== // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 2006, 2010 Free Software Foundation, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later // version. // // eCos is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): Oliver Munz, // Contributors: Andrew Lunn, bartv, ccoutand // Date: 2006-02-22 // // This code implements support for the on-chip USB port on the AT91 // family of processors. The code has been developed on the AT91SAM7S // and may or may not work on other members of the AT91 family. // //####DESCRIPTIONEND#### //========================================================================== #include #include #include #include #include #include CYGBLD_HAL_PLATFORM_H #include #include #include #include #include #include #include "bitops.h" #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #define AT91_UDP_CSR0 (AT91_UDP_CSR) #define AT91_UDP_FDR0 (AT91_UDP_FDR) #define pIER (AT91_UDP + AT91_UDP_IER) #define pIDR (AT91_UDP + AT91_UDP_IDR) #define pISR (AT91_UDP + AT91_UDP_ISR) #define pIMR (AT91_UDP + AT91_UDP_IMR) #define pICR (AT91_UDP + AT91_UDP_ICR) #define pCSR0 (AT91_UDP + AT91_UDP_CSR0) #define pFDR0 (AT91_UDP + AT91_UDP_FDR0) #define pCSRn(N) (pCSR0 + (N * 4)) #define pFDRn(N) (pFDR0 + (N * 4)) #if (AT91_USB_ENDPOINTS == 8) #define AT91_UDP_ALLOWED_IRQs \ (AT91_UDP_WAKEUP | AT91_UDP_ENDBUSRES | AT91_UDP_EXTRSM | \ AT91_UDP_RXRSM | AT91_UDP_RXSUSP | AT91_UDP_EPINT0 | \ AT91_UDP_EPINT1 | AT91_UDP_EPINT2 | AT91_UDP_EPINT3 | \ AT91_UDP_EPINT4 | AT91_UDP_EPINT5 | \ AT91_UDP_EPINT6 | AT91_UDP_EPINT7 ) #elif (AT91_USB_ENDPOINTS == 6) #define AT91_UDP_ALLOWED_IRQs \ (AT91_UDP_WAKEUP | AT91_UDP_ENDBUSRES | AT91_UDP_EXTRSM | \ AT91_UDP_RXRSM | AT91_UDP_RXSUSP | AT91_UDP_EPINT0 | \ AT91_UDP_EPINT1 | AT91_UDP_EPINT2 | AT91_UDP_EPINT3 | \ AT91_UDP_EPINT4 | AT91_UDP_EPINT5 ) #elif (AT91_USB_ENDPOINTS == 4) #define AT91_UDP_ALLOWED_IRQs \ (AT91_UDP_WAKEUP | AT91_UDP_ENDBUSRES | AT91_UDP_EXTRSM | \ AT91_UDP_RXRSM | AT91_UDP_RXSUSP | AT91_UDP_EPINT0 | \ AT91_UDP_EPINT1 | AT91_UDP_EPINT2 | AT91_UDP_EPINT3) #endif #define THERE_IS_A_NEW_PACKET_IN_THE_UDP 0xffff // Fifo size for each end point. #if defined(CYGHWR_HAL_ARM_AT91SAM7SE) static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = { 8, 64, 64, 64, 512, 512, 64, 64 }; #elif defined(CYGHWR_HAL_ARM_AT91SAM7X) static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = { 8, 64, 64, 64, 256, 256 }; #else static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = { 8, 64, 64, 64, }; #endif // Does an endpoint support ping pong buffering? #if defined(CYGHWR_HAL_ARM_AT91SAM7SE) static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = { false, true, true, false, true, true, true, true }; #elif defined(CYGHWR_HAL_ARM_AT91SAM7X) static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = { false, true, true, false, true, true }; #else static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = { false, true, true, false }; #endif static cyg_uint8 *usbs_at91_endpoint_pbegin[AT91_USB_ENDPOINTS] = #if (AT91_USB_ENDPOINTS == 8) { 0, 0, 0, 0, 0, 0, 0, 0 }; #elif (AT91_USB_ENDPOINTS == 6) { 0, 0, 0, 0, 0, 0 }; #else { 0, 0, 0, 0 }; #endif static cyg_uint8 *usbs_at91_endpoint_pend[AT91_USB_ENDPOINTS] = #if (AT91_USB_ENDPOINTS == 8) { 0, 0, 0, 0, 0, 0, 0, 0 }; #elif (AT91_USB_ENDPOINTS == 6) { 0, 0, 0, 0, 0, 0 }; #else { 0, 0 ,0, 0 }; #endif static bool usbs_at91_endpoint_bank1[AT91_USB_ENDPOINTS] = #if (AT91_USB_ENDPOINTS == 8) { false, false, false, false, false, false, false, false }; #elif (AT91_USB_ENDPOINTS == 6) { false, false, false, false, false, false }; #else { false, false, false, false }; #endif static cyg_uint16 usbs_at91_endpoint_bytes_in_fifo[AT91_USB_ENDPOINTS] = #if (AT91_USB_ENDPOINTS == 8) { 0, 0, 0, 0, 0, 0, 0, 0 }; #elif (AT91_USB_ENDPOINTS == 6) { 0, 0, 0, 0, 0, 0 }; #else { 0, 0, 0, 0 }; #endif static cyg_uint16 usbs_at91_endpoint_bytes_received[AT91_USB_ENDPOINTS] = { THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP, #if (AT91_USB_ENDPOINTS > 4) THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP, #if (AT91_USB_ENDPOINTS > 6) THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP, #endif #endif THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP}; static cyg_interrupt usbs_at91_intr_data; static cyg_handle_t usbs_at91_intr_handle; static void usbs_at91_ep0_start(usbs_control_endpoint *); static void usbs_at91_poll(usbs_control_endpoint *); static void usbs_at91_endpoint_start(usbs_rx_endpoint * pep); static void usbs_at91_endpoint_set_halted(usbs_rx_endpoint * pep, cyg_bool new_value); void usbs_at91_endpoint_init(usbs_rx_endpoint * pep, cyg_uint8 endpoint_type, cyg_bool enable); // Endpoint 0, the control endpoint, structure. usbs_control_endpoint usbs_at91_ep0 = { // The hardware does not distinguish between detached, attached and powered. state: USBS_STATE_POWERED, enumeration_data: (usbs_enumeration_data *) 0, start_fn: usbs_at91_ep0_start, poll_fn: usbs_at91_poll, interrupt_vector: CYGNUM_HAL_INTERRUPT_UDP, control_buffer: {0, 0, 0, 0, 0, 0, 0, 0}, state_change_fn: (void (*) (usbs_control_endpoint *, void *, usbs_state_change, int)) 0, state_change_data: (void *) 0, standard_control_fn: (usbs_control_return (*) (usbs_control_endpoint *, void *)) 0, standard_control_data: (void *) 0, class_control_fn: (usbs_control_return (*) (usbs_control_endpoint *, void *)) 0, class_control_data: (void *) 0, vendor_control_fn: (usbs_control_return (*) (usbs_control_endpoint *, void *)) 0, vendor_control_data: (void *) 0, reserved_control_fn: (usbs_control_return (*) (usbs_control_endpoint *, void *)) 0, reserved_control_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, fill_buffer_fn: (void (*)(usbs_control_endpoint *)) 0, fill_data: (void *) 0, fill_index: 0, complete_fn: (usbs_control_return (*)(usbs_control_endpoint *, int)) 0 }; // Endpoint 1 receive control structure usbs_rx_endpoint usbs_at91_ep1 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; // Endpoint 2 Receive control structure usbs_rx_endpoint usbs_at91_ep2 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; // Endpoint 3 Receive control structure usbs_rx_endpoint usbs_at91_ep3 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; #if (AT91_USB_ENDPOINTS > 4) // Endpoint 4 Receive control structure usbs_rx_endpoint usbs_at91_ep4 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; #endif #if (AT91_USB_ENDPOINTS > 5) // Endpoint 5 Receive control structure usbs_rx_endpoint usbs_at91_ep5 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; #endif #if (AT91_USB_ENDPOINTS > 6) // Endpoint 6 Receive control structure usbs_rx_endpoint usbs_at91_ep6 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; #endif #if (AT91_USB_ENDPOINTS > 7) // Endpoint 7 Receive control structure usbs_rx_endpoint usbs_at91_ep7 = { start_rx_fn: usbs_at91_endpoint_start, set_halted_fn: usbs_at91_endpoint_set_halted, complete_fn: (void (*)(void *, int)) 0, complete_data: (void *) 0, buffer: (unsigned char *) 0, buffer_size: 0, halted: 0, }; #endif // Array of end points. Used for translating end point pointer to an // end point number static const void *usbs_at91_endpoints[AT91_USB_ENDPOINTS] = { (void *) &usbs_at91_ep0, (void *) &usbs_at91_ep1, (void *) &usbs_at91_ep2, (void *) &usbs_at91_ep3 #if (AT91_USB_ENDPOINTS > 4) ,(void *) &usbs_at91_ep4, (void *) &usbs_at91_ep5 #if (AT91_USB_ENDPOINTS > 6) ,(void *) &usbs_at91_ep6, (void *) &usbs_at91_ep7 #endif #endif }; // Convert an endpoint pointer to an endpoint number, using the array // of endpoint structures static int usbs_at91_pep_to_number(const usbs_rx_endpoint * pep) { int epn; for(epn=0; epn < AT91_USB_ENDPOINTS; epn++) { if (pep == usbs_at91_endpoints[epn]) return epn; } CYG_FAIL("Unknown endpoint"); return 0; } typedef enum ep0_low_level_status_t { EP0_LL_IDLE = 0, EP0_LL_REQUEST, EP0_LL_SEND_READY, EP0_LL_ACK, EP0_LL_RECEIVE_READY, EP0_LL_ISOERROR, EP0_LL_STALL, EP0_LL_SET_ADDRESS, } ep0_low_level_status_t; // Enable/Disable interrupts for a specific endpoint. static void usbs_at91_endpoint_interrupt_enable (cyg_uint8 epn, bool enable) { CYG_ASSERT (epn < AT91_USB_ENDPOINTS, "Invalid endpoint"); if (enable) { HAL_WRITE_UINT32 (pIER, 1 << epn); } else { HAL_WRITE_UINT32 (pIDR, 1 << epn); } } static cyg_uint8 * read_fifo_uint8 (cyg_uint8 * pdest, cyg_addrword_t psource, cyg_uint32 size) { cyg_uint8 *preqbyte = pdest; cyg_uint8 reqbyte; while (size--) { HAL_READ_UINT8 (psource, reqbyte); *preqbyte = reqbyte; preqbyte++; } return preqbyte; } static cyg_uint8 * write_fifo_uint8 (cyg_addrword_t pdest, cyg_uint8 * psource, cyg_uint8 * psource_end) { cyg_uint8 *preqbyte; for (preqbyte = psource; preqbyte < psource_end; preqbyte++) { HAL_WRITE_UINT8 (pdest, (*preqbyte)); } return preqbyte; } /* Tell the host that the device is ready to start communication */ static void usbs_at91_set_pullup (bool set) { #ifdef CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_INTERNAL cyg_uint32 txvc; HAL_READ_UINT32(AT91_UDP + AT91_UDP_TXVC, txvc); if (set) { txvc |= AT91_UDP_TXVC_PUON; } else { txvc &= ~AT91_UDP_TXVC_PUON; } HAL_WRITE_UINT32(AT91_UDP + AT91_UDP_TXVC, txvc); #endif // CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_INTERNAL #ifndef CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN_NONE if ( #ifdef CYGNUM_DEVS_USB_AT91_GPIO_SET_PULLUP_INVERTED !set #else set #endif ) { HAL_ARM_AT91_GPIO_SET(CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN); } else { HAL_ARM_AT91_GPIO_RESET(CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN); } #endif } /* Is the USB powered? */ bool usbs_at91_read_power (void) { #ifndef CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN_NONE cyg_bool state; HAL_ARM_AT91_GPIO_GET(CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN, state); #ifdef CYGNUM_DEVS_USB_AT91_GPIO_READ_POWER_INVERTED return !state; #else return state; #endif #endif return true; } // Stop all transfers that are currently active. static void usbs_end_all_transfers (usbs_control_return returncode) { cyg_uint32 epn; usbs_rx_endpoint *pep; for (epn = 1; epn < AT91_USB_ENDPOINTS; epn++) { if (BITS_ARE_SET (pIMR, 1 << epn)) { // If the end point is transmitting, call the complete function // to terminate to transfer pep = (usbs_rx_endpoint *) usbs_at91_endpoints[epn]; if (pep->complete_fn) { (*pep->complete_fn) (pep->complete_data, returncode); } // Disable interrupts from the endpoint usbs_at91_endpoint_interrupt_enable (epn, false); } } } // There has been a change in state. Update the end point. static void usbs_state_notify (usbs_control_endpoint * pcep) { static int old_state = USBS_STATE_CHANGE_POWERED; int state = pcep->state & USBS_STATE_MASK; if (pcep->state != old_state) { usbs_end_all_transfers (-EPIPE); switch (state) { case USBS_STATE_DETACHED: case USBS_STATE_ATTACHED: case USBS_STATE_POWERED: // Nothing to do break; case USBS_STATE_DEFAULT: HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_GLB_STATE, 0); break; case USBS_STATE_ADDRESSED: HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_GLB_STATE, AT91_UDP_GLB_FADDEN); break; case USBS_STATE_CONFIGURED: HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_GLB_STATE, AT91_UDP_GLB_CONFG); break; default: CYG_FAIL("Unknown endpoint state"); } if (pcep->state_change_fn) { (*pcep->state_change_fn) (pcep, pcep->state_change_data, pcep->state, old_state); } old_state = pcep->state; } } static usbs_control_return usbs_parse_host_get_command (usbs_control_endpoint * pcep) { usbs_control_return retcode; cyg_uint8 dev_req_type = (((usb_devreq *) pcep->control_buffer)->type) & USB_DEVREQ_TYPE_MASK; switch (dev_req_type) { case USB_DEVREQ_TYPE_STANDARD: if (!pcep->standard_control_fn) { return usbs_handle_standard_control (pcep); } retcode = (*pcep->standard_control_fn) (pcep, pcep->standard_control_data); if (retcode == USBS_CONTROL_RETURN_UNKNOWN) { return usbs_handle_standard_control (pcep); } return retcode; case USB_DEVREQ_TYPE_CLASS: if (!pcep->class_control_fn) { return USBS_CONTROL_RETURN_STALL; } return (*pcep->class_control_fn) (pcep, pcep->class_control_data); case USB_DEVREQ_TYPE_VENDOR: if (!pcep->class_control_fn) { return USBS_CONTROL_RETURN_STALL; } return (*pcep->class_control_fn) (pcep, pcep->vendor_control_data); case USB_DEVREQ_TYPE_RESERVED: if (!pcep->reserved_control_fn) { return USBS_CONTROL_RETURN_STALL; } return (*pcep->reserved_control_fn) (pcep, pcep->reserved_control_data); default: return USBS_CONTROL_RETURN_STALL; } } static void usbs_at91_endpoint_set_halted (usbs_rx_endpoint * pep, cyg_bool new_value) { int epn = usbs_at91_pep_to_number(pep); cyg_addrword_t pCSR = pCSRn(epn); cyg_uint32 reg; cyg_drv_dsr_lock (); if (pep->halted != new_value) { /* There is something is to do */ pep->halted = new_value; if ( new_value ) { /* Halt endpoint */ SET_BITS (pCSR, AT91_UDP_CSR_FORCESTALL); } else { /* Restart endpoint */ CLEAR_BITS (pCSR, AT91_UDP_CSR_FORCESTALL); // Reset Fifo HAL_READ_UINT32 (AT91_UDP + AT91_UDP_RST_EP, reg); reg |= (1 << epn); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, reg); reg &= ~(1 << epn); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, reg); // Ready to use if (pep->complete_fn) { (*pep->complete_fn) (pep->complete_data, ENOERR); } } } cyg_drv_dsr_unlock (); } void usbs_at91_endpoint_init (usbs_rx_endpoint * pep, cyg_uint8 endpoint_type, cyg_bool enable) { int epn = usbs_at91_pep_to_number(pep); cyg_addrword_t pCSR = pCSRn(epn); CYG_ASSERT (AT91_USB_ENDPOINTS > epn, "Invalid end point"); usbs_at91_endpoint_interrupt_enable (epn, false); /* Reset endpoint */ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 1 << epn); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 0); pep->halted = false; /* Type | In */ HAL_WRITE_UINT32 (pCSR, (((((cyg_uint32) endpoint_type) & 0x03) << 8) | ((((cyg_uint32) endpoint_type) & 0x80) << 3))); usbs_at91_endpoint_bytes_in_fifo[epn] = 0; usbs_at91_endpoint_bytes_received[epn] = THERE_IS_A_NEW_PACKET_IN_THE_UDP; usbs_at91_endpoint_bank1[epn] = false; if (enable) { SET_BITS (pCSR, AT91_UDP_CSR_EPEDS); } } static void usbs_at91_reset_device (void) { int epn; usbs_end_all_transfers (-EPIPE); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_IDR, 0xffffffff); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_ICR, 0xffffffff); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 0xffffffff); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 0x00000000); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_FADDR, AT91_UDP_FADDR_FEN); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_CSR0, AT91_UDP_CSR_EPEDS | AT91_UDP_CSR_EPTYPE_CTRL); HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_IER, AT91_UDP_ALLOWED_IRQs); for (epn=1; epn < AT91_USB_ENDPOINTS; epn++) { usbs_at91_endpoint_init ((usbs_rx_endpoint *)usbs_at91_endpoints[epn], 0, false); } } static void usbs_at91_handle_reset (void) { int epn; const usb_endpoint_descriptor *usb_endpoints; cyg_uint8 endpoint_type; cyg_uint8 endpoint_number; usbs_at91_reset_device (); // Now walk the endpoints configuring them correctly. This only // works if there is one interface. usb_endpoints = usbs_at91_ep0.enumeration_data->endpoints; for (epn = 1; epn <= usbs_at91_ep0.enumeration_data->total_number_endpoints; epn++) { endpoint_type = (usb_endpoints[epn-1].attributes | (usb_endpoints[epn-1].endpoint & USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN ? USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT)); endpoint_number = usb_endpoints[epn-1].endpoint & ~(USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN); if ( endpoint_number < AT91_USB_ENDPOINTS ) { usbs_at91_endpoint_init((usbs_rx_endpoint *)usbs_at91_endpoints[endpoint_number], endpoint_type, true); } } } static void usbs_at91_ep0_start (usbs_control_endpoint * endpoint) { usbs_at91_handle_reset (); // If there is additional platform-specific initialization to // perform, do it now. This macro can come from the platform HAL, // but may not be available on all platforms. #ifdef AT91_USB_PLATFORM_INIT AT91_USB_PLATFORM_INIT (); #endif usbs_at91_set_pullup (true); CLEAR_BITS(AT91_UDP + AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); } static void usbs_at91_endpoint_start (usbs_rx_endpoint * pep) { int epn = usbs_at91_pep_to_number(pep); cyg_addrword_t pCSR = pCSRn(epn); cyg_addrword_t pFDR = pFDRn(epn); cyg_uint16 space = 0; cyg_uint16 endpoint_size = usbs_at91_endpoint_fifo_size[epn]; cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn]; CYG_ASSERT (pep->complete_fn, "No complete_fn()"); cyg_drv_dsr_lock (); if (usbs_at91_ep0.state != USBS_STATE_CONFIGURED) { /* If not configured it means there is nothing to do */ cyg_drv_dsr_unlock (); if (pep->complete_fn) { (*pep->complete_fn) (pep->complete_data, -EPIPE); } return; } // Is this endpoint currently stalled? If so then a size of 0 can // be used to block until the stall condition is clear, anything // else should result in an immediate callback. if (pep->halted) { /* Halted means nothing to do */ cyg_drv_dsr_unlock (); if (pep->complete_fn && pep->buffer_size != 0) { (*pep->complete_fn) (pep->complete_data, -EAGAIN); } return; } if (BITS_ARE_SET (pIMR, 1 << epn)) { cyg_drv_dsr_unlock (); if (pep->complete_fn) { (*pep->complete_fn) (pep->complete_data, -EIO); } return; } CYG_ASSERT (BITS_ARE_SET (pCSR, 1 << 9), "Wrong endpoint type"); *ppbegin = pep->buffer; /* Set the working pointers */ *ppend = (cyg_uint8 *) ((cyg_uint32) pep->buffer + pep->buffer_size); if (BITS_ARE_SET (pCSR, 0x400)) { /* IN: tx_endpoint */ space = (cyg_uint32) * ppend - (cyg_uint32) * ppbegin; #ifdef CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION if (space == endpoint_size) { *ppend = *ppbegin; /* Send zero-packet */ } #endif *ppbegin = write_fifo_uint8 (pFDR, *ppbegin, (cyg_uint8 *) ((cyg_uint32) * ppbegin + MIN (space, endpoint_size))); SET_BITS (pCSR, AT91_UDP_CSR_TXPKTRDY); if (*ppend == *ppbegin) { /* Last packet ? */ *ppend = *ppbegin - 1; /* The packet hasn't been sent yet */ } } usbs_at91_endpoint_interrupt_enable (epn, true); cyg_drv_dsr_unlock (); } // Perform transmit handling on an endpoint static bool usbs_at91_endpoint_isr_tx(cyg_uint8 epn) { cyg_addrword_t pCSR = pCSRn(epn); cyg_addrword_t pFDR = pFDRn(epn); cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn]; cyg_uint32 space = 0; cyg_uint16 endpoint_size = usbs_at91_endpoint_fifo_size[epn]; CLEAR_BITS (pCSR, AT91_UDP_CSR_TXCOMP); if (BITS_ARE_CLEARED (pCSR, AT91_UDP_CSR_TXPKTRDY)) { /* Ready to transmit ? */ if (*ppend > *ppbegin) { /* Something to send */ space = (cyg_uint32) * ppend - (cyg_uint32) * ppbegin; #ifdef CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION if (space == endpoint_size) { *ppend = *ppbegin; /* Send zero-packet */ } #endif *ppbegin = write_fifo_uint8 (pFDR, *ppbegin, (cyg_uint8 *) ((cyg_uint32) * ppbegin + MIN (space, endpoint_size))); SET_BITS (pCSR, AT91_UDP_CSR_TXPKTRDY); if (*ppend == *ppbegin) { /* Last packet ? */ *ppend = *ppbegin - 1; /* The packet isn't sent yet */ } } else { #ifdef CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION if (*ppend + 1 == *ppbegin) { *ppend = *ppbegin; /* Flag for DSR */ return true; } else { *ppend = *ppbegin - 1; /* Flag for zero-packet */ SET_BITS (pCSR, AT91_UDP_CSR_TXPKTRDY); /* Send no data */ } #else *ppend = *ppbegin; /* Flag for DSR */ return true; #endif } } CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1 | AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_ISOERROR); return false; } static bool usbs_at91_endpoint_isr_rx(cyg_uint8 epn) { cyg_addrword_t pCSR = pCSRn(epn); cyg_addrword_t pFDR = pFDRn(epn); cyg_uint16 *pinfifo = &usbs_at91_endpoint_bytes_in_fifo[epn]; cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn]; cyg_uint16 *preceived = &usbs_at91_endpoint_bytes_received[epn]; cyg_uint16 endpoint_size = usbs_at91_endpoint_fifo_size[epn]; if (*preceived == THERE_IS_A_NEW_PACKET_IN_THE_UDP) { /* There is a new packet */ *preceived = ((*(cyg_uint32 *)pCSR) >> 16) & 0x7ff; *pinfifo = *preceived; } while ((*ppbegin < *ppend) && *pinfifo) { /* If we have buffer-space AND data in the FIFO */ HAL_READ_UINT8(pFDR, **ppbegin); (*ppbegin)++; (*pinfifo)--; } if (*ppbegin == *ppend) { /* The buffer is full... call the DSR */ return true; } if (*pinfifo == 0) { /* If the FIFO is empty, then we can release it */ if (usbs_at91_endpoint_pingpong[epn]) { /* Time to clear the interrupt flag */ if (usbs_at91_endpoint_bank1[epn]) { CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK1); } else { CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK0); } usbs_at91_endpoint_bank1[epn] = !usbs_at91_endpoint_bank1[epn]; } else { CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK0); } if (*preceived < endpoint_size) { /* If the last packet was smaller then the endpoint-size... */ *ppend = *ppbegin; *preceived = THERE_IS_A_NEW_PACKET_IN_THE_UDP; /* Set flag */ return true; /* We can call the completion-function in the DSR */ } *preceived = THERE_IS_A_NEW_PACKET_IN_THE_UDP; /* Set flag */ } return false; } // ISR for an endpoint. Handle receive and transmit interrupts. static bool usbs_at91_endpoint_isr (cyg_uint8 epn) { cyg_addrword_t pCSR = pCSRn(epn); CYG_ASSERT (AT91_USB_ENDPOINTS > epn && epn, "Invalid end point"); // Host has acknowledged the endpoint being stall if (BITS_ARE_SET (pCSR, AT91_UDP_CSR_ISOERROR)) { CLEAR_BITS (pCSR, AT91_UDP_CSR_ISOERROR); } if (BITS_ARE_SET (pCSR, 0x400)) { /* IN: tx_endpoint */ if (usbs_at91_endpoint_isr_tx(epn)) return true; // Call the DSR } else { /* OUT: rx_endpoint */ if (!BITS_ARE_CLEARED (pCSR, AT91_UDP_CSR_RX_DATA_BK0) || !BITS_ARE_CLEARED (pCSR, AT91_UDP_CSR_RX_DATA_BK1)) { /* Sometime something received ? */ if(usbs_at91_endpoint_isr_rx(epn)) return true; // Call the DSR; } CLEAR_BITS (pCSR, AT91_UDP_CSR_TXCOMP | AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_ISOERROR); } return false; } // Handle a DSR for an endpoint static void usbs_at91_endpoint_dsr (cyg_uint8 epn) { usbs_rx_endpoint *pep = (usbs_rx_endpoint *) usbs_at91_endpoints[epn]; cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn]; CYG_ASSERT (AT91_USB_ENDPOINTS > epn && epn, "Invalid end point"); CYG_ASSERT (pep->complete_fn, "No complete_fn()"); if (*ppend == *ppbegin) { /* Transmitted/Received ? */ pep->buffer_size = (cyg_uint32) * ppbegin - (cyg_uint32) pep->buffer; if (pep->complete_fn) { /* Do not check on pep->buffer_size != 0, user should * be allowed to send empty packet */ if (!pep->halted) { (*pep->complete_fn) (pep->complete_data, pep->buffer_size); } else { (*pep->complete_fn) (pep->complete_data, -EAGAIN); } } usbs_at91_endpoint_interrupt_enable (epn, false); } } // Handle an error condition on the control endpoint static ep0_low_level_status_t usbs_at91_control_error(ep0_low_level_status_t status) { cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0]; usbs_at91_ep0.buffer_size = 0; usbs_at91_ep0.fill_buffer_fn = 0; usbs_at91_ep0.complete_fn = 0; *ppbegin = usbs_at91_ep0.buffer; *ppend = *ppbegin; if (status == EP0_LL_IDLE) { if (usbs_at91_ep0.complete_fn) { (*usbs_at91_ep0.complete_fn) (&usbs_at91_ep0, USBS_CONTROL_RETURN_STALL); } } status = EP0_LL_IDLE; CLEAR_BITS (pCSR0, AT91_UDP_CSR_ISOERROR | AT91_UDP_CSR_FORCESTALL); return status; } // Handle a get status setup message on the control end point static ep0_low_level_status_t usbs_at91_control_setup_get_status(void) { ep0_low_level_status_t status; usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer; cyg_uint8 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK; cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0]; cyg_uint16 word = 0; status = EP0_LL_SEND_READY; switch (recipient) { case USB_DEVREQ_RECIPIENT_DEVICE: case USB_DEVREQ_RECIPIENT_INTERFACE: // Nothing to do break; case USB_DEVREQ_RECIPIENT_ENDPOINT: if ((usbs_at91_ep0.state == USBS_STATE_CONFIGURED) && (req->index_lo > 0) && (req->index_lo < AT91_USB_ENDPOINTS)) { cyg_uint32 CSR; HAL_READ_UINT32(pCSRn(req->index_lo), CSR); if (CSR & AT91_UDP_CSR_EPEDS) { word = 1; } } else { status = EP0_LL_STALL; } break; default: status = EP0_LL_STALL; } *ppbegin = (cyg_uint8 *)&word; *ppend = *ppbegin + sizeof (word); return status; } // Setup the begin and end pointers such that an ACK is sent static void usbs_at91_control_setup_send_ack(void) { cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0]; *ppbegin = usbs_at91_ep0.buffer; *ppend = *ppbegin; } // Handle a get status set feature message on the control endpoint static ep0_low_level_status_t usbs_at91_control_setup_set_feature(void) { ep0_low_level_status_t status; usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer; cyg_uint8 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK; usbs_rx_endpoint * pep; cyg_uint8 ep_num = req->index_lo & 0x0F; usbs_at91_control_setup_send_ack(); status = EP0_LL_SEND_READY; switch(recipient) { case USB_DEVREQ_RECIPIENT_DEVICE: status = EP0_LL_STALL; break; case USB_DEVREQ_RECIPIENT_INTERFACE: // Nothing to do break; case USB_DEVREQ_RECIPIENT_ENDPOINT: if ((usbs_at91_ep0.state == USBS_STATE_CONFIGURED) && (ep_num > 0) && (ep_num < AT91_USB_ENDPOINTS)) { cyg_uint32 CSR; HAL_READ_UINT32(pCSRn(ep_num), CSR); pep = (usbs_rx_endpoint *) usbs_at91_endpoints[ep_num]; if ( (CSR & AT91_UDP_CSR_EPEDS) ) { usbs_at91_endpoint_set_halted ( pep , true ); } else status = EP0_LL_STALL; } else { status = EP0_LL_STALL; } break; default: status = EP0_LL_STALL; } return status; } // Handle a get status clear feature message on the control endpoint static ep0_low_level_status_t usbs_at91_control_setup_clear_feature(void) { ep0_low_level_status_t status; usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer; cyg_uint8 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK; usbs_rx_endpoint * pep; cyg_uint8 ep_num = req->index_lo & 0x0F; usbs_at91_control_setup_send_ack(); status = EP0_LL_SEND_READY; switch (recipient) { case USB_DEVREQ_RECIPIENT_DEVICE: status = EP0_LL_STALL; break; case USB_DEVREQ_RECIPIENT_INTERFACE: // Nothing to do break; case USB_DEVREQ_RECIPIENT_ENDPOINT: if ((usbs_at91_ep0.state == USBS_STATE_CONFIGURED) && (ep_num > 0) && (ep_num < AT91_USB_ENDPOINTS)) { cyg_uint32 CSR; HAL_READ_UINT32(pCSRn(ep_num), CSR); pep = (usbs_rx_endpoint *) usbs_at91_endpoints[ep_num]; if ( (CSR & AT91_UDP_CSR_EPEDS) && pep->halted ) { usbs_at91_endpoint_set_halted ( pep , false ); } else status = EP0_LL_STALL; } else { status = EP0_LL_STALL; } break; default: status = EP0_LL_STALL; } return status; } // Handle a setup message from the host static ep0_low_level_status_t usbs_at91_control_setup(ep0_low_level_status_t status) { cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0]; usb_devreq *req = (usb_devreq *) usbs_at91_ep0.control_buffer; cyg_uint8 protocol; cyg_uint16 length; bool dev_to_host; usbs_control_return usbcode; bool handled = false; usbs_at91_ep0.buffer_size = 0; usbs_at91_ep0.fill_buffer_fn = 0; usbs_at91_ep0.complete_fn = 0; read_fifo_uint8 ((cyg_uint8 *)req, pFDR0, sizeof (usb_devreq)); length = (req->length_hi << 8) | req->length_lo;; dev_to_host = req->type & USB_DEVREQ_DIRECTION_IN; CLEAR_BITS (pCSR0, AT91_UDP_CSR_DTGLE); status = EP0_LL_REQUEST; protocol = req->type & (USB_DEVREQ_TYPE_MASK); // Set the next transfer direction if (dev_to_host) { SET_BITS (pCSR0, AT91_UDP_CSR_DIR); /* Set IN direction */ } else { CLEAR_BITS (pCSR0, AT91_UDP_CSR_DIR); /* Set OUT direction */ } if (protocol == USB_DEVREQ_TYPE_STANDARD) { handled = true; switch (req->request) { case USB_DEVREQ_GET_STATUS: status = usbs_at91_control_setup_get_status(); break; case USB_DEVREQ_SET_ADDRESS: // Most of the hard work is done by the hardware. We just need // to send an ACK. usbs_at91_control_setup_send_ack(); status = EP0_LL_SEND_READY; break; case USB_DEVREQ_SET_FEATURE: status = usbs_at91_control_setup_set_feature(); break; case USB_DEVREQ_CLEAR_FEATURE: status = usbs_at91_control_setup_clear_feature(); break; default: handled = false; } } if ((protocol != USB_DEVREQ_TYPE_STANDARD) || !handled) { // Ask the layer above to process the message usbcode = usbs_parse_host_get_command (&usbs_at91_ep0); usbs_at91_ep0.buffer_size = MIN (usbs_at91_ep0.buffer_size, length); *ppbegin = usbs_at91_ep0.buffer; *ppend = *ppbegin + usbs_at91_ep0.buffer_size; /* Ready to send... */ if (usbcode == USBS_CONTROL_RETURN_HANDLED) { /* OK */ if (dev_to_host) { status = EP0_LL_SEND_READY; } else { status = EP0_LL_RECEIVE_READY; } } else { status = EP0_LL_STALL; } } // Clear the setup bit so indicating we have processed the message CLEAR_BITS (pCSR0, AT91_UDP_CSR_RXSETUP); return status; } static ep0_low_level_status_t usbs_at91_control_data_recv(ep0_low_level_status_t status) { cyg_uint32 received = 0; cyg_uint32 length; cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0]; usbs_control_return usbcode; if (status == EP0_LL_RECEIVE_READY) { received = ((*(cyg_uint32 *) pCSR0) >> 16) & 0x7ff; length = MIN (received, (cyg_uint32) *ppend - (cyg_uint32) *ppbegin); *ppbegin = read_fifo_uint8 (*ppbegin, pFDR0, length); if (received < usbs_at91_endpoint_fifo_size[0]) { /* Last packet ? */ *ppend = *ppbegin; } if (*ppbegin == *ppend) { /* All received ? */ usbs_at91_ep0.buffer_size = (cyg_uint32) *ppend - (cyg_uint32) usbs_at91_ep0.buffer; usbcode = USBS_CONTROL_RETURN_STALL; if (usbs_at91_ep0.complete_fn) { usbcode = (*usbs_at91_ep0.complete_fn) (&usbs_at91_ep0, 0); } if (usbcode == USBS_CONTROL_RETURN_HANDLED) { status = EP0_LL_SEND_READY; } else { status = EP0_LL_STALL; } } } CLEAR_BITS (pCSR0, AT91_UDP_CSR_RX_DATA_BK0); return status; } static ep0_low_level_status_t usbs_at91_control_data_sent(ep0_low_level_status_t status) { cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0]; cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0]; cyg_uint32 bytes_to_write = 0; usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer; cyg_uint16 value; switch (status) { case EP0_LL_SEND_READY: if (*ppbegin == *ppend && usbs_at91_ep0.fill_buffer_fn == NULL) { // All bytes are sent, send ACK status = EP0_LL_ACK; SET_BITS (pCSR0, AT91_UDP_CSR_TXPKTRDY); // Signal FIFO loaded } else { // We have more bytes to send bytes_to_write = MIN (*ppend - *ppbegin, usbs_at91_endpoint_fifo_size[0]); *ppbegin = write_fifo_uint8 (pFDR0, *ppbegin, (cyg_uint8 *) ((cyg_uint32) *ppbegin + bytes_to_write)); // Send next few bytes if (*ppbegin == *ppend) { /* Control-Endoints don't need ACK's */ if (usbs_at91_ep0.fill_buffer_fn) { // More Records ? (*usbs_at91_ep0.fill_buffer_fn) (&usbs_at91_ep0); *ppbegin = usbs_at91_ep0.buffer; *ppend = *ppbegin + usbs_at91_ep0.buffer_size; /* Ready to send... */ bytes_to_write = MIN (*ppend - *ppbegin, usbs_at91_endpoint_fifo_size[0] - bytes_to_write); *ppbegin = write_fifo_uint8 (pFDR0, *ppbegin, (cyg_uint8 *) ((cyg_uint32) *ppbegin + bytes_to_write)); // Send next few bytes } else { if (bytes_to_write == usbs_at91_endpoint_fifo_size[0]) { // Last packet is full, so we need to send a zero bytes // packet next time status = EP0_LL_SEND_READY; } else { status = EP0_LL_IDLE; } } } SET_BITS (pCSR0, AT91_UDP_CSR_TXPKTRDY); // Signal FIFO loaded } break; case EP0_LL_RECEIVE_READY: /* Maybe we have to send an ACK */ if (*ppbegin == *ppend) { // All bytes are received, send ACK status = EP0_LL_ACK; SET_BITS (pCSR0, AT91_UDP_CSR_TXPKTRDY); // Signal FIFO loaded } break; case EP0_LL_ACK: if (req->request == USB_DEVREQ_SET_ADDRESS) { // Special-processing HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_FADDR, req->value_lo | AT91_UDP_FADDR_FEN); value = (req->value_hi << 8) | req->value_lo; if (value) { usbs_at91_ep0.state = USBS_STATE_ADDRESSED; } } if (usbs_at91_ep0.complete_fn) { (*usbs_at91_ep0.complete_fn) (&usbs_at91_ep0, USBS_CONTROL_RETURN_HANDLED); } status = EP0_LL_IDLE; usbs_state_notify (&usbs_at91_ep0); break; default: break; } return status; } static void usbs_at91_control_dsr (void) { static ep0_low_level_status_t status = EP0_LL_IDLE; while (!BITS_ARE_CLEARED(pCSR0, AT91_UDP_CSR_TXCOMP | AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_ISOERROR | AT91_UDP_CSR_RX_DATA_BK1)) { // Check and handle any error conditions if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_ISOERROR)) { status = usbs_at91_control_error(status); } // Check for a setup message and handle it if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_RXSETUP)) { status = usbs_at91_control_setup(status); } // Check for received data on the control endpoint if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_RX_DATA_BK0)) { status = usbs_at91_control_data_recv(status); } // Check if the last packet has been sent if (BITS_ARE_CLEARED (pCSR0, AT91_UDP_CSR_TXPKTRDY)) { status = usbs_at91_control_data_sent(status); } // Received an ACK packet if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_TXCOMP)) { CLEAR_BITS (pCSR0, AT91_UDP_CSR_TXCOMP); } if (status == EP0_LL_STALL) { CLEAR_BITS (pCSR0, 0x7f); SET_BITS (pCSR0, AT91_UDP_CSR_FORCESTALL); } } } static void usbs_at91_dsr (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { cyg_uint8 n; CYG_ASSERT (CYGNUM_HAL_INTERRUPT_UDP == vector, "Wrong interrupts"); CYG_ASSERT (0 == data, "DSR needs no data"); CLEAR_BITS (AT91_UDP + AT91_UDP_GLB_STATE, 0x10); if (BITS_ARE_SET (pISR, AT91_UDP_WAKEUP)) { usbs_at91_ep0.state = USBS_STATE_DEFAULT; usbs_state_notify (&usbs_at91_ep0); HAL_WRITE_UINT32 (pICR, AT91_UDP_WAKEUP); } if (BITS_ARE_SET (pISR, AT91_UDP_ENDBUSRES)) { // RESET UDP usbs_at91_ep0.state = USBS_STATE_POWERED; usbs_state_notify (&usbs_at91_ep0); usbs_at91_handle_reset (); HAL_WRITE_UINT32 (pCSR0, AT91_UDP_CSR_EPEDS | AT91_UDP_CSR_EPTYPE_CTRL); HAL_WRITE_UINT32 (pIER, AT91_UDP_EPINT0); usbs_at91_ep0.state = USBS_STATE_DEFAULT; usbs_state_notify (&usbs_at91_ep0); HAL_WRITE_UINT32 (pICR, AT91_UDP_ENDBUSRES); } if (BITS_ARE_SET (pISR, AT91_UDP_SOFINT)) { HAL_WRITE_UINT32 (pICR, AT91_UDP_SOFINT); } if (BITS_ARE_SET (pISR, AT91_UDP_EXTRSM)) { usbs_at91_ep0.state = usbs_at91_ep0.state & ~USBS_STATE_SUSPENDED; usbs_state_notify (&usbs_at91_ep0); HAL_WRITE_UINT32 (pICR, AT91_UDP_EXTRSM); } if (BITS_ARE_SET (pISR, AT91_UDP_RXRSM)) { usbs_at91_ep0.state = usbs_at91_ep0.state & ~USBS_STATE_SUSPENDED; usbs_state_notify (&usbs_at91_ep0); HAL_WRITE_UINT32 (pICR, AT91_UDP_RXRSM); } if (BITS_ARE_SET (pISR, AT91_UDP_RXSUSP)) { usbs_at91_ep0.state = usbs_at91_ep0.state | USBS_STATE_SUSPENDED; usbs_state_notify (&usbs_at91_ep0); HAL_WRITE_UINT32 (pICR, AT91_UDP_RXSUSP); } if (BITS_ARE_SET (pISR, AT91_UDP_EPINT0)) { usbs_at91_control_dsr (); } for (n = 1; n < AT91_USB_ENDPOINTS; n++) { if (*(cyg_uint32 *) pIMR & (1 << n)) { usbs_at91_endpoint_dsr (n); } } cyg_drv_interrupt_unmask (vector); } static cyg_uint32 usbs_at91_isr (cyg_vector_t vector, cyg_addrword_t data) { cyg_uint8 n; bool need_dsr = false; cyg_uint32 IMR; cyg_uint32 ISR; CYG_ASSERT (CYGNUM_HAL_INTERRUPT_UDP == vector, "Wrong interrupts"); CYG_ASSERT (0 == data, "ISR needs no data"); HAL_READ_UINT32(pIMR, IMR); HAL_READ_UINT32(pISR, ISR); for (n = 1; n < AT91_USB_ENDPOINTS; n++) { /* Do any data endpoint need a data transfer ? */ if (IMR & ISR & (1 << n)) { need_dsr = usbs_at91_endpoint_isr (n) || need_dsr; } } /* If we don't need any DSR re-enable interrupts and finish */ if (BITS_ARE_CLEARED (pISR, AT91_UDP_ALLOWED_IRQs & 0xffffff01) && !need_dsr) { cyg_drv_interrupt_acknowledge (vector); return CYG_ISR_HANDLED; } /* Call the DSR */ cyg_drv_interrupt_mask (vector); cyg_drv_interrupt_acknowledge (vector); return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; } // ---------------------------------------------------------------------------- // Polling support. It is not clear that this is going to work particularly // well since according to the documentation the hardware does not generate // NAKs automatically - instead the ISR has to set the appropriate bits // sufficiently quickly to avoid confusing the host. // // Calling the isr directly avoids duplicating code, but means that // cyg_drv_interrupt_acknowledge() will get called when not inside a // real interrupt handler. This should be harmless. static void usbs_at91_poll (usbs_control_endpoint * endpoint) { CYG_ASSERT (endpoint == &usbs_at91_ep0, "Wrong endpoint"); if (CYG_ISR_CALL_DSR == usbs_at91_isr (CYGNUM_HAL_INTERRUPT_UDP, 0)) { usbs_at91_dsr (CYGNUM_HAL_INTERRUPT_UDP, 0, 0); } } // ---------------------------------------------------------------------------- // Initialization // // This routine gets called from a prioritized static constructor during // eCos startup. void usbs_at91_init (void) { cyg_uint32 reg; HAL_READ_UINT32 (AT91_PMC + AT91_PMC_PLLR, reg); /* Set USB divider so we have a 48MHz clock */ #if ((CYGNUM_HAL_ARM_AT91_CLOCK_SPEED < 48120000) && \ (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED > 47880000)) // 48MHz clock, divider set to 1 HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_PLLR, (reg & 0x0fffffff) | AT91_PMC_PLLR_USBDIV_1); #elif ((CYGNUM_HAL_ARM_AT91_CLOCK_SPEED < 96240000) && \ (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED > 95760000)) // 96MHz clock, divider set to 2 HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_PLLR, (reg & 0x0fffffff) | AT91_PMC_PLLR_USBDIV_2); #else #error CYGNUM_HAL_ARM_AT91_CLOCK_SPEED is not 48, 96 or 192MHz plusminus 0.25% ... #endif /* Enable USB clock */ HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_SCER, AT91_PMC_SCER_UDP); HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_UDP); usbs_at91_set_pullup (false); #ifndef CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN_NONE HAL_ARM_AT91_GPIO_CFG_DIRECTION(CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN, AT91_PIN_OUT); #endif #ifndef CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN_NONE HAL_ARM_AT91_GPIO_CFG_DIRECTION(CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN, AT91_PIN_IN); #endif usbs_at91_reset_device (); cyg_drv_interrupt_create (CYGNUM_HAL_INTERRUPT_UDP, 6, // priority 0, // data &usbs_at91_isr, &usbs_at91_dsr, &usbs_at91_intr_handle, &usbs_at91_intr_data); cyg_drv_interrupt_attach (usbs_at91_intr_handle); cyg_drv_interrupt_unmask (CYGNUM_HAL_INTERRUPT_UDP); usbs_at91_ep0.state = USBS_STATE_POWERED; usbs_state_notify (&usbs_at91_ep0); } // ---------------------------------------------------------------------------- // Testing support. usbs_testing_endpoint usbs_testing_endpoints[] = { { endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL, endpoint_number : 0, endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN, endpoint : (void*) &usbs_at91_ep0, #ifdef CYGVAR_DEVS_USB_AT91_EP0_DEVTAB_ENTRY devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "0c", #else devtab_entry : (const char*) 0, #endif min_size : 1, // zero-byte control transfers are meaningless max_size : 0x0FFFF, // limit imposed by protocol max_in_padding : 0, alignment : 0 }, { endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK, endpoint_number : 1, endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT, endpoint : (void*) &usbs_at91_ep1, #ifdef CYGVAR_DEVS_USB_AT91_EP1_DEVTAB_ENTRY devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "1r", #else devtab_entry : (const char*) 0, #endif min_size : 1, max_size : -1, // No hardware or driver limitation max_in_padding : 0, alignment : 0 }, { endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK, endpoint_number : 2, endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN, endpoint : (void*) &usbs_at91_ep2, #ifdef CYGVAR_DEVS_USB_AT91_EP2_DEVTAB_ENTRY devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "2w", #else devtab_entry : (const char*) 0, #endif min_size : 1, max_size : -1, // No hardware or driver limitation max_in_padding : 1, // hardware limitation alignment : 0 }, { endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK, endpoint_number : 3, endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN, endpoint : (void*) &usbs_at91_ep3, #ifdef CYGVAR_DEVS_USB_AT91_EP3_DEVTAB_ENTRY devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "3w", #else devtab_entry : (const char*) 0, #endif min_size : 1, max_size : -1, // No hardware or driver limitation max_in_padding : 1, // hardware limitation alignment : 0 }, USBS_TESTING_ENDPOINTS_TERMINATOR };