www.pudn.com > SMSC USB2.0.zip > mscbot.c


/*============================================================================ 
  ____________________________________________________________________________ 
                                ______________________________________________ 
   SSSS  M   M          CCCC          Standard Microsystems Corporation 
  S      MM MM   SSSS  C                    Austin Design Center 
   SSS   M M M  S      C                 11000 N. Mopac Expressway 
      S  M   M   SSS   C                Stonelake Bldg. 6, Suite 500 
  SSSS   M   M      S   CCCC                Austin, Texas 78759 
                SSSS            ______________________________________________ 
  ____________________________________________________________________________ 
 
  Copyright(C) 1999, Standard Microsystems Corporation 
  All Rights Reserved. 
 
  This program code listing is proprietary to SMSC and may not be copied, 
  distributed, or used without a license to do so.  Such license may have 
  Limited or Restricted Rights. Please refer to the license for further 
  clarification. 
  ____________________________________________________________________________ 
 
  Notice: The program contained in this listing is a proprietary trade 
  secret of SMSC, Hauppauge, New York, and is copyrighted 
  under the United States Copyright Act of 1976 as an unpublished work, 
  pursuant to Section 104 and Section 408 of Title XVII of the United 
  States code. Unauthorized copying, adaption, distribution, use, or 
  display is prohibited by this law. 
  ____________________________________________________________________________ 
 
  Use, duplication, or disclosure by the Government is subject to 
  restrictions as set forth in subparagraph(c)(1)(ii) of the Rights 
  in Technical Data and Computer Software clause at DFARS 52.227-7013. 
  Contractor/Manufacturer is Standard Microsystems Corporation, 
  80 Arkay Drive, Hauppauge, New York, 1178-8847. 
  ____________________________________________________________________________ 
  ____________________________________________________________________________ 
 
  mscbot.c - mass storage class bulk only transport 
  ____________________________________________________________________________ 
 
  comments tbd 
  ____________________________________________________________________________ 
 
  Revision History 
  Date      Who  Comment 
  ________  ___  _____________________________________________________________ 
  01/15/01  tbh  initial version 
  03/17/01  tbh  upgraded to use MinimOS-2.1 
  07/27/01  tbh  modulatized during port to '201 (was usbdp.c, now split apart) 
  09/20/01  tbh  made use of new sir_rd_configuration() port to 20x allowing 
                 elimination of some #ifdefs 
  09/##/01  tbh  optimized for speed 
  11/27/01  cds  enabled the data mux for the lun after validating CBW but 
                 before processing the cb. 
  12/04/01  tbh  check lun media flags after sending ststus to see if need 
                 to get out back to dev thread to re-establish the media. 
  02/26/02  tbh  added logical to physical lun mapping 
  03/11/02  tbh  added bit g_force_card_establish. on error on current card 
                 clear devce type to none so card wil be reset/initialized. 
  03/25/02  DS   Sometimes after we set the bit to stall the RX Pipe, we still get an outnak before 
                 the stall is realized. This is filtered out in  mscbot_wait_status_end(). 
  04/01/02  tbh  adjusted the leds to indicate the phy lun, sd and mmc combined 
  06/10/02  tbh  rewrote it as high priority thread (chained isr's) instead of 
                 foreground thread to reduce cbw/csw processing overhead. 
  06/20/02  tbh  only stall the rx pipe if there was an error AND there is residue 
                 AND the residue is more than 512 bytes.  this prevents seeing 
                 a stall on the next cbw. 
  06/21/02  ds   Windows 2000 has been noted to reset the device if a stall is issued on a successful read 
                 with a residue. So, changed it to issue a stall only on a failure. 
  06/24/02  tbh  tweaked code for k_pfm_led to get the SD activity light correct. 
  06/28/02  tbh/ds added _stall_ndp2_rx flag to know if writes need to be stalled 
                 since the presence of residue is insufficient to decide. 
                 _endpoint_unmask_in/outnak was broken.  it did not clear the 
                 correct interrupt bit.  it is fixed (knock on wood). 
  07/10/02  tbh  modified cpex to handle dfu requests 
  07/18/02  tbh  don't diddle leds in fgnd_hpbot_wait_create. 
                 also, no longed uses or manipulates g_active_lun, nor does it 
                 touch the fmc mux.  (it uses a new local _active_lun). 
  07/23/02  baw  increased the maximum allowed length of the cdb to 16, 
                 because password commands require 16 bytes 
  07/30/02  ds   Moved _endpoint_unmask_innak(k_rx_pipe) macro to mscbot.h so that sd.c can access it. 
  08/07/02  tbh  added _endpoint_unmask_outnak(k_rx_pipe) to be resume handler 
                 to ensure hearing outnak after suspend 
  09/06/02  ds   Implemented the 'custom' blinking light for data access, which is controlled from the 
                 high priority fns of data access. Refer the header for device.c for a detailed explanation 
  09/07/02  tbh  renames some globals to have meaningful names 
  09/20/02  tbh  modified activility led actions to use new functions. 
  09/22/02  tbh  eliminated unused code on 242 builds (dfu optional) 
  09/26/02  cds  added mscbot_tx_data_buffer() function to send simple small 
                 data buffers as data-phase response for commands... the function 
                 has some 32-bit calculations and minimos calls that make it 
                 somewhat code-heavy, and having this central location results 
                 in approximately 1K of code savings...   
  09/30/02  ds   Fixed the bug where the activity led goes to its idle state on suspend. This caused 
                 it to stay on if the idle state is to be ON. Changes to code: 
                 -Removed the call to dev_turn_off_activity_indicator from fgnd_hpbot_wait_create() 
  10/01/02  ds   Added a new funciton mscbot_rx_data_buffer to receive data on bulk pipe. 
  10/02/02  ds   Modified mscbot_rx_data_buffer so that we will wait for the data packet and return a timeout error 
                 on an error condition.   
  10/02/02  ds   buffer_rx_enable should be a macro.Rolling back to previous form of code. 
  10/02/02  ds   fixed mscbot_rx_data_buffer to strictly receive data on buffer B                              
  10/02/02  cds  moved g_bot_cbw into xdata to regain data/stack space to prevent  
                 stack overflows on hierarchy-heavy media protocols. 
============================================================================*/ 
#include "project.h" 
#include "dev.h" 
 
//------------------------------------------------------------------------------ 
// exported globals 
xdata t_cbw   g_bot_cbw;      // command block wrapper 
t_udw32 g_bot_data_len; // number of bytes to transfer 
t_xfer_dir g_bot_xfer_dir;  // k_dir_read or k_dir_write 
 
//------------------------------------------------------------------------------ 
// private 
static uint8 _active_lun;  // the lun for the current cbw/csw 
static uint8 _status;   // mscbot transfer status 
static bit _tx_missed_ack;  // if true must rexmit previous csw 
static bit _ndp2_rx_stalled;  // can't read register bit since fw doesnt process clear stall 
                              // and it could happed before we check the stall bit in ep2ctl. 
static bit _stall_ndp2_rx;  // set in wait_status_start on an outnak 
 
static bit _data_access = k_false;  //flag to tell if it is a data access cmd... there is a better way, but 
                                    //but I am in a hurry 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
#define _endpoint_unmask_outnak(k_rx_pipe);                                    \ 
{                                                                              \ 
  _mcu_register_wr(x_isr_nak, 1 << (2 * k_rx_pipe));                           \ 
  _mcu_register_clrbit(x_imr_nak, 2 * k_rx_pipe);                              \ 
} 
 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
#define _endpoint_tx_enable(k_tx_pipe);                                        \ 
{                                                                              \ 
  _mcu_register_set_bits(x_ep2_ctl, kbm_ep2_ctl_dir);                          \ 
} 
 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
#define _endpoint_rx_enable(k_rx_pipe);                                        \ 
{                                                                              \ 
  _mcu_register_clr_bits(x_ep2_ctl, kbm_ep2_ctl_dir);                          \ 
  _mcu_register_set_bits(x_isr0, kbm_isr0_ramwr_a |kbm_isr0_ramwr_b);          \ 
  _mcu_register_clr_bits(x_imr0, kbm_isr0_ramwr_a |kbm_isr0_ramwr_b);          \ 
  g_endpoint[2].rxpnr = 0;                                                     \ 
} 
 
 
//------------------------------------------------------------------------------ 
// Declaration: 
//   t_result mscbot_mngr(t_message_ref msgp); 
// 
// Purpose: 
//   This is the manager for all things related to the mscbot interface. 
// 
// Arguments: 
//   msgp - a reference to a message 
// 
// Return: 
//   k_success 
//   K-error 
// 
// Note: 
//   Do NOT yield from this routine.  It is NOT part of any thread.  It does 
//   not execute in an any thread's context.  Yielding from within this function 
//   will cause your firmware to crash. 
// 
// Since: 
//   atapi-2.0 
//------------------------------------------------------------------------------ 
t_result mscbot_mngr(t_message_ref msgp) reentrant 
{ 
  trace0(0, bot, 0, "mscbot_mngr()"); 
  switch (message_rd_id(msgp)) 
  { 
    //-------------------------------------------------------------------------- 
    // always return k_success for these. 
    //-------------------------------------------------------------------------- 
    case k_msg_initialize: 
      trace0(0, bot, 0, "k_msg_initialize"); 
      // create threads - mscbot could run on its own thread, but in this case 
      // its being run on the device thread. 
      //g_ix_mscbot_thread = thread_create(mscbot_thread_wait_create, NULL, k_yes); 
      // register endpoints - do nothing here as only the default control pipe is used 
      ctl_bind_endpoint(1, g_ix_dev_thread, ctl_cpex);  // unused interrupt pipe... 
      ctl_bind_endpoint(k_tx_pipe, g_ix_dev_thread, ctl_cpex); 
      // register interfaces - must jive with the configuration descriptor... 
      ctl_bind_interface(0, mscbot_cpex); 
      // init the isr hooks $$$. 
      _isr_bind_abort_sync(kbm_sync_abort); 
      _isr_bind_dma_owner(0, kbm_sync_none); 
      return (k_success); 
 
    case k_msg_usbrst: 
      trace0(0, bot, 0, "k_msg_usbrst"); 
      return (k_success); 
 
    case k_msg_resume: 
      trace0(0, bot, 0, "k_msg_resume"); 
      // 08/07/02 tbh added this line to be sure to hear outnak after suspend 
      _endpoint_unmask_outnak(k_rx_pipe); 
      return (k_success); 
 
    case k_msg_suspend: 
      trace0(0, bot, 0, "k_msg_suspend"); 
      // put the pipe into the tx direction so it will nak any cbw's until 
      // after the set-config is processed by the udc core AND it is noticed and 
      // processed by the foreground.  msc_bot_wait_pipe_ready_for_rx() 
      // will keep waking on outnaks and re-arming itself if the set-config 
      // has not been fully processed. 
      // this prevents on resume receiving a cbw before processing set-config 
      // thus sending the drive a command before it is powered on. 
      _endpoint_tx_enable(k_tx_pipe); 
      return (k_success); 
  } 
  // unexpected message 
  trace0(0, bot, 0, "error"); 
  return (k_error); 
} 
 
//------------------------------------------------------------------------------ 
// Declaration: 
//   t_result mscbot_cpex(t_message_ref msgp); 
// 
// Purpose: 
//   Handles requests targeted to its interface: standard, class, vendor. 
// 
// Arguments: 
//   msgp - a reference to a message 
// 
// Return: 
//   k_success  - Causes the protocol engine to complete the status stage successfully. 
//   k_error - Causes the protocol engine to stall the control pipe. 
//   k_in_progress - For k_msg_source_payload, causes the protocol engine to deliver 
//     another empty packet buffer to the cpex to be loaded for transmission. 
//     For k_msg_sink_payload, informs the protocol engine that the cpex expects at 
//     least one more payload buffer to be delivered to it by the protocol engine. 
//   k_finished - For k_msg_source_payload and k_msg_sink_payload, informs the 
//     protocol engine that the data phase is complete and no more data is expected 
//     in either direction. 
// 
// Note: 
//   Do NOT yield from this routine.  It is part of the protocol engine's thread. 
//   It executes in the context of g_ix_ctl_thread.  Yielding from within this 
//   function will cause the protocol engine's state machine to float belly up. 
//   And that will cause your firmware to shuffle off this mortal coil, post haste. 
// 
// Since: 
//   atapi-2.0 
//------------------------------------------------------------------------------ 
t_result mscbot_cpex(t_message_ref msgp) reentrant 
{ 
  uint8 pktsz; 
  uint8 pnr; 
  t_usb_rqst *rqstp; 
 
  rqstp = (t_usb_rqst *)message_rd_arg(msgp); 
  switch (message_rd_id(msgp)) 
  { 
    // standard requests 
    case k_dsi_usb_get_status: 
      trace0(0, nil, 0, "RQ_GET_STATUS: interface"); 
      // interface status is always 0 per usb spec 
      g_wtmp = 0; 
      // tell the data pump where to find it and how much there is 
      _payload_source(&g_wtmp, 2); 
      return (k_success); 
 
    case k_hsi_usb_clr_feature: 
      trace0(0, nil, 0, "RQ_CLEAR_FEATURE: interface?"); 
      // this is always an error per the usb spec 
      return (k_error); 
 
    case k_hsi_usb_set_feature: 
      trace0(0, nil, 0, "RQ_SET_FEATURE: interface?"); 
      // this is always an error per the usb spec 
      return (k_error); 
 
    case k_dsi_usb_get_interface: 
      trace0(0, nil, 0, "RQ_GET_INTERFACE"); 
      // this interface has no alternate settings 
      g_tmp = 0; 
      // tell the data pump where to find it and how much there is 
      _payload_source(&g_tmp, 1); 
      return (k_success); 
 
    case k_hsi_usb_set_interface: 
      trace0(0, bot, 0, "RQ_SET_INTERFACE"); 
      // this interface has no alternate settings, only 0 as a selection 
      if (rqstp->wValueLo) 
        return (k_error); 
      return (k_success); 
 
      // class specific requests 
    case k_hci_mscbot_reset: 
      // perform the class specific soft reset 
      trace0(0, bot, 0, "RQ k_hci_mscbot_reset"); 
      return (k_success); 
 
    case k_hci_mscbot_getmaxlun: 
      trace0(0, bot, 0, "RQ k_hci_mscbot_getmaxlun"); 
      g_tmp = k_max_log_lun-1; 
      trace1(0, bot, 0, "  maxlun:%d)", g_tmp); 
      // tell the data pump where to find it and how much there is 
      _payload_source(&g_tmp, 1); 
      return (k_success); 
 
    #ifdef k_opt_dfu 
 
      // dfu class specific message - must be intercepted here has a hack 
      // since the chip only has one physical interface so there cannot be 
      // a dangling dfu interface 
    case k_hci_dfu_detach: 
      trace0(0, dfu, 0, "mscbot_cpex intercepting: k_hci_dfu_detach"); 
      return (dfu_cpex(msgp)); 
    #endif 
 
    #ifdef k_opt_dfu 
    case k_msg_transaction_done: 
      trace0(0, dfu, 0, "mscbot_cpex routing k_msg_transaction_done to dfu_cpex"); 
      return (dfu_cpex(msgp)); 
    #endif 
 
 
      // os messages 
    case k_msg_source_payload: 
      // load some application specific data into a packet buffer 
      // return k_in_progress if app expects to supply at least one more packet's worth of data 
      // return k_finished otherwise 
      pnr = *(uint8 *)message_rd_arg(msgp); 
      pktsz = _min(g_data_len, k_maxpktsz); 
      g_data_len -= pktsz; 
      mmu_wr_pkt(0, pnr, g_source_addr, pktsz); 
      g_source_addr += pktsz; 
      return (g_data_len ? k_in_progress : k_finished); 
 
    case k_msg_sink_payload: 
      // unload data from a packet buffer and do something application specific with it 
      // return k_in_progress if app can process this request 
      // return k_finished otherwise 
      pnr = *(uint8 *)message_rd_arg(msgp); 
      pktsz = _min(g_data_len, k_maxpktsz); 
      g_data_len -= pktsz; 
      mmu_rd_pkt(pnr, pktsz, g_sink_addr); 
      g_sink_addr += pktsz; 
      return (g_data_len ? k_in_progress : k_finished); 
 
    default: 
      trace0(0, bot, 0, "intr ignored"); 
      return (k_error); 
  } 
  return (k_error); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
#define _hdw_clk_domain_bug_workaround() { _nop_(); _nop_(); } 
 
t_isr_fragment _hpbot_thread_entry; 
static t_result hpbot_wait_create(void) reentrant; 
static t_result hpbot_wait_pipe_ready_for_rx(uint8) reentrant; 
static t_result hpbot_wait_cbw(uint8) reentrant; 
static t_result hpbot_wait_status_start(uint8) reentrant; 
static t_result hpbot_wait_status_end(uint8) reentrant; 
 
//------------------------------------------------------------------------------ 
// Declaration: 
//  static void ndp2_tx_enable_ex(void); 
// 
// Purpose: 
//  Error recovery - retransmit csw after a dropped ack from the host error on the csw. 
// 
// Arguments: 
//  None. 
// 
// Return: 
//  None. 
// 
// Note: 
//  Only call from interrupt level. 
// 
// Since: 
//   atapi-2.0 
//------------------------------------------------------------------------------ 
static xdata t_csw _x_csw; 
static void ndp2_tx_enable_ex(void) reentrant; 
static void ndp2_tx_enable_ex() reentrant using 1 
{ 
  _endpoint_tx_enable(k_tx_pipe); 
  // 
  if (!_tx_missed_ack) 
    return; 
  // off the thin diagonal - missed the ack of previous csw transmission. 
  // must retransmit to get toggles resequenced. 
  trace0(0, bot, 0, "warning - missed ack on prev csw xmit.  retransmit archival copy."); 
  // dont need to copy if reuse same buf, but speed not important for this case 
  mmu_wr_pkt(2, 5, (t_memory_ref)&_x_csw, sizeof(t_csw)); 
  txfifo_wr(2, 5); 
  // enter a timer-protected synchronous loop. 
  thread_set_timer(50); 
  // if it doesn't go out in 50msec then its already too late - device is "dead" to the host 
  while (!thread_got_sync(kbm_sync_usbtx)) 
  { 
    if (thread_got_sync(kbm_sync_timer)) 
    { 
      trace0(0, bot, 0, "error - timeout waiting for transmit"); 
      return; 
    } 
  } 
  _tx_missed_ack = k_no; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
static t_result hpbot_send_status(uint8 status) reentrant using 1 
{ 
  trace0(0, bot, 0, "hpbot_send_status()"); 
  // ready for transmit, make sure toggles are sequenced. 
  if (_tx_missed_ack) 
    ndp2_tx_enable_ex(); 
  else 
  { 
    _endpoint_tx_enable(k_tx_pipe); 
  } 
  // fill in the archival csw to use for retransmit in case of rare dropped ack bus error 
  _x_csw.dwCSWSignatureLo = 0x55; 
  _x_csw.dwCSWSignatureHl = 0x53; 
  _x_csw.dwCSWSignatureLh = 0x42; 
  _x_csw.dwCSWSignatureHi = 0x53; 
  _x_csw.dwCSWtagLo = g_bot_cbw.dwCBWtagLo; 
  _x_csw.dwCSWtagHl = g_bot_cbw.dwCBWtagHl; 
  _x_csw.dwCSWtagLh = g_bot_cbw.dwCBWtagLh; 
  _x_csw.dwCSWtagHi = g_bot_cbw.dwCBWtagHi; 
  _x_csw.dwCSWResidueLo = g_bot_data_len.u8.lo; 
  _x_csw.dwCSWResidueHl = g_bot_data_len.u8.hl; 
  _x_csw.dwCSWResidueLh = g_bot_data_len.u8.lh; 
  _x_csw.dwCSWResidueHi = g_bot_data_len.u8.hi; 
  _x_csw.bCSWStatus = status; 
  trace1(0, bot, 0, "hpbot_send_status(%02X)", status); 
  trace1(0, bot, 0, " csw.dwCSWResidueLo %02X (lsw lsb)", _x_csw.dwCSWResidueLo); 
  trace1(0, bot, 0, " csw.dwCSWResidueHl %02X (lsw lsb)", _x_csw.dwCSWResidueHl); 
  trace1(0, bot, 0, " csw.dwCSWResidueLh %02X (lsw lsb)", _x_csw.dwCSWResidueLh); 
  trace1(0, bot, 0, " csw.dwCSWResidueHi %02X (lsw lsb)", _x_csw.dwCSWResidueHi); 
 
  // still on the thin diagonal 
  trace0(0, bot, 5, "ready to transmit status on the thin diagonal"); 
  trace0(0, bot, 6, "direction ==> transmit"); 
  _mcu_register_set_bits(x_ep2_ctl, kbm_ep2_ctl_dir); 
  _hdw_clk_domain_bug_workaround(); 
  _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[4]); 
  _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[4]); 
  _hdw_clk_domain_bug_workaround(); 
  _mcu_register_wr(x_sram_data, 0x55); 
  _mcu_register_wr(x_sram_data, 0x53); 
  _mcu_register_wr(x_sram_data, 0x42); 
  _mcu_register_wr(x_sram_data, 0x53); 
  _mcu_register_wr(x_sram_data, g_bot_cbw.dwCBWtagLo); 
  _mcu_register_wr(x_sram_data, g_bot_cbw.dwCBWtagHl); 
  _mcu_register_wr(x_sram_data, g_bot_cbw.dwCBWtagLh); 
  _mcu_register_wr(x_sram_data, g_bot_cbw.dwCBWtagHi); 
  _mcu_register_wr(x_sram_data, g_bot_data_len.u8.lo); 
  _mcu_register_wr(x_sram_data, g_bot_data_len.u8.hl); 
  _mcu_register_wr(x_sram_data, g_bot_data_len.u8.lh); 
  _mcu_register_wr(x_sram_data, g_bot_data_len.u8.hi); 
  _mcu_register_wr(x_sram_data, status); 
  _mcu_register_wr(x_ramrdbc_a2, 13); 
  _mcu_register_wr(x_ramrdbc_a1, 0); 
  // determine if the pipe needs to be stalled 
  if (g_bot_xfer_dir == k_dir_read) 
  { 
    // reads:  stall if error OR residue 
    if (status || g_bot_data_len.u32) 
    { 
      trace0(0, bot, 0, "stalling the tx pipe"); 
      endpoint_tx_stall(k_tx_pipe); 
    } 
  } 
  else                  // g_bot_xfer_dir == k_dir_write 
  { 
    // writes: stall if error AND residue (another out token is expected to see the stall) 
    //if (status && (g_bot_data_len.u32 > 512L)) 
    if (status && _stall_ndp2_rx) 
    { 
      // only stall the out-pipe if there is residue, otherwise the host 
      // won't see the stall until it tries to send the CBW.  That violates the 
      // protocol and the host resets us. 
      trace0(0, bot, 99, "alert - stalling the rx pipe"); 
      _ndp2_rx_stalled = k_yes; 
      endpoint_rx_stall(k_rx_pipe); 
    } 
  } 
  _stall_ndp2_rx = k_no; 
  // if you hafta stall, do it before que-ing the packet... 
  // txfifo_wr - transmit - tx from buffer 'a' 
  trace0(0, bot, 7, "hit the transmit bit - ramrd_a ==> enabled, masked in (because the csw must be sent)"); 
  _mcu_register_wr(x_ep2_ctl, kbm_ep2_ctl_rdtog_valid |(_mcu_register_rd(x_ep2_ctl) & ~kbm_ep2_ctl_ramrd_tog)); 
  _hdw_clk_domain_bug_workaround(); 
  _mcu_register_clr_bits(x_imr0, kbm_isr0_ramrd_a); 
  //thread_set_timer(255);  $$$ 
  _endpoint_unmask_outnak(k_rx_pipe); 
  _hpbot_thread_entry = hpbot_wait_status_end; 
  #ifdef k_pfm_demo 
 
  // turn off the debug lights of science 
  _mcu_register_set_bits(x_gpiob_out, kbm_gpio12 |kbm_gpio13 |kbm_gpio14 |kbm_gpio15); 
  #endif 
  return (k_success); 
} 
 
//------------------------------------------------------------------------------ 
// this is only called from the foreground. 
// it is called only once at por. 
// it allows the fgnd to start the high priority thread's interrupt chain 
//------------------------------------------------------------------------------ 
void fgnd_hpbot_wait_create() reentrant 
{ 
  trace0(0, bot, 0, "fgnd_hpbot_wait_create()"); 
  _mcu_begin_critical_section(); 
  _endpoint_unmask_outnak(k_rx_pipe); 
  _hpbot_thread_entry = hpbot_wait_pipe_ready_for_rx; 
  _tx_missed_ack = k_no; 
  _mcu_end_critical_section(); 
} 
 
//------------------------------------------------------------------------------ 
// this is only called form the isrs 
//------------------------------------------------------------------------------ 
static t_result hpbot_wait_create() reentrant using 1 
{ 
  trace0(0, bot, 0, "hpbot_wait_create()"); 
  _tx_missed_ack = k_no; 
  _thread_clr_sync(kbm_sync_outnak); 
  _endpoint_unmask_outnak(k_rx_pipe); 
  dev_turn_off_activity_indicator(); 
  #ifdef k_pfm_demo 
 
  // turn off the debug lights of science 
  _mcu_register_set_bits(x_gpiob_out, kbm_gpio12 |kbm_gpio13 |kbm_gpio14 |kbm_gpio15); 
  #endif 
  #ifdef k_pfm_led 
 
  // turn off the activity LEDs 
  _mcu_register_clr_bits(x_gpiob_out, kbm_gpio12 |kbm_gpio13 |kbm_gpio14 |kbm_gpio15); 
  #endif 
  _hpbot_thread_entry = hpbot_wait_pipe_ready_for_rx; 
  return (k_success); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
static t_result hpbot_wait_pipe_ready_for_rx(uint8 intr) reentrant using 1 
{ 
  trace1(0, bot, 0, "hpbot_wait_pipe_ready_for_rx(%d)", intr); 
  switch (intr) 
  { 
    // outnak 
    case k_irq_nak2rx: 
      if (!dev_rd_most_recent_config()) 
      { 
        // do nothing but rearm for outnaks until the set-config has been processed. 
        // this avoids a race condition where the hardware processes the set-config(1) 
        // then the first cbw arrives and is delivered to the drive -before- the drive 
        // is powered on because the fgnd has not yet processed the set-config() 
        // and powered up the drive.  (the fw does not process the control pipe for set-config... 
        // the hardware does, and then gives an interrupt letting the fw see the config value... 
        // so, leave the pipe in the "por nakking" state, or, after suspend, the "tx" 
        // state so the cbw will be nakked, not received, until the drive gets powered 
        // on and initialized.  (note this requires that the suspend handler put the 
        // pipe into the "tx" state so any cbw will be nakked in the case where the 
        // resume interrupt occurs, then a usbreset, then a set-config, and a quickly 
        // following inquiry on the bulk pipe. 
        _endpoint_unmask_outnak(k_rx_pipe); 
        return (k_success); 
      } 
      // the outnak signals that the host is trying to send us a cbw 
      trace0(0, bot, 0, "got the nak, enabling rx endpoint"); 
      _endpoint_rx_enable(k_rx_pipe); 
      _buffer_rx_enable(4); 
      _hpbot_thread_entry = hpbot_wait_cbw; 
      return (k_success); 
 
      // usbrst 
    case k_irq_usb_stat_reset: 
    case k_irq_usb_reset: 
      _thread_clr_sync(kbm_sync_all); 
      hpbot_wait_create(); 
      // let the default interrupt handler see this also 
      return (k_ignored); 
 
      // let the default inrt handler handle everything else 
    default: 
      trace0(0, bot, 0, "intr ignored"); 
      return (k_ignored); 
  } 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
static t_result hpbot_wait_cbw(uint8 intr) reentrant using 1 
{ 
  // t_data_ref bufp; 
  t_xdata_ref bufp; 
 
  trace1(0, bot, 0, "hpbot_wait_cbw(%d)", intr); 
  switch (intr) 
  { 
    // usbrx:  profiling shows this case takes about 58usec 
    case k_irq_ramwr_a: 
      //_profile_on(5); 
      //mmu_rd_pkt(pnr, sizeof(t_cbw), (t_memory_ref)&g_bot_cbw); 
      // bufp = (t_data_ref)&g_bot_cbw; 
      bufp=(t_xdata_ref)&g_bot_cbw; 
      _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[4]); 
      _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[4]); 
      _hdw_clk_domain_bug_workaround(); 
      // unrolled loop, 31 bytes of cbw 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      *bufp++=_mcu_register_rd(x_sram_data); 
      //mmu_deallocate(4); 
      _mcu_register_wr(x_isr0, kbm_isr0_ramwr_a); 
      _mcu_register_clr_bits(x_imr0, kbm_isr0_ramwr_a); 
      // dump its contents 
      trace1(0, bot, 0, "dwCBWSignature: %02X (lsw lsb)", g_bot_cbw.dwCBWSignatureLo); 
      trace1(0, bot, 0, "dwCBWSignature: %02X (lsw msb)", g_bot_cbw.dwCBWSignatureHl); 
      trace1(0, bot, 0, "dwCBWSignature: %02X (msw lsb)", g_bot_cbw.dwCBWSignatureLh); 
      trace1(0, bot, 0, "dwCBWSignature: %02X (msw msb)", g_bot_cbw.dwCBWSignatureHi); 
      trace1(0, bot, 0, "      dwCBWTag: %02X (lsw lsb)", g_bot_cbw.dwCBWtagLo); 
      trace1(0, bot, 0, "      dwCBWTag: %02X (lsw msb)", g_bot_cbw.dwCBWtagHl); 
      trace1(0, bot, 0, "      dwCBWTag: %02X (msw lsb)", g_bot_cbw.dwCBWtagLh); 
      trace1(0, bot, 0, "      dwCBWTag: %02X (msw msb)", g_bot_cbw.dwCBWtagHi); 
      trace1(0, bot, 0, "  dwXferLength: %02X (lsw lsb)", g_bot_cbw.dwCBWXferLengthLo); 
      trace1(0, bot, 0, "  dwXferLength: %02X (lsw msb)", g_bot_cbw.dwCBWXferLengthHl); 
      trace1(0, bot, 0, "  dwXferLength: %02X (msw lsb)", g_bot_cbw.dwCBWXferLengthLh); 
      trace1(0, bot, 0, "  dwXferLength: %02X (msw msb)", g_bot_cbw.dwCBWXferLengthHi); 
      trace1(0, bot, 0, "       bCBWlun: %02X",           g_bot_cbw.bCBWlun); 
      trace1(0, bot, 0, "    bmCBWFlags: %02X",           g_bot_cbw.bCBWFlags); 
      trace1(0, bot, 0, "  bCBWCBLength: %02X",           g_bot_cbw.bCBWCBLength); 
      // verify the signature 
      if ((0x55 != g_bot_cbw.dwCBWSignatureLo) || 
          (0x53 != g_bot_cbw.dwCBWSignatureHl) || 
          (0x42 != g_bot_cbw.dwCBWSignatureLh) || 
          (0x43 != g_bot_cbw.dwCBWSignatureHi)) 
      { 
        trace0(0, bot, 0, "error - bad signature"); 
        return (hpbot_send_status(k_command_failed)); 
      } 
      // verify the cdb length 
      if (0x10 < g_bot_cbw.bCBWCBLength) 
      { 
        trace0(0, bot, 0, "error - bad cdb length"); 
        return (hpbot_send_status(k_command_failed)); 
      } 
      // initialize everything we might need to use 
      // extract the parameters from the command 
      g_bot_data_len.u8.lo = g_bot_cbw.dwCBWXferLengthLo; 
      g_bot_data_len.u8.hl = g_bot_cbw.dwCBWXferLengthHl; 
      g_bot_data_len.u8.lh = g_bot_cbw.dwCBWXferLengthLh; 
      g_bot_data_len.u8.hi = g_bot_cbw.dwCBWXferLengthHi; 
      // what direction is the data supposed to flow? 
      g_bot_xfer_dir = g_bot_cbw.bCBWFlags & kbm_cbw_dir_d2h ? k_dir_read : k_dir_write; 
      // reverse pipe for reads 
      if (g_bot_xfer_dir == k_dir_read) 
      { 
        if (_tx_missed_ack) 
          ndp2_tx_enable_ex(); 
        else 
        { 
          _endpoint_tx_enable(k_tx_pipe); 
        } 
      } 
      // convert the logical lun (from the cbw) into a physical lun (index into lun tables) 
      _active_lun = _lun_log2phy(g_bot_cbw.bCBWlun); 
      #ifdef k_pfm_demo 
 
      // there are 5 phy luns, but only 4 leds.  assumes sd and mmc "share" an led. 
      _mcu_register_clr_bits(x_gpiob_out, 0x10 << _min(3, _active_lun)); 
      #endif 
 
 
      // the mux is no longer enabled here because this is an isr. 
      // the mux is enabled in the dev thread when it wakes on sync-cbw. 
      // _lun_enable_mux(_active_lun)(); 
      // data access led on during read/write 
      switch (g_bot_cbw.cdb[0]) 
      { 
        case k_protocol_read_10: 
        case k_protocol_read_12: 
        case k_protocol_write_10: 
        case k_protocol_write_12: 
        case k_protocol_verify_10: 
          if (g_post_access_blink_secs)  //Are we blinking still? 
          { 
            irq_control(k_irq_cpu_t1,  kbm_irqctl_mask);  //then stop the timer 
            g_post_access_blink_secs = 0x00; 
            trace0(0, dev, 0, "next data access blinker off"); 
          } 
          if (g_blink_interval)  //Are we supposed to blink? 
          { 
            irq_control(k_irq_cpu_t1,  kbm_irqctl_unmask);  //then start the timer 
            _data_access = k_true; 
          } 
          //rcc code to turn OFF leds during access to respective luns LEDs are ON when card inserted 
          // they go off when card is out or blink off when accessed. 
          #ifdef k_pfm_led 
          // there are 5 phy luns, but only 4 leds.  assumes sd and mmc "share" an led. 
          //_mcu_register_clr_bits(x_gpiob_out, 0x10 << _min(3, _active_lun)); 
          if (_lun_is_media_present(_active_lun)) 
          { 
            switch (_active_lun) 
            { 
              case k_lun_cf: 
                trace0(0, dev, 66, "CF LED OFF!"); 
                _mcu_register_clr_bits(x_gpiob_out,  kbm_gpio13); 
                break; 
              case k_lun_ms: 
                trace0(0, dev, 66, "MS LED OFF!"); 
                _mcu_register_clr_bits(x_gpiob_out,  kbm_gpio12); 
                break; 
              case k_lun_sm: 
                trace0(0, dev, 66, "SM LED OFF!"); 
                _mcu_register_clr_bits(x_gpiob_out,  kbm_gpio14); 
                break; 
              case k_lun_sd: 
                trace0(0, dev, 66, "SD LED OFF!"); 
                _mcu_register_clr_bits(x_gpiob_out,  kbm_gpio15); 
                break; 
              case k_lun_mmc: 
                trace0(0, dev, 66, "MMC LED OFF!"); 
                _mcu_register_clr_bits(x_gpiob_out,  kbm_gpio15); 
                break; 
            } 
          } 
          #endif 
          break; 
      } 
      // break the interrupt chain until the foreground is done and restarts the chain 
      _hpbot_thread_entry = NULL; 
      // tell the fgnd to run the command 
      //thread_set_sync(g_ix_dev_thread, kbm_sync_cbw); 
      g_thread[g_ix_dev_thread].bits_got |= kbm_sync_cbw; 
      //_profile_off(5); 
      return (k_success); 
 
      // usbrst 
    case k_irq_usb_stat_reset: 
    case k_irq_usb_reset: 
      _thread_clr_sync(kbm_sync_all); 
      hpbot_wait_create(); 
      // let the default interrupt handler see this also 
      return (k_ignored); 
 
      // let the default inrt handler handle everything else 
    default: 
      trace0(0, bot, 0, "intr ignored"); 
      return (k_ignored); 
  } 
} 
 
//------------------------------------------------------------------------------ 
// this is only called from the foreground. 
// it is called immediately after processing a msc command. 
// it allows the fgnd to restart the high priority thread's 
// interrupt chain after the data phase of a transfer 
//------------------------------------------------------------------------------ 
void fgnd_hpbot_wait_status_start(uint8 rslt) reentrant 
{ 
  // wait to hear the innak before sending the status 
  trace1(0, bot, 0, "fgnd_hpbot_wait_status_start(%d)", rslt); 
  _mcu_begin_critical_section(); 
  _endpoint_unmask_innak(k_rx_pipe); 
  _endpoint_unmask_outnak(k_rx_pipe); 
  _hpbot_thread_entry = hpbot_wait_status_start; 
  _status = rslt; 
  _mcu_end_critical_section(); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
static t_result hpbot_wait_status_start(uint8 intr) reentrant using 1 
{ 
  trace1(0, bot, 0, "hpbot_wait_status_start(%d)", intr); 
  switch (intr) 
  { 
    // innak 
    case k_irq_nak2tx: 
      trace0(0, bot, 99, "innak"); 
      // the status stage cannot start on a read until the final data packet has been transmitted 
      // to the host, and ack'd.  this can be detected by checking to see that both ramrd a and b 
      // are set (ie, not in use, and/or waiting to be transmitted).  this helps avoids mistaking 
      // an innak for the data as the innak for the status.  in theory the status stage shouldn't 
      // even be run intil after all the data has been sent and ack'd by the host.  but, since we 
      // are going for minimum csw turnaround time, we want to send the status asap. the tricky 
      // thing here is knowing that the data stage has ended.  this code may not be perfected 
      // handle all error conditions yet... caveat implementor... 
      if (g_bot_xfer_dir == k_dir_read) 
        if ((_mcu_register_rd(x_isr0) & (kbm_isr0_ramrd_a |kbm_isr0_ramrd_b)) != (kbm_isr0_ramrd_a |kbm_isr0_ramrd_b)) 
        { 
          // a transmittable buffer is still in play... not time to send the csw yet... 
          _endpoint_unmask_innak(k_tx_pipe); 
          return (k_ignored); 
        } 
        // it is not safe to turn off autoxfer until after innak is heard. 
        // otherwise you could lose possible retransmit of the final data packet. 
        // it does not hurt to turn it off if it was already off though... 
      trace0(0, bot, 0, "AUTOXFER ==> OFF"); 
      _mcu_register_clr_bits(x_fmc_ctl, kbm_fmc_ctl_auto_trans); 
      _stall_ndp2_rx = k_no; 
      return (hpbot_send_status(_status)); 
 
      // outnak 
    case k_irq_nak2rx: 
      trace0(0, bot, 99, "outnak"); 
      // it is not safe to turn off autoxfer until after innak is heard. 
      // otherwise you could lose possible retransmit of the final data packet. 
      // it does not hurt to turn it off if it was already off though... 
      trace0(0, bot, 0, "AUTOXFER ==> OFF"); 
      _mcu_register_clr_bits(x_fmc_ctl, kbm_fmc_ctl_auto_trans); 
      _stall_ndp2_rx = k_yes; 
      return (hpbot_send_status(_status)); 
 
      // usbrst 
    case k_irq_usb_stat_reset: 
    case k_irq_usb_reset: 
      _thread_clr_sync(kbm_sync_all); 
      hpbot_wait_create(); 
      // let the default interrupt handler see this also 
      return (k_ignored); 
 
      // let the default intr handler handle everything else 
    default: 
      trace0(0, bot, 0, "intr ignored"); 
      return (k_ignored); 
  } 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
static uint8 _nakkount = 0; 
static t_result hpbot_wait_status_end(uint8 intr) reentrant using 1 
{ 
  trace1(0, bot, 0, "hpbot_wait_status_end(%d)", intr); 
  if (_data_access)     // Is this a data access cmd? 
  { 
    if (g_blink_interval)  //Were we blinking? 
    { 
      //do we need to continue blinking? 
      //we could do a direct read from nvstore but I believe that is a waste of time on this fast code 
      g_post_access_blink_secs = g_nvstore_post_access_blink_secs; 
      if (!g_post_access_blink_secs)  //No need to blink 
      { 
        irq_control(k_irq_cpu_t1,  kbm_irqctl_mask);  //then stop the timer 
      } 
      else 
      { 
        trace0(0, dev, 0, "blinker on"); 
      } 
    } 
    _data_access = k_false; 
  } 
  dev_turn_off_activity_indicator(); 
 
#ifdef k_pfm_led 
  if (_lun_is_media_present(_active_lun)) 
  { 
    switch (_active_lun) 
    { 
      case k_lun_cf: 
        trace0(0, dev, 66, "CF LED ON!"); 
        _mcu_register_set_bits(x_gpiob_out,  kbm_gpio13); 
        break; 
      case k_lun_ms: 
        trace0(0, dev, 66, "MS LED ON!"); 
        _mcu_register_set_bits(x_gpiob_out,  kbm_gpio12); 
        break; 
      case k_lun_sm: 
        trace0(0, dev, 66, "SM LED ON!"); 
        _mcu_register_set_bits(x_gpiob_out,  kbm_gpio14); 
        break; 
      case k_lun_sd: 
        if (_lun_is_media_known(_active_lun)) 
        { 
          trace0(0, dev, 66, "SD LED ON!"); 
          _mcu_register_set_bits(x_gpiob_out,  kbm_gpio15); 
        } 
        break; 
      case k_lun_mmc: 
        if (_lun_is_media_known(_active_lun)) 
        { 
          trace0(0, dev, 66, "MMC LED ON!"); 
          _mcu_register_set_bits(x_gpiob_out,  kbm_gpio15); 
        } 
        break; 
    } 
  } 
#endif 
 
  switch (intr) 
  { 
    // usbtx: 
    case k_irq_ramrd_a: 
      trace0(0, bot, 10, "got the kbm_sync_usbtx"); 
      _nakkount = 0; 
      _endpoint_rx_enable(k_rx_pipe); 
      _buffer_rx_enable(4); 
      _hpbot_thread_entry = hpbot_wait_cbw; 
      return (k_success); 
 
      // outnak 
    case k_irq_nak2rx: 
      if (_nakkount++ < 200) 
      { 
        trace0(0, bot, 11, "alert - got the kbm_sync_outnak - *before* the tx - dropped ack?"); 
        _endpoint_unmask_outnak(k_rx_pipe); 
        return (k_success); 
      } 
      // detecting outnak before tx means dropped ack, unless pipe is stalled. 
      _nakkount = 0; 
      trace0(0, bot, 11, "got the kbm_sync_outnak - dropped ack?"); 
      if (_ndp2_rx_stalled) 
      { 
        _ndp2_rx_stalled = k_no; 
        trace0(0, bot, 12, "...while stall in progress..."); 
        return (k_success); 
      } 
      _tx_missed_ack = k_yes; 
      trace0(0, bot, 11, "dropped ack"); 
      _endpoint_unmask_outnak(k_rx_pipe); 
      _hpbot_thread_entry = hpbot_wait_pipe_ready_for_rx; 
      return (k_success); 
 
      // usbrst 
    case k_irq_usb_stat_reset: 
    case k_irq_usb_reset: 
      //case k_irq_timer1: - this case is kind of a hold over from the first seat 'o the pants 
      // debug session in the pre-comdex'00 days.  if the outnaks don't start coming (the only 
      // reason for a timeout to occur) then the device is dead.  the host ain't pinging. 
      // so you are eventually gonna either get a usbreset, or the host will drop you like 
      // a cold turd.  in either case, you have already lost. 
      _thread_clr_sync(kbm_sync_all); 
      hpbot_wait_create(); 
      // let the default interrupt handler see this also 
      return (k_ignored); 
 
      // let the default inrt handler handle everything else 
    default: 
      trace0(0, bot, 0, "intr ignored"); 
      return (k_ignored); 
  } 
} 
 
// length must be less than 64 bytes, and must contain the 
// entire data portion of the transfer.  this code takes up 
// a lot of instructions and is used for sending small bot 
// data payloads for non-media data commands like mode sense 
// inquiry, etc. 
void mscbot_tx_data_buffer(uint8* buffer, uint8 length) reentrant 
{ 
  uint8 pnr; 
 
  trace1(0, bot, 0, "mscbot_tx_data_buffer() - quick sending a %d byte data response", length) ; 
 
  pnr = mmu_allocate(); 
  _thread_clr_sync(kbm_sync_usbtx); 
  length = _min( (uint32) length, _mscbot_residue_32());  // alloc length 
  mmu_wr_pkt(k_tx_pipe, pnr, buffer, length); 
  txfifo_wr(k_tx_pipe, pnr); 
  _mscbot_decr_residue(length); 
 
} 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
 
// length must be less than 64 bytes, and must contain the 
// entire data portion of the transfer.  this code takes up 
// a lot of instructions and is used for receiving small bot 
// data payloads for non-media data commands like mode sense 
// inquiry, etc. 
//WARNING!! This fn assumes that this packet arrives at buffer B. This will work because we  
//receive our cbw only on buffer A 
uint8 mscbot_rx_data_buffer(uint8* buffer, uint8 length) reentrant 
{ 
  trace1(0, bot, 0, "mscbot_rx_data_buffer() - quick receiving a %d byte data", length) ; 
  thread_clr_sync(kbm_sync_usbrx); 
  _endpoint_rx_enable(k_rx_pipe); 
  _buffer_rx_enable(5); 
  thread_set_timer(200); 
  while (!thread_got_sync(kbm_sync_usbrx)) 
  { 
    if (thread_got_sync(kbm_sync_timer)) 
    { 
      return (k_timeout); 
    } 
  } 
  thread_clr_sync(kbm_sync_usbrx); 
  mmu_rd_pkt(5, length, buffer); 
  return (k_success); 
} 
 
 
//---eof------------------------------------------------------------------------