/*******************************************************************************
 *    usbHost.c
 *    include HUB driver
 *
 *    PIC32MZ HighSpeed USB HOST module.
 *    Support Multi Interface and Multi Device via external HUB.
 *         
 *    2025.7.17   https://www.suwa-koubou.jp/micom/usbHsHost
 */

//   HOSTドライバー機能概要
//   HOST driver function overview
//
//
// 　USBデバイスの管理
//   Managing USB devices
//
//   USBコネクタに直接デバイスが挿された時、USBモジュールの割込み処理で
// デバイスの接続が検出され、usbDeviceAttachフラグ に1が設定される.
//   When a device is directly inserted into the USB connector, the USB module
// interrupt process detects the device connection and sets the usbDeviceAttach
// flag to 1.
//
//   usbDeviceAttachはUSBコネクタに直接接続されたデバイスが外されるまでは
// 1のままである.デバイスの抜去はUSBモジュールの割込み処理で検出されて
// usbDeviceAttach フラグは0になる.
//    The usbDeviceAttach flag remains 1 until the device directly connected
// to the USB connector is removed. The removal of the device is detected by 
// the USB module interrupt processing and the usbDeviceAttach flag becomes 0.
//
//  　直接接続されるデバイスは、HUBかそれ以外の任意のデバイスであり、デバイスの
// 情報を管理する usbDevice[] 配列の 0番目のデバイスとなり、デバイスアドレスに
// は1が割り当てられる.
//    A directly connected device can be a HUB or any other device, and it
//  becomes the 0th device in the usbDevice[] array that manages device 
//  information, and is assigned a device address of 1.
//
//  HUBが接続されていない時には usbDevice[]配列の0番目以外の要素は無効であり、
// 管理情報は全て初期値0のままである。
//    When no HUB is connected, all elements in the usbDevice[] array other
//  than the 0th element are invalid, and all management information remains
//  at its initial value of 0.
//
//   HUBのn番ポートに接続されたデバイスは、usbDevice[n]で管理され、デバイスア
// ドレスは、n+1になる。HUBのポート番号は 1から始まり 0 はHUB自身を示す.
//   A device connected to the nth port of a HUB is managed by usbDevice[n]
// and its device address is n+1. The port number of a HUB starts from 1, 
// and 0 indicates the HUB itself.
//
//
//   USBコネクタデバイス挿入時の処理
//   Processing when a USB connector device is inserted
//
//   　 システム動作開始後、HOSTドライバー(isUsbConnect()関数)は、
//  usbDeviceAttachフラグによりUSBコネクタに直接接続されるデバイスの接続
//  状況を確認する. 
//    After the system starts, the HOST driver (isUsbConnect() function) 
// checks the connection status of the device directly connected to the USB
// connector using the usbDeviceAttach flag.
//
//     usbDeviceAttachが 0ならデバイス未接続であり 1ならUSBコネクタにデバイス
//  が接続されたので、バスリセットを行い、接続デバイスの速度を検出した後
//  このデバイスのエヌメレーション処理を行う.
//   If the usbDeviceAttach flag is 0, the device is not connected, and
// if it is 1, the device is connected to the USB connector. A bus reset is
// performed, the speed of the connected device is detected, and then the 
// enumeration process for this device is performed.
//
//    エヌメレーションは、get_device_descriptor(), set_address(), 
//  set_config()を順次呼び出して行う。
//   Enumeration is performed by calling get_device_descriptor(), set_address(),
// and set_config() in sequence.
//
//  　エヌメレーションされたデバイスがHUBであれば,HUBの初期化を行う. 
//  初期化の中でusbHubAtachedフラグが1に設定されHUBが接続されことが示される.
//   If the enumerated device is a HUB, the HUB is initialized.
// During initialization, the usbHubAtached flag is set to 1 to indicate that
// the HUB is connected.
//
//　　接続されたデバイスがHUBでない時は、HOSTドライバーがサポート可能な、
//  ベンダークラスデバイスであるか標準クラスデバイスであるか調べて、サポート
//  可能なデバイスであればそのデバイスの管理情報を usbDevice[0]　に設定する.
//  また接続デバイス数を管理する cnctDeviceNums 変数には1がセットされる. 
//   If the connected device is not a HUB, the HOST driver checks to see 
// if it is a vendor class device or a standard class device that it can 
// support, and if it is a supportable device, the device management 
// information is set to usbDevice[0]. The cnctDeviceNums variable, which 
// manages the number of connected devices, is also set to 1.
//
//　　なお、接続されたデバイスがHUBの時は、usbDevice[0].Readyに1が設定されるが、
//  接続デバイス数を管理する cnctDeviceNums 変数は 0のままである.
//   When the connected device is a HUB, usbDevice[0].Ready is set to 1, 
// but the cnctDeviceNums variable, which manages the number of connected 
// devices, remains 0.
//
//   USBコネクタデバイス取り外し時の処理
//   Processing when removing a USB connector device
//
//     USBコネクタに直接接続したデバイスが取り外されるとUSBモジュールの
//   割り込み処理にて検出され、HOSTドライバーがUSBモジュールの再初期化を
//  行い、全ての管理変数が初期値に戻される.
//    When a device directly connected to the USB connector is removed, 
// it is detected by the USB module interrupt processing, the HOST driver 
// reinitializes the USB module, and all management variables are returned 
// to their initial values.
//
//    usbDeviceAttachが1から0になったことでHUBを含む全てのデバイスが取り
//  外されたことを知ることが出来る.
//   When the usbDeviceAttach flag changes from 1 to 0, you can tell 
// that all devices, including the HUB, have been removed.
//
//   HUBポートへのデバイス挿入時の処理
//   Processing when a device is inserted into a HUB port
//
//    hubAttachedフラグによりHUBが接続されている場合には、HUBの処理を呼び
//  出してポートへのデバイス挿入の有無を検査する.
//   If a HUB is connected, it calls the HUB's processing to check whether
// a device is inserted in the HUB's port.
//
//    デバイスの挿入を検知した場合には、HUBの処理の中で、hubPortChanged 
//  フラグに PORT_DEVICE_ATTACHEDが設定されるとともに接続ポート番号が
//  hubPortNumber で通知される.
//   When the insertion of a device is detected, the hubPortChanged flag is 
// set to PORT_DEVICE_ATTACHED during HUB processing, and the connection port
// number is notified in hubPortNumber.
//
//  　 デバイスの挿入を通知された時には、デバイスのエヌメレーション処理を行う．
//  処理の結果,サポート可能なデバイスの場合、接続デバイス数が＋１され、
//  サポート不可能なデバイスであれば何もしない。  
//   When a device insertion is notified, the device enumeration process is 
// performed. As a result of the process, if the device is supported, the 
// number of connected devices is incremented by 1.
//
// 
//   HUBポートからのデバイス抜去時の処理
//   Processing when a device is removed from a HUB port
//
//    hubAttached フラグによりHUBが接続されている場合には、HUBの処理を呼び
//  出してポートからのデバイス抜去の有無を検査する.
//   If a HUB is connected using the hubAttached flag, the HUB process is 
// called to check whether a device has been removed from the port.
//
//    デバイスの抜去を検知した場合には、HUBの処理の中で、hubPortChanged フラグ
//  に PORT_DEVICE_DETACHED が設定されるとともに接続ポート番号がhubPortNumber
//  で通知される.
//    When a device removal is detected, the hubPortChanged flag is set to 
// PORT_DEVICE_DETACHED during HUB processing, and the connection port number
// is notified via hubPortNumber.
//
//    デバイスの抜去を通知された時には、接続デバイス数を-１する.
//   When a device removal is notified, the number of connected devices is 
// decremented by 1.
//
//
//   マルチインタフェース、マルチデバイス対応
//   Multi-interface, multi-device support
//
//     接続されたデバイスが複数のインタフェースを持っていて、それぞれの
//   インタフェース（デバイスクラス）がサポート可能な場合には、
//   インタフェースの情報（クラス情報、エンドポイント情報など）を 
//   usbDevice[]変数に登録します.
//    If the connected device has multiple interfaces and each interface 
// (device class) is supported, the interface information (class information,
// endpoint information, etc.) is registered in the usbDevice[] variable.
//
//      例えば、USBキーボード、USBマウス、USBゲームコントローラの3つの
//   インタフェースを持つUSBレシーバー（USBドングル）が接続された場合に
//   は、これら３つのデバイスからデータを読み出すことが出来ます.
//   For example, if a USB receiver (USB dongle) with three interfaces, 
// a USB keyboard, a USB mouse, and a USB game controller, is connected, 
// data can be read from these three devices.
//
//     HUBを経由して複数のデバイスを接続した時も同様にそれぞれのデバイスに
//  対してアクセスできます。
//    When multiple devices are connected via a HUB, you can access each 
// device in the same way.
//
//
//
//  このHOSTドライバーがサポートする標準クラス
//  Standard classes supported by this HOST driver
//
//  このHOSTドライバーでは、Audio Class MIDI、HID Class 、CDC class、MSC class
// のUSBデバイスを扱うことが出来ます。
//   This HOST driver can handle Audio Class MIDI, HID Class, CDC class, 
// and MSC class USB devices.
//
//   ただし、機能は限定的です。USBデバイスからパケットを受信したり、デバイスへ
//  パケットを送信することが出来るだけの機能しかありません。
//   However, its functionality is limited. It can only receive packets from 
// and send packets to USB devices.
//
//　 例えば、USBkeyboardから受信したキーのスキャンコードを文字コードに変換
// するような機能はありませんし、ゲームコントローラから受信したデータから、
// コントローラのどのボタンが押されたのか、スティックがどう変化したのかなどの
// 判定は行いません。これらは全てアプリケーションプログラムで行わねばなりません。
//   For example, it does not have the ability to convert key scan codes 
// received from a USB keyboard into character codes, and it does not determine
// which button on the controller was pressed or how the stick has changed 
// from the data received from a game controller. All of this must be done by
// the application program.
//
//   USBメモリなどを使う場合も、そのセクターへのデータの読み書きだけの機能
// しかありません。FATなどのファイルシステムが使いたければ、アプリケーション
// プログラム側で準備しなければなりません。
//   Even if you use a USB memory stick, it only has the ability to read 
// and write data to that sector. If you want to use a file system such as FAT,
// you must prepare it in the application program.
//
//   このプログラムが提供する機能をどのように使えばよいのか知る方法として、
// 一緒に公開しているデモを兼ねたテストプログラム（main.cに収録）を参照して
// ください。
//    To learn how to use the features provided by this program, please 
//  refer to the demo test program (included in main.c) provided with this
//  program.
//

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "uart2.h"
#include "timer.h"
#include "delay.h"
#include "usbHost.h"
#include "ftdi_sio.h"
#include "p32mz2048efh100.h"

#define DEBUG_MSG_PRINT
//#define DEBUG_DATA_PRINT
//#define DEBUG_DUMP_PRINT

#ifndef DEBUG_MSG_PRINT
    #define printf(x, ...)
#endif


//******************************************************************************
//     Definition
//******************************************************************************

#define MAXINTERFACE                        4     // One device 4 interface

#define LOW_PACKET_SIZE                     8     // low speed device max packet size. bulk, interrupt
#define FULL_PACKET_SIZE	                64    // full speed device max packet size. bulk, interrupt, control
#define USB_MAX_BUFF_SIZE                   512   // high speed bulk size

#define LOW_SPEED                           3
#define FULL_SPEED                          2
#define HIGH_SPEED                          1

#define MAX_EP                              8      // host Endpoint E0, E1-E7

#define USB_EP_TIMEOUT                      100    // [ms]
#define USB_EPN_TIMEOUT                     5      // [ms]


// USB queue buffer size
#define RXBUFSIZE                          50   // 50 [packets]
#define TXBUFSIZE                          10   // 10 [packets]


typedef struct {
  uint8_t  bLength;
  uint8_t  bDescriptorType;
  uint16_t bcdUSB;
  uint8_t  bDeviceClass;
  uint8_t  bDeviceSubClass;
  uint8_t  bDeviceProtocol;
  uint8_t  bMaxPacketSize;
  uint16_t idVendor;
  uint16_t idProduct;
  uint16_t bcdDevice;
  uint8_t  iManufacturer;
  uint8_t  iProduct;
  uint8_t  iSerialNumber;
  uint8_t  bNumConfigurations;
} __attribute__((packed)) DEVICE_DESCRIPTOR;


typedef struct usb_device_t {
    uint16_t VId;       // vender id in DEVICE DESCRIPTOR
    uint16_t PId;       // product id in DEVICE DESCRIPTOR
    uint8_t  Address;   // 1=direct device or HUB, 2- MAXPORT+1=HUB port connected device
    uint8_t  Speed;     // 3=low, 2=full, 1=high 
    uint8_t  Attached;  // 0=detached, 1=attached
    uint8_t  Ready;     // 0=not ready, 1=complete SET_ADDRESS, SET_CONFIGURATION
    uint8_t  interfaceNums;  // number of has interfaces
    struct interface_t {
        uint8_t  Class;
        uint8_t  SubClass;
        uint8_t  Protocol;
        uint8_t  interfaceNumber;
        uint8_t  INEpNum;
        uint16_t INEpSize;
        uint8_t  INEpProto;
        uint8_t  INinterval;
        uint8_t  OUTEpNum;
        uint16_t OUTEpSize;
        uint8_t  OUTEpProto;
        uint8_t  OUTinterval;
        uint8_t  HostEpNum;
    } interface[MAXINTERFACE];
} USB_DEVICE;

typedef struct {
    uint16_t   length;
    uint8_t    *packet;
} QUEUE_BUFFER;

typedef struct {
    int          rxWrtPtr;
    int          rxRdPtr;
    int          rxCount;
    int          txWrtPtr;
    int          txRdPtr;
    int          txCount;
    QUEUE_BUFFER rxBuf[RXBUFSIZE]; // ring buffer
    QUEUE_BUFFER txBuf[TXBUFSIZE]; // ring buffer
} USB_QUEUE;

// USB class code
#define AUDIO_CLASS            1
#define HID_CLASS              3
#define MSD_CLASS              8
#define HUB_CLASS              9
#define UVC_CLASS              14

// CDC Class SET CODE
//
// length of STOP BIT
#define ONE                    0
#define ONE_HALF               1
#define TWO                    2

// type of PARITY BIT
#define NONE                   0
#define ODD                    1
#define EVEN                   2
#define MARK                   3
#define SPACE                  4

// flow control
#define FNONE                  0
#define RTSCTS                 1
#define DTRDSR                 2
#define XONXOFF                4

// Serial Parameter
#define BAUDRATE          115200   // 57600/38400/19200/9600/4800/2400
#define STOPBIT              ONE
#define DATABIT                8   // 7/6/5
#define PARITYBIT           NONE
#define FLOWCONT            FNONE

#define SERIAL_CDC_ACM         1
#define SERIAL_FTDI            2

// MSC parameter
#define SECTOR_SIZE            512
#define USB_CBW_SIZE           31
#define USB_CSW_SIZE           13
#define MSC_UNIT_NOT_READY     0
#define MSC_UNIT_READY         1

// UVC SubClass
#define VIDEO_CONTROL          1
#define VIDEO_STREAM           2


// HUB Class Request Codes
#define	USB_HUB_GET_STATUS	                0
#define USB_HUB_CLEAR_FEATURE               1
#define	USB_HUB_SET_FEATURE	                3
#define USB_HUB_GET_DESCRIPTOR              6
#define USB_HUB_SET_DESCRIPTOR              7

// Hub Descriptor Type 
#define	HUB_DESCRIPTOR_TYPE                 (0x29)

// HUB Class Feature Selector
#define	C_HUB_LOCAL_POWER                   0
#define C_HUB_OVER_CURRENT                  1
#define PORT_CONNECTION                     0
#define PORT_ENABLE                         1
#define PORT_SUSPEND                        2
#define PORT_OVER_CURRENT                   3
#define PORT_RESET                          4
#define PORT_POWER                          8
#define PORT_LOW_SPEED                      9
#define C_PORT_CONNECTION                  16
#define C_PORT_ENABLE                      17
#define C_PORT_SUSPEND                     18
#define C_PORT_OVER_CURRENT                19
#define C_PORT_RESET                       20
#define C_PORT_TEST                        21
#define C_PORT_INDICATOR                   22

// HUB Port Status Bits
#define	PORT_CONNECTION_BIT     (0b0000000000000001)  
#define PORT_ENABLE_BIT         (0b0000000000000010)
#define PORT_SUSPEND_BIT        (0b0000000000000100)
#define PORT_OVER_CURRENT_BIT   (0b0000000000001000)
#define PORT_RESET_BIT          (0b0000000000010000)
#define PORT_POWER_BIT          (0b0000000100000000)
#define PORT_LOW_SPEED_BIT      (0b0000001000000000)
#define PORT_HIGH_SPEED_BIT     (0b0000010000000000)
#define	PORT_TEST_BIT           (0b0000100000000000)
#define	PORT_INDICATOR_BIT      (0b0001000000000000)
          
#define C_PORT_CONNECTION_BIT   (0b0000000000000001)    
#define C_PORT_ENABLE_BIT       (0b0000000000000010)    
#define C_PORT_SUSPEND_BIT      (0b0000000000000100)    
#define C_PORT_OVER_CURRENT_BIT (0b0000000000001000)    
#define C_PORT_RESET_BIT        (0b0000000000010000)

// using  HUB port
#define HUBMAXPORT              4 

#define PORT_DEVICE_NONE        0
#define PORT_DEVICE_ATTACHED    1
#define PORT_DEVICE_DETACHED    2


typedef struct {
    uint8_t   nums;                          // number of devices
    uint8_t   port[HUBMAXPORT];              // usbDevice[]のindex
    uint8_t   interfaceNumber[HUBMAXPORT];   // usbDevice[port].interface[]のindex
    uint8_t   option[HUBMAXPORT];            // this field used serial driver and msc driver, uvc driver    
} DRIVER_INFO;


//******************************************************************************
//      Variable
//******************************************************************************

static USB_DEVICE         usbDevice[HUBMAXPORT+1]; 
                             // Element [0] indicates a HUB or direct-connect device.
                             // Element [ from 1 to HUBMAXPORT] indicates the device
                             // connected to the HUB port.

static uint8_t            cnctDeviceNums;   // Indicates the number of currently connected valid devices

static volatile uint8_t   hubAttached;      // USB HUB attached flag (1=HUB attached 0=not HUB)
static volatile uint8_t   hubPortAttached;  // USB HUB port device attached flag
static volatile uint8_t   hubPortDetached;  // USB HUB port device detached flag
static volatile uint8_t   hubPortChanged;   // USB HUB port changed flag(0=none 1=attached 2=detached)
static volatile uint8_t   hubPortNumber;    // port device attache and detached port number in HUB
                                            // port number is index of usbDevice[] array

// HUB infromation
static uint8_t      usbHubAddress;    // USB HUB address(always 1)
static uint8_t      usbHubINEpNum;    // USB HUB IN endpoint number(normal 1)
static uint16_t     usbHubINEpSize;   // USB HUB IN endpoint transfer size(normal 1) 
static uint8_t      usbHubINEpProto;  // normaly interrupt transfer
static uint8_t      usbHubINinterval; // normaly 12

// control request packet
static uint8_t  GET_DESCRIPTOR[8]        = {0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00,};
static uint8_t  GET_CONFIG_DESCRIPTOR[8] = {0x80, 0x06, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00,};
static uint8_t  SET_ADDRESS[8]           = {0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,};
static uint8_t  SET_CONFIG[8]            = {0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,};
static uint8_t  SET_PROTOCOL[8]          = {0x21, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
static uint8_t  SET_INTERFACE[8]         = {0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};

// HID class control
static uint8_t  GET_REPORT_DESCRIPTOR[8] = {0x80, 0x06, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00,};
static uint8_t  SET_REPORT[8]            = {0x21, 0x09, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00,};

// hub class control
static uint8_t  GET_HUB_DESCRIPTOR[8]    = {0xA0, 0x06, 0x00, 0x29, 0x00, 0x00, 0x08, 0x00,};
static uint8_t  SET_HUB_FEATURE[8]       = {0x23, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,};
static uint8_t  GET_HUB_STATUS[8]        = {0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,};
static uint8_t  SET_CLEAR_PORT_FEATURE[8]= {0x23, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};

// cdc class control
static uint8_t  SET_LINE_CODING[8]       = {0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,};

static uint8_t  GET_LINE_CODING[8]       = {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,};
static uint8_t  SET_CONTROL_LINE_STATE[8]= {0x21, 0x22, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,};

// FTDI vendor class control
static uint8_t  FTDI_SET_CONTROL[8]      = {0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};

// MSC class SCSI command  CBW
uint8_t TEST_UNIT_READY[] = {	// TEST UNIT READY command contents(SCSI)
                        0x55, 0x53, 0x42, 0x43, 0x01, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x00,
                        0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

uint8_t REQUEST_SENSE[] = {	// REQUEST SENSE command contents(SCSI)
                        0x55, 0x53, 0x42, 0x43, 0x01, 0x00, 0x00, 0x00,
                        0x12, 0x00, 0x00, 0x00, 0x80, 0x00, 0x0c, 0x03,
                        0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

uint8_t READ_CAPACITY[] = {	// READ CAPACITY command contents(SCSI)
                        0x55, 0x53, 0x42, 0x43, 0x01, 0x00, 0x00, 0x00,
                        0x08, 0x00, 0x00, 0x00, 0x80, 0x00, 0x0a, 0x25,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

uint8_t READ_COMMAND[] = {	// READ command contents (SCSI)
                        0x55, 0x53, 0x42, 0x43, 0x01, 0x00, 0x00, 0x00,
                        0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x0a, 0x28,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

uint8_t WRITE_COMMAND[] = {	// WRITE command contents (SCSI)
                        0x55, 0x53, 0x42, 0x43, 0x01, 0x00, 0x00, 0x00,
                        0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x2a,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};


static const char *strStop[]   = { "1", "1.5", "2", };
static const char *strParity[] = { "None", "Odd", "Even", "Mark", "Space", };

// temporaly variables
static DEVICE_DESCRIPTOR   device_descriptor;            
static uint8_t             device_configuration[4096];  // UVC Class > 3000byte
static uint16_t            device_configuration_length; 

static volatile uint8_t    usbDeviceAttach;  // device attached flag
static volatile uint8_t    SOF_IF;
static volatile uint8_t    RST_IF;
static volatile uint8_t    USB_EP0_IF;       // EP0 interrupt flag
static volatile uint8_t    USB_EPN_RX_IF;    // EP1 - EP7 RX interrupt flag
static volatile uint8_t    USB_EPN_RX_IE;    // EP1 - EP7 RX interrupt call back flag
static volatile uint8_t    USB_EPN_TX_IF;    // EP1 - EP7 TX interrupt flag

// Packet size of endpoints per port
static uint16_t            packet_size[HUBMAXPORT][MAX_EP];   // MAX_EP 8 (0-7))

// USB Target Device Endpoint information (temporaly use)
static uint8_t             INEpNum,   OUTEpNum ;
static uint16_t            INEpSize,  OUTEpSize;
static uint8_t             INEpProto, OUTEpProto;
static uint8_t             INEpIntrv, OUTEpIntrv;

// manage HOST endpoint usage
static uint8_t             usedHostEp[MAX_EP];    // 0=not use 1=used, 
                              // Array index number is endpoint number

// work buffer for HUB function
static uint8_t             usbRecBuff[FULL_PACKET_SIZE];

// USB TX/RX queue 
USB_QUEUE  usbQueue[8];


// Support Vendor Product device 
//
//  For products of a vendor class whose functions and usage
// are already known, it is possible to use them by setting 
// a standard class that operates in a similar way.
//
//  In the case of FTDI USB serial converters, special 
// processing is built into the host driver, so set the class
// code to 0xF0. 0xF0 is the class code set specifically for 
// this program.
//
//  When a device with a vendor class code of 0xFF is connected,
// the host driver refers to this table to determine whether 
// the device is supported.
//
//  I'm registering devices I know, and you can register 
// additional devices you know.
//
//  Audio(1), CDC(2), HID(3), MSC(8)
typedef struct product_t {
    uint16_t  VId;
    uint16_t  PId;
    uint8_t   Class;
    uint8_t   SubClass;
    uint8_t   Protocol;
} PRODUCT;

static PRODUCT product_table[] = {
     {0x0499, 0x1008, 1,    3, 0, },   // YAMAHA UX96 USB-MIDI
     {0x0499, 0x1009, 1,    3, 0, },   // YAMAHA UX16 USB-MIDI
     {0x0582, 0x0005, 1,    3, 0, },   // Roland UM-2(C/EX)
     {0x0582, 0x0106, 1,    3, 0, },   // Roland UM-2G
     {0x0582, 0x012A, 1,    3, 0, },   // Roland UM-ONE
     {0x0582, 0x0014, 1,    3, 0, },   // EDIROL UM-88 MIDI I/F(native)
     {0x0590, 0x00D4, 0xF0, 2, 1, },   // OMRON 2JCIE_BU01 enviromental sensor
     {0x0403, 0x6015, 0xF0, 2, 1, },   // FTDI FT230X
//     {0x15D1, 0x0000, 10,   0, 0, },   // Hokuyo URG distance sensor
     {0x056E, 0x5004, 10,   0, 0, },   // Elecom UC-SGT usb serial converter
     {0x10C4, 0xEA60, 10,   0, 0, },   // SiliconLabs CP210X USB to UART Bridge
     {0x056E, 0x2013, 3,    0, 0, },   // ELECOM wireless Game controlle JC-U4113S
};

// Support standard class 
//
//  This is a list of the standard class codes 
// that the host driver currently supports.
//
//  The host driver refers to this table to determine 
// whether a device can be supported based on the 
// class code written in the Interface Descriptor.
//
//  Support for UVC classes is currently under development.
//
typedef struct support_class_t {
    int class;
    int subclass;
    int protocol;
} STANDARD;

static STANDARD class_table[] = {
    // Audio Class(1) MIDI streaming(3), none protocol(0) 
    // CDC Communications Class(2), Abstract(modem)(2), Protocol(v.25ter) (1)
    // HID(3) Class  BootInterface(1), keyboard(1)
    // HID(3) Class  BootInterface(1), mouse(2)
    // HID(3) Class  (0), (0),    hid generic 
    // Masstrage(8), (6), 80(scsi)
    // CDC Data Class(10), (0), (0) 
    // Video (14), Video Control(1), (0)
    // Video (14), Video Streaming(2), (0)
    { 1, 3, 0, },  // Audio Midi
    // { 2, 2, 0, },  // CDC(ctrl)      cdc control interface not support
    // { 2, 2, 1, },  // CDC(ctrl)
    { 3, 0, 0, },  // HID Generic
    { 3, 1, 1, },  // HID Keyboard
    { 3, 1, 2, },  // HID Mouse
    { 8, 6, 80 },  // MSC
    {10, 0, 0, },  // CDC (data)
    // {14, 1, 0, },  // UVC Video Control      not yet support UVC class
    // {14, 2, 0, },  // UVC Video Streaming
};

// USB class driver 
static DRIVER_INFO midi_driver;
static DRIVER_INFO keyboard_driver;
static DRIVER_INFO mouse_driver;
static DRIVER_INFO generic_driver;
static DRIVER_INFO cdc_driver;
static DRIVER_INFO msc_driver;
static DRIVER_INFO uvc_driver;


//******************************************************************************
//    prototype
//******************************************************************************

int  isUsbConnect(void);
void usb_host_init(void);

// HOST core funcion
static void device_enumeration(uint8_t port);
static int get_device_descriptor(uint8_t port, uint8_t addr);
static int set_device_address(uint8_t port, uint8_t addr);
static int set_device_configuration(uint8_t port, uint8_t addr);
static uint8_t *get_device_configuration(uint8_t port, uint8_t addr);
static int isProduct(uint8_t port);
static int analys_interface_class(void);
static int isSupportClass(uint8_t *interface_descriptor);
static void dump_interface_info(uint8_t hubPortNumber);
static void device_regist_to_class_driver(uint8_t hubPortNumber);
static void device_unregist_from_class_driver(uint8_t hubPortNumber);
static int  get_host_ep(void);
static void back_host_ep(int epnum);

static void set_port_device_endpoint(uint8_t port, uint8_t interfaceNumber, uint8_t intFlag);
static int  usb_ctrl(uint8_t port, uint8_t addr, uint8_t *ctrl, uint8_t *data);
// SETUP
static int usb_setup(uint8_t port, uint8_t addr, uint8_t *ctrl);
static int ep0_interrupt_wait(void);
// DATA(IN)
static int usb_ep0_read(uint8_t port, uint8_t addr, uint8_t epnum, uint8_t *buff, uint16_t buff_size);
static int usb_read(uint8_t port, uint8_t addr, uint8_t epnum, uint8_t *buff, uint16_t buff_size);
static int epn_rx_interrupt_wait(uint8_t epnum);
static void call_back_EPN_read(int epnum);
static void resetRxQueue(int epnum);
static void pushRxQueue(int epnum, uint8_t *packet, uint16_t length);
static uint8_t *popRxQueue(int epnum, uint16_t *length);
static int usb_packet_read_from_queue(DRIVER_INFO *driver, int dev_no, uint8_t *buff, uint16_t buff_size);
static int usb_packet_read(DRIVER_INFO *driver, int dev_no, uint8_t *buff, uint16_t buff_size);
// DATA(OUT)
static int usb_ep0_write(uint8_t port, uint8_t addr,  uint8_t epnum, uint8_t *data, uint16_t data_size);
static int usb_write(uint8_t port, uint8_t addr,  uint8_t epnum, uint8_t *data, uint16_t data_size);
static int txrdy_wait(uint8_t epnum);
static int usb_packet_write(DRIVER_INFO *driver, int dev_no, uint8_t *data, uint16_t data_size);
// keyboard
static int usb_hid_set_protocol(DRIVER_INFO *driver, int dev_no, uint8_t protocol);
// cdc 
static int set_serial_default_parameter(int dev_no);
static int set_cdc_parameter(int dev_no, int32_t *param);
static int set_ftdi_parameter(int dev_no, int32_t *param);
static int get_cdc_parameter(int dev_no, int32_t *param);
static int get_ftdi_parameter(int dev_no, int32_t *param);
static int set_cdc_state(int dev_no, uint8_t ctrl);
static int set_ftdi_state(int dev_no, uint8_t ctrl);
static int ftdi_set_control(int dev_no);
// MSC 
static int usb_msc_scsi_read_command(int dev_no, uint8_t *cmd, uint8_t *data);
static int parse_csw(uint8_t *buff, int size);
// HUB function
static void hub_initialize(void);
static void hub_loop(void);
static void port_loop(int port);
static int clearHubPortBit(int port, int selector);

/*******************************************************************************
   PIC32MZ  High Speed USB Host
   Class Driver Implementation

   Support Class:
      Audio MIDI
      HID Boot Device ( Mouse, Keyboard )
      HID Generic     ( Game Controller etc ) 
      CDC data communiction
      FTDI USB serial
      CP210X USB serial
      MSC             ( USB Memory/ Card Reader etc )

 application interface function
    //  Audio Midi driver 
    int get_nums_midi_device(void);
    int usb_midi_read(int dev_no, uint8_t *buff, uint16_t buff_size);
    int usb_midi_write(int dev_no, uint8_t *data, uint16_t data_size);
    //  HID Keyboard driver 
    int  get_nums_keyboard_device(void);
    int  usb_keyboard_read(int dev_no, uint8_t *buff, uint16_t buff_size);
    int  usb_keyboard_set_report(int dev_no, uint8_t led_ctrl);
    int  usb_keyboard_set_protocol(int dev_no, uint8_t protocol);
    //  HID mouse driver 
    int  get_nums_mouse_device(void);
    int  usb_mouse_read(int dev_no, uint8_t *buff, uint16_t buff_size);
    int  usb_mouse_set_protocol(int dev_no, uint8_t protocol);
    //  HID generic driver 
    int  get_nums_hid_generic_device(void);
    int  usb_hid_generic_read(int dev_no, uint8_t *buff, uint16_t buff_size);
    int  usb_hid_generic_write(int dev_no, uint8_t *data, uint16_t data_size);
    //  CDC class driver 
    int get_nums_serial_device(void);
    int usb_serial_read(int dev_no, uint8_t *buff, uint16_t buff_size);
    int usb_serial_write(int dev_no, uint8_t *data, uint16_t data_size);
    int set_serial_port_parameter(int dev_no, int32_t *param);
    int get_serial_port_parameter(int dev_no, int32_t *param);
    int set_serial_port_state(int dev_no, uint8_t ctrl);
    // MSC driver
    int get_nums_msc_device(void);
    int usb_msc_sector_read(int dev_no, uint32_t read_sector, uint8_t *buff);
    int usb_msc_sector_write(int dev_no, uint32_t write_sector, uint8_t *data);
    int usb_msc_test_unit_ready(int dev_no);
    uint32_t usb_msc_capacity_read(int dev_no);
    int usb_msc_request_sense(int dev_no);
 
     2025.7.23 Suwa-Koubou
  */


/*==============================================================================
     Audio Midi device driver
==============================================================================*/

/*------------------------------------------------------------------------------
    int get_nums_midi_device(void)
     Returns the number of connected MIDI devices
------------------------------------------------------------------------------*/

int get_nums_midi_device(void)
{
    return midi_driver.nums;
}


/*------------------------------------------------------------------------------
   int usb_midi_read(int dev_no, uint8_t *data)
------------------------------------------------------------------------------*/

int usb_midi_read(int dev_no, uint8_t *buff, uint16_t buff_size)
{
    return usb_packet_read_from_queue(&midi_driver, dev_no, buff, buff_size);
}


/*------------------------------------------------------------------------------
     int usb_midi_write(int dev_no, uint8_t *data, int len)
------------------------------------------------------------------------------*/

int usb_midi_write(int dev_no, uint8_t *data, uint16_t data_size)
{
    return usb_packet_write(&midi_driver, dev_no, data, data_size); 
}


/*==============================================================================
	HID device driver
==============================================================================*/

/*------------------------------------------------------------------------------
     int get_nums_keyboard_device(void)
     Returns the number of connected keyboard
------------------------------------------------------------------------------*/

int get_nums_keyboard_device(void)
{
    return keyboard_driver.nums;
}


/*------------------------------------------------------------------------------
   int usb_keyboard_read(int dev_no, uint8_t *data)
------------------------------------------------------------------------------*/

int usb_keyboard_read(int dev_no, uint8_t *buff, uint16_t buff_size)
{
    return usb_packet_read_from_queue(&keyboard_driver, dev_no, buff, buff_size);
}

/*------------------------------------------------------------------------------
     int usb_keyboard_set_protocol(int dev_no, uint8_t protocol)
 
   Set the Boot protocol or Report protocol.
      protocol = 0   boot 
               = 1   report
------------------------------------------------------------------------------*/

int usb_keyboard_set_protocol(int dev_no, uint8_t protocol)
{
    return usb_hid_set_protocol(&keyboard_driver, dev_no, protocol);
}

/*------------------------------------------------------------------------------
     int usb_keyboard_set_report(int dev_no, uint8_t led_ctrl)

   キーボードのLEDの点灯・消灯制御を行う
   Controls the lighting of the keyboard LEDs

  argument led_ctrl
   bit 0:   NUM_LOCK
   bit 1:   CAPS_LOCK
   bit 2:   SCROLL_LOCK
------------------------------------------------------------------------------*/

int usb_keyboard_set_report(int dev_no, uint8_t led_ctrl)
{
    uint8_t port;
    uint8_t addr;
    uint8_t interfaceNumber;
    uint8_t data;
    
    printf("USB Keyboard SET REPORT (led controlle).\n");

    port = keyboard_driver.port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = keyboard_driver.interfaceNumber[dev_no];
    data = led_ctrl;
    
    SET_REPORT[4]= usbDevice[port].interface[interfaceNumber].interfaceNumber;
    SET_REPORT[5]=0;

    return usb_ctrl(port, addr, SET_REPORT, &data);
}


/*------------------------------------------------------------------------------
     int get_nums_mouse_device(void)
     Returns the number of connected mice
------------------------------------------------------------------------------*/

int get_nums_mouse_device(void)
{
    return mouse_driver.nums;
}


/*------------------------------------------------------------------------------
   int usb_mouse_read(int dev_no, uint8_t *data)
------------------------------------------------------------------------------*/

int usb_mouse_read(int dev_no, uint8_t *buff, uint16_t buff_size)
{
    return usb_packet_read_from_queue(&mouse_driver, dev_no, buff, buff_size);
}


/*------------------------------------------------------------------------------
     int usb_mouse_set_protocol(int dev_no, uint8_t protocol)
 
   Set the Boot protocol or Report protocol.
      protocol = 0   boot 
               = 1   report
 ------------------------------------------------------------------------------*/

int usb_mouse_set_protocol(int dev_no, uint8_t protocol)
{
    return usb_hid_set_protocol(&mouse_driver, dev_no, protocol);
}


/*------------------------------------------------------------------------------
     int usb_hid_set_protocol(DRIVER_INFO *driver, int dev_no, uint8_t protocol)
------------------------------------------------------------------------------*/

static int usb_hid_set_protocol(DRIVER_INFO *driver, int dev_no, uint8_t protocol)
{
    // protocol 0= Boot Protocol
    //          1= Report Protocol

    uint8_t port;
    uint8_t addr;
    uint8_t interfaceNumber;
    
    printf("USB HID boot device set to %s protocol.\n", protocol ? "Report": "Boot"); 

    if(driver != &keyboard_driver && driver != &mouse_driver) return -1;    
    if(driver->nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= driver->nums) return -1;
    if(protocol != 0 && protocol != 1) return -1;

    port = driver->port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = driver->interfaceNumber[dev_no];
    
    SET_PROTOCOL[2]=protocol; // protocol
    SET_PROTOCOL[3]=0;
    SET_PROTOCOL[4]= usbDevice[port].interface[interfaceNumber].interfaceNumber;
    SET_PROTOCOL[5]=0;
    
    printf("interface number=%d\n",SET_PROTOCOL[4] );
    
    return usb_ctrl(port, addr, SET_PROTOCOL, NULL);
}


/*------------------------------------------------------------------------------
     int get_nums_hid_generic_device(void)
------------------------------------------------------------------------------*/

int get_nums_hid_generic_device(void)
{
    return generic_driver.nums;
}


/*------------------------------------------------------------------------------
   int usb_hid_generic_read(int dev_no, uint8_t *data)
------------------------------------------------------------------------------*/

int usb_hid_generic_read(int dev_no, uint8_t *buff, uint16_t buff_size)
{
    return usb_packet_read_from_queue(&generic_driver, dev_no, buff, buff_size);
}


/*------------------------------------------------------------------------------
   int usb_hid_generic_write(int dev_no, uint8_t *data, uint16_t data_size)
------------------------------------------------------------------------------*/

int usb_hid_generic_write(int dev_no, uint8_t *data, uint16_t data_size)
{
    return usb_packet_write(&generic_driver, dev_no, data, data_size);
}


/*==============================================================================
	CDC  device driver
      define SERIAL_CDC_ACM         1
      define SERIAL_FTDI            2
==============================================================================*/

/*------------------------------------------------------------------------------
     int get_nums_serial_device(void)
------------------------------------------------------------------------------*/

int get_nums_serial_device(void)
{
    return cdc_driver.nums;
}


/*------------------------------------------------------------------------------
     int usb_serial_read(int dev_no, uint8_t *data)
------------------------------------------------------------------------------*/

int usb_serial_read(int dev_no, uint8_t *buff, uint16_t buff_size)
{
    uint8_t  opt;
    int      size;
    
    opt = cdc_driver.option[dev_no];
    
    if(opt == SERIAL_CDC_ACM){
        return usb_packet_read_from_queue(&cdc_driver, dev_no, buff, buff_size);

    }else if(opt == SERIAL_FTDI){
        size = usb_packet_read_from_queue(&cdc_driver, dev_no, buff, buff_size);
        if(size == 0) return 0;
        if(size >=2) {
            memcpy(buff, buff+2, size-2); // refer ftdi_sio.h (line 529 IN Endpoint)
            return size-2;
        }else{
            return -1;
        }
    }else{
        return -1;
    }
}   


/*------------------------------------------------------------------------------
     int usb_serial_write(int dev_no, uint8_t *data, uint16_t data_size)
------------------------------------------------------------------------------*/

int usb_serial_write(int dev_no, uint8_t *data, uint16_t data_size)
{
    return usb_packet_write(&cdc_driver, dev_no, data, data_size); 
}


/*------------------------------------------------------------------------------
     static int set_serial_default_parameter(int dev_no);

 param[]:
    param[0] = BAUDRATE;
    param[1] = DATABIT;
    param[2] = STOPBIT; 
    param[3] = PARITYBIT;
    param[4] = FLOW
 
    BAUDRATE:     115200/57600/38400/19200/9600/4800/2400 
    STOPBIT:      0/1/2       0=1bit  1=1.5bit   2=2bit
    PARITYBIT:    0/1/2/3/4   0=none  1=odd  2=even  3=mark  4=space
    DATABIT:      8/7/6/5
    FLOW:         0/1/2/4     0=none  1=rts/cts 2=dtr/dsr 4=xon/xoff

  (*)  CDC Class Request not support flow control .
------------------------------------------------------------------------------*/

static int set_serial_default_parameter(int dev_no)
{
    int32_t param[4];
    uint8_t ctrl;
    
    param[0] = BAUDRATE;
    param[1] = DATABIT;
    param[2] = STOPBIT; 
    param[3] = PARITYBIT;
    param[4] = FLOWCONT;
    ctrl = 3; // DTR=1,RTS=1
    set_serial_port_parameter(dev_no, param);
    // comment out: Some usb serial converter do not support GET_LINE_CODING
    // get_serial_port_parameter(dev_no, param);
    set_serial_port_state(dev_no, ctrl);
}


/*------------------------------------------------------------------------------
     int set_serial_port_parameter(int dev_no, int32_t *param)
------------------------------------------------------------------------------*/

int set_serial_port_parameter(int dev_no, int32_t *param)
{
    uint8_t  opt;
    
    if(cdc_driver.nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= cdc_driver.nums) return -1;

    opt = cdc_driver.option[dev_no];
    
    if(opt == SERIAL_CDC_ACM){
        return set_cdc_parameter(dev_no, param);
    }else if(opt == SERIAL_FTDI){
        return set_ftdi_parameter(dev_no, param);
    }else{
        return -1;
    }
}

static int set_cdc_parameter(int dev_no, int32_t *param)
{
    uint8_t  port;
    uint8_t  addr;
    uint8_t interfaceNumber;
    uint8_t data[7];
    int      n;

    port = cdc_driver.port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = cdc_driver.interfaceNumber[dev_no];

    // set line coding data
    data[0] = (param[0] ) & 0xff;        // baudrate
    data[1] = (param[0] >> 8) & 0xff;
    data[2] = (param[0] >> 16) & 0xff;
    data[3] = (param[0] >> 24) & 0xff;
    data[4] = param[2] & 0xff;          // stop
    data[5] = param[3] & 0xff;          // parity
    data[6] = param[1] & 0xff;          // data
    // cdc class request not support flow control .
    
    SET_LINE_CODING[4] = usbDevice[port].interface[interfaceNumber].interfaceNumber;
    SET_LINE_CODING[5] = 0;
    
    n=0;
    while(usb_ctrl(port, addr, SET_LINE_CODING, data)==-1){
        if(++n >= 10) break;
        delay_ms(10);
    }
    if(n < 10){
        printf("complete SET_LINE_CODING.\n");
        return 0;
    }else{
        printf("fail. SET_LINE_CODING.\n");
    }
    return -1;
}


static int set_ftdi_parameter(int dev_no, int32_t *param)
{
    int  err;
    int  divide;

    err = 0;
    
    // SET LATENCY TIMER
    FTDI_SET_CONTROL[1] = FTDI_SIO_SET_LATENCY_TIMER; // 40 09 01 00 00 00 00 00
    memset(FTDI_SET_CONTROL+2, 0x00, 6); // clear wValue, wIndex, wLength
    FTDI_SET_CONTROL[2] = 1;
    if(ftdi_set_control(dev_no)==0){
       printf("complete SET_LATENCY_TIMER(1msec)\n");
    }else{
        printf("fail SET_LATENCY_TIMER.\n");
        err++;
    }

    // RESET
    FTDI_SET_CONTROL[1] = FTDI_SIO_RESET; // 40 00 00 00 00 00 00 00
    memset(FTDI_SET_CONTROL+2, 0x00, 6); // clear wValue, wIndex, wLength
    if(ftdi_set_control(dev_no)==0){
       printf("complete RESET\n");
    }else{
        printf("fail RESET\n");
        err++;
    }

    // SET DATA
    FTDI_SET_CONTROL[1] = FTDI_SIO_SET_DATA; // 40 04 PS 0D 00 00 00 00
    memset(FTDI_SET_CONTROL+2, 0x00, 6); // clear wValue, wIndex, wLength
    FTDI_SET_CONTROL[2] = param[1] & 0xff;                                // DATABIT;
    FTDI_SET_CONTROL[3] = (param[3] & 0xff) | ((param[2] & 0xff) << 3);     // PARITYBIT | (STOPBIT << 3);
    if(ftdi_set_control(dev_no) ==0){
        printf("complete SET_DATA_FORMAT\n");
    }else{
        printf("fail SET_DATA_FORMAT\n");
        err++;
    }
        
    // SET BAUDRATE
    FTDI_SET_CONTROL[1] = FTDI_SIO_SET_BAUD_RATE; // 40 03 nn nn 00 00 00 00
    memset(FTDI_SET_CONTROL+2, 0x00, 6); // clear wValue, wIndex, wLength
    divide = 48000000L / 16 / param[0];  // BAUDRATE; 
    FTDI_SET_CONTROL[2] = divide & 0xff;
    FTDI_SET_CONTROL[3] = (divide >> 8) & 0xff;
    if(ftdi_set_control(dev_no)==0){
        printf("complete SET_BAUD_RATE\n");
    }else{
        printf("fail SET_BAUD_RATE\n");
        err++;
    }

    // SET FLOW CONTROL
    FTDI_SET_CONTROL[1] = FTDI_SIO_SET_FLOW_CTRL; // 40 02 Xoff Xon 00 ?? 00 00
    memset(FTDI_SET_CONTROL+2, 0x00, 6); // clear wValue, wIndex, wLength
    FTDI_SET_CONTROL[4] = 0;
    FTDI_SET_CONTROL[5] = param[4];      // FLOWCONT;
    if(ftdi_set_control(dev_no) ==0){
        printf("complete SET_FLOW_CONTROL\n");
    }else{
        printf("fail SET_FLOW_CONTROL\n");
        err++;
    }

    if(err) return -1;
    return 0;
}


/*------------------------------------------------------------------------------
     int get_serial_port_parameter(int dev_no, int32_t *param)
 ------------------------------------------------------------------------------*/

int get_serial_port_parameter(int dev_no, int32_t *param)
{
    uint8_t  opt;
    
    if(cdc_driver.nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= cdc_driver.nums) return -1;

    opt = cdc_driver.option[dev_no];
    
    if(opt == SERIAL_CDC_ACM){
        return get_cdc_parameter(dev_no, param);
    }else if(opt == SERIAL_FTDI){
        return get_ftdi_parameter(dev_no, param);
    }else{
        return -1;
    }
}

static int get_cdc_parameter(int dev_no, int32_t *param)
{
    uint8_t  port;
    uint8_t  addr;
    uint8_t interfaceNumber;
    uint8_t  data[7];
    int      n;
    
    port = cdc_driver.port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = cdc_driver.interfaceNumber[dev_no];

    GET_LINE_CODING[4] = usbDevice[port].interface[interfaceNumber].interfaceNumber;
    GET_LINE_CODING[5] = 0;
    
    memset(param, 0, 7); // clear buff
    //  get current line coding 
    n=0;
    while(usb_ctrl(port, addr, GET_LINE_CODING, data)==-1){
        if(++n >= 10) break;
        delay_ms(10);
    }

    param[0] = data[0]+(data[1]<<8)+(data[2]<<16)+(data[3]<<24); // baudrate
    param[1] = data[6];                                          // data
    param[2] = data[4];                                          // stop
    param[3] = data[5];                                          // parity
    param[4] = 0; // FLOW not support, set to NONE
    
    if(n < 10){
        printf("Current line coding.\n");
        printf(" baudrate: %dbps\n", param[0]);
        printf(" data len: %dbit\n", param[1]);
        printf(" stop len: %sbit\n", strStop[param[2]]);
        printf(" parity:   %s\n",    strParity[param[3]]);
        return 0;
    }else{
        printf("fail. GET_LINE_CODING.\n");
    }
    return -1;
}

static int get_ftdi_parameter(int dev_no, int32_t *param)
{
    // ftdi driver not support get parameter function
    printf("ftdi driver not support the get serial parameter function\n");
    return -1;    
}


/*------------------------------------------------------------------------------
    int set_serial_port_state(int dev_no, uint8_t ctrl)
     ctrl:   bit0=DTR     1=on 0=off
             bit1=RTS     1=on 0=off
------------------------------------------------------------------------------*/

int set_serial_port_state(int dev_no, uint8_t ctrl)
{
    uint8_t  opt;
    
    if(cdc_driver.nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= cdc_driver.nums) return -1;

    opt = cdc_driver.option[dev_no];
    
    if(opt == SERIAL_CDC_ACM){
        return set_cdc_state(dev_no, ctrl);
    }else if(opt == SERIAL_FTDI){
        return set_ftdi_state(dev_no, ctrl);
    }else{
        return -1;
    }
}


static int set_cdc_state(int dev_no, uint8_t ctrl)
{
    uint8_t  port;
    uint8_t  addr;
    uint8_t  interfaceNumber;
    int      n;
    
    port = cdc_driver.port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = cdc_driver.interfaceNumber[dev_no];
    
    SET_CONTROL_LINE_STATE[2] = ctrl;
    SET_CONTROL_LINE_STATE[3] = 0;
    SET_CONTROL_LINE_STATE[4] = usbDevice[port].interface[interfaceNumber].interfaceNumber;
    SET_CONTROL_LINE_STATE[5] = 0;
    
    n=0;
    while(usb_ctrl(port, addr, SET_CONTROL_LINE_STATE, NULL)==-1){
        if(++n >= 10) break;
        delay_ms(10);
    }
    if(n < 10){
        printf("complete DTR %s, RTS %s\n", ((ctrl & 1)? "on": "off"), ((ctrl & 2)? "on": "off"));
        return 0;
    }else{
        printf("fail. SET_CONTROL_LINE_STATE.\n");
    }
    return -1;
}


static int set_ftdi_state(int dev_no, uint8_t ctrl)
{
    // SET MODEM CONTROL
    FTDI_SET_CONTROL[1] = FTDI_SIO_MODEM_CTRL; // 40 01 nn mm 00 00 00 00
    memset(FTDI_SET_CONTROL+2, 0x00, 6); // clear wValue, wIndex, wLength
    FTDI_SET_CONTROL[2] = ctrl;  // 0x03 = DTR on, RTS on
    FTDI_SET_CONTROL[3] = 0x03;  // DTR use, RTS use

    if(ftdi_set_control(dev_no)==0){
        printf("complete DTR %s, RTS %s\n", ((ctrl & 1)? "on": "off"), ((ctrl & 2)? "on": "off"));
        return 0;
    }else{
        printf("fail. MODEM_CONTROL \n");
    }   
    return -1;
}

static int ftdi_set_control(int dev_no)
{
    static uint8_t  __attribute__ ((coherent, aligned(8))) data[64];
    uint8_t  port;
    uint8_t  addr;
    int      n;
    
    port = cdc_driver.port[dev_no];
    addr = usbDevice[port].Address;

    n=0;
    while(usb_ctrl(port, addr, FTDI_SET_CONTROL, data)==-1){
        if(++n >= 10) break;
        delay_ms(10);
    }
    if(n < 10){
        return 0;
    }
    return -1;

}


/*==============================================================================
	MSC device driver

   support SCSI function
      test_unit_ready
      request sense
      read capacity
      sector read
      sector write
==============================================================================*/

/*------------------------------------------------------------------------------
     int get_nums_msc_device(void)
------------------------------------------------------------------------------*/

int get_nums_msc_device(void)
{
    return msc_driver.nums;
}


/*------------------------------------------------------------------------------
    int usb_msc_sector_read(int dev_no, uint32_t read_sector, uint8_t *data)
       one sector read
------------------------------------------------------------------------------*/

int usb_msc_sector_read(int dev_no, uint32_t read_sector, uint8_t *data)
{
    int     size, rec_size, sts;
    int     n;
    uint8_t cmd[64];

    if(dev_no < 0 || dev_no >= msc_driver.nums) return -1;
    if(msc_driver.option[dev_no] == MSC_UNIT_NOT_READY) return -2;

    memcpy(cmd, (void *)READ_COMMAND, sizeof(READ_COMMAND));
    cmd[17] = (read_sector >> 24) & 0xff;	// Set sector number 1
	cmd[18] = (read_sector >> 16) & 0xff;	// Set sector number 2
	cmd[19] = (read_sector >> 8) & 0xff;	// Set sector number 3
	cmd[20] = read_sector & 0xff;	        // Set sector number 4

    //printf("\nsent READ COMMAND\n");
    usb_packet_write(&msc_driver, dev_no, cmd, sizeof(READ_COMMAND));

    //printf("read DATA from sector\n");
retry_read:
    rec_size = 0;
    while(rec_size < SECTOR_SIZE){
        size = usb_packet_read(&msc_driver, dev_no, data + rec_size, SECTOR_SIZE);
        rec_size += size;
    }
    //printf("read STATUS\n");
    n=0;
    do {
        memset(cmd, 0, sizeof(cmd));
        sts = usb_packet_read(&msc_driver, dev_no, cmd, sizeof(cmd));
        n++;
    }while(sts <= 0 && n < 100);

    // status check
    if(parse_csw(cmd, sts) < 0){
        printf("fail READ STATUS(sts=%d)\n", sts);
        for(n=0; n<13; n++){
            printf("%02X ", cmd[n]);
        }
        printf("\n");
        if(sts==13 && cmd[12] == 0x80){
            goto retry_read;
        }
        return -1;
    }
    return SECTOR_SIZE;
}


/*------------------------------------------------------------------------------
    int usb_msc_sector_write(int dev_no, uint32_t write_sector, uint8_t *data)
       one sector write
------------------------------------------------------------------------------*/

int usb_msc_sector_write(int dev_no, uint32_t write_sector, uint8_t *data)
{
    int     n, sts;
    uint8_t cmd[64];
    
    if(dev_no < 0 || dev_no >= msc_driver.nums) return -1;
    if(msc_driver.option[dev_no] == MSC_UNIT_NOT_READY) return -2;

    memcpy(cmd, (void *)WRITE_COMMAND, sizeof(WRITE_COMMAND));
    cmd[17] = (write_sector >> 24) & 0xff;	// Set sector number 1
	cmd[18] = (write_sector >> 16) & 0xff;	// Set sector number 2
	cmd[19] = (write_sector >> 8) & 0xff;	// Set sector number 3
	cmd[20] = write_sector & 0xff;	        // Set sector number 4

    //printf("\nsent WRITE COMMAND\n");
    usb_packet_write(&msc_driver, dev_no, cmd, sizeof(WRITE_COMMAND));

    //printf("write DATA to sector\n");
    usb_packet_write(&msc_driver, dev_no, data, SECTOR_SIZE);
    
    //printf("read STATUS\n");
    n=0;
    do {
        memset(cmd, 0, sizeof(cmd));
        sts = usb_packet_read(&msc_driver, dev_no, cmd, sizeof(cmd));
        n++;
    }while(sts <= 0 && n < 100);

    // status check
    if(parse_csw(cmd, sts) < 0){
        printf("fail WRITE status.(sts=%d)\n", sts);
        for(n=0;n<13;n++){
            printf("%02X ", cmd[n]);
        }
        printf("\n");
        return -1;
    }
    return 0;
}


/*------------------------------------------------------------------------------
 uint32_t usb_msc_capacity_read(int dev_no)
    read logical last sector number
------------------------------------------------------------------------------*/

uint32_t usb_msc_capacity_read(int dev_no)
{
    uint8_t  data[64];
    uint32_t last_sector, sector_size;
    int      n;

    if(dev_no < 0 || dev_no >= msc_driver.nums) return 0;
    //if(msc_driver.option[dev_no] == MSC_UNIT_NOT_READY) return 0;
    
    if(usb_msc_scsi_read_command(dev_no, READ_CAPACITY, data)==-1){
        printf("fail READ CAPACITY\n");
        return 0;
    }
    
    last_sector = 0;
    for(n = 0; n < 4; n++){	// Get 4 byte value
        last_sector = (last_sector << 8) + data[n];	// Get 32bit number
    }
    sector_size = 0;
    for(;n<8;n++){
        sector_size = (sector_size << 8) + data[n];  // Get 32bit number
    }
    //printf("Last Sector Number = 0x%08X(%d)\n", last_sector, last_sector);
    //printf("Sector Size = %d\n", sector_size);
    //printf("Memory Size = %lld[byte]\n", (long long int)last_sector * (long long int)sector_size);
    return last_sector;
}


/*------------------------------------------------------------------------------
    int usb_msc_request_sense(int dev_no)
------------------------------------------------------------------------------*/

int usb_msc_request_sense(int dev_no)
{
    uint8_t sense_data[64];   
    int     n;

    if(dev_no < 0 || dev_no >= msc_driver.nums) return  -1;
    //if(msc_driver.option[dev_no] == MSC_UNIT_NOT_READY) return -1;
    
    if(usb_msc_scsi_read_command(dev_no, REQUEST_SENSE, sense_data)==-1){
        printf("fail REQUEST_SENSE\n");
        return -1;
    }
    //printf("Sense Data = ");
    //for(n=0; n<18; n++){
    //    printf("%02X ", sense_data[n]);
    //}
    //printf("\n");
    return 0;
}


/*------------------------------------------------------------------------------
    int usb_msc_test_unit_ready(int dev_no)
    device ready check, check wait time 2.5sec
------------------------------------------------------------------------------*/

int usb_msc_test_unit_ready(int dev_no)
{
    uint8_t  cmdbuf[64];
    int      sts, n;
    int tid;    if(dev_no < 0 || dev_no >= msc_driver.nums) return  -1;
    
    tid = timer_entry(2500); // 2.5[sec]
    while(timer_check(tid)){
        // command send
        memcpy(cmdbuf, (void *)TEST_UNIT_READY, USB_CBW_SIZE);
        usb_packet_write(&msc_driver, dev_no, cmdbuf, USB_CBW_SIZE);

        delay_ms(1);

        // read STATUS
        do{
            memset(cmdbuf, 0, USB_CSW_SIZE);
            sts = usb_packet_read(&msc_driver, dev_no, cmdbuf, sizeof(cmdbuf));
        }while(sts == 0 && timer_check(tid));

        if(timer_check(tid)==0) break;
        if(sts == 13 && cmdbuf[12] == 0) break;  // ready
    }

    if(timer_check(tid)==0){
        timer_delete(tid);
        msc_driver.option[dev_no]= MSC_UNIT_NOT_READY;
        printf("device is not Ready!\n");
        return -1;
    }else{
        timer_delete(tid);
        msc_driver.option[dev_no]= MSC_UNIT_READY;
        printf("device is Ready!\n");
        return 0;
    }
}


static int usb_msc_scsi_read_command(int dev_no, uint8_t *cmd, uint8_t *data)
{
    int      n, size, sts;
    uint8_t  cmdbuf[64];
    int      tid;

    // command send
    memcpy(cmdbuf, (void *)cmd, USB_CBW_SIZE);
    usb_packet_write(&msc_driver, dev_no, cmdbuf, USB_CBW_SIZE);
    
    // read data from device
    tid = timer_entry(3000); // 3[sec]]
    while(timer_check(tid)){
        size = usb_packet_read(&msc_driver, dev_no, data, SECTOR_SIZE);
        if(size>0) break;
    }
    timer_delete(tid);
    if(size <=0) return -1;   // read timed out

    // read STATUS
    memset(cmdbuf, 0, USB_CSW_SIZE);
    sts = usb_packet_read(&msc_driver, dev_no, cmdbuf, sizeof(cmdbuf));

    // status check
    if(parse_csw(cmdbuf, sts) < 0){
        printf("fail scsi command\n");
#ifdef DEBUG_DATA_PRINT
        for(n=0; n<USB_CSW_SIZE; n++){
            printf("%02X ", cmd[n]);
        }
        printf("\n");
#endif
        return -1;
    }
    return 0;
}



/*------------------------------------------------------------------------------
    static int parse_csw(uint8_t *buff, int size)
------------------------------------------------------------------------------*/

static int parse_csw(uint8_t *buff, int size)
{
	if(size != 13) return -1;
    if(memcmp(buff, "USBS", 4) !=0) return -1;
    if(buff[12] != 0) return -1;
	return 0;	// success
}


/*==============================================================================
    usb packet read and write function
==============================================================================*/

/*------------------------------------------------------------------------------
     read one packet from Queue
------------------------------------------------------------------------------*/

static int usb_packet_read_from_queue(DRIVER_INFO *driver, int dev_no, uint8_t *buff, uint16_t buff_size)
{
    uint16_t   size;
    uint8_t   *packet;
    uint8_t   epnum;
    uint8_t   port;
    uint8_t   interfaceNumber;

    
    if(driver->nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= driver->nums) return -1;
    
    port = driver->port[dev_no];
    interfaceNumber = driver->interfaceNumber[dev_no];
    epnum = usbDevice[port].interface[interfaceNumber].HostEpNum;

    // 受信キューからパケット取り出し
    packet = popRxQueue(epnum, &size);

    // 受信バッファにパケットデータがなければ戻る
    if(packet == NULL) return 0;  // length 0

    memcpy(buff, packet, size <= buff_size ? size: buff_size); // data copy from queue
    free(packet);

    return size <= buff_size ? size: buff_size;
}


/*------------------------------------------------------------------------------
     read one packet from USB RX module
------------------------------------------------------------------------------*/

static int usb_packet_read(DRIVER_INFO *driver, int dev_no, uint8_t *buff, uint16_t buff_size)
{
    uint8_t port;
    uint8_t addr;
    uint8_t interfaceNumber;
    uint8_t host_epnum, device_epnum;
    
    if(driver->nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= driver->nums) return -1;
    
    port = driver->port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = driver->interfaceNumber[dev_no];
    host_epnum = usbDevice[port].interface[interfaceNumber].HostEpNum;
    device_epnum = usbDevice[port].interface[interfaceNumber].INEpNum;
    return usb_read(port, addr, host_epnum, buff, buff_size);  // read one packet
}


/*------------------------------------------------------------------------------
     usb_packet_write()
------------------------------------------------------------------------------*/

static int usb_packet_write(DRIVER_INFO *driver, int dev_no, uint8_t *data, uint16_t data_size)
{
    uint8_t port;
    uint8_t addr;
    uint8_t interfaceNumber;
    uint8_t host_epnum, device_epnum;
    
    if(driver->nums ==0) return -1;  // driver not install
    if(dev_no < 0 || dev_no >= driver->nums) return -1;
    
    port = driver->port[dev_no];
    addr = usbDevice[port].Address;
    interfaceNumber = driver->interfaceNumber[dev_no];
    host_epnum = usbDevice[port].interface[interfaceNumber].HostEpNum;
    device_epnum = usbDevice[port].interface[interfaceNumber].OUTEpNum;
    return usb_write(port, addr, host_epnum, data, data_size);  // write data, possible length over than ep packet size

}


/*******************************************************************************
   PIC32MZ  High Speed USB Host
     Core Driver Implementation

   application interface function
     int  isUsbConnect(void);
     void usb_host_init(void);

    2025.7 Suwa-Koubou
 
  */


/*==============================================================================
    isUsbConnect()

     Returns the number of currently connected USB devices.

     値が 0 の時は、コネクタに何も接続されていないか、HUBは接続されているが、
   HUBのポートに何も接続されていない事を示します.
     A value of 0 indicates that nothing is connected to the connector, 
  or that a HUB is connected but nothing is connected to the HUB port.

    値が 1 の時は、コネクタにデバイスが接続されているか、コネクタには
  HUBが接続されており HUBのどれか一つのポートにのみデバイスが接続され
  ている事を示します.
     A value of 1 indicates that a device is connected to the connector, 
  or that a HUB is connected to the connector and a device is connected
  to only one port of the HUB.
 
     値が 2 以上の時は、HUBが接続されており、HUBの複数のポートに
  デバイスが接続されていることを示します.
     A value of 2 or more indicates that a HUB is connected and that
  multiple devices are connected to the HUB's ports.

    アプリケーションプログラムは、目的のデバイスが接続されるまで、この関数
 を呼び出します, デバイスが接続されたかどうかは、
       get_nums_<device_type>_device()関数
      <device_type> = [ midi / keyboard / mouse / hid_generic / serial / msc ]
 を呼び出して知ることができます.

   The application program calls this function until the desired device
 is connected. Whether or not the device is connected can be determined 
 by calling the get_nums_<device_type>_device() function. 
   <device_type> = [ midi / keyboard / mouse / hid_generic / serial / msc ]

     例えば USB mouse を使いたい時、mouse が接続されていない時は、 
 get_nums_mouse_device()は、0を返します. HUBを使って複数のmouseが
 接続されると接続しているmouse の数を返します.

  For example, if you want to use a USB mouse and no mouse is connected,
 get_nums_mouse_device() returns 0. If multiple mice are connected using
 a HUB, it returns the number of connected mice.

    while(get_nums_mouse_device()==0){
       isUsbConnect();
    } 
  
   mouseからデータを読み出すには、usb_mouse_read(int dev_no, uint8_t *data)
 関数を使います.引数の dev_no は 複数のmouse が接続されている時、どのmouse
 からデータを読み出すのか指定します. 一番目のmouseなら dev_noには 0 を設定
 します. 読み出した mouse のデータは、加工することなくそのまま 引数 data 
 に格納されます
 
   To read data from the mouse, use the 
 usb_mouse_read(int dev_no, uint8_t *data) function. When multiple mice are
 connected, the argument dev_no specifies which mouse to read data from. 
 If it is the first mouse, set dev_no to 0. The mouse data that is read is 
 stored in the argument data buffer without any processing.
 
==============================================================================*/

int isUsbConnect(void)
{
    int  n;

    //   usbDeviceAttach フラグが 0 を示した時、usbDevice[0].Readyが
    // 1 であれば、デバイスが抜去されたことを意味するので、デタッ
    // チの処理を行う. usbDevice[0].Ready が 0 の時は、デバイスが接続
    // されていない状態であるので何もしないで戻る
    
    //    When the usbDeviceAttach flag indicates 0, if usbDevice[0].Ready
    // is 1, it means that the device has been removed, so perform the detach
    // process. If usbDevice[0].Ready is 0, the device is not connected, so
    // return without doing anything.
    
    if(!usbDeviceAttach){
        if(usbDevice[0].Ready){
            if(RST_IF) printf("detect babble\n");
            printf("USB device detached\n");
            usb_host_init();  // reinitialize HOST module
        }
        cnctDeviceNums =0;
        return cnctDeviceNums;  
    }

    //   usbDeviceAttach フラグが 1 を示した時、usbDevice[0].Readyが
    // 0 なら、デバイスが接続されてからまだエヌメレーション処理が実行
    // されていない事を意味しているのでリセットからエヌメレーションま
    // での処理を実行する 

    //   When the usbDeviceAttach flag is 1, if usbDevice[0].Ready is 0,
    // it means that the enumeration process has not been performed since
    // the device was connected, so the process from bus reset to 
    // enumeration is performed.
    
    if(usbDevice[0].Ready == 0){
        // バスリセット、接続速度検出、
        // EP0デフォルトパケットサイズの設定

        // Bus reset, connection speed detection,
        // Set EP0 default packet size

        printf("USB device attached.\n");
        if (USBOTGbits.LSDEV){
            printf("Low speed device attached\n");
            USBE0CSR2bits.SPEED = 3; // Low Speed
            usbDevice[0].Speed = LOW_SPEED;
            packet_size[0][0] = LOW_PACKET_SIZE;
        }else{
            printf("Full or High speed device attached\n");
            USBE0CSR2bits.SPEED = 2; // Full Speed
            usbDevice[0].Speed = FULL_SPEED;
            packet_size[0][0] = FULL_PACKET_SIZE;
        }

        printf("USB Bus Reset\n");
        USBCSR0bits.RESET = 1;
        delay_ms(20);
        USBCSR0bits.RESET = 0;
        delay_ms(10);

        if(USBCSR0bits.HSMODE==1){
            printf("High speed negotiation complete.\n");
            USBE0CSR2bits.SPEED = 1; // High Speed
            usbDevice[0].Speed = HIGH_SPEED;
        }

        usbDevice[0].Address = 1;   // device address assign 1
        usbDevice[0].Ready = 0;     // not yet ready
        
        // エヌメレーションの実行
        // run enumeration
        device_enumeration(0);

        // エヌメレーションの結果、HUBが接続されていた時は
        // 現在の接続デバイスの数（初期値のままなので 0）を返す.
        // HUBの初期化はエヌメレーション処理中に完了しており, Readyも1になっている
        
        //  If the enumeration results in a HUB being connected,
        // it returns the number of currently connected devices (which is 
        // still the initial value, so 0). The HUB initialization is completed
        // during the enumeration process, and Ready is also set to 1.
        
        if(hubAttached){
            printf("HUB attached\n");
            return  cnctDeviceNums;   // cnctDeviceNums is value 0.
        }

        // HUB以外のデバイスが接続されていた時、エヌメレーションの結果
        // Ready が 1 になっていない時は、エヌメレーションが失敗したことを
        // 意味するので、現在の接続デバイスの数（初期値のままなので 0）を返す.

        //   If a device other than a HUB is connected and Ready remains 
        // at 0 as a result of enumeration, this means that the enumeration 
        // has failed, so the number of currently connected devices 
        // (which remains the initial value, so 0) is returned.
        
        if(usbDevice[0].Ready == 0){
            printf("Fail enumeration!\n");
            //  エヌメレーション失敗の要因によっては usbDeviceAttach フラグが 0 に成ることがある
            //   Depending on the cause of the enumeration failure, 
            // the usbDeviceAttach flag may become 0.
            if(!usbDeviceAttach){
                if(RST_IF) printf("detect babble\n");
                usb_host_init(); // reinitialize HOST module
                printf("USB device detached\n");
            }
            cnctDeviceNums =0;
            return  cnctDeviceNums; 
        }

        // コネクタにHUB以外のデバイスの接続が確認出来たので
        // 接続 ポート番号を 0 に設定する.
        //  0 はHUBのポートではなくUSBコネクタそのものを示す
        // HUBのポート番号は 1 から HUBMAXPORT までとなる
        // 次のサポートクラスのチェック処理に進む

        //  Since it was confirmed that a device other than a HUB was
        // connected to the connector, the connection port number was 
        // set to 0.  0 indicates the USB connector itself, not the 
        // HUB port. The HUB port number ranges from 1 to HUBMAXPORT.
        // Continue with the next support class check process

        hubPortNumber = 0;

    }else{

        // USBコネクタにデバイスが接続されていて Ready が 1 の時,
        // そのデバイスがHUBでなければ、何もしないで現在の接続数
        // (値は 1 である)を返す.

        //  When a device is connected to a USB connector and Ready is 1,
        // if the device is not a HUB, this does nothing and returns 
        // the current number of connections (which is 1).
        
        if(!hubAttached){
            // not HUB
            return  cnctDeviceNums;  // value 1
        }

        // HUBが接続されているのでHUBの処理を呼び出して.
        // ポートの状態を調べる.
        // Check for changes in HUB ports
        hub_loop();
        
        // ポートにデバイスが接続されていたらアタッチ、
        // デバイスが外されていたらデタッチの処理を行う

        //  If a device is connected to the HUB port, it performs the attach process,
        // if the device is removed, it performs the detach process.

        // if HUB port no change return
        if(hubPortChanged == PORT_DEVICE_NONE){
            return cnctDeviceNums;

        // ポートにデバイスが接続されたらそのデバイスのエヌメ
        // レーション処理を行う.
        // エヌメレーション失敗なら現在の接続数を返して終わる
        // 成功の時は、次のサポートクラスのチェック処理に進む
            
        //  When a device is connected to the port, the enumeration process
        // for that device is performed. If the enumeration fails, the current
        // number of connections is returned and the process ends.
        // If successful, the process proceeds to the next support class check process.

        }else if(hubPortChanged == PORT_DEVICE_ATTACHED){
            device_enumeration(hubPortNumber);
            hubPortChanged = PORT_DEVICE_NONE;
            if(usbDevice[hubPortNumber].Ready==0){
                printf("Fail enumeration!\n");
                return  cnctDeviceNums;
            }
            // proceeds to the next support class check process

            
        // デタッチの処理を行う Detach processing
        }else if(hubPortChanged == PORT_DEVICE_DETACHED){
            hubPortChanged = PORT_DEVICE_NONE;
            device_unregist_from_class_driver(hubPortNumber);             // driver uninstall
            memset(&usbDevice[hubPortNumber], 0, sizeof(usbDevice[0]));   // clear usbDevice[port]
            printf("Hub port device detached (port=%d)\n", hubPortNumber);

            // 接続数を減ずる Reduce the number of connections
            if(cnctDeviceNums) cnctDeviceNums--;
            printf("cnctDeviceNums=%d\n", cnctDeviceNums);
            return  cnctDeviceNums;
        }
    
    }

    // サポートしているクラスかどうか調べる.
    // Check if the class is supported.

    // サポートしているクラスならクラスドライバーにこのデバイスを登録する
    // If the class is supported, register this device with the class driver.    
    if(analys_interface_class() >= 1){
        usbDevice[hubPortNumber].Attached = 1;
        device_regist_to_class_driver(hubPortNumber);  // This device regist in class driver 

        // 接続数を増やす Increase the number of connections
        cnctDeviceNums++;

        dump_interface_info(hubPortNumber);
        printf("USB device attached\n");
        printf("cnctDeviceNums=%d\n", cnctDeviceNums);

    }else{
        // 未サポートなら何もしない
        // not supported, do nothing.
        usbDevice[hubPortNumber].Attached = 0;
        printf("no support USB device attached\n");
        printf("cnctDeviceNums=%d\n", cnctDeviceNums);
    }
    
    return cnctDeviceNums;
}

/*------------------------------------------------------------------------------
      void dump_interface_info(uint8_t hubPortNumber)
------------------------------------------------------------------------------*/

static void dump_interface_info(uint8_t hubPortNumber)
{
    USB_DEVICE *ud;
    int        i;
    
    ud = &(usbDevice[hubPortNumber]);
    printf("usbDevice[%d].interfacenums=%d\n", hubPortNumber, ud->interfaceNums);
    for(i=0; i < ud->interfaceNums; i++){
        // display device class 
        if(ud->interface[i].Class == 1){  // Audio MIDI
            printf("attached MIDI device.\n");
        }else if(ud->interface[i].Class == 2){  // CDC 
            printf("attached CDC serial device (control).\n"); 
        }else if(ud->interface[i].Class == 10){  // CDC 
            printf("attached CDC serial device (data).\n"); 
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 1 && ud->interface[i].Protocol == 1){
            printf("attached HID Keyboard device.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 1 && ud->interface[i].Protocol == 2){
            printf("attached HID Mouse device.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 0){
            printf("attached HID generic device.\n");
        }else if(ud->interface[i].Class == 8){
            printf("attached MSC device.\n");
        }else if(ud->interface[i].Class == 14 &&  ud->interface[i].SubClass == 2  && ud->interface[i].Protocol == 0){
            printf("attached UVC device.\n");
        }else if(ud->interface[i].Class == 0xF0){  // FTDI vendor class
            printf("attached FTDI serial device.\n"); 
        }
        // display interface number
        printf("interfaceNumber=%d\n",ud->interface[i].interfaceNumber);

        // display Endpoint info
        if(ud->interface[i].INEpNum){
            printf("IN   Ep number=%d, packet size=%d, protocol=%d\n",ud->interface[i].INEpNum, ud->interface[i].INEpSize, ud->interface[i].INEpProto);
        }
        if(ud->interface[i].OUTEpNum){
            printf("OUT  Ep number=%d, packet size=%d, protocol=%d\n",ud->interface[i].OUTEpNum, ud->interface[i].OUTEpSize, ud->interface[i].OUTEpProto);
        }
        
        if(ud->interface[i].HostEpNum){
            printf("HOST Ep number=%d\n", ud->interface[i].HostEpNum);
        }
    }
}


/*==============================================================================
     int analys_interface_class(void)
==============================================================================*/

static int analys_interface_class(void)
{
    int  numInterface, interfaceNumber, prevInterfaceNumber;
    int  numEndpoints;
    USB_DEVICE *ud;
    uint8_t *p;

    ud = &(usbDevice[hubPortNumber]);
    ud->interfaceNums = 0;
    
    // get device configuration descriptor
    if((p=get_device_configuration(hubPortNumber, ud->Address))== NULL){
        return 0;
    }

    prevInterfaceNumber = -1;
    numInterface = p[4];
    printf("numInterfaces=%d\n",numInterface);

    // インタフェースの数だけ調べる
    // Check as many interfaces as possible
    //while(numInterface){

    // configuration の最後に至るまでインターフェースを調べる
    while(p[0] != 0xFF){

        // インタフェースディスクリプタが出現するまで読み飛ばす
        // Interface descriptor ?
        while(p[1] != 4){
            p += p[0]; // skip this no interface descriptor
            if(p[0] == 0xFF) return ud->interfaceNums;
        }

        // ***** Interface Descriptor ****************
        interfaceNumber = p[2];
        numEndpoints = p[4];
        ud->interface[ud->interfaceNums].Class    = p[5];
        ud->interface[ud->interfaceNums].SubClass = p[6];
        ud->interface[ud->interfaceNums].Protocol = p[7];

        if(!isSupportClass(p)){
            p += p[0];  // このインタフェースが未サポートなら次のインタフェースへ
                        // If the class of this interface is not supported, check the next interface.
            continue;
        }

        // エンドポイントを持たないなら次のインタフェースへ
        // If it does not have an endpoint, check the next interface.
        if(numEndpoints==0){
            p+=p[0];  // skip this interface
            continue;
        }
        
        // 一つ前に登録した同じ番号のインタフェースならスキップ
        //   代替インタフェースは最初のものだけが登録される
        //   全代替インタフェースの情報処理はミドルドライバーが別途行う
        if(prevInterfaceNumber == interfaceNumber){
            p+=p[0];
            continue;
        }
        
        // このインタフェースを登録する
        prevInterfaceNumber = interfaceNumber;

        // get the Endpopint info.
        while(numEndpoints--){
            // Skip to the endpoint descriptor
            while(p[1] != 5){
                p += p[0];
            }

            if(p[2] & 0x80){ // IN ?
                ud->interface[ud->interfaceNums].INEpNum  = p[2] & 0x7f;
                ud->interface[ud->interfaceNums].INEpSize = ((p[5]<<8)|p[4]) & 0x7FF;
                ud->interface[ud->interfaceNums].INEpProto = p[3] & 3;  // 3=interrupt 2=bulk 1=isocronus
                ud->interface[ud->interfaceNums].INinterval = p[6];
                
            }else{
                ud->interface[ud->interfaceNums].OUTEpNum = p[2];
                ud->interface[ud->interfaceNums].OUTEpSize = ((p[5]<<8)|p[4]) & 0x7FF;
                ud->interface[ud->interfaceNums].OUTEpProto = p[3] & 3;  // 3=interrupt 2=bulk 1=isocronus
                ud->interface[ud->interfaceNums].OUTinterval = p[6];
            }
            p += p[0];
        }
        
        if(ud->interface[ud->interfaceNums].INEpNum != 0 || ud->interface[ud->interfaceNums].OUTEpNum != 0){
            // デバイス側のエンドポイントと通信するホスト側のエンドポイントを設定する
            // Configure a host endpoint to communicate with the device endpoint
            int hep;
            hep = get_host_ep();
            if(hep != -1){
                ud->interface[ud->interfaceNums].HostEpNum =  hep;
                ud->interface[ud->interfaceNums].interfaceNumber = interfaceNumber;

                // エンドポイントの通信設定を行う
                // Configure endpoint communication settings
                if(ud->interface[ud->interfaceNums].Class == MSD_CLASS || 
                   ud->interface[ud->interfaceNums].Class == UVC_CLASS ){   
                    set_port_device_endpoint(hubPortNumber, ud->interfaceNums, 0);  // no  auto receiev
                }else{
                    set_port_device_endpoint(hubPortNumber, ud->interfaceNums, 1);  // use auto receiev by rx interrupt
                }
                // このデバイスのサポート可能なインタフェースの数を増やす
                // Increase the number of interfaces that this device can support
                ud->interfaceNums++;
            }
        }
    }

    return ud->interfaceNums;

}

static int get_host_ep(void)
{
    int n;
    for(n=0; n<MAX_EP; n++){
        if(usedHostEp[n] == 0){
            usedHostEp[n] = 1;
            return n;
        }
    }
    return -1;
}

static void back_host_ep(int epnum)
{
    usedHostEp[epnum] = 0;
}


/*------------------------------------------------------------------------------
      int isSupportClass(uint8_t *descriptor)
------------------------------------------------------------------------------*/

static int isSupportClass(uint8_t *interface_descriptor)
{
    int      n;
    uint8_t *p;
    
    p = interface_descriptor;

    printf("Interface number=%d\n",p[2]);
    printf("Class=%d, SubClass=%d, Protocol=%d\n", p[5], p[6], p[7]);
    printf("has endpoints=%d\n", p[4]);

    if(p[5] == 0xFF){// Vendor Class
        if(isProduct(hubPortNumber)){  // Check for vendor class devices
            printf("support vendor class!\n");
            return 1;   // support class
        }
    }

    // Check for standard class devices
    for(n=0; n< sizeof(class_table)/sizeof(class_table[0]); n++){
        if(p[5] == class_table[n].class &&
           p[6] == class_table[n].subclass &&
           p[7] == class_table[n].protocol){
            printf("support standard class!\n");
            return 1;   // support class
        }
    }
    printf("no support!\n");
    
    return 0;   // no support class
}

/*------------------------------------------------------------------------------
    check Vendor code, Product code
------------------------------------------------------------------------------*/

static int  isProduct(uint8_t port)
{
    USB_DEVICE *ud;
    int        n, nums;
    
    ud = &(usbDevice[port]);

    nums = sizeof(product_table)/sizeof(product_table[0]);
    for(n=0; n < nums; n++){
        if(product_table[n].VId == ud->VId && product_table[n].PId == ud->PId){
            ud->interface[ud->interfaceNums].Class    = product_table[n].Class;
            ud->interface[ud->interfaceNums].SubClass = product_table[n].SubClass;
            ud->interface[ud->interfaceNums].Protocol = product_table[n].Protocol;
            break;
        }
    }
    if(n >= nums){
        return 0;   // not found 
    }
    return 1;    // found Product
}


/*==============================================================================
    USB device class driver initialize
==============================================================================*/

static void usb_driver_init(void)
{
    memset(&mouse_driver, 0, sizeof(DRIVER_INFO));
    memset(&keyboard_driver, 0, sizeof(DRIVER_INFO));
    memset(&generic_driver, 0, sizeof(DRIVER_INFO));
    memset(&midi_driver, 0, sizeof(DRIVER_INFO));
    memset(&cdc_driver, 0, sizeof(DRIVER_INFO));
    memset(&msc_driver, 0, sizeof(DRIVER_INFO));
    memset(&uvc_driver, 0, sizeof(DRIVER_INFO));
}


/*==============================================================================
    device regist to class driver
==============================================================================*/

static void device_regist_to_class_driver(uint8_t hubPortNumber)
{
    USB_DEVICE  *ud;
    DRIVER_INFO *driver;
    int          i, n;
    uint8_t     option;
    
    
    ud = &(usbDevice[hubPortNumber]);

    for(i=0; i<ud->interfaceNums; i++){
        option = 0;
        if(ud->interface[i].Class == 1){  // Audio MIDI
            driver = &midi_driver;
            printf("installed midi driver.\n");
        //}else if(ud->interface[i].Class == 2){  // CDC 
        //
        }else if(ud->interface[i].Class == 10){  // CDC 
            driver = &cdc_driver;
            option = SERIAL_CDC_ACM;
            printf("installed cdc driver.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 1 && ud->interface[i].Protocol == 1){
            driver = &keyboard_driver;
            printf("installed keyboard driver.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 1 && ud->interface[i].Protocol == 2){
            driver = &mouse_driver;
            printf("installed mouse driver.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 0){
            driver = &generic_driver;
            printf("installed hid generic driver.\n");
        }else if(ud->interface[i].Class == 8){
            driver = &msc_driver;
            printf("installed msc driver.\n");
        //}else if(ud->interface[i].Class == 14 && ud->interface[i].SubClass == 1 ){
        //    driver = &uvc_driver;
        //    printf("installed UVC Video Control device.\n");
        //}else if(ud->interface[i].Class == 14 && ud->interface[i].SubClass == 2 ){
        //    driver = &uvc_driver;
        //    printf("installed UVC Video driver.\n");
        }else if(ud->interface[i].Class == 0xF0){  // FTDI vendor class
            driver = &cdc_driver;
            option = SERIAL_FTDI; 
            printf("installed ftdi driver.\n");
        }
        
        if(driver->nums >= HUBMAXPORT){
            printf("too many drivers\n");
            printf("Skip driver install (port=%d)\n", hubPortNumber);
            continue;
        }

        
        driver->port[driver->nums]=hubPortNumber;
        driver->interfaceNumber[driver->nums]=i;
        driver->option[driver->nums]=option;
        driver->nums++;   
        
        
        // For serial devices, set the default communication parameters.
        if(driver == &cdc_driver){
            set_serial_default_parameter(cdc_driver.nums-1);
        }
   
        //If it is an MSC class device, perform a ready test
        if(driver == &msc_driver){
            usb_msc_test_unit_ready(msc_driver.nums-1);
            //if(usb_msc_test_unit_ready(msc_driver.nums-1) == -1){
            //    // Make it so that it cannot be used unless it is ready
            //    msc_driver.option[msc_driver.nums-1]= MSC_UNIT_NOT_READY;
            //}else{
            //    msc_driver.option[msc_driver.nums-1]= MSC_UNIT_READY;
            //}
        }

        //  initialize UVC host driver
        if(driver == &uvc_driver){
            // not yet imprement
            ;
        }
    }
}


/*==============================================================================
    device unregist from class driver
    This function is called only when a HUB port device is removed.
==============================================================================*/

static void device_unregist_from_class_driver(uint8_t hubPortNumber)
{
    USB_DEVICE  *ud;
    DRIVER_INFO *driver;
    int         i, n,  dev;

    ud = &(usbDevice[hubPortNumber]);

    for(i=0; i<ud->interfaceNums; i++){
        back_host_ep(ud->interface[i].HostEpNum);
        
        if(ud->interface[i].Class != MSD_CLASS && ud->interface[i].Class != UVC_CLASS ){   
            // complete auto receive
            *((int32_t *)&USBE1CSR1 + (ud->interface[i].HostEpNum-1) * 4) &= ~(0x10000);        // USBIENCSR1bits.RXPKTRDY = 0;
        }

        if(ud->interface[i].Class == 1){  // Audio MIDI
            driver = &midi_driver;
            printf("uninstalle midi driver.\n");
        //}else if(ud->interface[i].Class  == 2){  // CDC control
        //
        }else if(ud->interface[i].Class == 10){  // CDC data
            driver = &cdc_driver;
            printf("uninstalle cdc driver.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 1 && ud->interface[i].Protocol == 1){
            driver = &keyboard_driver;
            printf("uninstalle keyboard driver.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 1 && ud->interface[i].Protocol == 2){
            driver = &mouse_driver;
            printf("uninstalle mouse driver.\n");
        }else if(ud->interface[i].Class == 3 && ud->interface[i].SubClass == 0){
            driver = &generic_driver;
            printf("uninstalle hid generic driver.\n");
        }else if(ud->interface[i].Class == 8){
            driver = &msc_driver;
            printf("uninstalle msc driver.\n");
        }else if(ud->interface[i].Class == 14){
            driver = &uvc_driver;
            printf("uninstalle uvc driver.\n");
        }else if(ud->interface[i].Class == 0xF0){  // FTDI vendor class
            driver = &cdc_driver;
            printf("uninstalle ftdi driver.\n");
        }
    
        for(n=0; n<driver->nums; n++){
            if(driver->port[n] == hubPortNumber){
                for(dev=n; dev < driver->nums-1; dev++){
                    driver->port[dev] = driver->port[dev+1];
                    driver->interfaceNumber[dev] = driver->interfaceNumber[dev+1]; 
                    driver->option[dev] = driver->option[dev+1];
                }
                driver->nums--;
            }
        }
    }
}


/*==============================================================================
	device enumeration
    port : HUB port number
           For a direct connection, set HUB port number to 0.
==============================================================================*/

static void device_enumeration(uint8_t port)
{
    int  n;

    printf("HOST: device_enumeration(port=%d)\n",port);

    // Get device descriptor
    // access address is 0.
    if(get_device_descriptor(port, 0)==-1) return;


    // *** device is HUB ***********
    if(usbDevice[port].interface[usbDevice[port].interfaceNums].Class == HUB_CLASS){
        if(hubAttached) return;   // skip 2nd HUB.
        hub_initialize();
        return;
    }

    // **** attach Other device ******
    
    // Set device address
    if(set_device_address(port, usbDevice[port].Address)==-1) return;

    // Set configuration
    if(set_device_configuration(port, usbDevice[port].Address) ==-1) return;

	// set ready flag
	usbDevice[port].Ready = 1;

}


/*=============================================================================
	void get_device_descriptor(int port_number, int addr)

      最初のエヌメレーションの時は、addr に 0 を設定。
    SET_ADDRESSが完了後は、そのアドレスを addr に設定する。

      For the first enumeration, set addr to 0.
    After SET_ADDRESS is complete, set that address to addr.
==============================================================================*/

static int get_device_descriptor(uint8_t port, uint8_t addr)
{
	printf("HOST: get_device_descriptor. addr=%d, port=%d\n", addr, port);

    if(usb_ctrl(port, addr, (uint8_t *)GET_DESCRIPTOR, (uint8_t *)&device_descriptor) ==-1){
       printf("HOST: Fail GET_DEVICE_DESCRIPTOR\n");
       return -1;
   }

#ifdef DEBUG_DATA_PRINT
    { int n;
        for(n=0; n<18; n++){
            printf("%02X ", ((uint8_t *)&device_descriptor)[n]);
        }
        printf("\n");                    
    }
#endif
#ifdef DEBUG_DUMP_PRINT
   dump_descriptor((uint8_t *)&device_descriptor, sizeof(device_descriptor));
#endif
    // get the DeviceClass
    usbDevice[port].interface[usbDevice[port].interfaceNums].Class = device_descriptor.bDeviceClass;

	// get the VendorID, ProductID
	usbDevice[port].VId = device_descriptor.idVendor;
	usbDevice[port].PId = device_descriptor.idProduct;

    USBE0CSR0bits.TXMAXP = device_descriptor.bMaxPacketSize;
    packet_size[port][0]=device_descriptor.bMaxPacketSize;
    return 0;

}


/*==============================================================================
	int set_device_address(uint8_t port, uint8_t addr)
==============================================================================*/

static int set_device_address(uint8_t port, uint8_t addr)
{
    printf("HOST: set device address addr=%d, port=%d\n", addr, port);
    SET_ADDRESS[2] = addr;
    if(usb_ctrl(port, 0, SET_ADDRESS, NULL)==-1){
       printf("HOST: Fail SET_ADDRESS\n");
       return -1;
    }
    return 0;
}


/*=============================================================================
    int set_device_configuration(uint8_t port, uint8_t addr)
==============================================================================*/

static int set_device_configuration(uint8_t port, uint8_t addr)
{
    printf("HOST: set_device_configuration addr=%d, port=%d\n", addr, port);
    delay_us(150);  // My old memory reader needs a delay
    if(usb_ctrl(port, addr, SET_CONFIG, NULL)==-1){
        printf("HOST: Fail SET_CONFIG\n");
        return -1;
    }
    delay_us(150);
    return 0;
}


/*=============================================================================
	uint8_t *get_device_configuration(uint8_t port, uint8_t addr)
  
      port に接続された device address が addr の device の 
    configuration descriptor を 内部の device_configuration[]に
    読み込んで device_configuration を返す
      読み込み失敗の時は NULL を返す.
  
      Reads the configuration descriptor of the device with device 
    address addr connected to port into the internal 'device_configuration[]' array  
    and returns pointer to the 'device_configuration' array. 
      Returns NULL if the read fails.
==============================================================================*/

static uint8_t *get_device_configuration(uint8_t port, uint8_t addr)
{
    uint16_t  config_length, config_full_length;

    
	printf("HOST: get_device_configuration addr=%d, port=%d\n", addr, port);
    GET_CONFIG_DESCRIPTOR[6]=0x09;
    GET_CONFIG_DESCRIPTOR[7]=0x00;
    config_length = 9;
    printf("get device configuration(short length=%d)\n", config_length);
    if(usb_ctrl(port, addr, GET_CONFIG_DESCRIPTOR, device_configuration)==-1){
        printf("HOST: Fail GET_CONFIGURATION(1st)\n");
        return NULL;
    }

#ifdef DEBUG_DATA_PRINT
    { int n;
        for(n=0; n<config_length; n++){
            printf("%02X ", device_configuration[n]);
        }
        printf("\n");                    
    }
#endif
    config_full_length = (device_configuration[3]<<8)|device_configuration[2];
    
    if(config_full_length > (sizeof(device_configuration)-1)){
        printf("HOST: Fail GET_CONFIGURATION(data size too big. size=%d)\n", config_full_length);
        return NULL;
    }
    device_configuration_length = config_full_length;

    printf("get device configuration(full length=%d)\n", config_full_length);
    delay_us(200);   // my some device needs delay.
    GET_CONFIG_DESCRIPTOR[6] = config_full_length & 0xff;
    GET_CONFIG_DESCRIPTOR[7] = (config_full_length >> 8);
    if(usb_ctrl(port, addr, GET_CONFIG_DESCRIPTOR, device_configuration)==-1){
        printf("HOST: Fail GET_CONFIGURATION(2nd)\n");
        return NULL;
    }
    
    // descriptor の end mark を設定
    device_configuration[config_full_length] = 0xFF;
    
#ifdef DEBUG_DATA_PRINT
    { int n;
        for(n=0; n<config_full_length; n++){
            printf("%02X ", device_configuration[n]);
            if((n%16)==15) printf("\n");
        }
        printf("\n");                    
    }
#endif
#ifdef DEBUG_DUMP_PRINT
    dump_descriptor(device_configuration, device_configuration_length);    
#endif
    return device_configuration;
}


/*==============================================================================
     USB Control Transfar 
==============================================================================*/

static int  usb_ctrl(uint8_t port, uint8_t addr, uint8_t *ctrl, uint8_t *data)
{
    uint16_t size;

    //SETUP stage
    if(usb_setup(port, addr, ctrl) == -1){
        printf("SETUP stage error.\n");
        return -1;
    }

    delay_us(150);  // My old memory reader required this slight delay.
    
    // DATA stage
	if((size=((ctrl[7]<<8)|ctrl[6]))!=0){ // has data length
		// IN ?
		if(ctrl[0]&0x80){ // DATA Stage is IN transaction
			if(usb_ep0_read(port, addr, 0, data, size)==-1){
                printf("DATA(IN) stage error. size=%d\n", size);
				return -1;
			}
		}else{ // OUT transaction
	        if(usb_ep0_write(port, addr, 0, data, size)==-1){
                printf("DATA(OUT) stage error\n");
				return -1;
			}
		}
	}


    // STATUS stage (OUT or IN)
	if(ctrl[0]&0x80){   // is DATA stage IN ?
        // STATUS OUT 
        USB_EP0_IF = 0;
        *((uint8_t *)&USBE0CSR0 + 0x2) = 0x42; // STATUS + TXPKTRDY
        if(ep0_interrupt_wait() == -1){
            printf("STATUS(OUT) stage error\n");
            return -1;
        }
	}else{
        // STATUS IN
        USB_EP0_IF = 0;
        *((uint8_t *)&USBE0CSR0 + 0x2) = 0x60; // Set STATUS and REQPKT, no data stage
        if(ep0_interrupt_wait() == -1){
            printf("STATUS(IN) stage error\n");
            return -1;
        }
        *((uint8_t *)&USBE0CSR0 + 0x2) &= ~0x41; // Clear STATUS and RXPKTRDY
    }

    return 0;
}


/*==============================================================================
    SETUP transaction 
==============================================================================*/

static int usb_setup(uint8_t port, uint8_t addr, uint8_t *ctrl)
{
    int16_t    cnt;
    uint8_t    *FIFO_buffer;
    
    FIFO_buffer = (uint8_t *)&USBFIFO0;

    // パケットサイズ、通信速度を設定します
    // Set the packet size and communication speed
    USBE0CSR0bits.TXMAXP = packet_size[port][0];
    USBE0CSR2bits.SPEED = usbDevice[port].Speed;

    // USBE0TXAレジスタで通信先デバイスを特定します
    // USBコネクタに接続したデバイスは port==0 です.
    // HUBのポートに接続したデバイスは port >= 1 です.

    // The USBE0TXA register identifies the destination device.
    // Devices connected to a USB connector have a port number of 0.
    // Devices connected to a HUB port have a port number of 1 or higher.
    
    if(port){ // HUB connect device
        USBE0TXAbits.TXHUBADD = 1;      // HUBのアドレスは 1 固定です. The HUB address is fixed at 1.
        USBE0TXAbits.TXHUBPRT = port;
    }else{
        USBE0TXAbits.TXHUBADD = 0;
        USBE0TXAbits.TXHUBPRT = 0;
    }
    USBE0TXAbits.TXFADDR = addr;    

    // FIFO にリクエストパケットを格納します
    // Store the request packet in the FIFO
    for(cnt = 0;cnt <8; cnt++){
        *FIFO_buffer = *ctrl++; // Send the bytes
    }

    // USBE0CSR0 レジスタの SETUPとTXRDYのビットを同時にオンにして
    // FIFOに格納したリクエストパケットを送信します.
    //  TXRDYビットが一定時間内にクリアされれば送信成功です.

    // Turn on the SETUP and TXRDY bits of the USBE0CSR0 register 
    // simultaneously to transmit the request packet stored in the FIFO.
    // If the TXRDY bit is cleared within a certain period of time, 
    // the transmission is successful.

    USB_EP0_IF = 0;
    *((uint8_t *)&USBE0CSR0 + 0x2) = 0xA;   // SETUP + TXRDY
    if(txrdy_wait(0) == -1){
        return -1;
    }
    if(ep0_interrupt_wait() == -1){
        return -1;
    }
    return 0;
}


/*------------------------------------------------------------------------------
    ep0 interrupt wait 
------------------------------------------------------------------------------*/

static int ep0_interrupt_wait(void)
{
    int32_t tid;
    
    tid = timer_entry(USB_EP_TIMEOUT);
    while(timer_check(tid)){
        if(USB_EP0_IF)break;
    }
    timer_delete(tid);
    if(USB_EP0_IF) return 0;
    return -1; // timeout
}


/*==============================================================================
    DATA IN transaction 
 
    One packet receive only when len is 0. 
==============================================================================*/

static int  usb_ep0_read(uint8_t port, uint8_t addr, uint8_t epnum, uint8_t *buff, uint16_t buff_size)
{
    uint16_t cnt, size, rec_size;
    uint8_t *FIFO_buffer;
    uint32_t val;
    int to;

    // *** EP0 DATA(IN) transaction
    cnt =0;
    size =0;
    rec_size = 0;

    if(port){ // HUB connect device
        val = (port << 24)|(1 << 16)|addr;
    }else{
        val = addr;
    }
    
    USBE0CSR2bits.SPEED = usbDevice[port].Speed;
    USBE0RXA = val;

    to=0;
    while(rec_size < buff_size){
        // IN token    
        USB_EP0_IF =0;
        *((uint8_t *)&USBE0CSR0 + 0x2) = 0x20; // REQPKT
        if(ep0_interrupt_wait()==-1){
            return -1;
        }
        size =USBE0CSR2bits.RXCNT;
        FIFO_buffer = (uint8_t *)&USBFIFO0;
        for(cnt = 0; cnt < size; cnt++){
            *buff++ = *(FIFO_buffer + (cnt & 3));
        }
        USBE0CSR0bits.RXRDYC = 1;
        if(size==0){
            if(++to >= 200){    // NAK receive 200 times to TimedOut! 
                return -1;
            }
            delay_ms(1);  // コントロール転送なので少々遅くても問題なし
        }else{
            to = 0;       // reset TimedOut counter
        }
        rec_size += size;
    }
    return rec_size; // success
}

static int  usb_read(uint8_t port, uint8_t addr, uint8_t epnum, uint8_t *buff, uint16_t buff_size)
{
    uint16_t cnt, size, rec_size;
    uint8_t tmp8, *FIFO_buffer;
    uint32_t tmp32;

    cnt =0;
    size =0;
    rec_size = 0;
    *((int32_t *)&USBE0CSR0 + epnum * 4) &= ~(0x20000000);         // USBIENCSR0bits.MODE   = 0;
    *((int32_t *)&USBE1CSR1 + (epnum-1) * 4) |= 0x200000;          // USBIENCSR1bits.REQPKT = 1; IN transaction start
    if(epn_rx_interrupt_wait(epnum) ==-1) return 0;

    // received packet!
    USB_EPN_RX_IF &= ~(1<<epnum);                                  // clear RXIF
    size = *((int32_t *)&USBE0CSR2 + epnum * 4) & 0x3FF;           //size = USBIENCSR2bits.RXCNT;
    // data read from FIFO
    FIFO_buffer = (uint8_t *)((uint32_t)&USBFIFO0 + epnum * 4);    // FIFO_buffer = USBFIFON ;
    
    if((size & 3)==0 && (buff_size & 3)==0){
        for(cnt = 0; cnt <size/4; cnt++){
            if(cnt < buff_size/4){
                *((uint32_t *)buff) = *((uint32_t *)FIFO_buffer);
                buff += 4;
            }else{
                tmp32 = *((uint32_t *)FIFO_buffer);
            }
        }
    }else{
        for(cnt = 0; cnt < size; cnt++){
            if(cnt < buff_size){
                *buff++ = *(FIFO_buffer + (cnt & 3));
            }else{
                tmp8 = *(FIFO_buffer + (cnt & 3));
            }
        }
    }

    *((int32_t *)&USBE1CSR1 + (epnum-1) * 4) &= ~(0x10000);        // USBIENCSR1bits.RXPKTRDY = 0;

    return (size <= buff_size) ? size: buff_size;
}


static void pushRxQueue(int epnum, uint8_t *packet, uint16_t length)
{
    USB_QUEUE *uq;
    uq = &(usbQueue[epnum]);

    if(uq->rxCount >= RXBUFSIZE){  // queue not empty 
        free(packet);
        return;
    }
    
    uq->rxBuf[uq->rxWrtPtr].length = length;
    if(uq->rxBuf[uq->rxWrtPtr].packet) free(uq->rxBuf[uq->rxWrtPtr].packet);
    uq->rxBuf[uq->rxWrtPtr].packet = packet;

    uq->rxWrtPtr++;
    uq->rxWrtPtr %= RXBUFSIZE;
    uq->rxCount++;
    return;
}



static uint8_t *popRxQueue(int epnum, uint16_t *length)
{
    USB_QUEUE *uq;
    uint8_t   *packet;
    uq = &(usbQueue[epnum]);

    if(uq->rxCount == 0){ // queue empty
        return NULL;
    }

    *length = uq->rxBuf[uq->rxRdPtr].length;
    packet = uq->rxBuf[uq->rxRdPtr].packet;
    uq->rxBuf[uq->rxRdPtr].packet = NULL;
    uq->rxBuf[uq->rxRdPtr].length = 0;
    
    uq->rxRdPtr++;
    uq->rxRdPtr %= RXBUFSIZE;

    IEC4bits.USBIE = 0;         // disable the USB interrupt
    uq->rxCount--;
    IEC4bits.USBIE = 1;         // enable the USB interrupt

    return packet;   // 呼び出した方で free すること

}

static void resetRxQueue(int epnum)
{
    USB_QUEUE *uq;
    uint8_t   *packet;
    int       n;
    
    uq = &(usbQueue[epnum]);

    for(n=0; n< RXBUFSIZE; n++){
        if(uq->rxBuf[n].packet) free(uq->rxBuf[n].packet);
        uq->rxBuf[n].packet = NULL;
        uq->rxBuf[n].length = 0;
    }

    uq->rxRdPtr = 0;
    uq->rxWrtPtr = 0;
    uq->rxCount = 0;
}

// call back from USB interrupt function
static void call_back_EPN_read(int epnum)
{ 
    uint16_t cnt, size;
    uint8_t *FIFO_buffer;
    uint8_t *packet, *data;
    uint8_t tmp;
    
    cnt =0;
    size =0;
    // received packet!
    USB_EPN_RX_IF &= ~(1<<epnum);                                  // clear RXIF
    // data read from FIFO
    FIFO_buffer = (uint8_t *)((uint32_t)&USBFIFO0 + epnum * 4);    // FIFO_buffer = USBFIFON ;
    size = *((int32_t *)&USBE0CSR2 + epnum * 4) & 0x3FF;           //size = USBIENCSR2bits.RXCNT;

    if(size){ 
        packet = data = malloc(size);   // allocate buffer
        if(packet){
            if((size & 3)==0){
                for(cnt = 0; cnt <size/4; cnt++){
                    *((uint32_t *)data) = *((uint32_t *)FIFO_buffer);
                    data += 4;
                }
            }else{
                for(cnt = 0; cnt < size; cnt++){
                    *data++ = *(FIFO_buffer + (cnt & 3));
                }
            }
            // packet queueing
            pushRxQueue(epnum, packet, size); 
        }else{
            for(cnt = 0; cnt < size; cnt++){
                tmp= *(FIFO_buffer + (cnt & 3));
            }
        }            
    }   

    // complete receive
    *((int32_t *)&USBE1CSR1 + (epnum-1) * 4) &= ~(0x10000);        // USBIENCSR1bits.RXPKTRDY = 0;

    // next request
    *((int32_t *)&USBE0CSR0 + epnum * 4) &= ~(0x20000000);         // USBIENCSR0bits.MODE   = 0;
    *((int32_t *)&USBE1CSR1 + (epnum-1) * 4) |= 0x200000;          // USBIENCSR1bits.REQPKT = 1; IN transaction start

}


/*------------------------------------------------------------------------------
    epn rx interrupt wait 
------------------------------------------------------------------------------*/

static int epn_rx_interrupt_wait(uint8_t epnum)
{
    int32_t tid;
    
    tid = timer_entry(USB_EPN_TIMEOUT);
    while(timer_check(tid)){
        if(USB_EPN_RX_IF & (1<<epnum)){
            timer_delete(tid);
            return 0;
        }
    }
    timer_delete(tid);
    return -1; // timeout
}


/*==============================================================================
     DATA OUT transaction

     port: 0 or 1 〜 HUBMAXPORT   ==0  direct connect device
                                  >=1  hub port connected device
     addr  0 or 1 〜 HUBMAXPORT+1 ==0  before enumeration,
                                  ==1  direct connect device(HUB or Other)
                                  >=2  hub port connected device
==============================================================================*/

static int  usb_ep0_write(uint8_t port, uint8_t addr,  uint8_t epnum, uint8_t *data, uint16_t data_size)
{
    uint16_t cnt, size, pcktsz;
    uint32_t val;
    uint8_t  *FIFO_buffer;

    // DATA(OUT) transaction
    cnt =0;
    size =0;
    pcktsz = packet_size[port][0]; 
    USBE0CSR0bits.TXMAXP = pcktsz;
    USBE0CSR2bits.SPEED = usbDevice[port].Speed;

    if(port){ // HUB connect device
        USBE0TXAbits.TXHUBADD = 1;
        USBE0TXAbits.TXHUBPRT = port;
    }else{
        USBE0TXAbits.TXHUBADD = 0;
        USBE0TXAbits.TXHUBPRT = 0;
    }
    USBE0TXAbits.TXFADDR = addr;    

    while(data_size > 0){
        size = (data_size > pcktsz) ? pcktsz: data_size;
        FIFO_buffer = (uint8_t *)&USBFIFO0;
        for(cnt=0; cnt<size; cnt++){
            *FIFO_buffer = *data++;
        }
        USBE0CSR0bits.TXRDY = 1;
        if(txrdy_wait(0) == -1){
            return -1;
        }
        data_size -= size;
    }
    return 0;
}

static int  usb_write(uint8_t port, uint8_t addr,  uint8_t epnum, uint8_t *data, uint16_t data_size)
{
    uint16_t cnt, size, pcktsz, len;
    uint32_t val;
    uint8_t  *FIFO_buffer;

    // DATA(OUT) transaction
    cnt =0;
    size =0;

    pcktsz = packet_size[port][epnum]; 

    *((int32_t *)&USBE0CSR0 + epnum * 4) |= 0x20000000;         // USBIENCSR0bits.MODE = 1;

    len = data_size;
    while(data_size > 0){
        size = (data_size > pcktsz) ? pcktsz: data_size;
        FIFO_buffer = (uint8_t *)((uint32_t)&USBFIFO0 + epnum * 4);  // FIFO_buffer = (uint8_t *)USBFIFON;
        for(cnt=0; cnt<size; cnt++){
            *FIFO_buffer = *data++;
        }
        *((int32_t *)&USBE0CSR0 + epnum * 4) |= 0x10000;            // USBIENCSR0bits.TXPKTRDY = 1;
        if(txrdy_wait(epnum) == -1) return -1;
        data_size -= size;
    }
    return len;

}


/*------------------------------------------------------------------------------
    tx complete wait 
------------------------------------------------------------------------------*/

static int txrdy_wait(uint8_t epnum)
{
    int32_t tid;
    uint32_t *reg;
    
    reg =((int32_t *)&USBE0CSR0 + epnum * 4);


    tid = timer_entry(USB_EP_TIMEOUT);
    while(timer_check(tid)){
        if(epnum ==0){
            if(USBE0CSR0bits.TXRDY ==0)break;
        }else{
            if(!(*reg & 0x10000)) break;  //if(USBIENCSR0bits.TXPKTRDY ==0)break;
        }
    }
    timer_delete(tid);
    if(epnum ==0){
        if(USBE0CSR0bits.TXRDY ==0) return 0;
    }else{
        if(!(*reg & 0x10000)) return 0;   //if(USBIENCSR0bits.TXPKTRDY ==0)return 0;
    }
    return -1; // timeout
}


/*==============================================================================
    set port device endpoint 
==============================================================================*/

static void set_port_device_endpoint(uint8_t port, uint8_t interfaceNumber, uint8_t intFlag)
{
    USB_DEVICE  *ud;
    uint32_t    val;
    uint8_t     hep, backup;

    printf("set_port_device_endpoint()\n");
    printf("  port=%d,interfaceNumber=%d,intFlag=%d\n", port, interfaceNumber, intFlag);
    
    ud = &(usbDevice[port]);

    hep         = ud->interface[interfaceNumber].HostEpNum;
    INEpNum     = ud->interface[interfaceNumber].INEpNum;
    INEpSize    = ud->interface[interfaceNumber].INEpSize;
    INEpProto   = ud->interface[interfaceNumber].INEpProto;
    INEpIntrv   = ud->interface[interfaceNumber].INinterval;

    OUTEpNum    = ud->interface[interfaceNumber].OUTEpNum;
    OUTEpSize   = ud->interface[interfaceNumber].OUTEpSize;
    OUTEpProto  = ud->interface[interfaceNumber].OUTEpProto;
    OUTEpIntrv  = ud->interface[interfaceNumber].OUTinterval;

    backup = USBCSR3bits.ENDPOINT;
    USBCSR3bits.ENDPOINT = hep;
        
    if(port){ // HUB connect device
        val = (port << 24)|(1 << 16)|ud->Address;
    }else{
       val = ud->Address;
    }

    printf("  hostep=%d, TargetAddress=%08X\n", hep, val);    
    
    // *** RX (IN) ***
    if(INEpNum){
        // packet size
        packet_size[port][hep] = INEpSize;

        //if(INEpSize <= 512){
        //    USBOTGbits.RXFIFOSZ = 0b0110; // max 512byte
        //    USBFIFOAbits.RXFIFOAD = 1024 * INEpNum; 
        //    USBIENCSR3bits.RXFIFOSZ = 0b1001; // 512byte
        //}else{  // for high speed UVC isocronus transfer
        //    USBOTGbits.RXFIFOSZ = 0b1001; // max 4096byte
        //    USBFIFOAbits.RXFIFOAD = 1024 * INEpNum;     
        //    USBIENCSR3bits.RXFIFOSZ = 0b1100; // 4096byte
        //}
        
        USBOTGbits.RXFIFOSZ = 0b0110; // max 512byte
        USBFIFOAbits.RXFIFOAD = 1024 * INEpNum; 
        USBIENCSR3bits.RXFIFOSZ = 0b1001; // 512byte
        USBIENCSR1bits.RXMAXP = INEpSize;
        USBIENCSR1bits.CLRDT = 1;
        //USBIENCSR1bits.FLUSH = 1;  // fifo flush and reset before RDY bit
        USBIENCSR3bits.SPEED = ud->Speed;
        USBIENCSR3bits.PROTOCOL = INEpProto;
        USBIENCSR3bits.RXINTERV = INEpIntrv;
        USBIENCSR3bits.TEP = INEpNum; // RX endpoints numbers
        *((int32_t *)&USBE0RXA + (hep)*2) = val;

        if(intFlag){
            // 自動受信の開始
            // start receive  auto request
            USB_EPN_RX_IE |= (1<<hep);
            IEC4bits.USBIE = 0; 
            resetRxQueue(hep);
            IEC4bits.USBIE = 1;
            *((int32_t *)&USBE0CSR0 + hep * 4) &= ~(0x20000000);    // USBIENCSR0bits.MODE   = 0;
            *((int32_t *)&USBE1CSR1 + (hep-1) * 4) |= 0x200000;     // USBIENCSR1bits.REQPKT = 1; IN transaction start
        }else{
            USB_EPN_RX_IE &= ~(1<<hep);
        }
    }

    // *** TX (OUT) ***
    if(OUTEpNum){
        // packet size
        packet_size[port][hep] = OUTEpSize;

        USBCSR3bits.ENDPOINT = hep;
        USBOTGbits.TXFIFOSZ = 0b0110; // max 512byte
        USBFIFOAbits.TXFIFOAD = 1024 * OUTEpNum;
        USBIENCSR3bits.TXFIFOSZ = 0b1001; // 512byte
        USBIENCSR0bits.TXMAXP = OUTEpSize;
        USBIENCSR0bits.CLRDT = 1;
        USBIENCSR2bits.SPEED = ud->Speed;
        USBIENCSR2bits.PROTOCOL = OUTEpProto;
        USBIENCSR2bits.TXINTERV = OUTEpIntrv;
        USBIENCSR2bits.TEP = OUTEpNum;    //   TX endpoints numbers
        *((int32_t *)&USBE0TXA + (hep)*2) = val;
    }

    USBCSR3bits.ENDPOINT = backup;
}


/*==============================================================================
     USB HOST initialize
==============================================================================*/

void usb_host_init(void)
{
    // disable USB interrupt
   IEC4bits.USBIE = 0;         // Disable the USB interrupt    
   IFS4bits.USBIF = 0;         // Clear the USB interrupt flag.

    // clear usbDevice[]
    memset(packet_size, 0, sizeof(packet_size));
    memset(usbDevice, 0, sizeof(usbDevice));
    memset(usedHostEp, 0, sizeof(usedHostEp));
  
    // clear Queue Buffer
    resetRxQueue(1);    // Host Endpoint #1
    resetRxQueue(2);    //               #2
    resetRxQueue(3);    //               #3
    resetRxQueue(4);    //               #4
    resetRxQueue(5);    //               #5
    resetRxQueue(6);    //               #6
    resetRxQueue(7);    //               #7
    
    // clear driver info
    usb_driver_init();
    
    cnctDeviceNums = 0;
    usbDeviceAttach =0;
    hubAttached     =0;
    hubPortAttached =0;
    hubPortDetached =0;
    hubPortChanged  =0;
    hubPortNumber   =0;

    usedHostEp[0] = 1;   // EP0 is used always.
    
    // clear interrupt flag
    RST_IF =0;
    USB_EPN_RX_IF = 0;
    USB_EPN_RX_IE = 0;
    USB_EPN_TX_IF = 0;

    USBEOFRSTbits.NRSTX = 1;
    USBEOFRSTbits.NRST = 1;
    delay_ms(100);
    USBEOFRSTbits.NRSTX = 0;
    USBEOFRSTbits.NRST = 0;

    // reset USB Register     
    USBCRCON =0;
    USBOTG = 0;
    USBCSR0 = 0;
    USBCSR1 = 0;
    USBCSR2 = 0;
    
    // USB Register bit set
    USBCRCONbits.USBIE = 1;       // USB General interrupt Enable
    USBCRCONbits.USBIDOVEN = 1;   // USB ID Override enable
    USBCRCONbits.USBIDVAL = 0;    // USB ID Override value is 0
    USBCRCONbits.PHYIDEN = 1;     // PHY ID Monitoring enable


    USBCSR0bits.HSEN = 1;        // High-Speed negotiate enable
    //USBCSR0bits.HSEN = 0;      // don't High-Speed negotiate (Full speed mode)

    // usb interrupt enable   
    USBCSR1bits.EP0IE = 1;      // Endpoint 0 interrupt enable
    USBCSR1bits.EP7TXIE = 1;    // Endpoint 1-7 Transmit interrupt enable
    USBCSR1bits.EP6TXIE = 1;
    USBCSR1bits.EP5TXIE = 1;
    USBCSR1bits.EP4TXIE = 1;
    USBCSR1bits.EP3TXIE = 1;
    USBCSR1bits.EP2TXIE = 1;
    USBCSR1bits.EP1TXIE = 1;
    
    USBCSR2bits.VBUSERRIE = 1;  // Vbus Erroe interrupt enable
    USBCSR2bits.SESSRQIE = 0;   // Session Request interrupt enable
    USBCSR2bits.DISCONIE = 1;   // Device Disconnected interrupt enable
    USBCSR2bits.CONNIE = 1;     // Device Connection interrupt enable
    USBCSR2bits.SOFIE = 0;      // Start of Frame interrupt enable
    USBCSR2bits.RESETIE = 1;    // Reset/Babble interrupt enable
    USBCSR2bits.EP7RXIE = 1;    // Endpoint 1-7 Receive interrupt enable
    USBCSR2bits.EP6RXIE = 1;
    USBCSR2bits.EP5RXIE = 1;
    USBCSR2bits.EP4RXIE = 1;
    USBCSR2bits.EP3RXIE = 1;
    USBCSR2bits.EP2RXIE = 1;
    USBCSR2bits.EP1RXIE = 1;
    // set Endpoint
    USBCSR3bits.ENDPOINT = 0;     // Endpoint 0 Register select

    USBE0CSR2bits.NAKLIM = 3;

    USBOTGbits.HOSTMODE = 1;      // USB module is acting as a HOST mode
    USBOTGbits.BDEV = 0;          // USB is operationg as an 'A' device(HOST)
    
    // system usb interrupt disable
    IEC4bits.USBIE = 0;         // Disable the USB interrupt    
    IFS4bits.USBIF = 0;         // Clear the USB interrupt flag.
    IPC33bits.USBIP = 5;        // USB Interrupt Priority 5
    IPC33bits.USBIS = 1;        // USB Interrupt Sub-Priority 1
    IPC33bits.USBDMAIP = 5;
    IPC33bits.USBDMAIS = 1;
    IFS4bits.USBDMAIF = 0;
    IEC4bits.USBDMAIE = 0;      // DMA not use
    IFS4bits.USBIF = 0;         // Clear the USB interrupt flag.
    IEC4bits.USBIE = 1;         // Enable the USB interrupt

    USBOTGbits.SESSION = 1;     // Start a session

}


/*==============================================================================
        USB interrupt 
==============================================================================*/

void __attribute__ (( interrupt(IPL5SRS), vector(_USB_VECTOR) )) _UsbInterrupt(void)
{
    unsigned char EP0IF;
    unsigned char CONNIF, DISCONIF,VBUSIF;
    unsigned int CSR0, CSR1, CSR2;
    int      epn;

    CSR0 = USBCSR0;
    EP0IF = (CSR0 & (1<<16)) ? 1 : 0;   
    USB_EPN_TX_IF |= ((CSR0 >> 16)&0xFE);

    CSR1 = USBCSR1;
    USB_EPN_RX_IF |= (CSR1 & 0xFE);

    CSR2 = USBCSR2;
    CONNIF = (CSR2 & (1 << 20)) ? 1 : 0;
    DISCONIF = (CSR2 & (1 << 21)) ? 1: 0;
    SOF_IF   |= CSR2 & (1<<19) ? 1:0;
    RST_IF  |= CSR2 & (1<<18) ? 1:0;
    VBUSIF = CSR2 & (1<<23) ? 1:0;

    //UART2PutHex((CSR2>>16)&0xFF);
    //UART2PutHex(USBOTGbits.VBUS);

    // VBUS Voltage error, Recovery HOST reinitialize
    if(VBUSIF) usb_host_init();

    // DICONNECT or Babble Error to DETACHED state    
    if(DISCONIF || RST_IF) usbDeviceAttach = 0;

    // CONNECT to ATTACHED state
    if(CONNIF) usbDeviceAttach = 1;
    
    if(EP0IF) USB_EP0_IF = 1; 

    //UART2PutHex(USB_EPN_RX_IF);

    // interrupt Call Back 
    for(epn=1; epn<8; epn++){
        if( (USB_EPN_RX_IF & (1<<epn)) && (USB_EPN_RX_IE & (1<<epn)) ){
            call_back_EPN_read(epn);
        }
    }
    
    IFS4bits.USBIF = 0;             // Clear the main interrupt flag!
}


//******************************************************************************
//     HUB Driver  for PIC32 USB module
//
//     2019 Suwa-Koubou 
//
//     2019.3     first release for PIC32MX OTG USB module
//     2025.7     update for PIC32MZ HS OTG USB module
//******************************************************************************

// HUB descriptor
static uint8_t          HubDescriptor[8];       // Hub Descriptor

#define	HubNumberPorts	(HubDescriptor[2])      // 
#define HubPowerOnGood	(HubDescriptor[5]*2)	// not use


/*------------------------------------------------------------------------------
	USB HUB hardware initialize
    include HUB enumeration 
       SET_ADDRESS, SET_CONFIGURATION, GET_CONFIGURATIO
       GET_HUBDESCRIPTOR, SET_PORT_FEATURE(PORT_POWER_ON)
       SET_INTERFACE(select MTT interface)
------------------------------------------------------------------------------*/

static void hub_initialize(void)
{
    int    n;
    uint8_t *p;
    int   numendpoints, found_ep;
    int   port;
    uint8_t Class, SubClass, Protocol;
    uint8_t interfaceNumber, alternateNumber;
    uint16_t confLength;
    
    printf("HUB: initialize.\n");
    printf("HUB: set HUB device address.\n");
    usbHubAddress = usbDevice[0].Address; // always Address = 1

    // SET_ADDRESS
    // コネクタ接続は port に 0 を設定
    // For connector connection, set port to 0.
    if(set_device_address(0, usbHubAddress)==-1){
        printf("Fail, HUB SET_ADDRESS.\n");
        return;
    }

    printf("HUB: set HUB configuration.\n");
    // SET_CONFIGURATION
    if(set_device_configuration(0, usbHubAddress)==-1){
        printf("Fail, HUB SET_CONFIGURATION.\n");
        return;
    }

    printf("HUB: get HUB configuraion.\n");
    // GET_CONFIGURATION, GET_ENDPOINT
    if((p=get_device_configuration(0, usbHubAddress)) == NULL) return;
    
    n =0;
    confLength = (p[3]<<8)|p[2];
    found_ep = 0;

    while(n < confLength){
        // interface descriptor
        while(p[n+1] != 4){
            n+=p[n];
        }

        interfaceNumber=p[n+2];
        alternateNumber=p[n+3];
        numendpoints=p[n+4];
        Class = p[n+5];
        SubClass = p[n+6];
        Protocol = p[n+7];
        found_ep=0;
        printf("Class=%d,SubClass=%d,Protocol=%d\n", Class, SubClass, Protocol);
        while(numendpoints--){
            // endpoint descriptor
            while(p[n+1] != 5){
                n += p[n];
            }
            if(p[n+2] & 0x80){ // IN ?
                usbHubINEpNum = p[n+2] & 0x7f;
                usbHubINEpSize = (p[n+5]<<8)|p[n+4];
                usbHubINEpProto = p[n+3];
                usbHubINinterval = p[n+6];
                found_ep=1;
                break;
            }
            n+=p[n];
        }
        n+=p[n];
    }
    
    if(found_ep==0){
        printf("Fail, not found HUB EndPoint.\n");
        return;
    }
    printf("INEpNum=%d, INEpSize=%d, INEpProto=%d, INinterval=%d\n", 
              usbHubINEpNum, usbHubINEpSize, usbHubINEpProto, usbHubINinterval);

    printf("HUB: get HUB descriptor.\n");

    // GET_HUB_DESCRIPTOR
    if(usb_ctrl(0, usbHubAddress, (uint8_t *)GET_HUB_DESCRIPTOR, HubDescriptor)==-1){
        printf("Fail, GET_HUB_DESCRIPTOR.\n");
        return; 
    }
    printf("HUB: HubNumberPorts=%d[ports]\n", HubNumberPorts);
    printf("HUB: HubPowerOnGoodTime=%d[ms]\n", HubPowerOnGood);
    printf("HUB: set HUB Port power on.\n");

    // SET_PORT_FEATURE(PORT_POWER) all port power on
    for(port=1; port <= HubNumberPorts; port++){
       SET_HUB_FEATURE[2]=PORT_POWER;
       SET_HUB_FEATURE[3]=0;
       SET_HUB_FEATURE[4]=port;
       SET_HUB_FEATURE[5]=0;
       if(usb_ctrl(0, usbHubAddress, (uint8_t *)SET_HUB_FEATURE, usbRecBuff)==-1){
           printf("Fail, SET_HUB_FEATURE(port power on).\n");
           return; 
       }
       if(HubPowerOnGood) delay_ms(HubPowerOnGood);
    }

   
    // select Interface HighSpeed MTT
    if(Class==9 && SubClass==0 && Protocol==2 && alternateNumber !=0){
        printf("HUB: SET_INTERFACE(MTT)\n");
        SET_INTERFACE[2] = interfaceNumber;
        SET_INTERFACE[3] = 0;
        SET_INTERFACE[4] = alternateNumber;
        SET_INTERFACE[5] = 0;
        if(usb_ctrl(0, usbHubAddress, (uint8_t *)SET_HUB_FEATURE, usbRecBuff)==-1){
            printf("Fail, SET_INTERFACE(Class=9,SubClass=0,Protocol=2,interface=%d,alternate=%d).\n", interfaceNumber, alternateNumber);
            return; 
        }
    }

    // initialize end
    printf("HUB: complete HUB initialize.\n");

    usbDevice[0].Ready = 1;
    usbDevice[0].Attached = 1;
    usbDevice[0].interfaceNums = 1;
    usbDevice[0].interface[0].HostEpNum = 1;
    usedHostEp[1] = 1;
    usbDevice[0].interface[0].INEpNum = usbHubINEpNum;
    usbDevice[0].interface[0].INEpSize = usbHubINEpSize;
    usbDevice[0].interface[0].INEpProto = usbHubINEpProto;
    usbDevice[0].interface[0].INinterval = usbHubINinterval;

    set_port_device_endpoint(0, 0, 1);  // port 0, interface 0, auto receive 1

    hubPortNumber=0;
    hubPortChanged = PORT_DEVICE_NONE;
    hubAttached = 1;   // USB HUB attached flg (1=HUB attached 0=not HUB)
}


/*------------------------------------------------------------------------------
	USB HUB functions
------------------------------------------------------------------------------*/

static int hub_event_read(uint8_t *event)
{
    uint16_t  size;
    uint8_t   *packet;

    packet = popRxQueue(usbDevice[0].interface[0].HostEpNum, &size);
    if(packet == NULL) return 0;  // length 0

    memcpy(event, packet, size); // data copy from queue
    free(packet);

    return size;
}

static void hub_loop(void)
{
    uint8_t   HubEvent[2]; // limit 16 port HUB
    int       size, port, portMask;

    // ****************************************************
    //    Get Hub Port Event
    // ****************************************************

//    size = usb_read(0, usbHubAddress, usbHubINEpNum, HubEvent, 0);
    size = hub_event_read(HubEvent);
    if(size == -1){
        printf("Fail Get Hub Port Event.\n");
        return;
    }else if(size==0){
        //printf("size=0\n");
       return;
    }

	// check change port
    printf("HubEvent=0x%02X\n", HubEvent[0]);

    for(port = 1; port <= HubNumberPorts; port++){
        portMask = (1 << (port % 8));
        if(HubEvent[port/8] & portMask){
            port_loop(port);
            if(hubPortChanged != PORT_DEVICE_NONE) break;
        }
    }
    return;
}


static void port_loop(int port)
{
    unsigned short PortStatus[2];
    static int     port_resetting;
    int n;

    printf("HUB: port change event! port=%d\n", port);

    // ***************************************************
    //    Get Hub Port Staus
    // ***************************************************
 
    GET_HUB_STATUS[4]=port;
    GET_HUB_STATUS[5]=0;

    if(usb_ctrl(0, usbHubAddress, (uint8_t *)GET_HUB_STATUS, (uint8_t *)&PortStatus[0])==-1){
       printf("HUB: Fail, GET_HUB_STATUS(get port status).\n");
       return;
    }
    printf("PortStatus[0]=0x%04X\n", PortStatus[0]);
    printf("PortStatus[1]=0x%04X\n", PortStatus[1]);

    // ***************************************************
    //    Check and Clear changed bits
    // ***************************************************

    if(PortStatus[1] & C_PORT_CONNECTION_BIT){
        printf("HUB: Changed PORT_CONNECTION\n"); 

        if(!(PortStatus[1] & C_PORT_ENABLE_BIT)){ // connect insert
            //if(port_resetting == 1){
            if(port_resetting && (port_resetting != port)){   // 2019.5.1
                printf("wait clearHubPortbit, because other port resetting now.\n");
                return; // wait other port complete attached
            }
        }

        if(port_resetting == port){ // 2019.5.1
            printf("retry port connection bit clear.\n");
        }

        if(clearHubPortBit(port, C_PORT_CONNECTION)==-1) return;
    }

    if(PortStatus[1] & C_PORT_ENABLE_BIT){
        printf("HUB: Changed PORT_ENABLE\n"); 
        if(clearHubPortBit(port, C_PORT_ENABLE)==-1) return;
    }

    if(PortStatus[1] & C_PORT_SUSPEND_BIT){
        printf("HUB: Changed PORT_SUSPEND\n"); 
        if(clearHubPortBit(port, C_PORT_SUSPEND)==-1) return;
    }

    if(PortStatus[1] & C_PORT_OVER_CURRENT_BIT){
        printf("HUB: Changed PORT_OVER_CURRENT\n"); 
        if(clearHubPortBit(port, C_PORT_OVER_CURRENT)==-1) return;
    }

    if(PortStatus[1] & C_PORT_RESET_BIT) {
        printf("HUB: Changed PORT_RESET\n"); 
        if(clearHubPortBit(port, C_PORT_RESET)==-1) return;
    }

    // *************************************************************************
    //   Check and Port Status Bits
    // *************************************************************************

    //case HUB_PORT_STATUS_CHECK:

    // CHANGE PORT CONNECT
    if(PortStatus[1] & C_PORT_CONNECTION_BIT){
        if(!(PortStatus[0] & PORT_CONNECTION_BIT)){
            printf("HUB: port Device detached\n"); 
            usbDevice[port].Ready = 0;      // not enumeration
            usbDevice[port].Attached =0;
            hubPortNumber=port;
            hubPortChanged = PORT_DEVICE_DETACHED;
            return;
        }

        if((PortStatus[0] & (PORT_CONNECTION_BIT|PORT_ENABLE_BIT)) ==(PORT_CONNECTION_BIT|PORT_ENABLE_BIT)){ 
            printf("HUB: port Device attached(port connection)\n"); 
            usbDevice[port].Address=port+1;
            usbDevice[port].Ready = 0;      // not enumeration
            if(PortStatus[0] & PORT_LOW_SPEED_BIT){
                usbDevice[port].Speed = LOW_SPEED;
                packet_size[port][0] = LOW_PACKET_SIZE;
            }else if(PortStatus[0] & PORT_HIGH_SPEED_BIT){
                usbDevice[port].Speed = HIGH_SPEED;
                packet_size[port][0] = FULL_PACKET_SIZE;
            }else{
                usbDevice[port].Speed = FULL_SPEED;
                packet_size[port][0] = FULL_PACKET_SIZE;
            }
            hubPortNumber=port;
            hubPortChanged = PORT_DEVICE_ATTACHED;
            return;
        }

        if(PortStatus[0] & PORT_CONNECTION_BIT){
            printf("HUB: port Device connected\n");

            // 2019.5.1
            if(port_resetting == port){
                printf("retry PORT RESET.\n");

            }else if(port_resetting == 0){
                port_resetting=port;

            }else if(port_resetting != port){
                printf("skip PORT RESET, now resetting other port.\n");
                return;
            }

            //  デバイスをポートから取り外した時、誤ってリセット処理が動く場合
            // がある。この場合には正常にデバイスの取り外しを検出できない不具合
            // が発生する。そのため、ポートのリセット処理の前にはデバイスを強制
            // 的にデタッチ状態に初期化しておく。
            //　デバイス接続時のリセットであれば、この後atached状態に移行する。
            // デバイス取り外し時のミスリセットであれば、この後何も起こらない
            // がデバイスをデタッチ状態に移行させることが出来る。
            //
            //   When a device is removed from a port, the reset process may
            // be performed by mistake. In this case, a problem occurs 
            // in which the removal of the device cannot be detected correctly.
            //   For this reason, the device is forcibly initialized to a 
            // detached state before the port reset process.
            //   If the reset occurs when the device is connected, the device
            // will then move to the attached state. If the reset is an 
            // accidental reset when the device is removed, nothing will happen
            // after this, but the device can be moved to the detached state.

            if(usbDevice[port].Ready == 1){  // from detache state
                usbDevice[port].Ready = 0;
                usbDevice[port].Attached =0;
                hubPortNumber=port;
                hubPortChanged = PORT_DEVICE_DETACHED;
                port_resetting=0;
            }
            
            SET_HUB_FEATURE[2]=PORT_RESET;
            SET_HUB_FEATURE[3]=0;
            SET_HUB_FEATURE[4]=port;
            SET_HUB_FEATURE[5]=0;
            if(usb_ctrl(0, usbHubAddress, (uint8_t *)SET_HUB_FEATURE, usbRecBuff)==-1){
                printf("HUB: Fail, SET_HUB_FEATURE(PORT_RESET). port=%d\n",port);
                return;
            }
            printf("port=%d, PORT_RESET.\n", port);
            return;
       }

    }


    // CHANGE PORT RESET
    if(PortStatus[1] & C_PORT_RESET_BIT){
	    if((PortStatus[0] & (PORT_CONNECTION_BIT|PORT_ENABLE_BIT)) ==(PORT_CONNECTION_BIT|PORT_ENABLE_BIT)){
            printf("HUB: port Device attached(port reset)\n"); 
            usbDevice[port].Address=port+1;
            usbDevice[port].Ready = 0;      // not enumeration
            if(PortStatus[0] & PORT_LOW_SPEED_BIT){
                usbDevice[port].Speed = LOW_SPEED;
                packet_size[port][0] = LOW_PACKET_SIZE;
            }else if(PortStatus[0] & PORT_HIGH_SPEED_BIT){
                usbDevice[port].Speed = HIGH_SPEED;
                packet_size[port][0] = FULL_PACKET_SIZE;
            }else{
                usbDevice[port].Speed = FULL_SPEED;
                packet_size[port][0] = FULL_PACKET_SIZE;
            }
            hubPortNumber=port;
            hubPortChanged = PORT_DEVICE_ATTACHED;
            port_resetting=0;
            return;
        }
    }

    // CHANGE PORT OVER CURRENT
    if(PortStatus[1] & C_PORT_OVER_CURRENT_BIT){
        // to Power Down        
        printf("HUB: port Device over current, Device detached.\n"); 
        usbDevice[port].Ready = 0;      // not enumeration
        usbDevice[port].Attached =0;
        hubPortNumber=port;
        hubPortChanged = PORT_DEVICE_DETACHED;
        return;
    }
    
    // CHANGE PORT SUSPEND
    //  not support, do nothing
    return;

}


static int clearHubPortBit(int port, int selector)
{
    int n;

    SET_CLEAR_PORT_FEATURE[2]= selector;
    SET_CLEAR_PORT_FEATURE[3]= 0;
    SET_CLEAR_PORT_FEATURE[4]= port;
    SET_CLEAR_PORT_FEATURE[5]= 0;

    if(usb_ctrl(0, usbHubAddress, (uint8_t *)SET_CLEAR_PORT_FEATURE, usbRecBuff)==-1){
       printf("HUB: Fail, SET_CLEAR_PORT_FEATURE. port=%d, selector=%d\n", port, selector);
       return -1;
    }
    return 0;
}


/*** end of file **************************************************************/
