📄 usb_ep0.c
字号:
set_cs_bits( cs_bits ); break; case GET_INTERFACE: printk( "%sfixme: get interface not supported\n", pszMe ); cs_bits = queue_and_start_write( NULL, req.wLength, 0 ); set_cs_bits( cs_bits ); break; case SET_INTERFACE: printk( "%sfixme: set interface not supported\n", pszMe ); set_cs_bits( UDCCS0_DE | UDCCS0_SO ); break; default : printk("%sunknown request 0x%x\n", pszMe, req.bRequest); break; } /* switch( bRequest ) */sh_sb_end: return;}/* * common_wrtie_preamble() * Called before execution of sh_write() or sh_write_with_empty_packet() * Handles common abort conditions. * */static void common_write_preamble( void ){ /* If "setup end" has been set, the usb controller has ..terminated a setup transaction before we set DE. This ..happens during enumeration with some hosts. For example, ..the host will ask for our device descriptor and specify ..a return of 64 bytes. When we hand back the first 8, the ..host will know our max packet size and turn around and ..issue a new setup immediately. This causes the UDC to auto-ack ..the new setup and set SE. We must then "unload" (process) ..the new setup, which is what will happen after this preamble ..is finished executing. */ __u32 cs_reg_in = Ser0UDCCS0; if ( cs_reg_in & UDCCS0_SE ) { PRINTKD( "%swrite_preamble(): Early termination of setup\n", pszMe ); Ser0UDCCS0 = UDCCS0_SSE; /* clear setup end */ current_handler = sh_setup_begin; } if ( cs_reg_in & UDCCS0_SST ) { PRINTKD( "%swrite_preamble(): UDC sent stall\n", pszMe ); Ser0UDCCS0 = UDCCS0_SST; /* clear setup end */ current_handler = sh_setup_begin; } if ( cs_reg_in & UDCCS0_OPR ) { PRINTKD( "%swrite_preamble(): see OPR. Stopping write to " "handle new SETUP\n", pszMe ); /* very rarely, you can get OPR and leftover IPR. Try to clear */ UDC_clear( Ser0UDCCS0, UDCCS0_IPR ); current_handler = sh_setup_begin; }}/* * sh_write() * This is the setup handler when we are in the data return phase of * a setup request and have as much (or more) data than the host * requested. If we enter this routine and bytes left is zero, the * last data packet has gone (int is because IPR was just cleared) * so we just set DE and reset. Otheriwse, we write another packet * and set IPR. */static void sh_write(){ PRINTKD( "W\n" ); if ( Ser0UDCCS0 & UDCCS0_IPR ) { PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe ); return; } /* If bytes left is zero, we are coming in on the ..interrupt after the last packet went out. And ..we know we don't have to empty packet this transfer ..so just set DE and we are done */ if ( 0 == wr.bytes_left ) { /* that's it, so data end */ set_de(); wr.p = NULL; /* be anal */ current_handler = sh_setup_begin; } else { /* Otherwise, more data to go */ write_fifo(); set_ipr(); }}/* * sh_write_with_empty_packet() * This is the setup handler when we don't have enough data to * satisfy the host's request. After we send everything we've got * we must send an empty packet (by setting IPR and DE) so the * host can perform "short packet retirement" and not stall. * */static void sh_write_with_empty_packet( void ){ __u32 cs_reg_out = 0; PRINTKD( "WE\n" ); if ( Ser0UDCCS0 & UDCCS0_IPR ) { PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe ); return; } /* If bytes left is zero, we are coming in on the ..interrupt after the last packet went out. ..we must do short packet suff, so set DE and IPR */ if ( 0 == wr.bytes_left ) { set_ipr_and_de(); wr.p = NULL; current_handler = sh_setup_begin; PRINTKD( "%ssh_write empty() Sent empty packet \n", pszMe ); } else { write_fifo(); /* send data */ set_ipr(); /* flag a packet is ready */ } Ser0UDCCS0 = cs_reg_out;}/***************************************************************************Other Private Subroutines***************************************************************************//* * queue_and_start_write() * p == data to send * req == bytes host requested * act == bytes we actually have * Returns: bits to be flipped in ep0 control/status register * * Called from sh_setup_begin() to begin a data return phase. Sets up the * global "wr"-ite structure and load the outbound FIFO with data. * If can't send all the data, set appropriate handler for next interrupt. * */static __u32 queue_and_start_write( void * in, int req, int act ){ __u32 cs_reg_bits = UDCCS0_IPR; unsigned char * p = (unsigned char*) in; PRINTKD( "Qr=%d a=%d\n",req,act ); /* thou shalt not enter data phase until the serviced OUT is clear */ if ( ! clear_opr() ) { printk( "%sSO did not clear OPR\n", pszMe ); return ( UDCCS0_DE | UDCCS0_SO ) ; } wr.p = p; wr.bytes_left = MIN( act, req ); write_fifo(); if ( 0 == wr.bytes_left ) { cs_reg_bits |= UDCCS0_DE; /* out in 1 so data end */ wr.p = NULL; /* be anal */ } else if ( act < req ) /* we are going to short-change host */ current_handler = sh_write_with_empty_packet; /* so need nul to not stall */ else /* we have as much or more than requested */ current_handler = sh_write; return cs_reg_bits; /* note: IPR was set uncondtionally at start of routine */}/* * write_fifo() * Stick bytes in the 8 bytes endpoint zero FIFO. * This version uses a variety of tricks to make sure the bytes * are written correctly. 1. The count register is checked to * see if the byte went in, and the write is attempted again * if not. 2. An overall counter is used to break out so we * don't hang in those (rare) cases where the UDC reverses * direction of the FIFO underneath us without notification * (in response to host aborting a setup transaction early). * */static void write_fifo( void ){ int bytes_this_time = MIN( wr.bytes_left, 8 ); int bytes_written = 0; int i=0; PRINTKD( "WF=%d: ", bytes_this_time ); while( bytes_this_time-- ) { PRINTKD( "%2.2X ", *wr.p ); i = 0; do { Ser0UDCD0 = *wr.p; udelay( 20 ); /* voodo 28Feb01ww */ i++; } while( Ser0UDCWC == bytes_written && i < 10 ); if ( i == 50 ) { printk( "%swrite_fifo: write failure\n", pszMe ); usbd_info.stats.ep0_fifo_write_failures++; } wr.p++; bytes_written++; } wr.bytes_left -= bytes_written; /* following propagation voodo so maybe caller writing IPR in ..a moment might actually get it to stick 28Feb01ww */ udelay( 300 ); usbd_info.stats.ep0_bytes_written += bytes_written; PRINTKD( "L=%d WCR=%8.8X\n", wr.bytes_left, Ser0UDCWC );}/* * read_fifo() * Read 1-8 bytes out of FIFO and put in request. * Called to do the initial read of setup requests * from the host. Return number of bytes read. * * Like write fifo above, this driver uses multiple * reads checked agains the count register with an * overall timeout. * */static intread_fifo( usb_dev_request_t * request ){ int bytes_read = 0; int fifo_count; int i; unsigned char * pOut = (unsigned char*) request; fifo_count = ( Ser0UDCWC & 0xFF ); ASSERT( fifo_count <= 8 ); PRINTKD( "RF=%d ", fifo_count ); while( fifo_count-- ) { i = 0; do { *pOut = (unsigned char) Ser0UDCD0; udelay( 10 ); } while( ( Ser0UDCWC & 0xFF ) != fifo_count && i < 10 ); if ( i == 10 ) { printk( "%sread_fifo(): read failure\n", pszMe ); usbd_info.stats.ep0_fifo_read_failures++; } pOut++; bytes_read++; } PRINTKD( "fc=%d\n", bytes_read ); usbd_info.stats.ep0_bytes_read++; return bytes_read;}/* * get_descriptor() * Called from sh_setup_begin to handle data return * for a GET_DESCRIPTOR setup request. */static void get_descriptor( usb_dev_request_t * pReq ){ __u32 cs_bits = 0; string_desc_t * pString; ep_desc_t * pEndpoint; desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); int type = pReq->wValue >> 8; int idx = pReq->wValue & 0xFF; switch( type ) { case USB_DESC_DEVICE: cs_bits = queue_and_start_write( &pDesc->dev, pReq->wLength, pDesc->dev.bLength ); break; // return config descriptor buffer, cfg, intf, 2 ep case USB_DESC_CONFIG: cs_bits = queue_and_start_write( &pDesc->b, pReq->wLength, sizeof( struct cdb ) ); break; // not quite right, since doesn't do language code checking case USB_DESC_STRING: pString = sa1100_usb_get_string_descriptor( idx ); if ( pString ) { if ( idx != 0 ) { // if not language index printk( "%sReturn string %d: ", pszMe, idx ); psdesc( pString ); } cs_bits = queue_and_start_write( pString, pReq->wLength, pString->bLength ); } else { printk("%sunkown string index %d Stall.\n", pszMe, idx ); cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); } break; case USB_DESC_INTERFACE: if ( idx == pDesc->b.intf.bInterfaceNumber ) { cs_bits = queue_and_start_write( &pDesc->b.intf, pReq->wLength, pDesc->b.intf.bLength ); } break; case USB_DESC_ENDPOINT: /* correct? 21Feb01ww */ if ( idx == 1 ) pEndpoint = &pDesc->b.ep1; else if ( idx == 2 ) pEndpoint = &pDesc->b.ep2; else pEndpoint = NULL; if ( pEndpoint ) { cs_bits = queue_and_start_write( pEndpoint, pReq->wLength, pEndpoint->bLength ); } else { printk("%sunkown endpoint index %d Stall.\n", pszMe, idx ); cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); } break; default : printk("%sunknown descriptor type %d. Stall.\n", pszMe, type ); cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); break; } set_cs_bits( cs_bits );}/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE )#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS ))#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE)static void set_cs_bits( __u32 bits ){ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST ) ) Ser0UDCCS0 = bits; else if ( (bits & BOTH_BITS) == BOTH_BITS ) set_ipr_and_de(); else if ( bits & UDCCS0_IPR ) set_ipr(); else if ( bits & UDCCS0_DE ) set_de();}static void set_de( void ){ int i = 1; while( 1 ) { if ( OK_TO_WRITE ) { Ser0UDCCS0 |= UDCCS0_DE; } else { PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe ); break; } if ( Ser0UDCCS0 & UDCCS0_DE ) break; udelay( i ); if ( ++i == 50 ) { printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n", pszMe, UDCCS0_DE, Ser0UDCCS0 ); break; } }}static void set_ipr( void ){ int i = 1; while( 1 ) { if ( OK_TO_WRITE ) { Ser0UDCCS0 |= UDCCS0_IPR; } else { PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe ); break; } if ( Ser0UDCCS0 & UDCCS0_IPR ) break; udelay( i ); if ( ++i == 50 ) { printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n", pszMe, UDCCS0_IPR, Ser0UDCCS0 ); break; } }}static void set_ipr_and_de( void ){ int i = 1; while( 1 ) { if ( OK_TO_WRITE ) { Ser0UDCCS0 |= BOTH_BITS; } else { PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe ); break; } if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) break; udelay( i ); if ( ++i == 50 ) { printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n", pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 ); break; } }}static bool clear_opr( void ){ int i = 10000; bool is_clear; do { Ser0UDCCS0 = UDCCS0_SO; is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR ); if ( i-- <= 0 ) { printk( "%sclear_opr(): failed\n", pszMe ); break; } } while( ! is_clear ); return is_clear;}/* end usb_ep0.c */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -