2005-04-16 15:20:36 -07:00
/*
* BRIEF MODULE DESCRIPTION
* Au1000 USB Device - Side ( device layer )
*
* Copyright 2001 - 2002 MontaVista Software Inc .
* Author : MontaVista Software , Inc .
* stevel @ mvista . com or source @ mvista . com
*
* This program 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 of the License , or ( at your
* option ) any later version .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR 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 .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/ioport.h>
# include <linux/sched.h>
# include <linux/signal.h>
# include <linux/errno.h>
# include <linux/poll.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/fcntl.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/list.h>
# include <linux/smp_lock.h>
# define DEBUG
# include <linux/usb.h>
# include <asm/io.h>
# include <asm/uaccess.h>
# include <asm/irq.h>
# include <asm/mipsregs.h>
# include <asm/au1000.h>
# include <asm/au1000_dma.h>
# include <asm/au1000_usbdev.h>
# ifdef DEBUG
# undef VDEBUG
# ifdef VDEBUG
# define vdbg(fmt, arg...) printk(KERN_DEBUG __FILE__ ": " fmt "\n" , ## arg)
# else
# define vdbg(fmt, arg...) do {} while (0)
# endif
# else
# define vdbg(fmt, arg...) do {} while (0)
# endif
# define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL)
# define EP_FIFO_DEPTH 8
typedef enum {
SETUP_STAGE = 0 ,
DATA_STAGE ,
STATUS_STAGE
} ep0_stage_t ;
typedef struct {
int read_fifo ;
int write_fifo ;
int ctrl_stat ;
int read_fifo_status ;
int write_fifo_status ;
} endpoint_reg_t ;
typedef struct {
usbdev_pkt_t * head ;
usbdev_pkt_t * tail ;
int count ;
} pkt_list_t ;
typedef struct {
int active ;
struct usb_endpoint_descriptor * desc ;
endpoint_reg_t * reg ;
/* Only one of these are used, unless this is the control ep */
pkt_list_t inlist ;
pkt_list_t outlist ;
unsigned int indma , outdma ; /* DMA channel numbers for IN, OUT */
/* following are extracted from endpoint descriptor for easy access */
int max_pkt_size ;
int type ;
int direction ;
/* WE assign endpoint addresses! */
int address ;
spinlock_t lock ;
} endpoint_t ;
static struct usb_dev {
endpoint_t ep [ 6 ] ;
ep0_stage_t ep0_stage ;
struct usb_device_descriptor * dev_desc ;
struct usb_interface_descriptor * if_desc ;
struct usb_config_descriptor * conf_desc ;
u8 * full_conf_desc ;
struct usb_string_descriptor * str_desc [ 6 ] ;
/* callback to function layer */
void ( * func_cb ) ( usbdev_cb_type_t type , unsigned long arg ,
void * cb_data ) ;
void * cb_data ;
usbdev_state_t state ; // device state
int suspended ; // suspended flag
int address ; // device address
int interface ;
int num_ep ;
u8 alternate_setting ;
u8 configuration ; // configuration value
int remote_wakeup_en ;
} usbdev ;
static endpoint_reg_t ep_reg [ ] = {
// FIFO's 0 and 1 are EP0 default control
{ USBD_EP0RD , USBD_EP0WR , USBD_EP0CS , USBD_EP0RDSTAT , USBD_EP0WRSTAT } ,
{ 0 } ,
// FIFO 2 is EP2, IN
{ - 1 , USBD_EP2WR , USBD_EP2CS , - 1 , USBD_EP2WRSTAT } ,
// FIFO 3 is EP3, IN
{ - 1 , USBD_EP3WR , USBD_EP3CS , - 1 , USBD_EP3WRSTAT } ,
// FIFO 4 is EP4, OUT
{ USBD_EP4RD , - 1 , USBD_EP4CS , USBD_EP4RDSTAT , - 1 } ,
// FIFO 5 is EP5, OUT
{ USBD_EP5RD , - 1 , USBD_EP5CS , USBD_EP5RDSTAT , - 1 }
} ;
static struct {
unsigned int id ;
const char * str ;
} ep_dma_id [ ] = {
{ DMA_ID_USBDEV_EP0_TX , " USBDev EP0 IN " } ,
{ DMA_ID_USBDEV_EP0_RX , " USBDev EP0 OUT " } ,
{ DMA_ID_USBDEV_EP2_TX , " USBDev EP2 IN " } ,
{ DMA_ID_USBDEV_EP3_TX , " USBDev EP3 IN " } ,
{ DMA_ID_USBDEV_EP4_RX , " USBDev EP4 OUT " } ,
{ DMA_ID_USBDEV_EP5_RX , " USBDev EP5 OUT " }
} ;
# define DIR_OUT 0
# define DIR_IN (1<<3)
# define CONTROL_EP USB_ENDPOINT_XFER_CONTROL
# define BULK_EP USB_ENDPOINT_XFER_BULK
static inline endpoint_t *
epaddr_to_ep ( struct usb_dev * dev , int ep_addr )
{
if ( ep_addr > = 0 & & ep_addr < 2 )
return & dev - > ep [ 0 ] ;
if ( ep_addr < 6 )
return & dev - > ep [ ep_addr ] ;
return NULL ;
}
static const char * std_req_name [ ] = {
" GET_STATUS " ,
" CLEAR_FEATURE " ,
" RESERVED " ,
" SET_FEATURE " ,
" RESERVED " ,
" SET_ADDRESS " ,
" GET_DESCRIPTOR " ,
" SET_DESCRIPTOR " ,
" GET_CONFIGURATION " ,
" SET_CONFIGURATION " ,
" GET_INTERFACE " ,
" SET_INTERFACE " ,
" SYNCH_FRAME "
} ;
static inline const char *
get_std_req_name ( int req )
{
return ( req > = 0 & & req < = 12 ) ? std_req_name [ req ] : " UNKNOWN " ;
}
#if 0
static void
dump_setup ( struct usb_ctrlrequest * s )
{
dbg ( " %s: requesttype=%d " , __FUNCTION__ , s - > requesttype ) ;
dbg ( " %s: request=%d %s " , __FUNCTION__ , s - > request ,
get_std_req_name ( s - > request ) ) ;
dbg ( " %s: value=0x%04x " , __FUNCTION__ , s - > wValue ) ;
dbg ( " %s: index=%d " , __FUNCTION__ , s - > index ) ;
dbg ( " %s: length=%d " , __FUNCTION__ , s - > length ) ;
}
# endif
static inline usbdev_pkt_t *
alloc_packet ( endpoint_t * ep , int data_size , void * data )
{
usbdev_pkt_t * pkt = kmalloc ( sizeof ( usbdev_pkt_t ) + data_size ,
ALLOC_FLAGS ) ;
if ( ! pkt )
return NULL ;
pkt - > ep_addr = ep - > address ;
pkt - > size = data_size ;
pkt - > status = 0 ;
pkt - > next = NULL ;
if ( data )
memcpy ( pkt - > payload , data , data_size ) ;
return pkt ;
}
/*
* Link a packet to the tail of the enpoint ' s packet list .
* EP spinlock must be held when calling .
*/
static void
link_tail ( endpoint_t * ep , pkt_list_t * list , usbdev_pkt_t * pkt )
{
if ( ! list - > tail ) {
list - > head = list - > tail = pkt ;
list - > count = 1 ;
} else {
list - > tail - > next = pkt ;
list - > tail = pkt ;
list - > count + + ;
}
}
/*
* Unlink and return a packet from the head of the given packet
* list . It is the responsibility of the caller to free the packet .
* EP spinlock must be held when calling .
*/
static usbdev_pkt_t *
unlink_head ( pkt_list_t * list )
{
usbdev_pkt_t * pkt ;
pkt = list - > head ;
if ( ! pkt | | ! list - > count ) {
return NULL ;
}
list - > head = pkt - > next ;
if ( ! list - > head ) {
list - > head = list - > tail = NULL ;
list - > count = 0 ;
} else
list - > count - - ;
return pkt ;
}
/*
* Create and attach a new packet to the tail of the enpoint ' s
* packet list . EP spinlock must be held when calling .
*/
static usbdev_pkt_t *
add_packet ( endpoint_t * ep , pkt_list_t * list , int size )
{
usbdev_pkt_t * pkt = alloc_packet ( ep , size , NULL ) ;
if ( ! pkt )
return NULL ;
link_tail ( ep , list , pkt ) ;
return pkt ;
}
/*
* Unlink and free a packet from the head of the enpoint ' s
* packet list . EP spinlock must be held when calling .
*/
static inline void
free_packet ( pkt_list_t * list )
{
kfree ( unlink_head ( list ) ) ;
}
/* EP spinlock must be held when calling. */
static inline void
flush_pkt_list ( pkt_list_t * list )
{
while ( list - > count )
free_packet ( list ) ;
}
/* EP spinlock must be held when calling */
static inline void
flush_write_fifo ( endpoint_t * ep )
{
if ( ep - > reg - > write_fifo_status > = 0 ) {
au_writel ( USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF |
USBDEV_FSTAT_OF ,
ep - > reg - > write_fifo_status ) ;
//udelay(100);
//au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF,
// ep->reg->write_fifo_status);
}
}
/* EP spinlock must be held when calling */
static inline void
flush_read_fifo ( endpoint_t * ep )
{
if ( ep - > reg - > read_fifo_status > = 0 ) {
au_writel ( USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF |
USBDEV_FSTAT_OF ,
ep - > reg - > read_fifo_status ) ;
//udelay(100);
//au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF,
// ep->reg->read_fifo_status);
}
}
/* EP spinlock must be held when calling. */
static void
endpoint_flush ( endpoint_t * ep )
{
// First, flush all packets
flush_pkt_list ( & ep - > inlist ) ;
flush_pkt_list ( & ep - > outlist ) ;
// Now flush the endpoint's h/w FIFO(s)
flush_write_fifo ( ep ) ;
flush_read_fifo ( ep ) ;
}
/* EP spinlock must be held when calling. */
static void
endpoint_stall ( endpoint_t * ep )
{
u32 cs ;
2005-11-13 16:07:26 -08:00
warn ( " %s " , __FUNCTION__ ) ;
2005-04-16 15:20:36 -07:00
cs = au_readl ( ep - > reg - > ctrl_stat ) | USBDEV_CS_STALL ;
au_writel ( cs , ep - > reg - > ctrl_stat ) ;
}
/* EP spinlock must be held when calling. */
static void
endpoint_unstall ( endpoint_t * ep )
{
u32 cs ;
2005-11-13 16:07:26 -08:00
warn ( " %s " , __FUNCTION__ ) ;
2005-04-16 15:20:36 -07:00
cs = au_readl ( ep - > reg - > ctrl_stat ) & ~ USBDEV_CS_STALL ;
au_writel ( cs , ep - > reg - > ctrl_stat ) ;
}
static void
endpoint_reset_datatoggle ( endpoint_t * ep )
{
// FIXME: is this possible?
}
/* EP spinlock must be held when calling. */
static int
endpoint_fifo_read ( endpoint_t * ep )
{
int read_count = 0 ;
u8 * bufptr ;
usbdev_pkt_t * pkt = ep - > outlist . tail ;
if ( ! pkt )
return - EINVAL ;
bufptr = & pkt - > payload [ pkt - > size ] ;
while ( au_readl ( ep - > reg - > read_fifo_status ) & USBDEV_FSTAT_FCNT_MASK ) {
* bufptr + + = au_readl ( ep - > reg - > read_fifo ) & 0xff ;
read_count + + ;
pkt - > size + + ;
}
return read_count ;
}
#if 0
/* EP spinlock must be held when calling. */
static int
endpoint_fifo_write ( endpoint_t * ep , int index )
{
int write_count = 0 ;
u8 * bufptr ;
usbdev_pkt_t * pkt = ep - > inlist . head ;
if ( ! pkt )
return - EINVAL ;
bufptr = & pkt - > payload [ index ] ;
while ( ( au_readl ( ep - > reg - > write_fifo_status ) &
USBDEV_FSTAT_FCNT_MASK ) < EP_FIFO_DEPTH ) {
if ( bufptr < pkt - > payload + pkt - > size ) {
au_writel ( * bufptr + + , ep - > reg - > write_fifo ) ;
write_count + + ;
} else {
break ;
}
}
return write_count ;
}
# endif
/*
* This routine is called to restart transmission of a packet .
* The endpoint ' s TSIZE must be set to the new packet ' s size ,
* and DMA to the write FIFO needs to be restarted .
* EP spinlock must be held when calling .
*/
static void
kickstart_send_packet ( endpoint_t * ep )
{
u32 cs ;
usbdev_pkt_t * pkt = ep - > inlist . head ;
vdbg ( " %s: ep%d, pkt=%p " , __FUNCTION__ , ep - > address , pkt ) ;
if ( ! pkt ) {
err ( " %s: head=NULL! list->count=%d " , __FUNCTION__ ,
ep - > inlist . count ) ;
return ;
}
dma_cache_wback_inv ( ( unsigned long ) pkt - > payload , pkt - > size ) ;
/*
* make sure FIFO is empty
*/
flush_write_fifo ( ep ) ;
cs = au_readl ( ep - > reg - > ctrl_stat ) & USBDEV_CS_STALL ;
cs | = ( pkt - > size < < USBDEV_CS_TSIZE_BIT ) ;
au_writel ( cs , ep - > reg - > ctrl_stat ) ;
if ( get_dma_active_buffer ( ep - > indma ) = = 1 ) {
set_dma_count1 ( ep - > indma , pkt - > size ) ;
set_dma_addr1 ( ep - > indma , virt_to_phys ( pkt - > payload ) ) ;
enable_dma_buffer1 ( ep - > indma ) ; // reenable
} else {
set_dma_count0 ( ep - > indma , pkt - > size ) ;
set_dma_addr0 ( ep - > indma , virt_to_phys ( pkt - > payload ) ) ;
enable_dma_buffer0 ( ep - > indma ) ; // reenable
}
if ( dma_halted ( ep - > indma ) )
start_dma ( ep - > indma ) ;
}
/*
* This routine is called when a packet in the inlist has been
* completed . Frees the completed packet and starts sending the
* next . EP spinlock must be held when calling .
*/
static usbdev_pkt_t *
send_packet_complete ( endpoint_t * ep )
{
usbdev_pkt_t * pkt = unlink_head ( & ep - > inlist ) ;
if ( pkt ) {
pkt - > status =
( au_readl ( ep - > reg - > ctrl_stat ) & USBDEV_CS_NAK ) ?
PKT_STATUS_NAK : PKT_STATUS_ACK ;
vdbg ( " %s: ep%d, %s pkt=%p, list count=%d " , __FUNCTION__ ,
ep - > address , ( pkt - > status & PKT_STATUS_NAK ) ?
" NAK " : " ACK " , pkt , ep - > inlist . count ) ;
}
/*
* The write fifo should already be drained if things are
* working right , but flush it anyway just in case .
*/
flush_write_fifo ( ep ) ;
// begin transmitting next packet in the inlist
if ( ep - > inlist . count ) {
kickstart_send_packet ( ep ) ;
}
return pkt ;
}
/*
* Add a new packet to the tail of the given ep ' s packet
* inlist . The transmit complete interrupt frees packets from
* the head of this list . EP spinlock must be held when calling .
*/
static int
send_packet ( struct usb_dev * dev , usbdev_pkt_t * pkt , int async )
{
pkt_list_t * list ;
endpoint_t * ep ;
if ( ! pkt | | ! ( ep = epaddr_to_ep ( dev , pkt - > ep_addr ) ) )
return - EINVAL ;
if ( ! pkt - > size )
return 0 ;
list = & ep - > inlist ;
if ( ! async & & list - > count ) {
halt_dma ( ep - > indma ) ;
flush_pkt_list ( list ) ;
}
link_tail ( ep , list , pkt ) ;
vdbg ( " %s: ep%d, pkt=%p, size=%d, list count=%d " , __FUNCTION__ ,
ep - > address , pkt , pkt - > size , list - > count ) ;
if ( list - > count = = 1 ) {
/*
* if the packet count is one , it means the list was empty ,
* and no more data will go out this ep until we kick - start
* it again .
*/
kickstart_send_packet ( ep ) ;
}
return pkt - > size ;
}
/*
* This routine is called to restart reception of a packet .
* EP spinlock must be held when calling .
*/
static void
kickstart_receive_packet ( endpoint_t * ep )
{
usbdev_pkt_t * pkt ;
// get and link a new packet for next reception
if ( ! ( pkt = add_packet ( ep , & ep - > outlist , ep - > max_pkt_size ) ) ) {
err ( " %s: could not alloc new packet " , __FUNCTION__ ) ;
return ;
}
if ( get_dma_active_buffer ( ep - > outdma ) = = 1 ) {
clear_dma_done1 ( ep - > outdma ) ;
set_dma_count1 ( ep - > outdma , ep - > max_pkt_size ) ;
set_dma_count0 ( ep - > outdma , 0 ) ;
set_dma_addr1 ( ep - > outdma , virt_to_phys ( pkt - > payload ) ) ;
enable_dma_buffer1 ( ep - > outdma ) ; // reenable
} else {
clear_dma_done0 ( ep - > outdma ) ;
set_dma_count0 ( ep - > outdma , ep - > max_pkt_size ) ;
set_dma_count1 ( ep - > outdma , 0 ) ;
set_dma_addr0 ( ep - > outdma , virt_to_phys ( pkt - > payload ) ) ;
enable_dma_buffer0 ( ep - > outdma ) ; // reenable
}
if ( dma_halted ( ep - > outdma ) )
start_dma ( ep - > outdma ) ;
}
/*
* This routine is called when a packet in the outlist has been
* completed ( received ) and we need to prepare for a new packet
* to be received . Halts DMA and computes the packet size from the
* remaining DMA counter . Then prepares a new packet for reception
* and restarts DMA . FIXME : what if another packet comes in
* on top of the completed packet ? Counter would be wrong .
* EP spinlock must be held when calling .
*/
static usbdev_pkt_t *
receive_packet_complete ( endpoint_t * ep )
{
usbdev_pkt_t * pkt = ep - > outlist . tail ;
u32 cs ;
halt_dma ( ep - > outdma ) ;
cs = au_readl ( ep - > reg - > ctrl_stat ) ;
if ( ! pkt )
return NULL ;
pkt - > size = ep - > max_pkt_size - get_dma_residue ( ep - > outdma ) ;
if ( pkt - > size )
dma_cache_inv ( ( unsigned long ) pkt - > payload , pkt - > size ) ;
/*
* need to pull out any remaining bytes in the FIFO .
*/
endpoint_fifo_read ( ep ) ;
/*
* should be drained now , but flush anyway just in case .
*/
flush_read_fifo ( ep ) ;
pkt - > status = ( cs & USBDEV_CS_NAK ) ? PKT_STATUS_NAK : PKT_STATUS_ACK ;
if ( ep - > address = = 0 & & ( cs & USBDEV_CS_SU ) )
pkt - > status | = PKT_STATUS_SU ;
vdbg ( " %s: ep%d, %s pkt=%p, size=%d " , __FUNCTION__ ,
ep - > address , ( pkt - > status & PKT_STATUS_NAK ) ?
" NAK " : " ACK " , pkt , pkt - > size ) ;
kickstart_receive_packet ( ep ) ;
return pkt ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Here starts the standard device request handlers . They are
* all called by do_setup ( ) via a table of function pointers .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
static ep0_stage_t
do_get_status ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
switch ( setup - > bRequestType ) {
case 0x80 : // Device
// FIXME: send device status
break ;
case 0x81 : // Interface
// FIXME: send interface status
break ;
case 0x82 : // End Point
// FIXME: send endpoint status
break ;
default :
// Invalid Command
endpoint_stall ( & dev - > ep [ 0 ] ) ; // Stall End Point 0
break ;
}
return STATUS_STAGE ;
}
static ep0_stage_t
do_clear_feature ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
switch ( setup - > bRequestType ) {
case 0x00 : // Device
if ( ( le16_to_cpu ( setup - > wValue ) & 0xff ) = = 1 )
dev - > remote_wakeup_en = 0 ;
else
endpoint_stall ( & dev - > ep [ 0 ] ) ;
break ;
case 0x02 : // End Point
if ( ( le16_to_cpu ( setup - > wValue ) & 0xff ) = = 0 ) {
endpoint_t * ep =
epaddr_to_ep ( dev ,
le16_to_cpu ( setup - > wIndex ) & 0xff ) ;
endpoint_unstall ( ep ) ;
endpoint_reset_datatoggle ( ep ) ;
} else
endpoint_stall ( & dev - > ep [ 0 ] ) ;
break ;
}
return SETUP_STAGE ;
}
static ep0_stage_t
do_reserved ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
// Invalid request, stall End Point 0
endpoint_stall ( & dev - > ep [ 0 ] ) ;
return SETUP_STAGE ;
}
static ep0_stage_t
do_set_feature ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
switch ( setup - > bRequestType ) {
case 0x00 : // Device
if ( ( le16_to_cpu ( setup - > wValue ) & 0xff ) = = 1 )
dev - > remote_wakeup_en = 1 ;
else
endpoint_stall ( & dev - > ep [ 0 ] ) ;
break ;
case 0x02 : // End Point
if ( ( le16_to_cpu ( setup - > wValue ) & 0xff ) = = 0 ) {
endpoint_t * ep =
epaddr_to_ep ( dev ,
le16_to_cpu ( setup - > wIndex ) & 0xff ) ;
endpoint_stall ( ep ) ;
} else
endpoint_stall ( & dev - > ep [ 0 ] ) ;
break ;
}
return SETUP_STAGE ;
}
static ep0_stage_t
do_set_address ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
int new_state = dev - > state ;
int new_addr = le16_to_cpu ( setup - > wValue ) ;
dbg ( " %s: our address=%d " , __FUNCTION__ , new_addr ) ;
if ( new_addr > 127 ) {
// usb spec doesn't tell us what to do, so just go to
// default state
new_state = DEFAULT ;
dev - > address = 0 ;
} else if ( dev - > address ! = new_addr ) {
dev - > address = new_addr ;
new_state = ADDRESS ;
}
if ( dev - > state ! = new_state ) {
dev - > state = new_state ;
/* inform function layer of usbdev state change */
dev - > func_cb ( CB_NEW_STATE , dev - > state , dev - > cb_data ) ;
}
return SETUP_STAGE ;
}
static ep0_stage_t
do_get_descriptor ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
int strnum , desc_len = le16_to_cpu ( setup - > wLength ) ;
switch ( le16_to_cpu ( setup - > wValue ) > > 8 ) {
case USB_DT_DEVICE :
// send device descriptor!
desc_len = desc_len > dev - > dev_desc - > bLength ?
dev - > dev_desc - > bLength : desc_len ;
dbg ( " sending device desc, size=%d " , desc_len ) ;
send_packet ( dev , alloc_packet ( & dev - > ep [ 0 ] , desc_len ,
dev - > dev_desc ) , 0 ) ;
break ;
case USB_DT_CONFIG :
// If the config descr index in low-byte of
// setup->wValue is valid, send config descr,
// otherwise stall ep0.
if ( ( le16_to_cpu ( setup - > wValue ) & 0xff ) = = 0 ) {
// send config descriptor!
if ( desc_len < = USB_DT_CONFIG_SIZE ) {
dbg ( " sending partial config desc, size=%d " ,
desc_len ) ;
send_packet ( dev ,
alloc_packet ( & dev - > ep [ 0 ] ,
desc_len ,
dev - > conf_desc ) ,
0 ) ;
} else {
int len = le16_to_cpu ( dev - > conf_desc - > wTotalLength ) ;
dbg ( " sending whole config desc, "
" size=%d, our size=%d " , desc_len , len ) ;
desc_len = desc_len > len ? len : desc_len ;
send_packet ( dev ,
alloc_packet ( & dev - > ep [ 0 ] ,
desc_len ,
dev - > full_conf_desc ) ,
0 ) ;
}
} else
endpoint_stall ( & dev - > ep [ 0 ] ) ;
break ;
case USB_DT_STRING :
// If the string descr index in low-byte of setup->wValue
// is valid, send string descr, otherwise stall ep0.
strnum = le16_to_cpu ( setup - > wValue ) & 0xff ;
if ( strnum > = 0 & & strnum < 6 ) {
struct usb_string_descriptor * desc =
dev - > str_desc [ strnum ] ;
desc_len = desc_len > desc - > bLength ?
desc - > bLength : desc_len ;
dbg ( " sending string desc %d " , strnum ) ;
send_packet ( dev ,
alloc_packet ( & dev - > ep [ 0 ] , desc_len ,
desc ) , 0 ) ;
} else
endpoint_stall ( & dev - > ep [ 0 ] ) ;
break ;
default :
// Invalid request
err ( " invalid get desc=%d, stalled " ,
le16_to_cpu ( setup - > wValue ) > > 8 ) ;
endpoint_stall ( & dev - > ep [ 0 ] ) ; // Stall endpoint 0
break ;
}
return STATUS_STAGE ;
}
static ep0_stage_t
do_set_descriptor ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
// TODO: implement
// there will be an OUT data stage (the descriptor to set)
return DATA_STAGE ;
}
static ep0_stage_t
do_get_configuration ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
// send dev->configuration
dbg ( " sending config " ) ;
send_packet ( dev , alloc_packet ( & dev - > ep [ 0 ] , 1 , & dev - > configuration ) ,
0 ) ;
return STATUS_STAGE ;
}
static ep0_stage_t
do_set_configuration ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
// set active config to low-byte of setup->wValue
dev - > configuration = le16_to_cpu ( setup - > wValue ) & 0xff ;
dbg ( " set config, config=%d " , dev - > configuration ) ;
if ( ! dev - > configuration & & dev - > state > DEFAULT ) {
dev - > state = ADDRESS ;
/* inform function layer of usbdev state change */
dev - > func_cb ( CB_NEW_STATE , dev - > state , dev - > cb_data ) ;
} else if ( dev - > configuration = = 1 ) {
dev - > state = CONFIGURED ;
/* inform function layer of usbdev state change */
dev - > func_cb ( CB_NEW_STATE , dev - > state , dev - > cb_data ) ;
} else {
// FIXME: "respond with request error" - how?
}
return SETUP_STAGE ;
}
static ep0_stage_t
do_get_interface ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
// interface must be zero.
if ( ( le16_to_cpu ( setup - > wIndex ) & 0xff ) | | dev - > state = = ADDRESS ) {
// FIXME: respond with "request error". how?
} else if ( dev - > state = = CONFIGURED ) {
// send dev->alternate_setting
dbg ( " sending alt setting " ) ;
send_packet ( dev , alloc_packet ( & dev - > ep [ 0 ] , 1 ,
& dev - > alternate_setting ) , 0 ) ;
}
return STATUS_STAGE ;
}
static ep0_stage_t
do_set_interface ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
if ( dev - > state = = ADDRESS ) {
// FIXME: respond with "request error". how?
} else if ( dev - > state = = CONFIGURED ) {
dev - > interface = le16_to_cpu ( setup - > wIndex ) & 0xff ;
dev - > alternate_setting =
le16_to_cpu ( setup - > wValue ) & 0xff ;
// interface and alternate_setting must be zero
if ( dev - > interface | | dev - > alternate_setting ) {
// FIXME: respond with "request error". how?
}
}
return SETUP_STAGE ;
}
static ep0_stage_t
do_synch_frame ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
// TODO
return SETUP_STAGE ;
}
typedef ep0_stage_t ( * req_method_t ) ( struct usb_dev * dev ,
struct usb_ctrlrequest * setup ) ;
/* Table of the standard device request handlers */
static const req_method_t req_method [ ] = {
do_get_status ,
do_clear_feature ,
do_reserved ,
do_set_feature ,
do_reserved ,
do_set_address ,
do_get_descriptor ,
do_set_descriptor ,
do_get_configuration ,
do_set_configuration ,
do_get_interface ,
do_set_interface ,
do_synch_frame
} ;
// SETUP packet request dispatcher
static void
do_setup ( struct usb_dev * dev , struct usb_ctrlrequest * setup )
{
req_method_t m ;
dbg ( " %s: req %d %s " , __FUNCTION__ , setup - > bRequestType ,
get_std_req_name ( setup - > bRequestType ) ) ;
if ( ( setup - > bRequestType & USB_TYPE_MASK ) ! = USB_TYPE_STANDARD | |
( setup - > bRequestType & USB_RECIP_MASK ) ! = USB_RECIP_DEVICE ) {
err ( " %s: invalid requesttype 0x%02x " , __FUNCTION__ ,
setup - > bRequestType ) ;
return ;
}
if ( ( setup - > bRequestType & 0x80 ) = = USB_DIR_OUT & & setup - > wLength )
dbg ( " %s: OUT phase! length=%d " , __FUNCTION__ , setup - > wLength ) ;
if ( setup - > bRequestType < sizeof ( req_method ) / sizeof ( req_method_t ) )
m = req_method [ setup - > bRequestType ] ;
else
m = do_reserved ;
dev - > ep0_stage = ( * m ) ( dev , setup ) ;
}
/*
* A SETUP , DATA0 , or DATA1 packet has been received
* on the default control endpoint ' s fifo .
*/
static void
process_ep0_receive ( struct usb_dev * dev )
{
endpoint_t * ep0 = & dev - > ep [ 0 ] ;
usbdev_pkt_t * pkt ;
spin_lock ( & ep0 - > lock ) ;
// complete packet and prepare a new packet
pkt = receive_packet_complete ( ep0 ) ;
if ( ! pkt ) {
// FIXME: should put a warn/err here.
spin_unlock ( & ep0 - > lock ) ;
return ;
}
// unlink immediately from endpoint.
unlink_head ( & ep0 - > outlist ) ;
// override current stage if h/w says it's a setup packet
if ( pkt - > status & PKT_STATUS_SU )
dev - > ep0_stage = SETUP_STAGE ;
switch ( dev - > ep0_stage ) {
case SETUP_STAGE :
vdbg ( " SU bit is %s in setup stage " ,
( pkt - > status & PKT_STATUS_SU ) ? " set " : " not set " ) ;
if ( pkt - > size = = sizeof ( struct usb_ctrlrequest ) ) {
# ifdef VDEBUG
if ( pkt - > status & PKT_STATUS_ACK )
vdbg ( " received SETUP " ) ;
else
vdbg ( " received NAK SETUP " ) ;
# endif
do_setup ( dev , ( struct usb_ctrlrequest * ) pkt - > payload ) ;
} else
err ( " %s: wrong size SETUP received " , __FUNCTION__ ) ;
break ;
case DATA_STAGE :
/*
* this setup has an OUT data stage . Of the standard
* device requests , only set_descriptor has this stage ,
* so this packet is that descriptor . TODO : drop it for
* now , set_descriptor not implemented .
*
* Need to place a byte in the write FIFO here , to prepare
* to send a zero - length DATA ack packet to the host in the
* STATUS stage .
*/
au_writel ( 0 , ep0 - > reg - > write_fifo ) ;
dbg ( " received OUT stage DATAx on EP0, size=%d " , pkt - > size ) ;
dev - > ep0_stage = SETUP_STAGE ;
break ;
case STATUS_STAGE :
// this setup had an IN data stage, and host is ACK'ing
// the packet we sent during that stage.
if ( pkt - > size ! = 0 )
warn ( " received non-zero ACK on EP0?? " ) ;
# ifdef VDEBUG
else
vdbg ( " received ACK on EP0 " ) ;
# endif
dev - > ep0_stage = SETUP_STAGE ;
break ;
2005-03-01 06:33:16 +00:00
}
2005-04-16 15:20:36 -07:00
spin_unlock ( & ep0 - > lock ) ;
2005-03-01 06:33:16 +00:00
// we're done processing the packet, free it
kfree ( pkt ) ;
2005-04-16 15:20:36 -07:00
}
/*
* A DATA0 / 1 packet has been received on one of the OUT endpoints ( 4 or 5 )
*/
static void
process_ep_receive ( struct usb_dev * dev , endpoint_t * ep )
{
usbdev_pkt_t * pkt ;
spin_lock ( & ep - > lock ) ;
pkt = receive_packet_complete ( ep ) ;
spin_unlock ( & ep - > lock ) ;
dev - > func_cb ( CB_PKT_COMPLETE , ( unsigned long ) pkt , dev - > cb_data ) ;
}
/* This ISR handles the receive complete and suspend events */
static void
req_sus_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
struct usb_dev * dev = ( struct usb_dev * ) dev_id ;
u32 status ;
status = au_readl ( USBD_INTSTAT ) ;
au_writel ( status , USBD_INTSTAT ) ; // ack'em
if ( status & ( 1 < < 0 ) )
process_ep0_receive ( dev ) ;
if ( status & ( 1 < < 4 ) )
process_ep_receive ( dev , & dev - > ep [ 4 ] ) ;
if ( status & ( 1 < < 5 ) )
process_ep_receive ( dev , & dev - > ep [ 5 ] ) ;
}
/* This ISR handles the DMA done events on EP0 */
static void
dma_done_ep0_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
struct usb_dev * dev = ( struct usb_dev * ) dev_id ;
usbdev_pkt_t * pkt ;
endpoint_t * ep0 = & dev - > ep [ 0 ] ;
u32 cs0 , buff_done ;
spin_lock ( & ep0 - > lock ) ;
cs0 = au_readl ( ep0 - > reg - > ctrl_stat ) ;
// first check packet transmit done
if ( ( buff_done = get_dma_buffer_done ( ep0 - > indma ) ) ! = 0 ) {
// transmitted a DATAx packet during DATA stage
// on control endpoint 0
// clear DMA done bit
if ( buff_done & DMA_D0 )
clear_dma_done0 ( ep0 - > indma ) ;
if ( buff_done & DMA_D1 )
clear_dma_done1 ( ep0 - > indma ) ;
pkt = send_packet_complete ( ep0 ) ;
2005-03-01 06:33:16 +00:00
kfree ( pkt ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Now check packet receive done . Shouldn ' t get these ,
* the receive packet complete intr should happen
* before the DMA done intr occurs .
*/
if ( ( buff_done = get_dma_buffer_done ( ep0 - > outdma ) ) ! = 0 ) {
// clear DMA done bit
if ( buff_done & DMA_D0 )
clear_dma_done0 ( ep0 - > outdma ) ;
if ( buff_done & DMA_D1 )
clear_dma_done1 ( ep0 - > outdma ) ;
//process_ep0_receive(dev);
}
spin_unlock ( & ep0 - > lock ) ;
}
/* This ISR handles the DMA done events on endpoints 2,3,4,5 */
static void
dma_done_ep_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
struct usb_dev * dev = ( struct usb_dev * ) dev_id ;
int i ;
for ( i = 2 ; i < 6 ; i + + ) {
u32 buff_done ;
usbdev_pkt_t * pkt ;
endpoint_t * ep = & dev - > ep [ i ] ;
if ( ! ep - > active ) continue ;
spin_lock ( & ep - > lock ) ;
if ( ep - > direction = = USB_DIR_IN ) {
buff_done = get_dma_buffer_done ( ep - > indma ) ;
if ( buff_done ! = 0 ) {
// transmitted a DATAx pkt on the IN ep
// clear DMA done bit
if ( buff_done & DMA_D0 )
clear_dma_done0 ( ep - > indma ) ;
if ( buff_done & DMA_D1 )
clear_dma_done1 ( ep - > indma ) ;
pkt = send_packet_complete ( ep ) ;
spin_unlock ( & ep - > lock ) ;
dev - > func_cb ( CB_PKT_COMPLETE ,
( unsigned long ) pkt ,
dev - > cb_data ) ;
spin_lock ( & ep - > lock ) ;
}
} else {
/*
* Check packet receive done ( OUT ep ) . Shouldn ' t get
* these , the rx packet complete intr should happen
* before the DMA done intr occurs .
*/
buff_done = get_dma_buffer_done ( ep - > outdma ) ;
if ( buff_done ! = 0 ) {
// received a DATAx pkt on the OUT ep
// clear DMA done bit
if ( buff_done & DMA_D0 )
clear_dma_done0 ( ep - > outdma ) ;
if ( buff_done & DMA_D1 )
clear_dma_done1 ( ep - > outdma ) ;
//process_ep_receive(dev, ep);
}
}
spin_unlock ( & ep - > lock ) ;
}
}
/***************************************************************************
* Here begins the external interface functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* allocate a new packet
*/
int
usbdev_alloc_packet ( int ep_addr , int data_size , usbdev_pkt_t * * pkt )
{
endpoint_t * ep = epaddr_to_ep ( & usbdev , ep_addr ) ;
usbdev_pkt_t * lpkt = NULL ;
if ( ! ep | | ! ep - > active | | ep - > address < 2 )
return - ENODEV ;
if ( data_size > ep - > max_pkt_size )
return - EINVAL ;
lpkt = * pkt = alloc_packet ( ep , data_size , NULL ) ;
if ( ! lpkt )
return - ENOMEM ;
return 0 ;
}
/*
* packet send
*/
int
usbdev_send_packet ( int ep_addr , usbdev_pkt_t * pkt )
{
unsigned long flags ;
int count ;
endpoint_t * ep ;
if ( ! pkt | | ! ( ep = epaddr_to_ep ( & usbdev , pkt - > ep_addr ) ) | |
! ep - > active | | ep - > address < 2 )
return - ENODEV ;
if ( ep - > direction ! = USB_DIR_IN )
return - EINVAL ;
spin_lock_irqsave ( & ep - > lock , flags ) ;
count = send_packet ( & usbdev , pkt , 1 ) ;
spin_unlock_irqrestore ( & ep - > lock , flags ) ;
return count ;
}
/*
* packet receive
*/
int
usbdev_receive_packet ( int ep_addr , usbdev_pkt_t * * pkt )
{
unsigned long flags ;
usbdev_pkt_t * lpkt = NULL ;
endpoint_t * ep = epaddr_to_ep ( & usbdev , ep_addr ) ;
if ( ! ep | | ! ep - > active | | ep - > address < 2 )
return - ENODEV ;
if ( ep - > direction ! = USB_DIR_OUT )
return - EINVAL ;
spin_lock_irqsave ( & ep - > lock , flags ) ;
if ( ep - > outlist . count > 1 )
lpkt = unlink_head ( & ep - > outlist ) ;
spin_unlock_irqrestore ( & ep - > lock , flags ) ;
if ( ! lpkt ) {
/* no packet available */
* pkt = NULL ;
return - ENODATA ;
}
* pkt = lpkt ;
return lpkt - > size ;
}
/*
* return total queued byte count on the endpoint .
*/
int
usbdev_get_byte_count ( int ep_addr )
{
unsigned long flags ;
pkt_list_t * list ;
usbdev_pkt_t * scan ;
int count = 0 ;
endpoint_t * ep = epaddr_to_ep ( & usbdev , ep_addr ) ;
if ( ! ep | | ! ep - > active | | ep - > address < 2 )
return - ENODEV ;
if ( ep - > direction = = USB_DIR_IN ) {
list = & ep - > inlist ;
spin_lock_irqsave ( & ep - > lock , flags ) ;
for ( scan = list - > head ; scan ; scan = scan - > next )
count + = scan - > size ;
spin_unlock_irqrestore ( & ep - > lock , flags ) ;
} else {
list = & ep - > outlist ;
spin_lock_irqsave ( & ep - > lock , flags ) ;
if ( list - > count > 1 ) {
for ( scan = list - > head ; scan ! = list - > tail ;
scan = scan - > next )
count + = scan - > size ;
}
spin_unlock_irqrestore ( & ep - > lock , flags ) ;
}
return count ;
}
void
usbdev_exit ( void )
{
endpoint_t * ep ;
int i ;
au_writel ( 0 , USBD_INTEN ) ; // disable usb dev ints
au_writel ( 0 , USBD_ENABLE ) ; // disable usb dev
free_irq ( AU1000_USB_DEV_REQ_INT , & usbdev ) ;
free_irq ( AU1000_USB_DEV_SUS_INT , & usbdev ) ;
// free all control endpoint resources
ep = & usbdev . ep [ 0 ] ;
free_au1000_dma ( ep - > indma ) ;
free_au1000_dma ( ep - > outdma ) ;
endpoint_flush ( ep ) ;
// free ep resources
for ( i = 2 ; i < 6 ; i + + ) {
ep = & usbdev . ep [ i ] ;
if ( ! ep - > active ) continue ;
if ( ep - > direction = = USB_DIR_IN ) {
free_au1000_dma ( ep - > indma ) ;
} else {
free_au1000_dma ( ep - > outdma ) ;
}
endpoint_flush ( ep ) ;
}
2005-03-01 06:33:16 +00:00
kfree ( usbdev . full_conf_desc ) ;
2005-04-16 15:20:36 -07:00
}
int
usbdev_init ( struct usb_device_descriptor * dev_desc ,
struct usb_config_descriptor * config_desc ,
struct usb_interface_descriptor * if_desc ,
struct usb_endpoint_descriptor * ep_desc ,
struct usb_string_descriptor * str_desc [ ] ,
void ( * cb ) ( usbdev_cb_type_t , unsigned long , void * ) ,
void * cb_data )
{
endpoint_t * ep0 ;
int i , ret = 0 ;
u8 * fcd ;
if ( dev_desc - > bNumConfigurations > 1 | |
config_desc - > bNumInterfaces > 1 | |
if_desc - > bNumEndpoints > 4 ) {
err ( " Only one config, one i/f, and no more "
" than 4 ep's allowed " ) ;
ret = - EINVAL ;
goto out ;
}
if ( ! cb ) {
err ( " Function-layer callback required " ) ;
ret = - EINVAL ;
goto out ;
}
if ( dev_desc - > bMaxPacketSize0 ! = USBDEV_EP0_MAX_PACKET_SIZE ) {
warn ( " EP0 Max Packet size must be %d " ,
USBDEV_EP0_MAX_PACKET_SIZE ) ;
dev_desc - > bMaxPacketSize0 = USBDEV_EP0_MAX_PACKET_SIZE ;
}
memset ( & usbdev , 0 , sizeof ( struct usb_dev ) ) ;
usbdev . state = DEFAULT ;
usbdev . dev_desc = dev_desc ;
usbdev . if_desc = if_desc ;
usbdev . conf_desc = config_desc ;
for ( i = 0 ; i < 6 ; i + + )
usbdev . str_desc [ i ] = str_desc [ i ] ;
usbdev . func_cb = cb ;
usbdev . cb_data = cb_data ;
/* Initialize default control endpoint */
ep0 = & usbdev . ep [ 0 ] ;
ep0 - > active = 1 ;
ep0 - > type = CONTROL_EP ;
ep0 - > max_pkt_size = USBDEV_EP0_MAX_PACKET_SIZE ;
spin_lock_init ( & ep0 - > lock ) ;
ep0 - > desc = NULL ; // ep0 has no descriptor
ep0 - > address = 0 ;
ep0 - > direction = 0 ;
ep0 - > reg = & ep_reg [ 0 ] ;
/* Initialize the other requested endpoints */
for ( i = 0 ; i < if_desc - > bNumEndpoints ; i + + ) {
struct usb_endpoint_descriptor * epd = & ep_desc [ i ] ;
endpoint_t * ep ;
if ( ( epd - > bEndpointAddress & 0x80 ) = = USB_DIR_IN ) {
ep = & usbdev . ep [ 2 ] ;
ep - > address = 2 ;
if ( ep - > active ) {
ep = & usbdev . ep [ 3 ] ;
ep - > address = 3 ;
if ( ep - > active ) {
err ( " too many IN ep's requested " ) ;
ret = - ENODEV ;
goto out ;
}
}
} else {
ep = & usbdev . ep [ 4 ] ;
ep - > address = 4 ;
if ( ep - > active ) {
ep = & usbdev . ep [ 5 ] ;
ep - > address = 5 ;
if ( ep - > active ) {
err ( " too many OUT ep's requested " ) ;
ret = - ENODEV ;
goto out ;
}
}
}
ep - > active = 1 ;
epd - > bEndpointAddress & = ~ 0x0f ;
epd - > bEndpointAddress | = ( u8 ) ep - > address ;
ep - > direction = epd - > bEndpointAddress & 0x80 ;
ep - > type = epd - > bmAttributes & 0x03 ;
ep - > max_pkt_size = le16_to_cpu ( epd - > wMaxPacketSize ) ;
spin_lock_init ( & ep - > lock ) ;
ep - > desc = epd ;
ep - > reg = & ep_reg [ ep - > address ] ;
}
/*
* initialize the full config descriptor
*/
usbdev . full_conf_desc = fcd = kmalloc ( le16_to_cpu ( config_desc - > wTotalLength ) ,
ALLOC_FLAGS ) ;
if ( ! fcd ) {
err ( " failed to alloc full config descriptor " ) ;
ret = - ENOMEM ;
goto out ;
}
memcpy ( fcd , config_desc , USB_DT_CONFIG_SIZE ) ;
fcd + = USB_DT_CONFIG_SIZE ;
memcpy ( fcd , if_desc , USB_DT_INTERFACE_SIZE ) ;
fcd + = USB_DT_INTERFACE_SIZE ;
for ( i = 0 ; i < if_desc - > bNumEndpoints ; i + + ) {
memcpy ( fcd , & ep_desc [ i ] , USB_DT_ENDPOINT_SIZE ) ;
fcd + = USB_DT_ENDPOINT_SIZE ;
}
/* Now we're ready to enable the controller */
au_writel ( 0x0002 , USBD_ENABLE ) ;
udelay ( 100 ) ;
au_writel ( 0x0003 , USBD_ENABLE ) ;
udelay ( 100 ) ;
/* build and send config table based on ep descriptors */
for ( i = 0 ; i < 6 ; i + + ) {
endpoint_t * ep ;
if ( i = = 1 )
continue ; // skip dummy ep
ep = & usbdev . ep [ i ] ;
if ( ep - > active ) {
au_writel ( ( ep - > address < < 4 ) | 0x04 , USBD_CONFIG ) ;
au_writel ( ( ( ep - > max_pkt_size & 0x380 ) > > 7 ) |
( ep - > direction > > 4 ) | ( ep - > type < < 4 ) ,
USBD_CONFIG ) ;
au_writel ( ( ep - > max_pkt_size & 0x7f ) < < 1 , USBD_CONFIG ) ;
au_writel ( 0x00 , USBD_CONFIG ) ;
au_writel ( ep - > address , USBD_CONFIG ) ;
} else {
u8 dir = ( i = = 2 | | i = = 3 ) ? DIR_IN : DIR_OUT ;
au_writel ( ( i < < 4 ) | 0x04 , USBD_CONFIG ) ;
au_writel ( ( ( 16 & 0x380 ) > > 7 ) | dir |
( BULK_EP < < 4 ) , USBD_CONFIG ) ;
au_writel ( ( 16 & 0x7f ) < < 1 , USBD_CONFIG ) ;
au_writel ( 0x00 , USBD_CONFIG ) ;
au_writel ( i , USBD_CONFIG ) ;
}
}
/*
* Enable Receive FIFO Complete interrupts only . Transmit
* complete is being handled by the DMA done interrupts .
*/
au_writel ( 0x31 , USBD_INTEN ) ;
/*
* Controller is now enabled , request DMA and IRQ
* resources .
*/
/* request the USB device transfer complete interrupt */
if ( request_irq ( AU1000_USB_DEV_REQ_INT , req_sus_intr , SA_INTERRUPT ,
" USBdev req " , & usbdev ) ) {
err ( " Can't get device request intr " ) ;
ret = - ENXIO ;
goto out ;
}
/* request the USB device suspend interrupt */
if ( request_irq ( AU1000_USB_DEV_SUS_INT , req_sus_intr , SA_INTERRUPT ,
" USBdev sus " , & usbdev ) ) {
err ( " Can't get device suspend intr " ) ;
ret = - ENXIO ;
goto out ;
}
/* Request EP0 DMA and IRQ */
if ( ( ep0 - > indma = request_au1000_dma ( ep_dma_id [ 0 ] . id ,
ep_dma_id [ 0 ] . str ,
dma_done_ep0_intr ,
SA_INTERRUPT ,
& usbdev ) ) < 0 ) {
err ( " Can't get %s DMA " , ep_dma_id [ 0 ] . str ) ;
ret = - ENXIO ;
goto out ;
}
if ( ( ep0 - > outdma = request_au1000_dma ( ep_dma_id [ 1 ] . id ,
ep_dma_id [ 1 ] . str ,
NULL , 0 , NULL ) ) < 0 ) {
err ( " Can't get %s DMA " , ep_dma_id [ 1 ] . str ) ;
ret = - ENXIO ;
goto out ;
}
// Flush the ep0 buffers and FIFOs
endpoint_flush ( ep0 ) ;
// start packet reception on ep0
kickstart_receive_packet ( ep0 ) ;
/* Request DMA and IRQ for the other endpoints */
for ( i = 2 ; i < 6 ; i + + ) {
endpoint_t * ep = & usbdev . ep [ i ] ;
if ( ! ep - > active )
continue ;
// Flush the endpoint buffers and FIFOs
endpoint_flush ( ep ) ;
if ( ep - > direction = = USB_DIR_IN ) {
ep - > indma =
request_au1000_dma ( ep_dma_id [ ep - > address ] . id ,
ep_dma_id [ ep - > address ] . str ,
dma_done_ep_intr ,
SA_INTERRUPT ,
& usbdev ) ;
if ( ep - > indma < 0 ) {
err ( " Can't get %s DMA " ,
ep_dma_id [ ep - > address ] . str ) ;
ret = - ENXIO ;
goto out ;
}
} else {
ep - > outdma =
request_au1000_dma ( ep_dma_id [ ep - > address ] . id ,
ep_dma_id [ ep - > address ] . str ,
NULL , 0 , NULL ) ;
if ( ep - > outdma < 0 ) {
err ( " Can't get %s DMA " ,
ep_dma_id [ ep - > address ] . str ) ;
ret = - ENXIO ;
goto out ;
}
// start packet reception on OUT endpoint
kickstart_receive_packet ( ep ) ;
}
}
out :
if ( ret )
usbdev_exit ( ) ;
return ret ;
}
EXPORT_SYMBOL ( usbdev_init ) ;
EXPORT_SYMBOL ( usbdev_exit ) ;
EXPORT_SYMBOL ( usbdev_alloc_packet ) ;
EXPORT_SYMBOL ( usbdev_receive_packet ) ;
EXPORT_SYMBOL ( usbdev_send_packet ) ;
EXPORT_SYMBOL ( usbdev_get_byte_count ) ;