⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb_ep0.c

📁 上传linux-jx2410的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (C) Extenex Corporation 2001 * Much folklore gleaned from original code: *    Copyright (C) Compaq Computer Corporation, 1998, 1999 * *  usb_ep0.c - SA1100 USB controller driver. *              Endpoint zero management * *  Please see: *    linux/Documentation/arm/SA1100/SA1100_USB *  for details. (Especially since Intel docs are full of *  errors about ep0 operation.) ward.willats@extenex.com. * * Intel also has a "Universal Serial Bus Client Device * Validation for the StrongARM SA-1100 Microprocessor" * document, which has flow charts and assembler test driver, * but be careful, since it is just for validation and not * a "real world" solution. * * A summary of three types of data-returning setups: * * 1. Setup request <= 8 bytes. That is, requests that can *    be fullfilled in one write to the FIFO. DE is set *    with IPR in queue_and_start_write(). (I don't know *    if there really are any of these!) * * 2. Setup requests > 8 bytes (requiring more than one *    IN to get back to the host), and we have at least *    as much or more data than the host requested. In *    this case we pump out everything we've got, and *    when the final interrupt comes in due to the UDC *    clearing the last IPR, we just set DE. * * 3. Setup requests > 8 bytes, but we don't have enough *    data to satisfy the request. In this case, we send *    everything we've got, and when the final interrupt *    comes in due to the UDC clearing the last IPR *    we write nothing to the FIFO and set both IPR and DE *    so the UDC sends an empty packet and forces the host *    to perform short packet retirement instead of stalling *    out. * */#include <linux/delay.h>#include "sa1100_usb.h"  /* public interface */#include "usb_ctl.h"     /* private stuff */// 1 == lots of trace noise,  0 = only "important' stuff#define VERBOSITY 0enum { true = 1, false = 0 };typedef int bool;#ifndef MIN#define MIN( a, b ) ((a)<(b)?(a):(b))#endif#if 1 && !defined( ASSERT )#  define ASSERT(expr) \          if(!(expr)) { \          printk( "Assertion failed! %s,%s,%s,line=%d\n",\          #expr,__FILE__,__FUNCTION__,__LINE__); \          }#else#  define ASSERT(expr)#endif#if VERBOSITY#define PRINTKD(fmt, args...) printk( fmt , ## args)#else#define PRINTKD(fmt, args...)#endif/*================================================ * USB Protocol Stuff *//* Request Codes   */enum { GET_STATUS=0,         CLEAR_FEATURE=1,     SET_FEATURE=3,	   SET_ADDRESS=5,        GET_DESCRIPTOR=6,	  SET_DESCRIPTOR=7,	   GET_CONFIGURATION=8,  SET_CONFIGURATION=9, GET_INTERFACE=10,	   SET_INTERFACE=11 };/* USB Device Requests */typedef struct{    __u8 bmRequestType;    __u8 bRequest;    __u16 wValue;    __u16 wIndex;    __u16 wLength;} usb_dev_request_t  __attribute__ ((packed));/***************************************************************************Prototypes***************************************************************************//* "setup handlers" -- the main functions dispatched to by the   .. isr. These represent the major "modes" of endpoint 0 operaton */static void sh_setup_begin(void);				/* setup begin (idle) */static void sh_write( void );      				/* writing data */static void sh_write_with_empty_packet( void ); /* empty packet at end of xfer*//* called before both sh_write routines above */static void common_write_preamble( void );/* other subroutines */static __u32  queue_and_start_write( void * p, int req, int act );static void write_fifo( void );static int read_fifo( usb_dev_request_t * p );static void get_descriptor( usb_dev_request_t * pReq );/* some voodo helpers  01Mar01ww */static void set_cs_bits( __u32 set_bits );static void set_de( void );static void set_ipr( void );static void set_ipr_and_de( void );static bool clear_opr( void );/***************************************************************************Inline Helpers***************************************************************************//* Data extraction from usb_request_t fields */enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 };static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); }static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); }inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }/* following is hook for self-powered flag in GET_STATUS. Some devices   .. might like to override and return real info */static inline bool self_powered_hook( void ) { return true; }/* print string descriptor */static inline void psdesc( string_desc_t * p ){	 int i;	 int nchars = ( p->bLength - 2 ) / sizeof( __u16 );	 printk( "'" );	 for( i = 0 ; i < nchars ; i++ ) {		  printk( "%c", (char) p->bString[i] );	 }	 printk( "'\n" );}#if VERBOSITY/* "pcs" == "print control status" */static inline void pcs( void ){	 __u32 foo = Ser0UDCCS0;	 printk( "%8.8X: %s %s %s %s\n",			 foo,			 foo & UDCCS0_SE ? "SE" : "",			 foo & UDCCS0_OPR ? "OPR" : "",			 foo & UDCCS0_IPR ? "IPR" : "",			 foo & UDCCS0_SST ? "SST" : ""	 );}static inline void preq( usb_dev_request_t * pReq ){	 static char * tnames[] = { "dev", "intf", "ep", "oth" };	 static char * rnames[] = { "std", "class", "vendor", "???" };	 char * psz;	 switch( pReq->bRequest ) {	 case GET_STATUS: psz = "get stat"; break;	 case CLEAR_FEATURE: psz = "clr feat"; break;	 case SET_FEATURE: psz = "set feat"; break;	 case SET_ADDRESS: psz = "set addr"; break;	 case GET_DESCRIPTOR: psz = "get desc"; break;	 case SET_DESCRIPTOR: psz = "set desc"; break;	 case GET_CONFIGURATION: psz = "get cfg"; break;	 case SET_CONFIGURATION: psz = "set cfg"; break;	 case GET_INTERFACE: psz = "get intf"; break;	 case SET_INTERFACE: psz = "set intf"; break;	 default: psz = "unknown"; break;	 }	 printk( "- [%s: %s req to %s. dir=%s]\n", psz,			 rnames[ (pReq->bmRequestType >> 5) & 3 ],			 tnames[ pReq->bmRequestType & 3 ],			 ( pReq->bmRequestType & 0x80 ) ? "in" : "out" );}#elsestatic inline void pcs( void ){}static inline void preq( void ){}#endif/***************************************************************************Globals***************************************************************************/static const char pszMe[] = "usbep0: ";/* pointer to current setup handler */static void (*current_handler)(void) = sh_setup_begin;/* global write struct to keep write   ..state around across interrupts */static struct {		unsigned char *p;		int bytes_left;} wr;/***************************************************************************Public Interface***************************************************************************//* reset received from HUB (or controller just went nuts and reset by itself!)  so udc core has been reset, track this state here  */voidep0_reset(void){	 /* reset state machine */	 current_handler = sh_setup_begin;	 wr.p = NULL;	 wr.bytes_left = 0;	 usbd_info.address=0;}/* handle interrupt for endpoint zero */voidep0_int_hndlr( void ){	 PRINTKD( "/\\(%d)\n", Ser0UDCAR );	 pcs();	 /* if not in setup begin, we are returning data.		execute a common preamble to both write handlers	 */	 if ( current_handler != sh_setup_begin )		  common_write_preamble();	 (*current_handler)();	 PRINTKD( "---\n" );	 pcs();	 PRINTKD( "\\/\n" );}/***************************************************************************Setup Handlers***************************************************************************//* * sh_setup_begin() * This setup handler is the "idle" state of endpoint zero. It looks for OPR * (OUT packet ready) to see if a setup request has been been received from the * host. Requests without a return data phase are immediately handled. Otherwise, * in the case of GET_XXXX the handler may be set to one of the sh_write_xxxx * data pumpers if more than 8 bytes need to get back to the host. * */static voidsh_setup_begin( void ){	 unsigned char status_buf[2];  /* returned in GET_STATUS */	 usb_dev_request_t req;	 int request_type;	 int n;	 __u32 cs_bits;	 __u32 address;	 __u32 cs_reg_in = Ser0UDCCS0;	 if (cs_reg_in & UDCCS0_SST) {		  PRINTKD( "%ssetup begin: sent stall. Continuing\n", pszMe );		  set_cs_bits( UDCCS0_SST );	}	 if ( cs_reg_in & UDCCS0_SE ) {		  PRINTKD( "%ssetup begin: Early term of setup. Continuing\n", pszMe );		  set_cs_bits( UDCCS0_SSE );  		 /* clear setup end */	 }	 /* Be sure out packet ready, otherwise something is wrong */	 if ( (cs_reg_in & UDCCS0_OPR) == 0 ) {		  /* we can get here early...if so, we'll int again in a moment  */		  PRINTKD( "%ssetup begin: no OUT packet available. Exiting\n", pszMe );		  goto sh_sb_end;	 }	 /* read the setup request */	 n = read_fifo( &req );	 if ( n != sizeof( req ) ) {		  printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "				  " Stalling out...\n",				  pszMe, sizeof( req ), n );		  /* force stall, serviced out */		  set_cs_bits( UDCCS0_FST | UDCCS0_SO  );		  goto sh_sb_end;	 }	 /* Is it a standard request? (not vendor or class request) */	 request_type = type_code_from_request( req.bmRequestType );	 if ( request_type != 0 ) {		  printk( "%ssetup begin: unsupported bmRequestType: %d ignored\n",				  pszMe, request_type );		  set_cs_bits( UDCCS0_DE | UDCCS0_SO );		  goto sh_sb_end;	 }#if VERBOSITY	 {	 unsigned char * pdb = (unsigned char *) &req;	 PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",			 pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]		  );	 preq( &req );	 }#endif	 /* Handle it */	 switch( req.bRequest ) {		  /* This first bunch have no data phase */	 case SET_ADDRESS:		  address = (__u32) (req.wValue & 0x7F);		  /* when SO and DE sent, UDC will enter status phase and ack,			 ..propagating new address to udc core. Next control transfer			 ..will be on the new address. You can't see the change in a			 ..read back of CAR until then. (about 250us later, on my box).			 ..The original Intel driver sets S0 and DE and code to check			 ..that address has propagated here. I tried this, but it			 ..would only work sometimes! The rest of the time it would			 ..never propagate and we'd spin forever. So now I just set			 ..it and pray...		  */		  Ser0UDCAR = address;		  usbd_info.address = address;		  usbctl_next_state_on_event( kEvAddress );		  set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */		  printk( "%sI have been assigned address: %d\n", pszMe, address );		  break;	 case SET_CONFIGURATION:		  if ( req.wValue == 1 ) {			   /* configured */			   if (usbctl_next_state_on_event( kEvConfig ) != kError){								/* (re)set the out and in max packet sizes */					desc_t * pDesc = sa1100_usb_get_descriptor_ptr();					__u32 out = __le16_to_cpu( pDesc->b.ep1.wMaxPacketSize );					__u32 in  = __le16_to_cpu( pDesc->b.ep2.wMaxPacketSize );					Ser0UDCOMP = ( out - 1 );					Ser0UDCIMP = ( in - 1 );					printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in );			   }		  } else if ( req.wValue == 0 ) {			   /* de-configured */			   if (usbctl_next_state_on_event( kEvDeConfig ) != kError )					printk( "%sDe-Configured\n", pszMe );		  } else {			   printk( "%ssetup phase: Unknown "					   "\"set configuration\" data %d\n",					   pszMe, req.wValue );		  }		  set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */		  break;	 case CLEAR_FEATURE:		  /* could check data length, direction...26Jan01ww */		  if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */			   int ep = windex_to_ep_num( req.wIndex );			   if ( ep == 1 ) {					printk( "%sclear feature \"endpoint halt\" "							" on receiver\n", pszMe );					ep1_reset();			   }			   else if ( ep == 2 ) {					printk( "%sclear feature \"endpoint halt\" "							"on xmitter\n", pszMe );					ep2_reset();			   } else {					printk( "%sclear feature \"endpoint halt\" "							"on unsupported ep # %d\n",							pszMe, ep );			   }		  } else {			   printk( "%sUnsupported feature selector (%d) "					   "in clear feature. Ignored.\n" ,					   pszMe, req.wValue );		  }		  set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */		  break;	 case SET_FEATURE:		  if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */			   int ep = windex_to_ep_num( req.wValue );			   if ( ep == 1 ) {					printk( "%set feature \"endpoint halt\" "							"on receiver\n", pszMe );					ep1_stall();			   }			   else if ( ep == 2 ) {					printk( "%sset feature \"endpoint halt\" "							" on xmitter\n", pszMe );					ep2_stall();			   } else {					printk( "%sset feature \"endpoint halt\" "							"on unsupported ep # %d\n",							pszMe, ep );			   }		  }		  else {			   printk( "%sUnsupported feature selector "					   "(%d) in set feature\n",					   pszMe, req.wValue );		  }		  set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */		  break;		  /* The rest have a data phase that writes back to the host */	 case GET_STATUS:		  /* return status bit flags */		  status_buf[0] = status_buf[1] = 0;		  n = request_target(req.bmRequestType);		  switch( n ) {		  case kTargetDevice:			   if ( self_powered_hook() )					status_buf[0] |= 1;			   break;		  case kTargetInterface:			   break;		  case kTargetEndpoint:			   /* return stalled bit */			   n = windex_to_ep_num( req.wIndex );			   if ( n == 1 )					status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4;			   else if ( n == 2 )					status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5;			   else {					printk( "%sUnknown endpoint (%d) "							"in GET_STATUS\n", pszMe, n );			   }			   break;		  default:			   printk( "%sUnknown target (%d) in GET_STATUS\n",					   pszMe, n );			   /* fall thru */			   break;		  }		  cs_bits  = queue_and_start_write( status_buf,											req.wLength,											sizeof( status_buf ) );		  set_cs_bits( cs_bits );		  break;	 case GET_DESCRIPTOR:		  get_descriptor( &req );		  break;	 case GET_CONFIGURATION:		  status_buf[0] = (usbd_info.state ==  USB_STATE_CONFIGURED)			   ? 1			   : 0;		  cs_bits = queue_and_start_write( status_buf, req.wLength, 1 );

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -