/* USB Serial Example for Teensy USB Development Board
 * http://www.pjrc.com/teensy/usb_serial.html
 * Copyright (c) 2008,2010,2011 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef __AVR_ATmega32U4__
#define __AVR_ATmega32U4__
#endif

#define USB_SERIAL_PRIVATE_INCLUDE
#include "usb_hdi.h"


/**************************************************************************
 *
 *  Configurable Options
 *
 **************************************************************************/

// You can change these to give your code its own name.  On Windows,
// these are only used before an INF file (driver install) is loaded.
#define STR_MANUFACTURER   L"Test"
#define STR_PRODUCT        L"Test"

// All USB serial devices are supposed to have a serial number
// (according to Microsoft).  On windows, a new COM port is created
// for every unique serial/vendor/product number combination.  If
// you program 2 identical boards with 2 different serial numbers
// and they are assigned COM7 and COM8, each will always get the
// same COM port number because Windows remembers serial numbers.
//
// On Mac OS-X, a device file is created automatically which
// incorperates the serial number, eg, /dev/cu-usbmodem12341
//
// Linux by default ignores the serial number, and creates device
// files named /dev/ttyACM0, /dev/ttyACM1... in the order connected.
// Udev rules (in /etc/udev/rules.d) can define persistent device
// names linked to this serial number, as well as permissions, owner
// and group settings.
#define STR_SERIAL_NUMBER  L"0000000000000001"

// Mac OS-X and Linux automatically load the correct drivers.  On
// Windows, even though the driver is supplied by Microsoft, an
// INF file is needed to load the driver.  These numbers need to
// match the INF file.
#define VENDOR_ID    0x0001
#define PRODUCT_ID   0x0001





// These determine the bandwidth that will be allocated
// for your communication.  You do not need to use it
// all, but allocating more than necessary means reserved
// bandwidth is no longer available to other USB devices.
#define TESTHID_TX_SIZE     0x21  // transmit packet size
#define TESTHID_TX_INTERVAL 1     // max # of ms between transmit packets
#define TESTHID_RX_SIZE     0x21  // receive packet size
#define TESTHID_RX_INTERVAL 1     // max # of ms between receive packets





/**************************************************************************
 *
 *  Endpoint Buffer Configuration
 *
 **************************************************************************/

// These buffer sizes are best for most applications, but perhaps if you
// want more buffering on some endpoint at the expense of others, this
// is where you can make such changes.  The AT90USB162 has only 176 bytes
// of DPRAM (USB buffers) and only endpoints 3 & 4 can double buffer.


#define ENDPOINT0_SIZE     8 //32
#define TESTHID_INTERFACE      0
#define TESTHID_TX_ENDPOINT    1
#define TESTHID_RX_ENDPOINT    2


#define RAWHID_TX_BUFFER	EP_DOUBLE_BUFFER
#define RAWHID_RX_BUFFER	EP_DOUBLE_BUFFER


static const uint8_t endpoint_config_table[] PROGMEM = {
	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(TESTHID_TX_SIZE) | RAWHID_TX_BUFFER,
	1, EP_TYPE_INTERRUPT_OUT,  EP_SIZE(TESTHID_RX_SIZE) | RAWHID_RX_BUFFER,
	0,
	0
};

/**************************************************************************
 *
 *  Descriptor Data
 *
 **************************************************************************/

// Descriptors are the data that your computer reads when it auto-detects
// this USB device (called "enumeration" in USB lingo).  The most commonly
// changed items are editable at the top of this file.  Changing things
// in here should only be done by those who've read chapter 9 of the USB
// spec and relevant portions of any USB class specifications!

static unsigned char  device_descriptor[] PROGMEM = {
   18,                                 // bLength            Descriptor size in Bytes (18 Bytes)
   1,                                  // bDescriptorType    Descriptor Type -> Constant DEVICE (01h)
   0x10, 0x01,                         // bcdUSB             USB specification release number (BCD) (Complies to USB Spec. Release 1.10)
   0,                                  // bDeviceClass       Class code (0)
   0,                                  // bDeviceSubClass    Subclass code (0)
   0,                                  // bDeviceProtocol    Protocol code (No specific protocol)
   ENDPOINT0_SIZE,                     // bMaxPacketSize0    Maximum packet size for Endpoint 0 (8 Bytes)
   LSB(VENDOR_ID), MSB(VENDOR_ID),     // idVendor           Vendor ID (No yet available)
   LSB(PRODUCT_ID), MSB(PRODUCT_ID),   // idProduct          Product ID (No yet available)
   0x10, 0x01,                         // bcdDevice          Device release number (BCD) (1.10)
   1,                                  // iManufacturer      Index of string descriptor for the manufacturer (1)
   2,                                  // iProduct           Index of string descriptor for the product (2)
   3,                                  // iSerialNumber      Index of string descriptor containing the serial number (3)
   1                                   // bNumConfigurations Number of possible configurations (1)
};

#define RAWHID_USAGE_PAGE     0xFFA0   // recommended: 0xFF00 to 0xFFFF
#define RAWHID_USAGE          0xA5     // recommended: 0x0100 to 0xFFFF



static uint8_t Hid_Report_Desc[] PROGMEM =
{
   0x06,0xA0,0xFF,      //Usage Page (vendor defined)
   0x09,0xA5,           //Usage (vendor defined)
   //Collection
   0xA1,0x01,           //Collection (Application)
   0x09,0xA6,           //Usage (vendor defined)
   //Input Report
   0x09,0xA7,           //Usage (vendor defined)
   0x15,0x00,           //Logical minimum (0)
   0x25,0xFF,           //Logical maximum (255)
   0x75,0x08,           //Report Size (8 bits)
   0x95,TESTHID_TX_SIZE, //Report Count (33 fields/bytes)
   0x82,0x22,0x01,      //Input (Data, Variable, Absolute, No pref. state, Buffered bytes)
   //Output Report
   0x09,0xA9,           //Usage (vendor defined)
   0x15,0x00,           //Logical minimum (0)
   0x25,0xFF,           //Logical maximum (255)
   0x75,0x08,           //Report Size (8 bits)
   0x95,TESTHID_RX_SIZE, //Report Count (33 fields/bytes)
   0x92,0x22,0x01,      //Input (Data, Variable, Absolute, No pref. state, Buffered bytes)
   //Collection
   0xC0                 //End Collection
};





#define CONFIG1_DESC_SIZE (9+9+9+7+7)
#define TESTHID_HID_DESC_OFFSET   (9+9)


static uint8_t config1_descriptor[CONFIG1_DESC_SIZE] PROGMEM = {
   // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
   9,                      // bLength;             Descriptor size in Bytes (9 Bytes)
   2,                      // bDescriptorType;     Descriptor Type -> Constant CONFIGURATION (02h)
   LSB(CONFIG1_DESC_SIZE), // wTotalLength         Size of all data returned for this configuration in bytes (41 Bytes)
   MSB(CONFIG1_DESC_SIZE),
   1,                      // bNumInterfaces       Number of interfaces the configuration supports (1)
   1,                      // bConfigurationValue  Identifier for Set_Configuration and Get_Configuration requests (1)
   0,                      // iConfiguration       Index of string descriptor for the configuration (None)
   0x80,                   // bmAttributes         Self power/bus power and remote wakeup settings (Bus powered)
   0x64,                   // bMaxPower            Bus power required, expressed as (maximum milliamperes/2) (200 mA)

   // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
   9,                      // bLength              Descriptor size in Bytes (9 Bytes)
   4,                      // bDescriptorType      Descriptor Type -> Constant INTERFACE (04h)
   TESTHID_INTERFACE,       // bInterfaceNumber     Number identifying this interface (0)
   0,                      // bAlternateSetting    Value used to select an alternate setting (0)
   2,                      // bNumEndpoints        Number of endpoints supported, except Endpoint 0 (2)
   0x03,                   // bInterfaceClass      Class code (03h for HID Class)
   0x00,                   // bInterfaceSubClass   Subclass code (None)
   0x00,                   // bInterfaceProtocol   Protocol code (None)
   0,                      // iInterface           Index of string descriptor for the interface (None)

//HID_CLASS_DESC:
   0x09,                   // Descriptor size in Bytes (9 Bytes)
   0x21,                   //;Descriptor Type -> Constant HID (21h)
   0x10,0x01,              //;HID specification release number (BCD) (1.10)
   0x00,                   //;Numeric expression identifying the country for localized hardware (BCD) (None)
   0x01,                   //;Number of subordinate class descriptors supported (1)
   0x22,                   //;The type of class descriptor (HID -> 22h)
   sizeof(Hid_Report_Desc),0, // wDescriptorLength  0//Total length of report descriptor (36 Bytes)


//ENDPOINT1_DESC:
   0x07,                      //;Descriptor size in Bytes (7 Bytes)
   0x05,                      //;Descriptor type -> Constant ENDPOINT (05h)
   TESTHID_TX_ENDPOINT | 0x80, //;Endpoint number and direction (Respond to IN, Endpoint 1
   0x03,                      //Transfer type supported (Interrupt -> 03h)
   TESTHID_TX_SIZE,0x00,       //;Maximum packet size supported (33 Bytes)
   TESTHID_TX_INTERVAL,        //;Maximum latency / polling interval / NAK rate (1 ms)


//ENDPOINT2_DESC:
   0x07,                      //;Descriptor size in Bytes (7 Bytes)
   0x05,                      //;Descriptor type -> Constant ENDPOINT (05h)
   TESTHID_RX_ENDPOINT,        //;Endpoint number and direction (Respond to OUT, Endpoint 2
   0x03,                      //;Transfer type supported (Interrupt -> 03h)
   TESTHID_RX_SIZE,0x00,       //;Maximum packet size supported (33 Bytes)
   TESTHID_RX_INTERVAL         //;Maximum latency / polling interval / NAK rate (1 ms)

//CONFIG_SET_DESC_END:
};



// If you're desperate for a little extra code memory, these strings
// can be completely removed if iManufacturer, iProduct, iSerialNumber
// in the device desciptor are changed to zeros.
struct usb_string_descriptor_struct
{
   uint8_t bLength;
   uint8_t bDescriptorType;
   int16_t wString[];
};
static struct usb_string_descriptor_struct  string0 PROGMEM =
{
   4,
   3,
   {0x0409}
};
static struct usb_string_descriptor_struct string1 PROGMEM =
{
   sizeof (STR_MANUFACTURER),
   3,
   STR_MANUFACTURER
};
static struct usb_string_descriptor_struct string2 PROGMEM=
{
   sizeof (STR_PRODUCT),
   3,
   STR_PRODUCT
};
static struct usb_string_descriptor_struct string3 PROGMEM=
{
   sizeof (STR_SERIAL_NUMBER),
   3,
   STR_SERIAL_NUMBER
};

// This table defines which descriptor data is sent for each specific
// request from the host (in wValue and wIndex).
static struct descriptor_list_struct
{
   uint16_t wValue;
   uint16_t wIndex;
   const uint8_t  *addr;
   uint8_t     length;
}
descriptor_list[] PROGMEM =
{
   {0x0100, 0x0000, device_descriptor, sizeof (device_descriptor)},
   {0x0200, 0x0000, config1_descriptor, sizeof (config1_descriptor)},
   // 0x400     ;Interface Descriptor
   // 0x500     ;Endpoint 1 Descriptor
   // 0x501     ;Endpoint 2 Descriptor

   {0x2200, TESTHID_INTERFACE, Hid_Report_Desc, sizeof (Hid_Report_Desc)},
	{0x2100, TESTHID_INTERFACE, config1_descriptor+TESTHID_HID_DESC_OFFSET, 9},
   {0x0300, 0x0000, (const uint8_t *)&string0, 4},
   {0x0301, 0x0409, (const uint8_t *)&string1, sizeof (STR_MANUFACTURER)},
   {0x0302, 0x0409, (const uint8_t *)&string2, sizeof (STR_PRODUCT)},
   {0x0303, 0x0409, (const uint8_t *)&string3, sizeof (STR_SERIAL_NUMBER)}
};
#define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct))


/**************************************************************************
 *
 *  Variables - these are the only non-stack RAM usage
 *
 **************************************************************************/

// zero when we are not configured, non-zero when enumerated
static volatile uint8_t usb_configuration=0;

// these are a more reliable timeout than polling the
// frame counter (UDFNUML)
static volatile uint8_t rx_timeout_count=0;
static volatile uint8_t tx_timeout_count=0;




/**************************************************************************
 *
 *  Public Functions - these are the API intended for the user
 *
 **************************************************************************/

// initialize USB serial
void usb_init(void)
{
   HW_CONFIG();
   USB_FREEZE(); // enable USB
   PLL_CONFIG(); // config PLL, 16 MHz xtal
   while (!(PLLCSR & (1<<PLOCK))); // wait for PLL lock
   USB_CONFIG(); // start USB clock
   UDCON = 0; // enable attach resistor
   usb_configuration = 0;
  // cdc_line_rtsdtr = 0;
   UDIEN = (1<<EORSTE)|(1<<SOFE);
   sei();
}

// return 0 if the USB is not configured, or the configuration
// number selected by the HOST
uint8_t usb_configured(void)
{
   return usb_configuration;
}



// receive a packet, with timeout
int8_t usb_rawhid_recv(uint8_t *buffer, uint8_t timeout)
{
   uint8_t intr_state;
   // if we're not online (enumerated and configured), error
   if (!usb_configuration)
      return -1;
   intr_state = SREG;
   cli();
   rx_timeout_count = timeout;
   UENUM = TESTHID_RX_ENDPOINT;
   // wait for data to be available in the FIFO
   while (1)
   {
      if (UEINTX & (1<<RWAL))
         break;
      SREG = intr_state;
      if (rx_timeout_count == 0)
         return 0;
      if (!usb_configuration)
         return -1;
      intr_state = SREG;
      cli();
      UENUM = TESTHID_RX_ENDPOINT;
   }
   // read bytes from the FIFO
 
   #if (TESTHID_RX_SIZE >= 35)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 34)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 33)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 32)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 31)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 30)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 29)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 28)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 27)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 26)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 25)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 24)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 23)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 22)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 21)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 20)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 19)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 18)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 17)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 16)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 15)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 14)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 13)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 12)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 11)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 10)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 9)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 8)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 7)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 6)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 5)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 4)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 3)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 2)
   *buffer++ = UEDATX;
   #endif
   #if (TESTHID_RX_SIZE >= 1)
   *buffer++ = UEDATX;
   #endif
   // release the buffer
   UEINTX = 0x6B;
   SREG = intr_state;
   return TESTHID_RX_SIZE;
}

// send a packet, with timeout
int8_t usb_rawhid_send(const uint8_t *buffer, uint8_t timeout)
{
   uint8_t intr_state;
   // if we're not online (enumerated and configured), error
   if (!usb_configuration)
      return -1;
   intr_state = SREG;
   cli();
   tx_timeout_count = timeout;
   UENUM = TESTHID_TX_ENDPOINT;
   // wait for the FIFO to be ready to accept data
   while (1)
   {
      if (UEINTX & (1<<RWAL))
         break;
      SREG = intr_state;
      if (tx_timeout_count == 0)
         return 0;
      if (!usb_configuration)
         return -1;
      intr_state = SREG;
      cli();
      UENUM = TESTHID_TX_ENDPOINT;
   }
   // write bytes from the FIFO

   #if (TESTHID_TX_SIZE >= 34)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 33)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 32)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 31)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 30)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 29)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 28)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 27)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 26)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 25)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 24)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 23)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 22)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 21)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 20)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 19)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 18)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 17)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 16)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 15)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 14)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 13)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 12)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 11)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 10)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 9)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 8)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 7)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 6)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 5)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 4)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 3)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 2)
   UEDATX = *buffer++;
   #endif
   #if (TESTHID_TX_SIZE >= 1)
   UEDATX = *buffer++;
   #endif
   // transmit it now
   UEINTX = 0x3A;
   SREG = intr_state;
   return TESTHID_TX_SIZE;
}






/**************************************************************************
 *
 *  Private Functions - not intended for general user consumption....
 *
 **************************************************************************/
// USB Device Interrupt - handle all device-level events
// the transmit buffer flushing is triggered by the start of frame
//
ISR(USB_GEN_vect)
{
   unsigned char intbits, t;

   intbits = UDINT;
   UDINT = 0;
   if (intbits & (1<<EORSTI))
   {
      UENUM = 0;
      UECONX = 1;
      UECFG0X = EP_TYPE_CONTROL;
      UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER;
      UEIENX = (1<<RXSTPE);
      usb_configuration = 0;
   }
   if ((intbits & (1<<SOFI)) && usb_configuration)
   {
      t = rx_timeout_count;
      if (t)
      {
         rx_timeout_count = --t;
      }
      t = tx_timeout_count;
      if (t)
      {
         tx_timeout_count = --t;
      }
   }
}



// Misc functions to wait for ready and send/receive packets
static inline void usb_wait_in_ready(void)
{
   while (!(UEINTX & (1<<TXINI)));
}
static inline void usb_send_in(void)
{
   UEINTX = ~(1<<TXINI);
}
static inline void usb_wait_receive_out(void)
{
   while (!(UEINTX & (1<<RXOUTI)));
}
static inline void usb_ack_out(void)
{
   UEINTX = ~(1<<RXOUTI);
}



// USB Endpoint Interrupt - endpoint 0 is handled here.  The
// other endpoints are manipulated by the user-callable
// functions, and the start-of-frame interrupt.
//
ISR(USB_COM_vect)
{
   uint8_t intbits;
   const uint8_t *list;
   const uint8_t *cfg;
   uint8_t i, n, len, en;
   uint8_t bmRequestType;
   uint8_t bRequest;
   uint16_t wValue;
   uint16_t wIndex;
   uint16_t wLength;
   uint16_t desc_val;
   const uint8_t *desc_addr;
   uint8_t  desc_length;

   UENUM = 0;
   intbits = UEINTX;
   if (intbits & (1<<RXSTPI))
   {
      bmRequestType = UEDATX;
      bRequest = UEDATX;
      wValue = UEDATX;
      wValue |= (UEDATX << 8);
      wIndex = UEDATX;
      wIndex |= (UEDATX << 8);
      wLength = UEDATX;
      wLength |= (UEDATX << 8);
      UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
      if (bRequest == GET_DESCRIPTOR)
      {
         list = (const uint8_t *)descriptor_list;
         for (i=0; ; i++)
         {
            if (i >= NUM_DESC_LIST)
            {
               UECONX = (1<<STALLRQ)|(1<<EPEN); //stall
               return;
            }
            desc_val = pgm_read_word(list);
            if (desc_val != wValue)
            {
               list += sizeof (struct descriptor_list_struct);
               continue;
            }
            list += 2;
            desc_val = pgm_read_word(list);
            if (desc_val != wIndex)
            {
               list += sizeof (struct descriptor_list_struct)-2;
               continue;
            }
            list += 2;
            desc_addr = (const uint8_t *)pgm_read_word(list);
            list += 2;
            desc_length = pgm_read_byte(list);
            break;
         }
         len = (wLength < 256) ? wLength : 255;
         if (len > desc_length)
            len = desc_length;
         do
         {
            // wait for host ready for IN packet
            do
            {
               i = UEINTX;
            }
            while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
            if (i & (1<<RXOUTI))
               return; // abort
            // send IN packet
            n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
            for (i = n; i; i--)
            {
               UEDATX = pgm_read_byte(desc_addr++);
            }
            len -= n;
            usb_send_in();
         }
         while (len || n == ENDPOINT0_SIZE);
         return;
      }
      if (bRequest == SET_ADDRESS)
      {
         usb_send_in();
         usb_wait_in_ready();
         UDADDR = wValue | (1<<ADDEN);
         return;
      }
      if (bRequest == SET_CONFIGURATION && bmRequestType == 0)
      {
         usb_configuration = wValue;
         usb_send_in();
         cfg = endpoint_config_table;
         for (i=1; i<5; i++)
         {
            UENUM = i;
            en = pgm_read_byte(cfg++);
            UECONX = en;
            if (en)
            {
               UECFG0X = pgm_read_byte(cfg++);
               UECFG1X = pgm_read_byte(cfg++);
            }
         }
         UERST = 0x1E;
         UERST = 0;
         return;
      }
      if (bRequest == GET_CONFIGURATION && bmRequestType == 0x80)
      {
         usb_wait_in_ready();
         UEDATX = usb_configuration;
         usb_send_in();
         return;
      }

      if (bRequest == GET_STATUS)
      {
         usb_wait_in_ready();
         i = 0;
         if (bmRequestType == 0x82)
         {
            UENUM = wIndex;
            if (UECONX & (1<<STALLRQ))
               i = 1;
            UENUM = 0;
         }
         UEDATX = i;
         UEDATX = 0;
         usb_send_in();
         return;
      }
      if ((bRequest == CLEAR_FEATURE || bRequest == SET_FEATURE)
         && bmRequestType == 0x02 && wValue == 0)
      {
         i = wIndex & 0x7F;
         if (i >= 1 && i <= MAX_ENDPOINT)
         {
            usb_send_in();
            UENUM = i;
            if (bRequest == SET_FEATURE)
            {
               UECONX = (1<<STALLRQ)|(1<<EPEN);
            }
            else
            {
               UECONX = (1<<STALLRQC)|(1<<RSTDT)|(1<<EPEN);
               UERST = (1 << i);
               UERST = 0;
            }
            return;
         }
      }
      if (wIndex == TESTHID_INTERFACE)
      {
         if (bmRequestType == 0xA1 && bRequest == HID_GET_REPORT)
         {
            len = TESTHID_TX_SIZE;
            do
            {
               // wait for host ready for IN packet
               do
               {
                  i = UEINTX;
               }
               while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
               if (i & (1<<RXOUTI))
                  return; // abort
               // send IN packet
               n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
               for (i = n; i; i--)
               {
                  // just send zeros
                  UEDATX = 0;
               }
               len -= n;
               usb_send_in();
            }
            while (len || n == ENDPOINT0_SIZE);
            return;
         }
         if (bmRequestType == 0x21 && bRequest == HID_SET_REPORT)
         {
            len = TESTHID_RX_SIZE;
            do
            {
               n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
               usb_wait_receive_out();
               // ignore incoming bytes
               usb_ack_out();
               len -= n;
            }
            while (len);
            usb_wait_in_ready();
            usb_send_in();
            return;
         }
      }
   }
   UECONX = (1<<STALLRQ) | (1<<EPEN); // stall
}




