///////////////////////////////////////////////////////////////////////////
////                             usb.c                                 ////
////                                                                   ////
//// Standard USB request and token handler code.                      ////
////                                                                   ////
//// This file is part of CCS's PIC USB driver code, which includes:   ////
////   usb_desc_*.h - an example set of config and device descriptor   ////
////   usb.c - USB token and request handler code                      ////
////   usb.h - definitions, prototypes and global variables            ////
////                                                                   ////
//// The following examples are provided by CCS:                       ////
////   ex_usb_mouse.c - A HID Mouse.                                   ////
////   ex_usb_hid.c - A custom application using HID protocol.         ////
////   ex_usb_kbmouse.c - A HID Mouse/Keyboard combo using multiple    ////
////                      interfaces.                                  ////
////   ex_usb_kbmouse.c - A HID Mouse/Keyboard combo using multiple    ////
////                      HID Reports.                                 ////
////   ex_usb_scope.c - A digital oscilloscope using a custom          ////
////                    protocol requiring custom Windows drivers.     ////
////                                                                   ////
//// Many settings important to the USB API are defined in usb.h.      ////
//// See the documentation in usb.h about these settings.              ////
////                                                                   ////
//// The majority of this code is called and used by the interrupt     ////
//// generated by the hardware level, and therefore it is not meant    ////
//// to be called by the user.  The following functions are of use to  ////
//// the user (for more docs on the functions, read the comments at    ////
//// each function):                                                   ////
////                                                                   ////
////    **********************  FUNCTIONS  ***********************     ////
////                                                                   ////
//// usb_enumerated() - returns TRUE if device has been enumerated     ////
////                    (configured) by host, FALSE if it has not.     ////
////                    Do not try to use the USB peripheral until you ////
////                    are enumerated.                                ////
////                                                                   ////
//// usb_wait_for_enumeration() - Sits in an infinte loop until device ////
////                              is enumerated.                       ////
////                                                                   ////
//// usb_kbhit() - Returns true if OUT endpoint contains data from     ////
////               host.                                               ////
////                                                                   ////
//// usb_puts() - Sends a multiple packet message to the host          ////
////                                                                   ////
//// usb_gets() - Gets multiple packets from the host                  ////
////                                                                   ////
//// The rest of the functions in this file are part of the USB        ////
//// interrupt service routine and are not meant to be called by the   ////
//// user.                                                             ////
////                                                                   ////
////   *************************  NOTE  **************************     ////
////    This code will not create a multiple configuration device.     ////
////    If you wish to create a multiple configuration device then you ////
////    will have to modify these drivers.                             ////
////                                                                   ////
////   *************************  NOTE  **************************     ////
////   This code does not support Get_Idle or Set_Idle HID-specific    ////
////   requests.  These requests are optional.  If you want to support ////
////   these requests you must provide the code yourself.  See         ////
////   usb_isr_tkn_setup_ClassInterface() if you wish to add this      ////
////   support.                                                        ////
///////////////////////////////////////////////////////////////////////////
////                                                                   ////
//// If you wish to provide your own USB peripheral hardware layer, it ////
//// must include the following functions:                             ////
////                                                                   ////
//// void usb_stall_ep(int8 endpoint);                                 ////
//// void usb_unstall_ep(int8 endpoint);                               ////
//// int1 usb_endpoint_stalled(int8 endpoint);                         ////
//// void usb_set_address(int8 address);                               ////
//// void usb_set_configured(int config);                              ////
//// int8 usb_get_packet(int8 endpoint, int8 * ptr, int8 max);         ////
//// int1 usb_put_packet(int endpoint, int * ptr, int len, USB_DTS_BIT toggle); ////
//// void usb_request_send_response(int8 len);                                ////
//// int1 usb_kbhit(int8 endpoint);                                    ////
////                                                                   ////
//// CCS provides a USB peripheral hardware layer for PIC16C7x5        ////
//// (pic_usb.h), 18Fxx5x (pic18_usb.h) and National's USBN960x        ////
//// (usbn960x.c).  See these files for more documentation.            ////
////                                                                   ////
///////////////////////////////////////////////////////////////////////////
////                                                                   ////
//// Version History:                                                  ////
////                                                                   ////
//// July 13th, 2005:                                                  ////
////  usb_puts() packet_size and this_packet_len changed to 16bits.    ////
////  usb_gets() len, packet_size and this_packet_len to 16bits.       ////
////                                                                   ////
//// June 20th, 2005:                                                  ////
////  Initial 18fxx5x release.                                         ////
////  usb_kbhit() removed, usb_kbhit() now implemented in hardware     ////
////    layer.                                                         ////
////                                                                   ////
//// May 13th, 2005:                                                   ////
////  Beta release, only works with 18Fxx5x hardware layer.            ////
////  Now supports multiple interfaces (many defines in descriptors    ////
////   will have to be changed, see examples)                          ////
////  TODO: alot of indexing and length handling for descriptors is    ////
////   only 8bit, so make sure all descriptor tables are less than     ////
////   256 bytes long.                                                 ////
////                                                                   ////
//// Apr 21st, 2005:                                                   ////
////  Initial Alpha Release with PIC18Fxx5x support.  (ONLY TESTED     ////
////   WITH 18F4550)                                                   ////
////  usb_puts() doesn't need max packet size as a paremeter, uses     ////
////   usb_ep_tx_size[] defined in usb.h                               ////
////  usb_puts() timeout parameter now in ms, not seconds.             ////
////  USB Stack no longer buffers incoming data.  If there is data to  ////
////   get usb_kbhit(en) will return TRUE and the data will sit in     ////
////   the endpoint buffer until you usb_get_packet(), which will      ////
////   then free the endpoint buffer for more data.  This affects      ////
////   routines such as usb_gets() and usb_kbhit().                    ////
////  usb_gets() no longer reads buffered data (see above note),       ////
////   now it reads multiple packets in the same way usb_puts()        ////
////   writes multiple packets                                         ////
////  usb_kbhit() is hardware specific, so has been moved to hardware  ////
////   layer.                                                          ////
////                                                                   ////
//// Nov 11th, 2004:                                                   ////
////  No longer includes wrong descriptor header.                      ////
////                                                                   ////
//// June 24th, 2004:                                                  ////
////  Optimization and cleanup.                                        ////
////                The following definitions changed:                 ////
////  USB_EPx_TX_ENABLE and USB_EPx_RX_ENABLE have changed. See usb.h  ////
////  USB_CONFIG_DESCRIPTORS[] removed                                 ////
////  USB_CONFIG_DESC_LEN changed to USB_DESC_CONFIG_LEN               ////
////  USB_INTERFACE_DESC_LEN changed to USB_DESC_INTERFACE_LEN         ////
////  USB_CLASS_DESC_LEN changed to USB_DESC_CLASS_LEN                 ////
////  USB_ENDPOINT_DESC_LEN changed to USB_DESC_ENDPOINT_LEN           ////
////  USB_CONFIG_DESC_KEY changed to USB_DESC_CONFIG_TYPE              ////
////  USB_INTERFACE_DESC_KEY changed to USB_DESC_INTERFACE_TYPE        ////
////  USB_CLASS_DESC_KEY changed to USB_DESC_CLASS_TYPE                ////
////  USB_ENDPOINT_DESC_KEY changed to USB_DESC_ENDPOINT_TYPE          ////
////  USB_STRING_X[] arrays removed, see USB_STRING_DESC[] and         ////
////                                 USB_STRING_DESC_OFFSET[]          ////
////  dev_req, curr_config, status_device and getdesc_type global      ////
////        variables moved into struct USB_stack_status               ////
////                                                                   ////
//// December 5th, 2003: Fixed a potential bug where descriptors are   ////
////         evenly dividable by 8 (MAX_EP0_PACKET_SIZE)               ////
////                                                                   ////
//// October 15th, 2003: Support for boot protocol added.              ////
////         Set USB_HID_BOOT_PROTOCOL to TRUE to support this.        ////
////         The array hid_protocol[] saves which protocol mode each   ////
////         interface is in.  It is your applications job to send     ////
////         data that either fit the boot protocol or HID protocol.   ////
////                                                                   ////
//// May 6th, 2003: Fixed a potential stack overflow using PCM         ////
////                                                                   ////
//// October 28th, 2002: Problem with usb_puts and timeout fixed.      ////
////                                                                   ////
//// October 28th, 2002: Typo fixed in get_next_string_character(),    ////
////                     although it didn't cause any serious problems ////
////                                                                   ////
//// October 25th, 2002: Another change to usb_puts() was made to fix  ////
////                     problems with multiple packet messages        ////
//// October 29th, 2002: Fixed a problem with multiple packet string   ////
////                     descriptors that require a 0 len packet to    ////
////                     end message.                                  ////
////                                                                   ////
//// October 23rd, 2002: usb_puts() will bomb out of error quicker     ////
////                                                                   ////
//// August 2nd, 2002: Initial Public Release                          ////
////                                                                   ////
///////////////////////////////////////////////////////////////////////////
////        (C) Copyright 1996,2005 Custom Computer Services           ////
//// This source code may only be used by licensed users of the CCS    ////
//// C compiler.  This source code may only be distributed to other    ////
//// licensed users of the CCS C compiler.  No other use,              ////
//// reproduction or distribution is permitted without written         ////
//// permission.  Derivative programs created using this software      ////
//// in object code form are not restricted in any way.                ////
///////////////////////////////////////////////////////////////////////////

#IFNDEF __USB_DRIVER__
#DEFINE __USB_DRIVER__

#include <usb.h>

#IFNDEF __USB_HARDWARE__
   #ERROR You must include USB hardware driver.
#ENDIF

#IFNDEF __USB_DESCRIPTORS__
   #ERROR You must include USB descriptors.
#ENDIF

int8 USB_address_pending;                        //save previous state because packets can take several isrs
int8 usb_getdesc_ptr; int8 usb_getdesc_len=0;             //for reading string and config descriptors

#IF USB_HID_BOOT_PROTOCOL
int8 hid_protocol[USB_NUM_HID_INTERFACES];
#ENDIF

int8 USB_Interface[USB_MAX_NUM_INTERFACES];              //config state for all of our interfaces, NUM_INTERFACES defined with descriptors

/// BEGIN User Functions

/****************************************************************************
/* usb_kbhit(endpoint)
/*
/* Input: endpoint - endpoint to check
/*
/* Output: TRUE if there is new data in RX buffer, FALSE if there is not.
/*
/* Summary: Similar to kbhit(), sees if there is new data in the RX USB buffers.
/*
/*****************************************************************************/
#define usb_kbhit(x) bit_test(__usb_kbhit_status,x)

/**************************************************************
/* usb_enumerated()
/*
/* Input: Global variable USB_Curr_Config
/* Returns: Returns a 1 if device is configured / enumerated,
/*          Returns a 0 if device is un-configured / not enumerated.
/*
/* Summary: Use this to see if device is configured / enumerated.
/***************************************************************/
#define usb_enumerated() (USB_stack_status.curr_config)

/**************************************************************
/* usb_wait_for_enumeration()
/*
/* Input: Global variable USB_Curr_Config
/*
/* Summary: Waits in-definately until device is configured / enumerated.
/***************************************************************/
void usb_wait_for_enumeration(void) {
   while (USB_stack_status.curr_config == 0) {restart_wdt();}
}

/****************************************************************************
/* usb_puts(endpoint, *ptr, len, timeout)
/*
/* Inputs: endpoint - endpoint to send data out
/*         ptr - points to array of data to send
/*         len - amount of data to send
/*         timeout - time in milli-seconds, for each packet, to wait before timeout.
/*                   set to 0 for no timeout.
/*
/* Summary: Used for sending multiple packets of data as one message.  If sending
/*          only one packet it is more effecient to use only usb_put_packet()
/*
/*****************************************************************************/
int1 usb_puts(int8 endpoint, int8 * ptr, int16 len, int8 timeout) {
   int16 i=0;
   int1 res;
   int16 this_packet_len;
   int16 packet_size;
   int32 timeout_1us;

   packet_size=usb_ep_tx_size[endpoint];

   //send data packets until timeout or no more packets to send
   while (i < len) {
      timeout_1us=(int32)timeout*1000;
      if ((len - i) > packet_size) {this_packet_len=packet_size;}
      else {this_packet_len=len-i;}
      do {
         res=usb_put_packet(endpoint,ptr + i,this_packet_len,USB_DTS_TOGGLE);   //send 64 byte packets
         if (!res) {
            delay_us(1);
            timeout_1us--;
         }
      } while (!res && timeout_1us);
      i+=packet_size;
   }


   //send 0len packet if needed
   if (i==len) {
      timeout_1us=(int32)timeout*1000;
      do {
         res=usb_put_packet(endpoint,0,0,USB_DTS_TOGGLE);   //indicate end of message
         if (!res) {
            delay_us(1);
            timeout_1us--;
         }
      } while (!res && timeout_1us);
   }

   return(res);
}

/****************************************************************************
/* usb_gets(endpoint, ptr, max, timeout)
/*
/* Input: endpoint - endpoint to get data from
/*        ptr - place / array to store data to
/*        max - max amount of data to get from USB and store into ptr
/*         timeout - time in milliseconds, for each packet, to wait before timeout.
/*                   set to 0 for no timeout.
/*
/* Output: Amount of data returned.  It may be less than max.
/*
/* Summary: Gets data from the host.  Will get multiple-packet messages
/*          and finish when either it receives a 0-len packet or a packet
/*          of less size than maximum.
/*
/* NOTE: NOT THOUROUGHLY TESTED.  I AM NOT EVEN SURE IF THIS IS A GOOD IDEA
/*
/*****************************************************************************/
int8 usb_gets(int8 endpoint, int8 * ptr, int16 max, int16 timeout) {
   int16 ret=0;
   int16 to;
   int16 len;
   int16 packet_size;
   int16 this_packet_max;

   packet_size=usb_ep_rx_size[endpoint];

   do {
      if (packet_size < max) {this_packet_max=packet_size;} else {this_packet_max=max;}
      to=0;
      do {
         if (usb_kbhit(endpoint)) {
            len=usb_get_packet(endpoint,ptr,this_packet_max);
            ptr+=len;
            max-=len;
            ret+=len;
            break;
         }
         else {
            to++;
            delay_ms(1);
         }
      } while (to!=timeout);
   } while ((len == packet_size) && (to!=timeout) && max);

   return(ret);
}

/// END User Functions


/// BEGIN USB Token, standard and HID request handler (part of ISR)

/**************************************************************
/* usb_token_reset()
/*
/* Output: modifies global variables USB_Interface[], in[], usb_getdesc_ptr,
/*         usb_getdesc_len, USB_status_device, USB_dev_req
/*
/* Summary: Resets the token handler to initial (unconfigured) state.
/***************************************************************/
void usb_token_reset() {
   int i;



      for (i=0;i<USB_MAX_NUM_INTERFACES;i++) {
         USB_Interface[i]=0;   //reset each interface to default
      }

  #IF USB_HID_BOOT_PROTOCOL
   for (i=0;i<USB_NUM_HID_INTERFACES; i++) {
       hid_protocol[i]=1;
   }
  #endif

  #if USB_CDC_DEVICE
   usb_cdc_init();
  #endif

    USB_stack_status.curr_config=0;      //unconfigured device

    USB_stack_status.status_device=1;    //previous state.  init at none
    USB_stack_status.dev_req=NONE;       //previous token request state.  init at none
}

//send a 0len packet to endpoint 0 (optimization)
//notice that this doesnt return the status
#define usb_put_0len_0() usb_request_send_response(0)

/**************************************************************
/* usb_endpoint_is_valid(endpoint)
/*
/* Input: endpoint - endpoint to check.
/*                   bit 7 is direction (set is IN, clear is OUT)
/*
/* Output: TRUE if endpoint is valid, FALSE if not
/*
/* Summary: Checks the dynamic configuration to see if requested
/*          endpoint is a valid endpoint.
/***************************************************************/
//
int1 usb_endpoint_is_valid(int8 endpoint) {
   int1 direction;
   direction = bit_test(endpoint,7);
   endpoint &= 0x7F;
   if (direction) { //IN
      return(usb_ep_tx_type[endpoint] != USB_ENABLE_DISABLED);
   }
   else {   //OUT
      return(usb_ep_rx_type[endpoint] != USB_ENABLE_DISABLED);
   }
}

///---------------------------------------------------------------///
/// Processing Message stages is the biggest portion of the ISR   ///
///---------------------------------------------------------------///

/**************************************************************
/* usb_isr_tok_in_dne(endpoint)
/*
/* Input: endpoint - which endpoint we are processing a setup token.  Should be 0.
/*
/* Summary: When receiving an IN token from the PC on endpoint 0 that means the
/*          host is asking for a response from a setup token, or the next packet
/*          from a currently processing token.  (For example, a 24 byte descriptor
/*          would require 3 IN tokens if the packet size is 8 bytes.)  USB_dev_req
/*          is a global variable that defines what setup token we are currently processing.
/*
/* Part of USB interrupt service routine.
/* Only checks endpoint 0.
/***************************************************************/
void usb_isr_tok_in_dne(int8 endpoint) {
   if (endpoint==0) {
      if (USB_stack_status.dev_req == GET_DESCRIPTOR) {usb_copy_desc_seg_to_ep();} //check this, we are missing report descriptor?
      else if (USB_stack_status.dev_req == SET_ADDRESS) {usb_finish_set_address();}
   }
  #if USB_CDC_DEVICE
  else if (endpoint==USB_CDC_DATA_IN_ENDPOINT) { //see ex_usb_serial.c example and usb_cdc.h driver
      usb_isr_tok_in_cdc_data_dne();
  }
  #endif
}

/**************************************************************
/* usb_isr_tok_out_dne(endpoint)
/*
/* Input: endpoint contains which endpoint we are receiving data.
/*        This code doesn't allow reception of data from EP0. (Add later)
/*
/* Summary: Processes out tokens (out is respective of the host, so actualy incoming
/*          to the pic), but not out setup tokens.  Data is placed into a
/*          a buffer if it is empty, and the rx flag is set.  If the buffer
/*          is not empty then the overrun bit of that EP status byte is set it.
/***************************************************************/
void usb_isr_tok_out_dne(int8 endpoint) {
   //TODO:
   if (endpoint==0) {
     debug_usb(debug_putc,"TOUT 0 ");
     #if USB_CDC_DEVICE
      usb_isr_tok_out_cdc_control_dne();
     #else
      usb_init_ep0_setup();
     #endif
   }
  #if USB_CDC_DEVICE
   else if (endpoint==USB_CDC_DATA_OUT_ENDPOINT) { //see ex_usb_serial.c example and usb_cdc.h driver
      usb_isr_tok_out_cdc_data_dne();
   }
  #endif
   else {
      bit_set(__usb_kbhit_status,endpoint);
   }
}


//---- process setup message stage -----------//
////// HUGE - most of our code is to read setup messages ////

/**************************************************************
/* usb_isr_tok_setup_dne()
/*
/* Input: usb_ep0_rx_buffer[] contains the 8 bytes of the setup packet.
/*
/* Output: The DTS we expect for the next packet on OUT 0.
/*
/* Summary: This function is the start of code that handles the setup token.
/*          We must handle all relevant requests, such as Set_Configuration, Get_Descriptor, etc.
/*
/*  usb_ep0_rx_buffer[8] now contains setup data packet, which has the following records
/*  -------------------------------------------------------------------------------------------
/*  usb_ep0_rx_buffer[ 0 ]=bmRequestType; Where the setup packet goes
/*                              bit7   (0) host-to-device
/*                                     (1) device-to-host
/*                              bit6-5 (00) usb standard request;
/*                                     (01) class request;
/*                                     (10) vendor request
                                       (11) reserved
/*                              bit4-0 (0000) device
/*                                     (0001) interface
/*                                     (0010) endpoint
/*                                     (0011) other element
/*                                     (0100) to (1111) reserved
/*  usb_ep0_rx_buffer[ 1 ]=bRequest ; the request
/*  usb_ep0_rx_buffer[2,3]=wValue ; a value which corresponds to request
/*  usb_ep0_rx_buffer[4,5]=wIndex ; could correspond to interface or endpoint...
/*  usb_ep0_rx_buffer[6,7]=wLength ; number of bytes in next data packet
/*                        for host-to-device, this exactly how many bytes in data packet
/*                        for device-to-host, this is the maximum bytes that can fit one packet
/***************************************************************/
void usb_isr_tok_setup_dne(void) {
   USB_stack_status.dev_req=NONE; // clear the device request..

   switch(usb_ep0_rx_buffer[0] & 0x7F) {

      case 0x00:  //standard to device
         debug_usb(debug_putc," d");
         usb_isr_tkn_setup_StandardDevice();
         break;

      case 0x01:  //standard to interface
         debug_usb(debug_putc," i");
         usb_isr_tkn_setup_StandardInterface();
         break;

      case 0x02:  //standard to endpoint
         debug_usb(debug_putc," e");
         usb_isr_tkn_setup_StandardEndpoint();
         break;

#IF USB_HID_DEVICE
      case 0x21:  //class specific request.  the only class this driver supports is HID
         debug_usb(debug_putc," hid");
         usb_isr_tkn_setup_ClassInterface();
         break;
#endif

#if USB_CDC_DEVICE
      case 0x21:
         debug_usb(debug_putc," cdc");
         usb_isr_tkn_cdc();
         break;
#endif
      //TODO: IF YOU WANT VENDOR SPECIFC REQUEST SUPPORT YOU MUST ADD IT HERE

      default:
         usb_request_stall();
         break;
   }
}

/**************************************************************
/* usb_isr_tkn_setup_StandardDevice()
/*
/* Input: usb_ep0_rx_buffer[1] == bRequest
/*
/* Summary: bmRequestType told us it was a Standard Device request.
/*          bRequest says which request.  Only certain requests are valid,
/*          if a non-valid request was made then return with an Wrong-Statue (IDLE)
/*
/* Part of usb_isr_tok_setup_dne()
/***************************************************************/
void usb_isr_tkn_setup_StandardDevice(void) {
   switch(usb_ep0_rx_buffer[1]) {

      case USB_STANDARD_REQUEST_GET_STATUS:  //0
            debug_usb(debug_putc,"GS");
            usb_ep0_tx_buffer[0]=USB_stack_status.status_device;
            usb_ep0_tx_buffer[1]=0;
            usb_request_send_response(2);
            break;

      case USB_STANDARD_REQUEST_CLEAR_FEATURE:  //1
            if (usb_ep0_rx_buffer[2] == 1) {
               debug_usb(debug_putc,"CF");
               USB_stack_status.status_device &= 1;
               usb_put_0len_0();
            }
            else
               usb_request_stall();
            break;

      case USB_STANDARD_REQUEST_SET_FEATURE: //3
            if (usb_ep0_rx_buffer[2] == 1) {
               debug_usb(debug_putc,"SF");
               USB_stack_status.status_device |= 2;
               usb_put_0len_0();
            }
            else
               usb_request_stall();
            break;

      case USB_STANDARD_REQUEST_SET_ADDRESS: //5
            debug_usb(debug_putc,"SA");
            USB_stack_status.dev_req=SET_ADDRESS; //currently processing set_address request
            USB_address_pending=usb_ep0_rx_buffer[2];
            #ifdef __USBN__   //NATIONAL part handles this differently than pic16c7x5
            USB_stack_status.dev_req=NONE; //currently processing set_address request
            usb_set_address(USB_address_pending);
         	USB_stack_status.curr_config=0;	// make sure current configuration is 0
            #endif
            usb_put_0len_0();
            break;

      case USB_STANDARD_REQUEST_GET_DESCRIPTOR: //6
            debug_usb(debug_putc,"GD");
            usb_Get_Descriptor();
            break;

      case USB_STANDARD_REQUEST_GET_CONFIGURATION: //8
            debug_usb(debug_putc,"GC");
            usb_ep0_tx_buffer[0]=USB_stack_status.curr_config;
            usb_request_send_response(1);
            break;

      case USB_STANDARD_REQUEST_SET_CONFIGURATION: //9
            if (usb_ep0_rx_buffer[2] <= USB_NUM_CONFIGURATIONS) {
               debug_usb(debug_putc,"SC");
               USB_stack_status.curr_config=usb_ep0_rx_buffer[2];
               usb_set_configured(usb_ep0_rx_buffer[2]);
               usb_put_0len_0();
            }
            break;

      default:
            usb_request_stall();
            break;
   }
}

/**************************************************************
/* usb_isr_tkn_setup_StandardInterface()
/*
/* Input: usb_ep0_rx_buffer[1] == bRequest
/*
/* Summary: bmRequestType told us it was a Standard Interface request.
/*          bRequest says which request.  Only certain requests are valid,
/*          if a non-valid request was made then return with an Wrong-Statue (IDLE)
/*
/* Part of usb_isr_tok_setup_dne()
/***************************************************************/
void usb_isr_tkn_setup_StandardInterface(void) {
   int8 curr_config;

   curr_config=USB_stack_status.curr_config;

   switch (usb_ep0_rx_buffer[1]) {
      case USB_STANDARD_REQUEST_GET_STATUS:
            debug_usb(debug_putc,"GS");
            usb_ep0_tx_buffer[0]=0;
            usb_ep0_tx_buffer[1]=0;
            usb_request_send_response(2);
            break;

      case USB_STANDARD_REQUEST_GET_INTERFACE:
            if ( curr_config && (usb_ep0_rx_buffer[4] < USB_NUM_INTERFACES[curr_config-1]) ) {   //book says only supports configed state
               debug_usb(debug_putc,"GI");
               usb_ep0_tx_buffer[0]=USB_Interface[usb_ep0_rx_buffer[4]];//our new outgoing byte
               usb_request_send_response(1);; //send byte back
            }
            else
               usb_request_stall();
            break;

      case USB_STANDARD_REQUEST_SET_INTERFACE:
            if (curr_config) { //if configured state
               debug_usb(debug_putc,"SI");
               USB_Interface[usb_ep0_rx_buffer[4]]=usb_ep0_rx_buffer[2];
               usb_put_0len_0();
            }
            else
               usb_request_stall();
            break;

#IF USB_HID_DEVICE
      case USB_STANDARD_REQUEST_GET_DESCRIPTOR:
            debug_usb(debug_putc,"GD");
            usb_Get_Descriptor();
            break;
#endif

//      case USB_STANDARD_REQUEST_CLEAR_FEATURE:
//      case USB_STANDARD_REQUEST_SET_FEATURE:
//                let default take care of these, goto wrongstate
      default:
            usb_request_stall();
            break;
   }
}

/**************************************************************
/* usb_isr_tkn_setup_StandardEndpoint()
/*
/* Input: usb_ep0_rx_buffer[1] == bRequest
/*
/* Summary: bmRequestType told us it was a Standard Endpoint request.
/*          bRequest says which request.  Only certain requests are valid,
/*          if a non-valid request was made then return with an Wrong-Statue (IDLE)
/*
/* Part of usb_isr_tok_setup_dne()
/***************************************************************/
void usb_isr_tkn_setup_StandardEndpoint(void) {
   if (usb_endpoint_is_valid(usb_ep0_rx_buffer[4])) {
      switch(usb_ep0_rx_buffer[1]) {

         case USB_STANDARD_REQUEST_CLEAR_FEATURE:
               debug_usb(debug_putc,"CF");
               usb_unstall_ep(usb_ep0_rx_buffer[4]);
               usb_put_0len_0();
               break;

         case USB_STANDARD_REQUEST_SET_FEATURE:
                     debug_usb(debug_putc,"SF");
                     usb_stall_ep(usb_ep0_rx_buffer[4]);
                     usb_put_0len_0();
                     break;

         case USB_STANDARD_REQUEST_GET_STATUS:
               debug_usb(debug_putc,"GS");
               usb_ep0_tx_buffer[0]=0;
               usb_ep0_tx_buffer[1]=0;
               if (usb_endpoint_stalled(usb_ep0_rx_buffer[4])) {
                  usb_ep0_tx_buffer[0]=1;
               }
               usb_request_send_response(2);
               break;

         default:
            usb_request_stall();
            break;
      }
   }
}

/**************************************************************
/* usb_isr_tkn_setup_ClassInterface()
/*
/* Input: usb_ep0_rx_buffer[1] == bRequest
/*
/* Summary: bmRequestType told us it was a Class request.  The only Class this drivers supports is HID.
/*          bRequest says which request.  Only certain requests are valid,
/*          if a non-valid request was made then return with an Wrong-Statue (IDLE)
/*
/* Part of usb_isr_tok_setup_dne()
/* Only compiled if HID_DEVICE is TRUE
/***************************************************************/
#IF USB_HID_DEVICE
void usb_isr_tkn_setup_ClassInterface(void) {
   switch(usb_ep0_rx_buffer[1]) {

    #IF USB_HID_BOOT_PROTOCOL
      case USB_HID_REQUEST_GET_PROTOCOL:  //03
            debug_usb(debug_putc,"GP");
            usb_ep0_tx_buffer[0]=hid_protocol[usb_ep0_rx_buffer[4]];
            usb_request_send_response(1);
            break;
    #ENDIF

    #IF USB_HID_BOOT_PROTOCOL
      case USB_HID_REQUEST_SET_PROTOCOL:  //0b
            debug_usb(debug_putc,"SP");
            hid_protocol[usb_ep0_rx_buffer[4]]=usb_ep0_rx_buffer[2];
            usb_put_0len_0(); //send 0len packet69
            break;
    #ENDIF

   #IF USB_HID_IDLE
      case USB_HID_REQUEST_SET_IDLE:   //0a
         #error TODO: if you want to support SET_IDLE, add code here
   #ENDIF

   #IF USB_HID_IDLE
      case USB_HID_REQUEST_GET_IDLE:   //02
         #error TODO: if you want to support GET_IDLE, add code here
   #ENDIF

      default:
            usb_request_stall();
            break;
   }
}
#ENDIF

/**************************************************************
/* usb_Get_Descriptor()
/*
/* Input: usb_ep0_rx_buffer[3] == wValue, which descriptor we want
/*        usb_ep0_rx_buffer[6,7] == Max length the host will accept
/*
/* Summary: Checks to see if we want a standard descriptor (Interface, Endpoint, Config, Device, String, etc.),
/*          or a class specific (HID) descriptor.  Since some pics (especially the PIC167x5) doesn't have
/*          pointers to constants we must simulate or own by setting up global registers that say
/*          which constant array to deal with, which position to start in this array, and the length.
/*          Once these globals are setup the first packet is sent.  If a descriptor takes more than one packet
/*          the PC will send an IN request to endpoint 0, and this will be handled by usb_isr_tok_in_dne()
/*          which will send the rest of the data.
/*
/* Part of usb_isr_tok_setup_dne()
/***************************************************************/
void usb_Get_Descriptor() {
   usb_getdesc_ptr=0;
   USB_stack_status.getdesc_type=USB_GETDESC_CONFIG_TYPE;

   switch(usb_ep0_rx_buffer[3]) {
      case USB_DESC_DEVICE_TYPE:    //1
            usb_getdesc_len=USB_DESC_DEVICE_LEN;
            USB_stack_status.getdesc_type=USB_GETDESC_DEVICE_TYPE;
            break;

      //windows hosts will send a FF max len and expect you to send all configs without asking for them individually.
      case USB_DESC_CONFIG_TYPE:   //2
            usb_getdesc_len=USB_TOTAL_CONFIG_LEN;
            break;

      case USB_DESC_STRING_TYPE: //3
            USB_stack_status.getdesc_type=USB_GETDESC_STRING_TYPE;
            usb_getdesc_ptr=USB_STRING_DESC_OFFSET[usb_ep0_rx_buffer[2]];
            usb_getdesc_len=USB_STRING_DESC[usb_getdesc_ptr];
            break;

#IF USB_HID_DEVICE
      case USB_DESC_CLASS_TYPE:  //0x21
            usb_getdesc_ptr=USB_CLASS_DESCRIPTORS[0][0][0];
            if (usb_getdesc_ptr!=0xFF) {
               usb_getdesc_len=USB_CONFIG_DESC[usb_getdesc_ptr];
               break;
            }
            else {
               usb_request_stall();
               return;
            }


      case USB_DESC_HIDREPORT_TYPE: //0x22
            usb_getdesc_ptr=USB_CLASS_SPECIFIC_DESC_LOOKUP[0][usb_ep0_rx_buffer[4]];
            if (usb_getdesc_ptr !=0xFF) {
               USB_stack_status.getdesc_type=USB_GETDESC_HIDREPORT_TYPE;
               usb_getdesc_len=USB_CLASS_SPECIFIC_DESC_LOOKUP_SIZE[0][usb_ep0_rx_buffer[4]];
               break;
            }
            else {
               usb_request_stall();
               return;
            }
#endif

      default:
            usb_request_stall();
            return;
   }
   if (usb_ep0_rx_buffer[7]==0) {
      if (usb_getdesc_len > usb_ep0_rx_buffer[6])
         usb_getdesc_len = usb_ep0_rx_buffer[6];
   }
   USB_stack_status.dev_req=GET_DESCRIPTOR;
   usb_copy_desc_seg_to_ep();
}

/**************************************************************
/* usb_finish_set_address()
/*
/* Input: USB_address_pending holds the address we were asked to set to.
/*
/* Summary: Sets the address.
/*
/* This code should only be run on the PIC USB peripheral, and not the
/* National peripheral.
/*
/* Part of usb_isr_tok_setup_dne()
/***************************************************************/
 void usb_finish_set_address() {
   debug_usb(debug_putc," FSA ");
	USB_stack_status.curr_config=0;	// make sure current configuration is 0

   #ifdef __PIC__
	USB_stack_status.dev_req=NONE;  // no request pending
   usb_set_address(USB_address_pending);
   #endif
}

////////////////////////////////////////////////////////////////////////////
///
/// The following function retrieve data from constant arrays.  This may
/// look un-optimized, but remember that you can't create a pointer to
/// a constant array.
///
///////////////////////////////////////////////////////////////////////////
void usb_copy_desc_seg_to_ep(void) {
   int i=0;
   char c;

   while ((usb_getdesc_len)&&(i<USB_MAX_EP0_PACKET_LENGTH))
   {
      switch(USB_stack_status.getdesc_type) {
         case USB_GETDESC_CONFIG_TYPE:
            c=USB_CONFIG_DESC[usb_getdesc_ptr];
            break;

        #IF USB_HID_DEVICE
         case USB_GETDESC_HIDREPORT_TYPE:
            c=USB_CLASS_SPECIFIC_DESC[usb_getdesc_ptr];
            break;
        #endif

         case USB_GETDESC_STRING_TYPE:
            c=USB_STRING_DESC[usb_getdesc_ptr];
            break;

         case USB_GETDESC_DEVICE_TYPE:
            c=USB_DEVICE_DESC[usb_getdesc_ptr];
            break;
      }
      usb_getdesc_ptr++;
      usb_getdesc_len--;
      usb_ep0_tx_buffer[i++]=c;
   }

   if ((!usb_getdesc_len)&&(i!=USB_MAX_EP0_PACKET_LENGTH)) {
         USB_stack_status.dev_req = NONE;
   }

   usb_request_send_response(i);
}

#ENDIF
