📄 usb_ctl.c
字号:
/* * Copyright (C) Compaq Computer Corporation, 1998, 1999 * Copyright (C) Extenex Corporation, 2001 * * usb_ctl.c * * SA1100 USB controller core driver. * * This file provides interrupt routing and overall coordination * of the three endpoints in usb_ep0, usb_receive (1), and usb_send (2). * * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. * */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/tqueue.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/mach-types.h>#include "sa1100_usb.h"#include "usb_ctl.h"//////////////////////////////////////////////////////////////////////////////// Prototypes//////////////////////////////////////////////////////////////////////////////int usbctl_next_state_on_event( int event );static void udc_int_hndlr(int, void *, struct pt_regs *);static void initialize_descriptors( void );static void soft_connect_hook( int enable );static void udc_disable(void);static void udc_enable(void);#if CONFIG_PROC_FS#define PROC_NODE_NAME "sausb"static int usbctl_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);#endif//////////////////////////////////////////////////////////////////////////////// Globals//////////////////////////////////////////////////////////////////////////////static const char pszMe[] = "usbctl: ";struct usb_info_t usbd_info; /* global to ep0, usb_recv, usb_send *//* device descriptors */static desc_t desc;#define MAX_STRING_DESC 8static string_desc_t * string_desc_array[ MAX_STRING_DESC ];static string_desc_t sd_zero; /* special sd_zero holds language codes */// called when configuredstatic usb_notify_t configured_callback = NULL;enum { kStateZombie = 0, kStateZombieSuspend = 1, kStateDefault = 2, kStateDefaultSuspend = 3, kStateAddr = 4, kStateAddrSuspend = 5, kStateConfig = 6, kStateConfigSuspend = 7};static int device_state_machine[8][6] = {// suspend reset resume adddr config deconfig/* zombie */ { kStateZombieSuspend, kStateDefault, kError, kError, kError, kError },/* zom sus */ { kError, kStateDefault, kStateZombie, kError, kError, kError },/* default */ { kStateDefaultSuspend, kError, kStateDefault, kStateAddr, kError, kError },/* def sus */ { kError, kStateDefault, kStateDefault, kError, kError, kError },/* addr */ { kStateAddrSuspend, kStateDefault, kError, kError, kStateConfig, kError },/* addr sus */{ kError, kStateDefault, kStateAddr, kError, kError, kError },/* config */ { kStateConfigSuspend, kStateDefault, kError, kError, kError, kStateAddr },/* cfg sus */ { kError, kStateDefault, kStateConfig, kError, kError, kError }};/* "device state" is the usb device framework state, as opposed to the "state machine state" which is whatever the driver needs and is much more fine grained*/static int sm_state_to_device_state[8] =// zombie zom suspend default default sus{ USB_STATE_POWERED, USB_STATE_SUSPENDED, USB_STATE_DEFAULT, USB_STATE_SUSPENDED,// addr addr sus config config sus USB_STATE_ADDRESS, USB_STATE_SUSPENDED, USB_STATE_CONFIGURED, USB_STATE_SUSPENDED};static char * state_names[8] ={ "zombie", "zombie suspended", "default", "default suspended", "address", "address suspended", "configured", "config suspended"};static char * event_names[6] ={ "suspend", "reset", "resume", "address assigned", "configure", "de-configure"};static char * device_state_names[] ={ "not attached", "attached", "powered", "default", "address", "configured", "suspended" };static int sm_state = kStateZombie;//////////////////////////////////////////////////////////////////////////////// Async//////////////////////////////////////////////////////////////////////////////static void core_kicker(void);static inline void enable_resume_mask_suspend( void );static inline void enable_suspend_mask_resume(void);static voidudc_int_hndlr(int irq, void *dev_id, struct pt_regs *regs){ __u32 status = Ser0UDCSR; /* ReSeT Interrupt Request - UDC has been reset */ if ( status & UDCSR_RSTIR ) { if ( usbctl_next_state_on_event( kEvReset ) != kError ) { /* starting 20ms or so reset sequence now... */ printk("%sResetting\n", pszMe); ep0_reset(); // just set state to idle ep1_reset(); // flush dma, clear false stall ep2_reset(); // flush dma, clear false stall } // mask reset ints, they flood during sequence, enable // suspend and resume Ser0UDCCR |= UDCCR_REM; // mask reset Ser0UDCCR &= ~(UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume UDC_flip( Ser0UDCSR, status ); // clear all pending sources return; // <-- no reason to continue if resetting } // else we have done something other than reset, so be sure reset enabled UDC_clear( Ser0UDCCR, UDCCR_REM ); /* RESume Interrupt Request */ if ( status & UDCSR_RESIR ) { usbctl_next_state_on_event( kEvResume ); core_kicker(); enable_suspend_mask_resume(); } /* SUSpend Interrupt Request */ if ( status & UDCSR_SUSIR ) { usbctl_next_state_on_event( kEvSuspend ); enable_resume_mask_suspend(); } UDC_flip(Ser0UDCSR, status); // clear all pending sources if (status & UDCSR_EIR) ep0_int_hndlr(); if (status & UDCSR_RIR) ep1_int_hndlr(status); if (status & UDCSR_TIR) ep2_int_hndlr(status);}static inline void enable_resume_mask_suspend( void ){ int i = 0; while( 1 ) { Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events udelay( i ); if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR) ) break; if ( ++i == 50 ) { printk( "%senable_resume(): Could not set SUSIM %8.8X\n", pszMe, Ser0UDCCR ); break; } } i = 0; while( 1 ) { Ser0UDCCR &= ~UDCCR_RESIM; udelay( i ); if ( ( Ser0UDCCR & UDCCR_RESIM ) == 0 || (Ser0UDCSR & UDCSR_RSTIR) ) break; if ( ++i == 50 ) { printk( "%senable_resume(): Could not clear RESIM %8.8X\n", pszMe, Ser0UDCCR ); break; } }}static inline void enable_suspend_mask_resume(void){ int i = 0; while( 1 ) { Ser0UDCCR |= UDCCR_RESIM; // mask future resume events udelay( i ); if ( Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR) ) break; if ( ++i == 50 ) { printk( "%senable_suspend(): Could not set RESIM %8.8X\n", pszMe, Ser0UDCCR ); break; } } i = 0; while( 1 ) { Ser0UDCCR &= ~UDCCR_SUSIM; udelay( i ); if ( ( Ser0UDCCR & UDCCR_SUSIM ) == 0 || (Ser0UDCSR & UDCSR_RSTIR) ) break; if ( ++i == 50 ) { printk( "%senable_suspend(): Could not clear SUSIM %8.8X\n", pszMe, Ser0UDCCR ); break; } }}//////////////////////////////////////////////////////////////////////////////// Public Interface///////////////////////////////////////////////////////////////////////////////* Open SA usb core on behalf of a client, but don't start running */intsa1100_usb_open( const char * client ){ if ( usbd_info.client_name != NULL ) return -EBUSY; usbd_info.client_name = (char*) client; memset(&usbd_info.stats, 0, sizeof(struct usb_stats_t)); memset(string_desc_array, 0, sizeof(string_desc_array)); /* hack to start in zombie suspended state */ sm_state = kStateZombieSuspend; usbd_info.state = USB_STATE_SUSPENDED; /* create descriptors for enumeration */ initialize_descriptors(); printk( "%sOpened for %s\n", pszMe, client ); return 0;}/* Start running. Must have called usb_open (above) first */intsa1100_usb_start( void ){ if ( usbd_info.client_name == NULL ) { printk( "%s%s - no client registered\n", pszMe, __FUNCTION__ ); return -EPERM; } /* start UDC internal machinery running */ udc_enable(); udelay( 100 ); /* clear stall - receiver seems to start stalled? 19Jan01ww */ /* also clear other stuff just to be thurough 22Feb01ww */ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC ); UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC ); /* mask everything */ Ser0UDCCR = 0xFC; /* flush DMA and fire through some -EAGAINs */ ep1_init( usbd_info.dmach_rx ); ep2_init( usbd_info.dmach_tx ); /* give endpoint notification we are starting */ ep1_state_change_notify( USB_STATE_SUSPENDED ); ep2_state_change_notify( USB_STATE_SUSPENDED ); /* enable any platform specific hardware */ soft_connect_hook( 1 ); /* clear all top-level sources */ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ; /* EXERIMENT - a short line in the spec says toggling this ..bit diddles the internal state machine in the udc to ..expect a suspend */ Ser0UDCCR |= UDCCR_RESIM; /* END EXPERIMENT 10Feb01ww */ /* enable any platform specific hardware */ soft_connect_hook( 1 ); /* enable interrupts. If you are unplugged you will immediately get a suspend interrupt. If you are plugged and have a soft connect-circuit, you will get a reset If you are plugged without a soft-connect, I think you also get suspend. In short, start with suspend masked and everything else enabled */ UDC_write( Ser0UDCCR, UDCCR_SUSIM ); printk( "%sStarted for %s\n", pszMe, usbd_info.client_name ); return 0;}/* Stop USB core from running */intsa1100_usb_stop( void ){ if ( usbd_info.client_name == NULL ) { printk( "%s%s - no client registered\n", pszMe, __FUNCTION__ ); return -EPERM; } /* mask everything */ Ser0UDCCR = 0xFC; ep1_reset(); ep2_reset(); udc_disable(); printk( "%sStopped\n", pszMe ); return 0;}/* Tell SA core client is through using it */intsa1100_usb_close( void ){ if ( usbd_info.client_name == NULL ) { printk( "%s%s - no client registered\n", pszMe, __FUNCTION__ ); return -EPERM; } usbd_info.client_name = NULL; printk( "%sClosed\n", pszMe ); return 0;}/* set a proc to be called when device is configured */usb_notify_t sa1100_set_configured_callback( usb_notify_t func ){ usb_notify_t retval = configured_callback; configured_callback = func; return retval;}/*==================================================== * Descriptor Manipulation. * Use these between open() and start() above to setup * the descriptors for your device. * *//* get pointer to static default descriptor */desc_t *sa1100_usb_get_descriptor_ptr( void ) { return &desc; }/* optional: set a string descriptor */intsa1100_usb_set_string_descriptor( int i, string_desc_t * p ){ int retval; if ( i < MAX_STRING_DESC ) { string_desc_array[i] = p; retval = 0; } else { retval = -EINVAL; } return retval;}/* optional: get a previously set string descriptor */string_desc_t *sa1100_usb_get_string_descriptor( int i ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -