usbs_d12.c

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

C
1,974
字号
    return;
  }
  
  // ----- If prev packet was last, signal that we're done -----
  
  if (nRemaining == 0 && !ep0.tx_empty) {
    TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted, 
	      ep0.common.complete_fn);
    usbs_d12_ep0_complete(0);
    return;
  }
  
  // ----- Load the next tx packet onto the chip -----
  
  if (nRemaining < D12_ENDP0_SIZE) {
    n = (uint8) nRemaining;
    ep0.tx_empty = false;
  }
  else
    n = D12_ENDP0_SIZE;
  
  d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 
		     &ep0_tx_buffer[ep0.transmitted], n);
  
  TRACE_D12("EP0: Wrote %u bytes\n", (unsigned) n);
  TRACE_BUF0("\t", &ep0_tx_buffer[ep0.transmitted], n);
  
  ep0.transmitted += n;
  
  // ----- If empty packet, D12 won't interrupt, so end now ----- */
  
  if (n == 0) {
    TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted, 
	      ep0.common.complete_fn);
    usbs_d12_ep0_complete(0);
  }
}

// --------------------------------------------------------------------------
// This function is called when a packet has been successfully sent on the
// primary control endpoint (ep0). It indicates that the chip is ready for 
// another packet. We read the LastTransStatus for the endpoint to clear 
// the interrupt bit, then call ep0_tx() to continue the transfer.

static void 
usbs_d12_ep0_tx_intr(void)
{
  d12_read_last_trans_status(D12_BASE_ADDR, D12_TX_ENDP0);
  usbs_d12_ep0_tx();
}

// --------------------------------------------------------------------------
// Try to handle standard requests. This is a three step process:
//     1.   If it's something we should handle internally we take care of it.
//          Currently we can handle SET_ADDRESS requests, and a few others.
//     2.   If the upper level code has installed a standard control handler
//          we let that function have a crack at it.
//     3.   If neither of those handle the packet we let 
//          usbs_handle_standard_control() have a last try at it.
//
// Locally:
//          SET_ADDRESS: The host is demanding that we change our USB address.
//          This is done by updating the Address/Enable register on the D12. 
//          Note, however that the USB protocol requires us to ack at the old 
//          address, change address, and then accept the next control message
//          at the new      address. The D12 address reg is buffered to do this 
//          automatically for us. The updated address on the chip won't take
//          affect until after the empty ack is sent. Nice.
//

static usbs_control_return 
usbs_d12_handle_std_req(usb_devreq *req)
{
  usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
  int recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;

  if (req->request == USB_DEVREQ_SET_ADDRESS) {
    TRACE_D12("Setting Addr: %u\n", (unsigned) req->value_lo);
    d12_set_addr_enable(D12_BASE_ADDR, req->value_lo, true);
    result = USBS_CONTROL_RETURN_HANDLED;
  }
  else if (req->request == USB_DEVREQ_GET_STATUS) {
    if (recipient == USB_DEVREQ_RECIPIENT_DEVICE) {
      const usbs_enumeration_data *enum_data = ep0.common.enumeration_data;
      if (enum_data && enum_data->device.number_configurations == 1 &&
	  enum_data->configurations) {
	ep0.common.control_buffer[0]  = 
	  (enum_data->configurations[0].attributes
	   & USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED) ? 1 : 0;
	ep0.common.control_buffer[0] |= 
	  (enum_data->configurations[0].attributes
	   & USB_CONFIGURATION_DESCRIPTOR_ATTR_REMOTE_WAKEUP) ? 2 : 0;
	ep0.common.control_buffer[1] = 0;
	result = USBS_CONTROL_RETURN_HANDLED;
      }
    }
    else if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
      bool halted = false;
      result = USBS_CONTROL_RETURN_HANDLED;

      switch (req->index_lo) {
#if defined(_RX_EP1)
      case 0x01 : halted = rx_ep1.common.halted;      break;
#endif
#if defined(_TX_EP1)
      case 0x81 : halted = tx_ep1.common.halted;      break;
#endif
#if defined(_RX_EP2)
      case 0x02 : halted = rx_ep2.common.halted;      break;
#endif
#if defined(_TX_EP2)
      case 0x82 : halted = tx_ep2.common.halted;      break;
#endif

      default:
	result = USBS_CONTROL_RETURN_STALL;
      }

      TRACE_D12("Get Status: Endp [0x%02X] %s\n", (unsigned) req->index_lo, 
		halted ? "Halt" : "Unhalt");
      if (result == USBS_CONTROL_RETURN_HANDLED) {
	ep0.common.control_buffer[0] = (halted) ? 1 : 0;
	ep0.common.control_buffer[1] = 0;
      }
    }

    if (result == USBS_CONTROL_RETURN_HANDLED) {
      ep0.common.buffer                       = ep0.common.control_buffer;
      ep0.common.buffer_size                  = 2;
      ep0.common.fill_buffer_fn               = 0;
      ep0.common.complete_fn                  = 0;
    }
  }
  else if ((req->request == USB_DEVREQ_SET_FEATURE || 
	    req->request == USB_DEVREQ_CLEAR_FEATURE) && 
	   recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
    
    bool halt = (req->request == USB_DEVREQ_SET_FEATURE);
    result = USBS_CONTROL_RETURN_HANDLED;
    TRACE_D12("Endpoint [0x%02X] %s\n", (unsigned) req->index_lo, 
	      halt ? "Halt" : "Unhalt");

    switch (req->index_lo) {
#if defined(_RX_EP1)
    case 0x01 :     usbs_d12_stall_rx_ep(&rx_ep1, halt);    break;
#endif
#if defined(_TX_EP1)
    case 0x81 : usbs_d12_stall_tx_ep(&tx_ep1, halt);        break;
#endif
#if defined(_RX_EP2)
    case 0x02 :     usbs_d12_stall_rx_ep(&rx_ep2, halt);    break;
#endif
#if defined(_TX_EP2)
    case 0x82 : usbs_d12_stall_tx_ep(&tx_ep2, halt);        break;
#endif
      
    default:
      result = USBS_CONTROL_RETURN_STALL;
    }
  }
  else if (ep0.common.standard_control_fn != 0) {
    result = (*ep0.common.standard_control_fn)
      (&ep0.common,
       ep0.common.standard_control_data);
  }
  
  if (result == USBS_CONTROL_RETURN_UNKNOWN)
    result = usbs_handle_standard_control(&ep0.common);
  
  return result;
}

// --------------------------------------------------------------------------
// Handler for the receipt of a setup (dev request) packet from the host.
// We examine the packet to determine what function(s) should get a crack
// at trying to handle it, then pass control to the proper function. If
// the function handles the message we either ACK (len==0) or prepare for
// an IN or OUT data phase. If no one handled the message, we stall the
// control endpoint.

static void 
usbs_d12_ep0_setup_packet(usb_devreq* req)
{
  int             len, dir, protocol, recipient;
  usbs_control_return     result = USBS_CONTROL_RETURN_UNKNOWN;
  
  // ----- See who should take the request -----
  
  len = make_word(req->length_hi, req->length_lo);
  
  dir                     = req->type & USB_DEVREQ_DIRECTION_MASK;
  protocol    = req->type & USB_DEVREQ_TYPE_MASK;
  recipient   = req->type & USB_DEVREQ_RECIPIENT_MASK;
  
  TRACE_BUF0("DevReq: ", ep0.common.control_buffer, sizeof(usb_devreq));
  
  if (protocol == USB_DEVREQ_TYPE_STANDARD)
    result = usbs_d12_handle_std_req(req);
  else {
    // Pass on non-standard requests to registered handlers
    
    usbs_control_return     (*callback_fn)(usbs_control_endpoint*, void*);
    void *callback_arg;
    
    if (protocol == USB_DEVREQ_TYPE_CLASS) {
      callback_fn  = ep0.common.class_control_fn;
      callback_arg = ep0.common.class_control_data;
    }
    else if (protocol == USB_DEVREQ_TYPE_VENDOR) {
      callback_fn  = ep0.common.vendor_control_fn;
      callback_arg = ep0.common.vendor_control_data;
    }
    else {
      callback_fn  = ep0.common.reserved_control_fn;
      callback_arg = ep0.common.reserved_control_data;
    }
    
    result = (callback_fn)  ? (*callback_fn)(&ep0.common, callback_arg)
      : USBS_CONTROL_RETURN_STALL;
  }
  
  // ----- If handled prep/handle data phase, otherwise stall -----
  
  if (result == USBS_CONTROL_RETURN_HANDLED) {
    if (len == 0) {
      TRACE_D12("\tCtrl ACK\n");
      d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
    }
    else {
      // Set EP0 state to  IN or OUT mode for data phase
      ep0.transmitted = 0;
      ep0.length = len;
      
      if (dir == USB_DEVREQ_DIRECTION_OUT) {
	// Wait for the next packet from the host.
	ep0.ep_state = ENDP_STATE_OUT;
	CYG_ASSERT(ep0.common.buffer != 0, 
		   "A rx buffer should have been provided for EP0");
	CYG_ASSERT(ep0.common.complete_fn != 0, 
		   "A completion function should be provided for EP0 OUT control messages");
      }
      else {
	ep0.tx_empty = true;
	ep0.ep_state = ENDP_STATE_IN;
	ep0_fill_tx_buffer();
	usbs_d12_ep0_tx();
      }
    }
  }
  else {
    TRACE_D12("\t*** Unhandled Device Request ***\n");
    // The request wasn't handled, so stall control endpoint
    d12_stall_ctrl_endp(D12_BASE_ADDR, true);
  }
}

// --------------------------------------------------------------------------
// This is called when the chip indicates that a packet has been received
// on control endpoint 0. If it's a setup packet, we handle it accordingly,
// otherwise it's a data packet coming in on ep0.
//

static void 
usbs_d12_ep0_rx_intr(void)
{
  byte byStat = d12_read_last_trans_status(D12_BASE_ADDR, D12_RX_ENDP0);
  TRACE_D12("\tEP0 Status: 0x%02X\n", (unsigned) byStat);
  
  if (byStat & D12_LAST_TRANS_SETUP_PACKET) {
    usb_devreq *req = (usb_devreq *) ep0.common.control_buffer;
    
    if (!d12_read_setup_packet(D12_BASE_ADDR, (byte*) req)) {
      TRACE_D12("ep0_rx_dsr: Error reading setup packet\n");
      d12_stall_ctrl_endp(D12_BASE_ADDR, true);
    }
    else
      usbs_d12_ep0_setup_packet(req);
  }
  else {
    if (ep0.common.buffer) {
      uint8 n = d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0, 
				  ep0.common.buffer + ep0.transmitted);
      ep0.transmitted += n;
      
      TRACE_D12("EP0: Received %d bytes\n", (unsigned) n);
      
      if (n < D12_ENDP0_SIZE || 
	  ep0.common.buffer_size - ep0.transmitted < D12_ENDP0_SIZE) {
	TRACE_D12("\tEP0: Rx Complete (%d) %p\n", 
		  ep0.transmitted, ep0.common.complete_fn);
	
	if (usbs_d12_ep0_complete(0) == USBS_CONTROL_RETURN_HANDLED)
	  d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
	else
	  d12_stall_ctrl_endp(D12_BASE_ADDR, true);
      }
    }
    else {
      TRACE_D12("EP0: No Rx buffer. Discarding packet\n");
      d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0, NULL);
    }
  }
}

// --------------------------------------------------------------------------
// Handler for when the device is put into or taken out of suspend mode.
// It updates the state variable in the control endpoint and calls the
// registered state change function, if any.

// TODO: Put the chip into low power mode??? Stop clocks, etc???

static void 
usbs_d12_suspend(bool suspended)
{
  int                     old_state = ep0.common.state;
  usbs_state_change       state_change;
  
  if (suspended) {
    ep0.common.state |= USBS_STATE_SUSPENDED;
    state_change = USBS_STATE_CHANGE_SUSPENDED;
  }
  else {
    ep0.common.state &= USBS_STATE_MASK;
    state_change = USBS_STATE_CHANGE_RESUMED;
  }
  
  if (ep0.common.state_change_fn) {
    (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
				  state_change, old_state);
  }
}

// --------------------------------------------------------------------------
// Common Rx Endpoint 1 & 2
// --------------------------------------------------------------------------

#if defined(_RX_EP1) || defined(_RX_EP2)

static void usbs_d12_clear_rx_ep(rx_endpoint *ep)
{
  ep->common.buffer               = 0;
  ep->common.buffer_size          = 0;
  ep->common.complete_fn          = 0;
  ep->common.complete_data        = 0;
  
  ep->received                    = 0;
}

// --------------------------------------------------------------------------
// This is called when an rx operation is completed. It resets the endpoint
// vars and calls the registered completion function.
//

static void 
usbs_d12_ep_rx_complete(rx_endpoint *ep, int result)
{
  completion_fn fn = ep->common.complete_fn;
  void *data = ep->common.complete_data;
  
  usbs_d12_clear_rx_ep(ep);
  
  if (fn)
    (*fn)(data, result);
}

// --------------------------------------------------------------------------
// This routine is called when an rx buffer in the chip is full and ready to
// be read. If there's an endpoint buffer available and room to hold the data
// we read it in, otherwise we call the completion function, but leave the 
// data in the chip. The hardware will automatically NAK packages from the
// host until the app calls another start read to continue receiving data.
//
// CONTEXT:
//         Called from either the DSR or application thread, via start rx.
//         In either case, it's assumed that the chip is locked.
//              

static void 
usbs_d12_ep_rx(rx_endpoint *ep)
{
  int             n, ep_size, buf_remaining, endp = ep->endp;
  bool    done;
  
  // The main endp is double buffered and we need to be prepared
  // to read both simultaneously.
  ep_size = (endp == D12_MAIN_ENDP) ? (2 * D12_MAIN_ENDP_SIZE) 
    : RX_ENDP_SIZE[endp];
  
  buf_remaining = ep->common.buffer_size - ep->received;
  
  // ----- If no space left in buffer, call completion fn -----
  
  if (!ep->common.buffer || buf_remaining < ep_size) {
    int ret = ep->received;
    

⌨️ 快捷键说明

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