usbs_d12.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,974 行 · 第 1/5 页

C
1,974
字号
    // See if caller requested a read smaller than the endp. Read &
    // throw away extra
    if (ep->common.buffer_size < ep_size) {
      byte tmp_buf[D12_MAX_PACKET_SIZE];
      
      if (endp == D12_MAIN_ENDP)
	n = d12_read_main_endp_buf(D12_BASE_ADDR, tmp_buf, &done);
      else
	n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp], tmp_buf);
      
      if (n > ep->common.buffer_size) {
	n = ep->received = ep->common.buffer_size;
	ret = -ENOMEM;
	TRACE_D12("\tEP%d: *** Rx Buffer too small. Data Lost ***\n", endp);
      }
      else
	ret = ep->received = n;
      
      memcpy(ep->common.buffer, tmp_buf, n);
      buf_remaining = ep->common.buffer_size - n;
    }
    
    TRACE_D12("\tEP%d: Rx Complete. Buffer (nearly) full. [%d]\n", 
	      endp, buf_remaining);
    usbs_d12_ep_rx_complete(ep, ret);
    return;
  }
  
  // ----- Read the data from the chip -----
  
  if (endp == D12_MAIN_ENDP)
    n = d12_read_main_endp_buf(D12_BASE_ADDR, 
			       ep->common.buffer + ep->received, &done);
  else {
    n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp],
			  ep->common.buffer + ep->received);
    done = (n < RX_ENDP_SIZE[endp]);
  }
  
  ep->received += n;
  buf_remaining = ep->common.buffer_size - ep->received;
  
  done = done || (buf_remaining < ep_size);
  
  TRACE_D12("EP%d: Received %d bytes.\n", endp, n);
  TRACE_BUF("\t", ep->common.buffer + ep->received-n, n);
  
  // ----- If we're done, complete the receive -----
  
  if (done) {
    TRACE_D12("\tEP%d Rx Complete (%d)  %p\n", endp, 
	      ep->received, ep->common.complete_fn);
    usbs_d12_ep_rx_complete(ep, ep->received);
  }
}

// --------------------------------------------------------------------------
// Stalls/unstalls the specified endpoint.

static void 
usbs_d12_stall_rx_ep(rx_endpoint *ep, cyg_bool halt)
{
  ep->common.halted = halt;
  d12_stall_endp(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp], halt);
}

// --------------------------------------------------------------------------
// Handler for an Rx endpoint full interrupt. It clears the interrupt on the
// D12 by reading the endpoint's status register then calls the routine to
// read the data into the buffer.
//
// Called from the DSR context only.
//

static void 
usbs_d12_ep_rx_intr(rx_endpoint *ep)
{
  d12_read_last_trans_status(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp]);
  usbs_d12_ep_rx(ep);
}

#endif

// --------------------------------------------------------------------------
// Common Tx Endpoint 1 & 2
// --------------------------------------------------------------------------

#if defined(_TX_EP1) || defined(_TX_EP2)

// Clears out the endpoint data structure before/after a tx is complete.

static void usbs_d12_clear_tx_ep(tx_endpoint *ep)
{
  ep->common.buffer = 0;
  ep->common.buffer_size = 0;
  ep->common.complete_fn = 0;
  ep->common.complete_data = 0;
  
  ep->transmitted = 0;
  ep->tx_empty = false;
}

// --------------------------------------------------------------------------
// This is called when a transmit is completed. It resets the endpoint vars
// and calls the registered completion function, if any.
//
// CONTEXT:
//         Called from either the DSR or the app thread that started tx. 

static void usbs_d12_ep_tx_complete(tx_endpoint *ep, int result)
{
  completion_fn fn = ep->common.complete_fn;
  void *data = ep->common.complete_data;
  
  usbs_d12_clear_tx_ep(ep);
  
  if (fn)
    (*fn)(data, result);
}

// --------------------------------------------------------------------------
// The routine writes data to the chip and updates the endpoint's counters. 
// It gets called at the start of a transfer operation to prime the device
// and then gets called each time the chip finishes sending a packet to the
// host and is ready for more data. If the amount of data remaining is 
// smaller than can fit in the chip's endpoint buffer, then this is the last
// packet to send, so we call the completion function.
//
// CONTEXT:
//        Called from either the DSR or the app thread that started the tx
//        In either case, it's assumed the chip is locked.

static void 
usbs_d12_ep_tx(tx_endpoint *ep)
{
  int n, nRemaining;
  
  // ----- Already done. Intermittent interrupt, so get out -----
  
  if (!ep->common.buffer)
    return;
  
  // ----- See how many bytes remaining in buffer -----
  
  nRemaining = ep->common.buffer_size - ep->transmitted;
  
  TRACE_D12("EP%d: Tx %p, %d Done, %d Remaining\n", ep->endp, 
	    ep->common.buffer, ep->transmitted, nRemaining);
  
  // ----- If prev packet was last, signal that we're done -----
  
  if (nRemaining == 0 && !ep->tx_empty) {
    TRACE_D12("\tEP%d: Tx complete (%d)  %p\n", ep->endp, 
	      ep->transmitted, ep->common.complete_fn);
    usbs_d12_ep_tx_complete(ep, ep->transmitted);
    return;
  }
  
  // ----- Write the next packet to chip -----
  
  if (nRemaining < TX_ENDP_SIZE[ep->endp]) {
    n = nRemaining;
    ep->tx_empty = false;
  }
  else
    n = TX_ENDP_SIZE[ep->endp];
  
  TRACE_D12("EP%d: Writing %d bytes. %s\n", ep->endp, 
	    n, (n == 0) ? "DONE" : "");
  TRACE_BUF("\t", ep->common.buffer + ep->transmitted, n);
  
  d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp], 
		     ep->common.buffer + ep->transmitted, (uint8) n);
  
  ep->transmitted += n;
  
  // ----- If empty packet, complete now -----
  
  if (n == 0) {
    TRACE_D12("\tEP%d: Tx complete (%d)  %p\n", ep->endp, 
	      ep->transmitted, ep->common.complete_fn);
    usbs_d12_ep_tx_complete(ep, ep->transmitted);
    return;
  }
}

// --------------------------------------------------------------------------
// Stalls/unstalls the specified tx endpoint.

static void 
usbs_d12_stall_tx_ep(tx_endpoint *ep, cyg_bool halt)
{
  ep->common.halted = halt;
  d12_stall_endp(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp], halt);
}

// --------------------------------------------------------------------------
// Handler for when the chip's tx RAM for an endoint has just been emptied 
// (sent to the host) and the chip is ready for more data.
// We read the endpoint's last trans status register to clear the interrupt
// on the D12, then call the tx function to send the next packet or 
// complete the transfer.

static void 
usbs_d12_ep_tx_intr(tx_endpoint *ep)
{
  d12_read_last_trans_status(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp]);
  usbs_d12_ep_tx(ep);
}

#endif // defined(_TX_EP1) || defined(_TX_EP2)

// --------------------------------------------------------------------------
// Application Program Interface (API)
// --------------------------------------------------------------------------

#if defined(_RX_EP1) || defined(_RX_EP2)
// Starts a receive operation on the specified endpoint. If the buffer size
// is zero the completion function is called immediately. The routine checks
// if tehre is data in the chip's endpoint buffer, and if so it will call
// ep_rx() to start reading the data out of the chip.
//
// If the endpoint is currently stalled, a read size of zero can be used to 
// block the calling thread until the stall is cleared. If the read size is
// non-zero and the endpoint is stalled the completion function is called
// immediately with an error result.

static void 
usbs_d12_api_start_rx_ep(usbs_rx_endpoint *ep)
{
  rx_endpoint *epx = (rx_endpoint *) ep;
  
  if (ep->halted) {
    if (ep->buffer_size != 0)
      usbs_d12_ep_rx_complete(epx, -EAGAIN);
  }
  else if (ep->buffer_size == 0) {
    usbs_d12_ep_rx_complete(epx, 0);
  }
  else {
    TRACE_D12("EP%d: Starting Rx, %p, %d\n", epx->endp, ep->buffer,
	      ep->buffer_size);
    usbs_d12_lock();
    
    epx->received = 0;
    if (d12_data_available(D12_BASE_ADDR, RX_ENDP_INDEX[epx->endp]))
      usbs_d12_ep_rx(epx);
    
    usbs_d12_unlock();
  }
}

// --------------------------------------------------------------------------
// Halts/unhalts one of the generic rx (OUT) endpoints.
//

static void usbs_d12_api_stall_rx_ep(usbs_rx_endpoint *ep, cyg_bool halt)
{
  usbs_d12_lock();
  usbs_d12_stall_rx_ep((rx_endpoint*) ep, halt);
  usbs_d12_unlock();
}

#endif // defined(_RX_EP1) || defined(_RX_EP2)

// --------------------------------------------------------------------------
// Tx API
// --------------------------------------------------------------------------

#if defined(_TX_EP1) || defined(_TX_EP2)

// This starts a transmit on one of the data endpoints. If the endpoint is
// stalled a buffer size of zero can be used to block until the stall is
// cleared. Any other size on a stalled endpoint will result in an error
// callback immediately. The first packet is sent to the chip immediately,
// in the application context. If the chip's buffer can contain the whole
// transfer, the completion function will be called immediately, again,
// still in the application context.
//
// If an empty packet is requested we send one from here and call the 
// completion function. This should not cause an intr on the D12.
//
// CONTEXT:
//        Called from an application thread

static void usbs_d12_api_start_tx_ep(usbs_tx_endpoint *ep)
{
  tx_endpoint *epx = (tx_endpoint*) ep;
  
  if (ep->halted) {
    if (ep->buffer_size != 0) 
      usbs_d12_ep_tx_complete(epx, -EAGAIN);
  }
  else if (ep->buffer_size == 0) {
    usbs_d12_lock();
    
    d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[epx->endp], 0, 0);
    usbs_d12_ep_tx_complete(epx, 0);
    
    usbs_d12_unlock();
  }
  else {
    TRACE_D12("EP%d: Starting Tx, %p, %d\n", epx->endp, ep->buffer,
	      ep->buffer_size);
    usbs_d12_lock();
    
    epx->tx_empty = true;
    epx->transmitted = 0;
    usbs_d12_ep_tx(epx);
    
    usbs_d12_unlock();
  }
}

// --------------------------------------------------------------------------
// Halts/unhalts one of the generic endpoints.

static void 
usbs_d12_api_stall_tx_ep(usbs_tx_endpoint *ep, cyg_bool halt)
{
  usbs_d12_lock();
  usbs_d12_stall_tx_ep((tx_endpoint*) ep, halt);
  usbs_d12_unlock();
}

#endif // defined(_TX_ENDP1) || defined(_TX_EP2)

// --------------------------------------------------------------------------
// DSR
// --------------------------------------------------------------------------

// The DSR for the D12 chip. This is normally called in the DSR context when
// the D12 has raised its interrupt flag indicating that it needs to be 
// serviced. The interrupt register contains bit flags that are OR'ed togther
// indicating what items need to be serviced. There are flags for the 
// following:
//              - The endpoints (one bit for each)
//              - Bus Reset
//              - Suspend Change
//              - DMA (terminal count)
//
// Care must be taken in that the D12's interrupt output is level-sensitive
// (in that the interrupt sources are OR'ed together and not all cleared 
// atomically in a single operation). Platforms (such as the PC) may be 
// expecting edge-triggered interrupts, so we must work around that.
// So, we loop on the interrupt register. Even though, in each loop, we
// perform all of the required operations to clear the interrupts, a new
// one may have arrived before we finished clearing the previous ones.
// So we read the intr reg again. Once the intr reg gives a zero reading
// we know that the D12 has dropped its IRQ line.
//
// Note, if we're configured to use a thread, this routine is called from
// within a thread context (not a DSR context).
//

static void 
usbs_d12_dsr(cyg_vector_t vector, cyg_ucount32 count, 
                                                 cyg_addrword_t data)
{
  uint16  status;
  bool    suspended;
  
  CYG_ASSERT(vector == CYGNUM_DEVS_USB_D12_INT,
	     "DSR should only be invoked for D12 interrupts");
  
  while ((status = d12_read_intr_reg(D12_BASE_ADDR)) != 0) {
    TRACE_D12("Intr Status: 0x%04X\n", (unsigned) status);
    
    if (status & D12_INTR_BUS_RESET) {
      TRACE_D12("\n>>> Bus Reset <<<\n");
      usbs_d12_reset();
    }
    else {
      
      // ----- Suspend Change -----
      
      suspended = (bool) (ep0.common.state & USBS_STATE_SUSPENDED);
      
      if (status & D12_INTR_SUSPEND_CHANGE) {
	if (!suspended && (status & ~D12_INTR_SUSPEND_CHANGE) == 0)
	  usbs_d12_suspend(true);
      }
      else if (suspended)
	usbs_d12_suspend(false);
      
      // ----- Bulk Endpoints -----
      
#ifdef _TX_EP2
      if (status & D12_INTR_TX_ENDP2)
	usbs_d12_ep_tx_intr(&tx_ep2);
#endif
      
#ifdef _RX_EP2
      if (status & D12_INTR_RX_EN

⌨️ 快捷键说明

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