📄 usb_ctl.c
字号:
/* * Copyright (C) Compaq Computer Corporation, 1998, 1999 * Copyright (C) Extenex Corporation, 2001 * Copyright (C) Intrinsyc, Inc., 2002 * * S3C2440A USB controller core driver. * * 22-Jul-2003 * -derived from pxa usb_ctl.c, s3c2410 usb_ctl.c * 15-April-2004 - Ported for 24A0A board */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/tqueue.h>#include <linux/delay.h>#include <linux/slab.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/mach-types.h>#include "s3c2440_usb.h"#include "usb_ctl.h"// #define DEBUG 1#if DEBUGstatic unsigned int usb_debug = DEBUG;#else#define usb_debug 0 /* gcc will remove all the debug code for us */#endif//////////////////////////////////////////////////////////////////////////////// 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);static void reconfig_usbd(void);#if CONFIG_PROC_FS#define PROC_NODE_NAME "driver/usbd"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};/* * FIXME: The S3C2440 UDC handles several host device requests without user * notification/intervention. The table could be collapsed quite a bit... */static int device_state_machine[8][6] = {// suspend reset resume adddr config deconfig/* zombie */ { kStateZombieSuspend , kStateDefault, kStateZombie , kError , kError , kError },/* zom sus */ { kStateZombieSuspend , kStateDefault, kStateZombie , kError , kError , kError },/* default */ { kStateDefaultSuspend, kStateDefault, kStateDefault, kStateAddr, kStateConfig, kError },/* def sus */ { kStateDefaultSuspend, kStateDefault, kStateDefault, kError , kError , kError },/* addr */ { kStateAddrSuspend , kStateDefault, kStateAddr , kError , kStateConfig, kError },/* addr sus */{ kStateAddrSuspend , kStateDefault, kStateAddr , kError , kError , kError },/* config */ { kStateConfigSuspend , kStateDefault, kStateConfig , kError , kError , kStateDefault },/* cfg sus */ { kStateConfigSuspend , 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 USB_STATE_POWERED, USB_STATE_SUSPENDED, // default default susUSB_STATE_DEFAULT, USB_STATE_SUSPENDED,// addr addr sus USB_STATE_ADDRESS, USB_STATE_SUSPENDED, // config config susUSB_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;static voidudc_int_hndlr(int irq, void *dev_id, struct pt_regs *regs){ __u8 saveIdx = UD_INDEX; __u8 usb_status = UD_USBINT; __u8 usbd_status = UD_INT; /* SUSpend Interrupt Request */ if ( usb_status & UD_USBINT_SUSPND ) { UD_USBINT = UD_USBINT_SUSPND; /* ack */ if( usb_debug) printk("%sSuspend...\n", pszMe); usbctl_next_state_on_event( kEvSuspend ); } /* RESume Interrupt Request */ if ( usb_status & UD_USBINT_RESUM ) { UD_USBINT = UD_USBINT_RESUM; /* ack */ if( usb_debug) printk("%sResume...\n", pszMe); usbctl_next_state_on_event( kEvResume ); } /* ReSeT Interrupt Request - UDC has been reset */ if ( usb_status & UD_USBINT_RESET ) { if ( usbctl_next_state_on_event( kEvReset ) != kError ) { /* starting reset sequence now... */ if( usb_debug) printk("%sResetting\n", pszMe); ep0_reset(); ep_bulk_in1_reset(); ep_bulk_out1_reset(); } else { printk("%sUnexpected reset\n", pszMe); } /* reconfig everything */ reconfig_usbd(); /* clear the reset interrupt */ UD_USBINT = UD_USBINT_RESET; ep0_state = EP0_STATE_IDLE; } else { /* ep0 int */ if ( usbd_status & UD_INT_EP0 ) { UD_INT = UD_INT_EP0; /* ack */ ep0_int_hndlr(); } /* receive bulk */ if ( usbd_status & UD_INT_EP1 ) { UD_INT = UD_INT_EP1; /* ack */ ep_bulk_out1_int_hndlr(usbd_status); } /* transmit bulk */ if ( usbd_status & UD_INT_EP2 ) { UD_INT = UD_INT_EP2; /* ack */ ep_bulk_in1_int_hndlr(usbd_status); }#if 0 if(usbd_status & UD_INT_EP3) UD_INT = UD_INT_EP3; if(usbd_status & UD_INT_EP4) UD_INT = UD_INT_EP4;#endif } UD_INDEX= saveIdx; /* restore index. needed ? */}//////////////////////////////////////////////////////////////////////////////// Public Interface///////////////////////////////////////////////////////////////////////////////* Open S3C2440 usb core on behalf of a client, but don't start running */intelfin_usb_open( const char * client ){ if ( usbd_info.client_name != NULL ) { printk( "%sUnable to register %s (%s already registered).\n", pszMe, client, usbd_info.client_name ); 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( "%s%s registered.\n", pszMe, client ); return 0;}/* Start running. Must have called usb_open (above) first */intelfin_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 ); /* flush DMA and fire through some -EAGAINs */ ep_bulk_out1_init( usbd_info.dmach_rx ); ep_bulk_in1_init( usbd_info.dmach_tx ); /* give endpoint notification we are starting */ ep_bulk_out1_state_change_notify( USB_STATE_SUSPENDED ); ep_bulk_in1_state_change_notify( USB_STATE_SUSPENDED ); /* enable any platform specific hardware */ soft_connect_hook( 1 ); /* enable suspend/resume, reset */ UD_USBINT = UD_USBINT_RESET | UD_USBINT_RESUM | UD_USBINT_SUSPND; /* enable ep0, ep1, ep2 */ UD_INT = UD_INT_EP0 | UD_INT_EP1 | UD_INT_EP2; if( usb_debug) printk( "%sStarted %s\n", pszMe, usbd_info.client_name ); return 0;}/* Stop USB core from running */intelfin_usb_stop( void ){ if ( usbd_info.client_name == NULL ) { printk( "%s%s - no client registered\n", pszMe, __FUNCTION__ ); return -EPERM; } /* mask everything */ /* disable suspend/resume, reset */ UD_USBINT &= ~(UD_USBINT_RESUM | UD_USBINT_SUSPND); /* disable ep0, ep1, ep2 */ UD_INT &= ~(UD_INT_EP0 | UD_INT_EP1 | UD_INT_EP2); ep_bulk_out1_reset(); ep_bulk_in1_reset(); udc_disable(); if( usb_debug) printk( "%sStopped %s\n", pszMe, usbd_info.client_name ); return 0;}/* Tell S3C2440 core client is through using it */intelfin_usb_close( void ){ if ( usbd_info.client_name == NULL ) { printk( "%s%s - no client registered\n", pszMe, __FUNCTION__ ); return -EPERM; } printk( "%s%s closed.\n", pszMe, (char*)usbd_info.client_name ); usbd_info.client_name = NULL; return 0;}/* set a proc to be called when device is configured */usb_notify_t elfin_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 *elfin_usb_get_descriptor_ptr( void ) { return &desc; }/* optional: set a string descriptor */intelfin_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 *elfin_usb_get_string_descriptor( int i ){ return ( i < MAX_STRING_DESC ) ? string_desc_array[i] : NULL;}/* optional: kmalloc and unicode up a string descriptor */string_desc_t *elfin_usb_kmalloc_string_descriptor( const char * p ){ string_desc_t * pResult = NULL; if ( p ) { int len = strlen( p ); int uni_len = len * sizeof( __u16 ); pResult = (string_desc_t*) kmalloc( uni_len + 2, GFP_KERNEL ); /* ugh! */ if ( pResult != NULL ) { int i; pResult->bLength = uni_len + 2; pResult->bDescriptorType = USB_DESC_STRING; for( i = 0; i < len ; i++ ) { pResult->bString[i] = make_word( (__u16) p[i] ); } } } return pResult;}//////////////////////////////////////////////////////////////////////////////// Exports to rest of driver///////////////////////////////////////////////////////////////////////////////* called by the int handler here and the two endpoint files when interesting .."events" happen */intusbctl_next_state_on_event( int event ){ int next_state = device_state_machine[ sm_state ][ event ]; if ( next_state != kError )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -