📄 usbs_at91.c
字号:
//==========================================================================
//
// usbs_at91.c
//
// Driver for the AT91 USB device
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2006 eCosCentric
// Copyright (C) 2006 Andrew Lunn
//
// 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.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
//
// 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
// 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 <pkgconf/devs_usb_at91.h>
#include <cyg/io/usb/usb.h>
#include <cyg/io/usb/usbs.h>
#include <cyg/io/usb/usbs_at91.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_platform_ints.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#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))
#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)
#define THERE_IS_A_NEW_PACKET_IN_THE_UDP 0xffff
// Fifo size for each end point.
static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = {
8,
64,
64,
64,
};
// Does an endpoint support ping pong buffering?
static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = {
false,
true,
true,
false
};
static cyg_uint8 *usbs_at91_endpoint_pbegin[AT91_USB_ENDPOINTS] =
{ 0, 0, 0, 0 };
static cyg_uint8 *usbs_at91_endpoint_pend[AT91_USB_ENDPOINTS] =
{ 0, 0 ,0, 0 };
static bool usbs_at91_endpoint_bank1[AT91_USB_ENDPOINTS] =
{ false, false, false, false };
static cyg_uint16 usbs_at91_endpoint_bytes_in_fifo[AT91_USB_ENDPOINTS] =
{ 0, 0, 0, 0 };
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,
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,
};
// 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
};
// 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)
{
#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, 0, 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_drv_dsr_lock ();
if (pep->halted != new_value) {
/* There is something is to do */
pep->halted = new_value;
if (new_value && BITS_ARE_SET (pIMR, 1 << epn)) {
/* Ready to transmit */
if (pep->complete_fn) {
(*pep->complete_fn) (pep->complete_data, -EAGAIN);
}
usbs_at91_endpoint_interrupt_enable (epn, false);
SET_BITS (pCSR, AT91_UDP_CSR_FORCESTALL);
} else {
CLEAR_BITS (pCSR, AT91_UDP_CSR_FORCESTALL);
}
}
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_handle_reset (void)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -