Esempio n. 1
0
void usb_eth_tx(uint8_t ep, uint8_t stat)
{
  int len;
  int i;
  
  if (!packet_pending || bytes_to_send == 0) {
    ETH_DEV_DBG_INFO("usb_eth_tx, send buffer is empty, packet_pending=%d", packet_pending);
    usbEnableNAKInterrupts(0);
    return;
  }

  len = MIN(bytes_to_send - bytes_sent, MAX_PACKET_SIZE);
  
  usbWrite(ep, ((uint8_t*) &send_buffer)+bytes_sent, len);
  bytes_sent += len;

  ETH_DEV_DBG_INFO("usb_eth_tx: send %d bytes, frame data sent %d/%d",
                   len, bytes_sent, bytes_to_send);


  if (bytes_sent >= bytes_to_send) {
    /* finished sending data */
    for (i=0; i< bytes_to_send;i++) {
      send_buffer[i] = 0;
    }
    bytes_to_send = 0;
    bytes_sent = 0;
    packet_pending = 0;
  }
}
Esempio n. 2
0
void network_device_send()
{
  int i;
  uint16_t len;

  if (packet_pending) {
    ETH_DEV_DBG_ERROR("network_device_send: already pending packet, discarding request");
    return;
  }

  if (uip_len == 0) {
    ETH_DEV_DBG_ERROR("network_device_send: uip_len == 0");
    return;
  }

  len = MIN(sizeof(send_buffer), uip_len);
  
  vicDisable(INT_CHANNEL_USB);
  
  for (i=0; i<len; i++) {
    send_buffer[i] = uip_buf[i];
  }
  bytes_sent=0;
  bytes_to_send = len;
  packet_pending = 1;

  usbEnableNAKInterrupts(INACK_BI);
  vicEnable(INT_CHANNEL_USB);

}
Esempio n. 3
0
int usbnet_send(uint8_t* buffer, uint16_t length) {
    vicDisable(INT_CHANNEL_USB);
    int success = usbring_post_buffer(&send_ring, buffer, length);
    vicEnable(INT_CHANNEL_USB);
    
    if (success) {
        eth_nak_interrupts |= INACK_BI;
        usbEnableNAKInterrupts(eth_nak_interrupts);
    }

    return success;
}
static void vcomBulkIn(uint8_t EP, uint8_t EPStatus) {
	int i;

	if (fifoAvailable(&txfifo) == 0) {
		usbEnableNAKInterrupts(0);
		return;
	}

	for (i = 0; i < MAX_PACKET_SIZE; i++)
		if (!fifoGet(&txfifo, &_vcom_buffer[i])) break;

	if (i > 0) usbWrite(EP, _vcom_buffer, i);
}
Esempio n. 5
0
static void usb_device_status_handler(uint8_t dev_status) {
    if (dev_status & DEV_STATUS_RESET) {
        LOG_INFO("USB Bus Reset status=%x\n\n",dev_status);

        recv_ring_drop = 0;
        usbring_reset(&recv_ring);
        usbring_reset(&send_ring);
        
        eth_nak_interrupts = 0;
        if (send_ring.size > 0) {
            eth_nak_interrupts |= INACK_BI;
        }
        usbEnableNAKInterrupts(eth_nak_interrupts);
    }
}
Esempio n. 6
0
void rndisReset(void) {
  ETH_DEV_DBG_INFO("rndis reset start");

  rndisRxState = RNDIS_RX_IDLE;

  network_device_init();

  //usbClear(USB_EP_OUT | RNDIS_BULK_EP);
  //usbClear(USB_EP_OUT | RNDIS_NOTIFICATION_EP);

  ETH_DEV_DBG_INFO("rndis reset end");

  eth_nak_interrupts = 0;
  usbEnableNAKInterrupts(eth_nak_interrupts);
}
Esempio n. 7
0
void usb_rndis_notification(uint8_t ep, uint8_t stat) {

  if (stat & EP_STATUS_DATA) return; /* next buffer is full, wait */

  if (rndisResponseReady) {
    //ETH_DEV_DBG_INFO("rndis notification: Yes! status=%02x\n",stat);
    usbWrite(ep, rndisResponseAvailable, 8);
  } else {
    //ETH_DEV_DBG_INFO("rndis notification: no...  status=%02x\n",stat);
  }
  eth_nak_interrupts &= ~INACK_II;
  usbEnableNAKInterrupts(eth_nak_interrupts);
  //usbEnableNAKInterrupts(0);
  return;
}
Esempio n. 8
0
void network_device_send()
{
  int i;
  uint16_t len;

  DBG("network device send! (%d bytes)",uip_len);

  if (uip_len == 0) {
    ETH_DEV_DBG_ERROR("network_device_send: uip_len == 0");
    return;
  }

  vicDisable(INT_CHANNEL_USB);
  uint8_t packet_pending_copy = packet_pending;
  vicEnable(INT_CHANNEL_USB);

  if (packet_pending_copy) {
    ETH_DEV_DBG_ERROR("network_device_send: already pending packet, discarding request");
    return;
  }

  len = MIN(sizeof(send_buffer), uip_len);

  for (i=0; i<len; i++) {
    send_buffer[i] = uip_buf[i];
  }

  vicDisable(INT_CHANNEL_USB);

  bytes_sent=0;
  bytes_to_send = len;
  packet_pending = 1;

  eth_nak_interrupts |= INACK_BI;
  usbEnableNAKInterrupts(eth_nak_interrupts);

  vicEnable(INT_CHANNEL_USB);

  //ETH_DEV_DBG_ERROR("network_device_send: NACK Interrupts enable on bulk in");

  //DBG("leaving network send");
}
Esempio n. 9
0
void usb_cdc_ecm_tx(uint8_t ep, uint8_t stat) {
  int len;
  int i;

  //printf(" cdc_ecm tx ");
  if (!stat & EP_STATUS_NACKED) {
    DBG("tx endpoint interrupt, but not NAK");
    return;
  }

  if (!packet_pending || bytes_to_send == 0) {
    ETH_DEV_DBG_INFO("usb_eth_tx, send buffer is empty, packet_pending=%d", packet_pending);

    eth_nak_interrupts &= ~INACK_BI;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    return;
  }


  len = MIN(bytes_to_send - bytes_sent, MAX_PACKET_SIZE);

  usbWrite(ep, ((uint8_t*) &send_buffer)+bytes_sent, len);
  bytes_sent += len;

  if (bytes_sent >= bytes_to_send   /* we sent all the data */
      && len < MAX_PACKET_SIZE)     /* and we don't need to send a zero-length termination packet */
  {
    /* finished sending data */

    rndisTxPackets++;

    bytes_to_send = 0;
    bytes_sent = 0;
    packet_pending = 0;

    //eth_nak_interrupts &= ~INACK_BI;
    //usbEnableNAKInterrupts(eth_nak_interrupts);
  }
}
Esempio n. 10
0
static void usb_cdc_ecm_tx(uint8_t ep, uint8_t stat) {
    int len;
    
    // Not a NAK interrupt - irrelevant
    //if (!stat & EP_STATUS_NACKED) {
    //    return;
    //}
    
    // if nothing to send anymore - disable NAK interrupts
    if (send_ring.size == 0) {
        eth_nak_interrupts &= ~INACK_BI;
        usbEnableNAKInterrupts(eth_nak_interrupts);
        return;
    }
    
    if (stat & EP_STATUS_DATA) {
        return;
    }
    
    // get first buffer from the ring and send it starting from current point
    volatile usb_buffer_t* buffer = &send_ring.buffers[send_ring.begin];
    // calculate how much bytes are left and limit it with maximal USB packet size
    len = MIN(buffer->length - buffer->current, MAX_USB_PACKET_SIZE);
    // send the bytes and update current position inside the buffer
    usbWrite(ep, buffer->data + buffer->current, len);
    buffer->current += len;
    
    // in case we finished sending the current buffer,
    // check whether the sent USB packet was shorter than maximal - that's how
    // we notify the host about end of frame.
    // If so, free the buffer from the ring (host is already notified)
    // Otherwise do nothing, so the next time zero length packet will be sent.
    if (buffer->length == buffer->current && len < MAX_USB_PACKET_SIZE) {
        usbring_free_buffer(&send_ring);
    }
}
Esempio n. 11
0
static bool usb_control_class_handler(void) {
  uint32_t oid;
  uint32_t buflen;
  uint32_t bufptr;

  //printf("control_class_handler!!!");
  ETH_DEV_DBG_INFO("class control request=%d interface=%u len=%u dir=%d\n",usbRequest.request,usbRequest.index,usbRequest.length,REQTYPE_GET_DIR(usbRequest.type));
  DBG("class control request=%d interface=%u len=%u dir=%d\n",usbRequest.request,usbRequest.index,usbRequest.length,REQTYPE_GET_DIR(usbRequest.type));

  if (REQTYPE_GET_DIR(usbRequest.type) == REQTYPE_DIR_TO_HOST) {
    if (rndisResponseReady) {
      //ETH_DEV_DBG_INFO("sending data back to the host, %d bytes\n",rndisResponseLen);
      usbControlTransferPtr = rndisResponse;
      usbControlTransferLen = rndisResponseLen;
      rndisResponseReady = 0;
    } else {
      //ETH_DEV_DBG_INFO("sending NO RESPONE AVAILABLE to the host");
      usbControlTransferPtr = &rndisResponseNotAvailable;
      usbControlTransferLen = 1;
    }
    return TRUE;
  }

  uint32_t type = uint32FromLittleEndian(usbControlTransferPtr);
  uint32_t len  = uint32FromLittleEndian(usbControlTransferPtr+4);
  uint32_t id   = uint32FromLittleEndian(usbControlTransferPtr+8);

  //ETH_DEV_DBG_INFO("RNDIS request type = %x len=%u id=%u\n",type,len,id);
  if (type > 10 || len > 200) {
    ETH_DEV_DBG_INFO("Woopa!!!\n");
    oid    = uint32FromLittleEndian(usbControlTransferPtr+12);
    buflen = uint32FromLittleEndian(usbControlTransferPtr+16);
    bufptr = uint32FromLittleEndian(usbControlTransferPtr+20);
    if (type==4) ETH_DEV_DBG_INFO("QUERY OID=%x infolen=%d infooffset=%d",oid,buflen,bufptr);
    int iii;
    for (iii=0; iii<usbRequest.length; iii++)
      ETH_DEV_DBG_INFO("%02x",usbControlTransferPtr[iii]);
    while (1);
  }

  switch (type) {
  case 0x02: /* REMOTE_NDIS_INITIALIZE_MSG */
    ETH_DEV_DBG_INFO("  INITIALIZE_MSG version=%u.%u max device->host transfer size = %u",
        uint32FromLittleEndian(usbControlTransferPtr+12),
        uint32FromLittleEndian(usbControlTransferPtr+16),
        uint32FromLittleEndian(usbControlTransferPtr+20));


    if (rndisState != RNDIS_UNINITIALIZED) {
      ETH_DEV_DBG_ERROR("INITIALIZE message when we are not in the uninitialized state (state=%d)",rndisState);
      break;
    }
    //int i;
    //for (i=0; i<len; i++) {
    //  ETH_DEV_DBG_INFO("%d %02x",i,usbControlTransferPtr[i]);
    //}

    uint32ToLittleEndian(0x80000002,rndisResponse); /* INITIALIZE_CMPLT */
    uint32ToLittleEndian(52,rndisResponse+4);         /* length */
    uint32ToLittleEndian(id,rndisResponse+8);         /* request id to which we are responding */
    uint32ToLittleEndian(RNDIS_STATUS_SUCCESS,rndisResponse+12); /* status */
    uint32ToLittleEndian(1,rndisResponse+16);          /* version, major */
    uint32ToLittleEndian(0,rndisResponse+20);          /* version, minor */
    uint32ToLittleEndian(1,rndisResponse+24);          /* flags = connectionless */
    uint32ToLittleEndian(0,rndisResponse+28);          /* medium = 802.3 */
    uint32ToLittleEndian(1,rndisResponse+32);          /* max packets per transfer */
    /* the 22 is some voodoo from Linux drivers/gadget */
    uint32ToLittleEndian((11*4+ETHERNET_FRAME_SIZE_MAX+22),rndisResponse+36);  /* max transfer size; rndis header + ethernet packet */
    uint32ToLittleEndian(0,rndisResponse+40);          /* packet alignment for multi-packet transfers */
    uint32ToLittleEndian(0,rndisResponse+44);          /* reserved */
    uint32ToLittleEndian(0,rndisResponse+48);          /* reserved */
    rndisResponseLen   = 52;

    //usbControlTransferPtr = rndisResponse;
    //usbControlTransferLen = 24;

    rndisResponseReady = 1;
    eth_nak_interrupts |= INACK_II;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    //usbEnableNAKInterrupts(INACK_II);

    rndisState = RNDIS_INITIALIZED; /* we should really do this only after sending the response */

    break;
  case 0x03: /* HALT */
    ETH_DEV_DBG_INFO("HALT!?!");

    rndisState = RNDIS_UNINITIALIZED; /* we should really do this only after sending the response */
    break;
  case 0x04: /* REMOTE_NDIS_QUERY_MSG */
    oid    = uint32FromLittleEndian(usbControlTransferPtr+12);
    buflen = uint32FromLittleEndian(usbControlTransferPtr+16);
    bufptr = uint32FromLittleEndian(usbControlTransferPtr+20);

    ETH_DEV_DBG_INFO("QUERY OID=%x infolen=%d infooffset=%d",oid,buflen,bufptr);
    if (buflen > 0) {
      //ETH_DEV_DBG_INFO("parameters???");
      //int iii;
      //for (iii=0; iii<usbRequest.length; iii++)
      //  ETH_DEV_DBG_INFO("%02x",usbControlTransferPtr[iii]);
      //while(1);
    }

    if (buflen+28 != len) {
      ETH_DEV_DBG_INFO("wrong size?");
      int iii;
      for (iii=0; iii<usbRequest.length; iii++)
        ETH_DEV_DBG_INFO("%02x",usbControlTransferPtr[iii]);
      while(1);
    }

    if (buflen>0 && bufptr != 28-8) {
      ETH_DEV_DBG_INFO("wrong offset?");
      int iii;
      for (iii=0; iii<usbRequest.length; iii++)
        ETH_DEV_DBG_INFO("%02x",usbControlTransferPtr[iii]);
      while(1);

    }

    uint32ToLittleEndian(0x80000004,rndisResponse); /* INITIALIZE_CMPLT */
    uint32ToLittleEndian(id,rndisResponse+8);         /* request id to which we are responding */

    rndisResponseLen   = 24;

    uint32_t response_size = 0;
    switch (oid) {

    case OID_GEN_SUPPORTED_LIST:
      rndisResponseLen += sizeof(rndisSupportedOIDs);
      memcpy(rndisResponse + 24, rndisSupportedOIDs, sizeof(rndisSupportedOIDs));
      break;

    case OID_GEN_PHYSICAL_MEDIUM:
      rndisResponseLen += sizeof(uint32_t);
      /* Indicate that the device is a true ethernet link */
      uint32ToLittleEndian(0,rndisResponse+24);

      break;
    case OID_GEN_HARDWARE_STATUS:
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(0 /* ready */,rndisResponse+24);
      break;

    case OID_GEN_MEDIA_SUPPORTED:
    case OID_GEN_MEDIA_IN_USE:
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(REMOTE_NDIS_MEDIUM_802_3,rndisResponse+24);
      break;

    case OID_GEN_VENDOR_ID:
      rndisResponseLen += sizeof(uint32_t);
      /* Vendor ID 0x0xFFFFFF is reserved for vendors who have not purchased a NDIS VID */
      uint32ToLittleEndian(0x00FFFFFF,rndisResponse+24);
      break;

    case OID_GEN_MAXIMUM_TOTAL_SIZE:
      rndisResponseLen += sizeof(uint32_t);
      /* Indicate maximum overall buffer (Ethernet frame and RNDIS header) the adapter can handle */
      uint32ToLittleEndian(11*4 + ETHERNET_FRAME_SIZE_MAX,rndisResponse+24);
      break;

    case OID_GEN_TRANSMIT_BLOCK_SIZE:
    case OID_GEN_RECEIVE_BLOCK_SIZE:
    case OID_GEN_MAXIMUM_FRAME_SIZE:
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(ETHERNET_FRAME_SIZE_MAX,rndisResponse+24);
      //uint32ToLittleEndian(256,rndisResponse+24);
      break;

    //case OID_GEN_TRANSMIT_BLOCK_SIZE:
    //case OID_GEN_RECEIVE_BLOCK_SIZE:
    //  rndisResponseLen += sizeof(uint32_t);
    //  uint32ToLittleEndian(102 /* from contiki */,rndisResponse+24);
    //  break;

    case OID_GEN_VENDOR_DESCRIPTION:
      rndisResponseLen += 16;
      memcpy(rndisResponse+24, "Sivan Toledo\0\0\0\0",16);
      break;

    case OID_GEN_MEDIA_CONNECT_STATUS:
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(REMOTE_NDIS_MEDIA_STATE_CONNECTED,rndisResponse+24);
      break;

    case OID_GEN_LINK_SPEED:
      rndisResponseLen += sizeof(uint32_t);
      /* Indicate 10Mb/s link speed */
      uint32ToLittleEndian(100000,rndisResponse+24);
      //DBG("get link speed");
      break;

    case OID_802_3_PERMANENT_ADDRESS:
    case OID_802_3_CURRENT_ADDRESS:
      rndisResponseLen += 6; // mac address
      memcpy(rndisResponse + 24, "\x22\x33\x44\x55\x66\x77", 6);
      // MAC address reused from some old Meraki Mini
      //memcpy(rndisResponse + 24, "\x00\x18\x0A\x01\xDF\x44", 6);

      break;

    case OID_802_3_MAXIMUM_LIST_SIZE:
      rndisResponseLen += sizeof(uint32_t);
      /* Indicate only one multicast address supported */
      uint32ToLittleEndian(1,rndisResponse+24);
      break;

    case OID_GEN_CURRENT_PACKET_FILTER:
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(rndisCurrentPacketFilter,rndisResponse+24);
      break;

    case OID_GEN_RCV_ERROR:
    case OID_GEN_XMIT_ERROR:
    case OID_GEN_RCV_NO_BUFFER:
    case OID_802_3_MAC_OPTIONS:
    case OID_802_3_RCV_ERROR_ALIGNMENT:
    case OID_802_3_XMIT_ONE_COLLISION:
    case OID_802_3_XMIT_MORE_COLLISIONS:
      rndisResponseLen += sizeof(uint32_t);
      /* Unused statistic OIDs - always return 0 for each */
      uint32ToLittleEndian(0,rndisResponse+24);

      break;
    case OID_GEN_RCV_OK: // received by the interface means we sent the packet to teh host.
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(rndisTxPackets,rndisResponse+24);
      break;
    case OID_GEN_XMIT_OK:
      rndisResponseLen += sizeof(uint32_t);
      uint32ToLittleEndian(rndisRxPackets,rndisResponse+24);
      break;
    case OID_802_3_MULTICAST_LIST:
      rndisResponseLen += sizeof(uint32_t);
      /* from Linux drivers/gadget */
      uint32ToLittleEndian(0xE0000000,rndisResponse+24);
      break;
    default:
      break;
    }

    if (rndisResponseLen == 24) {
      DBG("No response to query!?!");
      DBG("QUERY OID=%x infolen=%d infooffset=%d",oid,buflen,bufptr);
      uint32ToLittleEndian(RNDIS_STATUS_NOT_SUPPORTED,rndisResponse+12); /* status */
      //uint32ToLittleEndian(RNDIS_STATUS_FAILURE,rndisResponse+12); /* status */
      uint32ToLittleEndian(0,rndisResponse+16);         /* buffer length */
      uint32ToLittleEndian(0,rndisResponse+20);         /* buffer ptr */
    } else {
      ETH_DEV_DBG_INFO("response with %d bytes",rndisResponseLen);
      uint32ToLittleEndian(RNDIS_STATUS_SUCCESS,rndisResponse+12); /* status */
      uint32ToLittleEndian(rndisResponseLen-24,rndisResponse+16);         /* buffer length */
      uint32ToLittleEndian(24-8,rndisResponse+20);         /* buffer ptr */
    }
    uint32ToLittleEndian(rndisResponseLen,rndisResponse+4);         /* length */

    // xxx wrong!!!

    rndisResponseReady = 1;
    eth_nak_interrupts |= INACK_II;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    //usbEnableNAKInterrupts(INACK_II);

    break;
  case 0x05: /* REMOTE_NDIS_SET_MSG */
    uint32ToLittleEndian(0x80000005,rndisResponse); /* INITIALIZE_CMPLT */
    uint32ToLittleEndian(16,rndisResponse+4);         /* length */
    uint32ToLittleEndian(id,rndisResponse+8);         /* request id to which we are responding */
    uint32ToLittleEndian(RNDIS_STATUS_SUCCESS,rndisResponse+12); /* status */
    rndisResponseLen   = 16;

    oid    = uint32FromLittleEndian(usbControlTransferPtr+12);
    buflen = uint32FromLittleEndian(usbControlTransferPtr+16);
    bufptr = uint32FromLittleEndian(usbControlTransferPtr+20);
    ETH_DEV_DBG_INFO("SET OID=%x infolen=%d infooffset=%d",oid,buflen,bufptr);
    switch (oid) {
    case OID_GEN_CURRENT_PACKET_FILTER:
      // the 8 byte offset is for the header, which is the first two words in the message
      rndisCurrentPacketFilter = uint32FromLittleEndian(usbControlTransferPtr+8+bufptr);
      ETH_DEV_DBG_INFO("packet filter = %x",rndisCurrentPacketFilter);
      if (rndisCurrentPacketFilter) rndisState = RNDIS_DATA_INITIALIZED;
      else                          rndisState = RNDIS_INITIALIZED;
      break;
    case OID_802_3_MULTICAST_LIST:
      // do nothing
      break;
    default:
      ETH_DEV_DBG_INFO("cannot set this...");
      while(1);
      break;
    }
    rndisResponseReady = 1;
    eth_nak_interrupts |= INACK_II;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    //usbEnableNAKInterrupts(INACK_II);

    break;

  case 0x06: /* RESET */
    ETH_DEV_DBG_INFO("RESET");
    DBG("rndis RESET");
    //while (1); // for testing
    uint32ToLittleEndian(0x80000006,rndisResponse); /* INITIALIZE_CMPLT */
    uint32ToLittleEndian(16,rndisResponse+4);         /* length */
    uint32ToLittleEndian(RNDIS_STATUS_SUCCESS,rndisResponse+8); /* status */
    uint32ToLittleEndian(0,rndisResponse+8); /* addressing reset: no, don't send again */
    rndisResponseLen   = 16;

    rndisResponseReady = 1;
    eth_nak_interrupts |= INACK_II;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    //usbEnableNAKInterrupts(INACK_II);

    rndisReset();

    break;
  case 0x07: /* INDICATE_STATUS */
    // wrong direction; we need to send this to the host...
    ETH_DEV_DBG_INFO("INDICATE STATUS");
    break;
  case 0x08: /* KEEPALIVE */
    ETH_DEV_DBG_INFO("KEEPALIVE");
    uint32ToLittleEndian(0x80000008,rndisResponse); /* INITIALIZE_CMPLT */
    uint32ToLittleEndian(12,rndisResponse+4);         /* length */
    uint32ToLittleEndian(id,rndisResponse+8);         /* request id to which we are responding */

    rndisResponseReady = 1;
    eth_nak_interrupts |= INACK_II;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    break;
  default:
    ETH_DEV_DBG_INFO("class control request=%x interface=%u len=%u dir=%d\n",usbRequest.request,usbRequest.index,usbRequest.length,REQTYPE_GET_DIR(usbRequest.type));
    ETH_DEV_DBG_INFO("RNDIS request type = %x len=%u id=%u\n",type,len,id);
    break;

  }

  return TRUE;
}
Esempio n. 12
0
void usb_rndis_tx(uint8_t ep, uint8_t stat) {
  int len;
  int i;

  //DBG("rndis tx\n");
  if (!stat & EP_STATUS_NACKED) {
    DBG("tx endpoint interrupt, but not NAK");
    return;
  }

  if (!packet_pending || bytes_to_send == 0) {
    ETH_DEV_DBG_INFO("usb_eth_tx, send buffer is empty, packet_pending=%d", packet_pending);

    eth_nak_interrupts &= ~INACK_BI;
    usbEnableNAKInterrupts(eth_nak_interrupts);
    return;
  }

  if (bytes_sent == 0) {
    /* prepend rndis header */
    memset(rndisPacketHeader,0,44);
    uint32ToLittleEndian(1,rndisPacketHeader); // type
    uint32ToLittleEndian(44+bytes_to_send,rndisPacketHeader+4);
    uint32ToLittleEndian(44-8,rndisPacketHeader+8); // type
    uint32ToLittleEndian(bytes_to_send,rndisPacketHeader+12); // type

    /* copy start of frame to buffer */
    len = MIN(bytes_to_send, 64-44);
    memcpy(rndisPacketHeader + 44, send_buffer, len);
    usbWrite(ep, rndisPacketHeader, 44+len);
    bytes_sent += len;
    //DBG("sent header and %d bytes out of %d",len,bytes_to_send);
  } else {

    len = MIN(bytes_to_send - bytes_sent, MAX_PACKET_SIZE);

    usbWrite(ep, ((uint8_t*) &send_buffer)+bytes_sent, len);
    bytes_sent += len;

    //DBG("usb_eth_tx: send %d bytes, frame data sent %d/%d",
    //    len, bytes_sent, bytes_to_send);

  }

  if (bytes_sent >= bytes_to_send) {
    /* finished sending data */
    //DBG("usb_rndis_tx: send done... (%d bytes)",bytes_to_send);
    //for (i=0; i< bytes_to_send;i++) {
    //  send_buffer[i] = 0;
    //}

    rndisTxPackets++;

    bytes_to_send = 0;
    bytes_sent = 0;
    packet_pending = 0;

    //eth_nak_interrupts &= ~INACK_BI;
    //usbEnableNAKInterrupts(eth_nak_interrupts);
  }
}
void vcomPutchar(int c) {
  vicDisable(INT_CHANNEL_USB);
  fifoPut(&txfifo, c);
  usbEnableNAKInterrupts(INACK_BI);
  vicEnable(INT_CHANNEL_USB);
}