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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "uart2.h"
#include "timer.h"
#include "usbHost.h"
#include "debug.h"

/*------------------------------------------------------------------------------
      PIC32MX2048EFH100 cpu configuration
 -----------------------------------------------------------------------------*/

#include "pic32mz_config.h"


/*------------------------------------------------------------------------------
      definition
 -----------------------------------------------------------------------------*/

#define   led_on()           LATGbits.LATG6=0
#define   led_off()          LATGbits.LATG6=1



/*------------------------------------------------------------------------------
     prototype
 -----------------------------------------------------------------------------*/

int  main(void);
void test_loop(void);

// test USB Device Read and Write
void test_mouse(void);
void test_keyboard(void);
void ctrl_keyboard_led(void);
void test_midi(void);
void test_serial(void);
static void current_serial_parameter(int dev_no);
static void set_serial_parameter(int dev_no);
static void communication_test(int dev_no);
void test_game_controller(void);
void test_usb_memory(void);
void test_usb_camera(void);
int sector_read(int dev_no, uint32_t sector);

// test Fat File System on the USB Memory
extern void test_fatfile(void);

// system initialize
void InitializeSystem (void);

/*==============================================================================
     main()  
==============================================================================*/

int  main(void)
{  
    InitializeSystem();
	timer_init();
    UART2Init();
    usb_host_init();
    for(;;){
        test_loop();
    }
    return 0;
}


/*==============================================================================
     USB HOST test loop
 =============================================================================*/

void test_loop(void)
{
    int c;

 
    for(;;){
        printf("\n**** PIC32MZ High Speed USB Host Module Evaluation   *****\n");
        printf("select evaluation USB Device\n");
        printf("1: mouse\n");
        printf("2: keyboard\n");
        printf("3: midi\n");
        printf("4: serial\n");
        printf("5: game controller(HID generic)\n");
        printf("6: usb memory\n");
        //printf("7: usb camera\n");
        printf("==>");
        while(inkey()==0){
            if(isUsbConnect()) led_on();
            else led_off();
        }
        c = getch();
        printf("%c\n", c);
        if(c >= '1' && c <= '6') break;
    }

    switch(c){
        case '1':
            test_mouse();
            break;
        case '2':
            test_keyboard();
            break;
        case '3':
            test_midi();
            break;
        case '4':
            test_serial();
            break;
        case '5':
            test_game_controller();
            break;
        case '6':
            test_usb_memory();
            break;
        //case '7':
        //    test_usb_camera();
        //    break;
    }

    printf("*** test done.\n\n");
    return;
}


/*==============================================================================
     MOUSE test
     Reads and displays data from a USB mouse.
  Press the 'm' or 'M' key to change the mouse protocol.
  (Toggles between Boot and Report protocols.) 
==============================================================================*/

void test_mouse(void)
{
    int     size, n;
    int     devices;
    int     dev_no;
    char    buf[10];
    uint8_t data[64];
    int     c; 
    uint8_t prtcl;


    if(get_nums_mouse_device() ==0){    
        printf("Wait for MOUSE connect.\n");
        printf("Hit ctrl+C to test cancel.\n");
    }
    
    while((devices = get_nums_mouse_device())==0){
        if(isUsbConnect()) led_on();
        else led_off();
        if(inkey()){
            if(getch() == 0x03) return;
        }
    }

    if(devices > 1){
        for(;;){
            printf("%d MOUSE connects\n", devices);
            printf("which MOUSE (0 - %d) =>", devices -1);
            if(getstr(buf,10) == NULL) return;
            dev_no = strtol((char *)buf, NULL, 10);
            if(dev_no >= 0 && dev_no <= devices-1) break;
        }
    }else{
        printf("1 MOUSE connects\n");
        dev_no = 0;
    }

    prtcl = 0; // 0=Boot Protocol   1=Report Protocol
    while(isUsbConnect()){
        led_on();
        if(inkey()){
            c= getch();
            if(c == 0x03) return;
            if( c=='m' || c=='M'){
                usb_mouse_set_protocol(dev_no, prtcl);
                prtcl ^= 1;
            }
            continue;
        }

        size = usb_mouse_read(dev_no, data, sizeof(data));
        if(size > 0){
            printf("mouse dev%d: ", dev_no);
            for(n=0; n<size; n++){
                printf("%02X ", data[n]);
            }
            printf("\n");
        }
    }
    led_off();
    return;
}


/*==============================================================================
    USB KEYBOARD test
    Reads and displays data from a USB keyboard .
  Each time you press the '1' key on the USB keyboard, the CapsLock, NumLock,
  and ScrollLock LEDs on the keyboard will turn on and off.
 =============================================================================*/

void test_keyboard(void)
{
    int     size, n;
    int     devices;
    int     dev_no;
    char    buf[10];
    int     keypress;
    int     led;
    uint8_t data[64];
    
    if(get_nums_keyboard_device() ==0){    
        printf("Wait for USB Keyboard connect.\n");
        printf("Hit ctrl+C to test cancel.\n");
    }

    while((devices = get_nums_keyboard_device())==0){
        if(isUsbConnect()) led_on();
        else led_off();
        if(inkey()){
            if(getch() == 0x03) return;
        }
    }

    if(devices > 1){
        for(;;){
            printf("%d USB Keyboard connects\n", devices);
            printf("which Keyboard (0 - %d) =>", devices-1);
            if(getstr(buf,10) == NULL) return;
            dev_no = strtol((char *)buf, NULL, 10);
            if(dev_no >= 0 && dev_no <= devices-1) break;
        }
    }else{
        printf("1 Keyboard connects\n");
        dev_no = 0;
    }
    
    led = 0;
    keypress = 0;
    while(isUsbConnect()){
        led_on();
        if(inkey()){
            if(getch() == 0x03) return;
        }

        size = usb_keyboard_read(dev_no, data, sizeof(data));
        if(size > 0){
            printf("keyboard dev%d: ", dev_no);
            for(n=0; n<size; n++){
                printf("%02X ", data[n]);
            }
            printf("\n");

            if(data[2]==0x1E){  // press '1'   
                if(keypress ==0){
                    keypress = 1;
                    led++;
                    led &= 7;
                    usb_keyboard_set_report(dev_no, led);
                }
            }else{
                keypress = 0;
            }
        }
    }
    led_off();
    return;
}


/*==============================================================================
    MIDI test
    This test reads and displays MIDI data from a USB MIDI keyboard.
    To prepare for testing, connect a MIDI keyboard to the USB connector
  or a port on the connected hub.
==============================================================================*/

void test_midi(void)
{
    int     size, n;
    int     devices;
    int     dev_no;
    char    buf[10];
    uint8_t data[64];
    
    if(get_nums_midi_device() ==0){    
        printf("Wait for MIDI device connect.\n");
        printf("Hit ctrl+C to test cancel.\n");
    }
    
    while((devices = get_nums_midi_device())==0){
        if(isUsbConnect()) led_on();
        else led_off();
        if(inkey()){
            if(getch() == 0x03) return;
        }
    }

    if(devices > 1){
        for(;;){
            printf("%d MIDI device connects\n", devices);
            printf("which MIDI device (0 - %d) =>", devices-1);
            if(getstr(buf, 10) == NULL) return;
            dev_no = strtol((char *)buf, NULL, 10);
            if(dev_no >= 0 && dev_no <= devices-1) break;
        }
    }else{
        printf("1 MIDI device connects\n");
        dev_no = 0;
    }

    while(isUsbConnect()){
        led_on();
        if(inkey()){
            if(getch() == 0x03) return;
        }
       size = usb_midi_read(dev_no, data, sizeof(data));
        if(size > 0){
            if((data[0] & 0xf0) > 0x40) continue;
            if(data[0] == 0) continue;
            printf("midi dev%d: ", dev_no);
            for(n=0; n<size; n++){
                if((n%4)==0 && data[n] == 0) break;
                printf("%02X ", data[n]);
            }
            printf("\n");
        }
    }
    led_off();
    return;
}

/*==============================================================================
    GAME CONTROLLER test
    This test reads and displays control data from a USB HID Game Controller.
 =============================================================================*/

void test_game_controller(void)
{
    int     size, n;
    int     devices;
    int     dev_no;
    char    buf[10];
    uint8_t data[64];
    
    if(get_nums_hid_generic_device() ==0){    
    printf("Wait for Game Controller connect.\n");
    printf("Hit ctrl+C to test cancel.\n");
    }

    while((devices = get_nums_hid_generic_device())==0){
        if(isUsbConnect()) led_on();
        else led_off();
        if(inkey()){
            if(getch() == 0x03) return;
        }
    }

    if(devices > 1){
        for(;;){
            printf("%d Game Controller connects\n", devices);
            printf("which Game Controller (0 - %d) =>", devices-1);
            if(getstr(buf, 10) == NULL) return;
            dev_no = strtol((char *)buf, NULL, 10);
            if(dev_no >= 0 && dev_no <= devices-1) break;
        }
    }else{
        printf("1 Game Controller connects\n");
        dev_no = 0;
    }

    while(isUsbConnect()){
        led_on();
        if(inkey()){
            if(getch() == 0x03) return;
        }

        size = usb_hid_generic_read(dev_no, data, sizeof(data));
        if(size > 0){
            printf("Game Controller dev%d: ", dev_no);
            for(n=0; n<size; n++){
                printf("%02X ", data[n]);
            }
            printf("\n");
        }
    }
    led_off();
    return;
}


/*==============================================================================
    Serial test
    This performs a communications test using a USB serial device. 
  The default settings for the serial parameters are 115200bps, 8data bits,
  1stop bit, and no parity.
    In addition to CDC devices, FTDI USB Serial devices can also be used
 =============================================================================*/

void test_serial(void)
{
    int     size, n;
    int     devices;
    int     dev_no;
    char    buf[10];
    uint8_t c;

    if(get_nums_serial_device() ==0){    
        printf("Wait for Serial device connect.\n");
        printf("Hit ctrl+C to test cancel.\n");
    }
    
    while((devices = get_nums_serial_device())==0){
        if(isUsbConnect()) led_on();
        else led_off();
        if(inkey()){
            if(getch() == 0x03) return;
        }
    }

    if(devices > 1){
        for(;;){
            printf("%d serial device connects\n", devices);
            printf("which serial device (0 - %d) =>", devices-1);
            if(getstr(buf, 10) == NULL) return;
            dev_no = strtol((char *)buf, NULL, 10);
            if(dev_no >= 0 && dev_no <= devices-1) break;
        }
    }else{
        printf("1 serial device connects\n");
        dev_no = 0;
    }

    for(;;){
        printf("\n1:Display current serial parameter\n");
        printf("2:Set serial parameter\n");
        printf("3:Communication test\n");
        printf("==>");

        while(!inkey()){
            if(isUsbConnect()==0){
                led_off();
                return;
            }
        }
        c = getch();
        if(c == 0x03)return;
        printf("%c\n", c);

        switch(c - '0'){
            case 1: // Display current serial parameter
                current_serial_parameter(dev_no);
                break;
         
            case 2: // Set serial parameter
                set_serial_parameter(dev_no);
                break;

            case 3: // Communication test
                communication_test(dev_no);
                break;
            default:
                break;
        }
    }
}

static int8_t *stop_bit_str[] = { "1bit", "1.5bit", "2bit", };
static int8_t *parity_bit_str[] = { "none", "odd", "even", "mark", "space", };
static int8_t *flow_ctrl_str[] = { "none", "rtc/cts", "dtr/dsr", "", "xon/xoff", };

static void current_serial_parameter(int dev_no)
{
    int32_t param[5];
    
    if(get_serial_port_parameter(dev_no, param)==-1){
        printf("Fail Get serial port parameter\n");
        return;
    }
    printf("\n*** Current serial parameter ***\n");
    printf("baudrate: %dbps\n", param[0]);
    printf("databit: %dbit\n", param[1]);
    printf("stopbit: %s\n", stop_bit_str[param[2]]);
    printf("parity: %s\n", parity_bit_str[param[3]]);
    printf("flow ctrl: %s\n", flow_ctrl_str[param[4]]);
}

static void set_serial_parameter(int dev_no)
{
    char buf[10];
    int32_t param[5];
    
    printf("\n*** Set serial parameter ***\n");

    for(;;){
        printf("Baudrate (115200/57600/38400/19200/9600/4800/2400) =>");
        if(getstr(buf, 10) == NULL) return;
        param[0] = strtol((char *)buf, NULL, 10);
        if(param[0] == 115200 ||
           param[0] == 57600 ||
           param[0] == 38400 ||
           param[0] == 19200 ||
           param[0] == 9600 ||
           param[0] == 4800 ||
           param[0] == 2400 ) break;
    }      
    for(;;){
        printf("Data Bit (8/7/6/5) =>");
        if(getstr(buf, 10) == NULL) return;
        param[1] = strtol((char *)buf, NULL, 10);
        if(param[1] >= 5 && param[1] <=  8) break;
    }
    for(;;){
        printf("Stop Bit (1/2) =>");
        if(getstr(buf, 10) == NULL) return;
        param[2] = strtol((char *)buf, NULL, 10);
        if(param[2] == 1 || param[2] == 2){
            if(param[2]==1) param[2]=0;
            break;
        }
    }
    for(;;){
        printf("Parity (none=0,odd=1,even=2,mark=3,space=4) =>");
        if(getstr(buf, 10) == NULL) return;
        param[3] = strtol((char *)buf, NULL, 10);
        if(param[3] >= 0 && param[3] <= 4) break;
    }
    for(;;){
        printf("Flow ctrl (none=0,rts/cts=1,dtr/dsr=2,xon/xoff=4) =>");
        if(getstr(buf, 10) == NULL) return;
        param[4] = strtol((char *)buf, NULL, 10);
        if(param[4]!=3 && (param[4] >= 0 && param[4] <= 4)) break;
    }
    
    if(set_serial_port_parameter(dev_no, param)==-1){
        printf("Fail Set serial port parameter\n");
    }
    
}

static void communication_test(int dev_no)
{
    int     size, n;
    uint8_t txbuf[64];
    uint8_t rxbuf[64];
    uint8_t c;
    int     txlen;
      
    txlen = 0;    
    while(isUsbConnect()){
        led_on();
        
        if(inkey()){
            c = getch();
            if(c == 0x03) return;
            txbuf[txlen] = c;
            txlen++;
            if(txlen >=1){
                usb_serial_write(dev_no, txbuf, txlen);
                txlen = 0;
            }
        }


        size = usb_serial_read(dev_no, rxbuf, sizeof(rxbuf));
        if(size >0){
            for(n=0; n<size;n++){
                if(rxbuf[n] >= ' ' && rxbuf[n] <= 0x7f){
                    printf("%c", rxbuf[n]);
                }else if(rxbuf[n] == '\t' || rxbuf[n] == '\n' || rxbuf[n] == '\r' || rxbuf[n] == '\b'){
                    printf("%c", rxbuf[n]);
                }else{
                    printf(".");
                }
            }
        }
    }
    led_off();
    return;
}


/*==============================================================================
     USB Memory test
     This tests the physical reading and writing of data from the specified
   sector of a USB memory. It also tests the reading of directories and files
   using the Fat File System.
 =============================================================================*/

void  test_usb_memory(void)
{
    int      size, n;
    int      devices;
    int      dev_no;
    char     buf[10];
    uint8_t  data[512], dt;
    uint32_t sector;
    int32_t  number;
    uint32_t last_sector;
    int      c;
    int      res;

    if(get_nums_msc_device()==0){
        printf("Wait for USB Memory connect.\n");
        printf("Hit ctrl+C to test cancel.\n");
    }

    while((devices = get_nums_msc_device())==0){
        if(isUsbConnect()) led_on();
        else led_off();
        if(inkey()){
            if(getch() == 0x03) return;
        }
    }

    if(devices > 1){
        for(;;){
            printf("%d USB Memory connects\n", devices);
            printf("which USB Memory (0 - %d) =>", devices-1);
            if(getstr(buf, 10) == NULL) return;
            dev_no = strtol((char *)buf, NULL, 10);
            if(dev_no >= 0 && dev_no <= devices-1) break;
        }
    }else{
        printf("1 USB Memory connects\n");
        dev_no = 0;
    }

    last_sector = 0;

    for(;;){
        printf("\n1:Read Capacity\n");
        printf("2:Sector Read\n");
        printf("3:Sector Write\n");
        printf("4:Fat File System\n");
        printf("==>");

        while(!inkey()){
            if(isUsbConnect()==0){
                led_off();
                return;
            }
        }
        c = getch();
        if(c == 0x03)return;
        printf("%c\n", c);

        switch(c - '0'){
            case 1:  // Read Capacity
                last_sector = usb_msc_capacity_read(dev_no);
                if(last_sector ==0){
                    printf("Fail Read Capacity\n");
                    break;    
                }
                printf("Last Sector Number = 0x%08X(%d)\n", last_sector, last_sector);
                break;

            case 2:  // Read Sector
                if(last_sector ==0) last_sector = usb_msc_capacity_read(dev_no);
                if(last_sector ==0){
                    printf("Fail Read Capacity\n");
                    break;    
                }
                printf("Last Sector Number = %d\n", last_sector);
                printf("Read Start Sector  =>");
                if(getstr(buf, 10)==NULL) return;
                sector = strtol((char *)buf, NULL, 10);
                if(sector > last_sector){
                    printf("illegal Read Start Sector.\n");
                    break;
                }
                printf("number of sectors =>");
                if(getstr(buf, 10)==NULL) return;
                number = strtol((char *)buf, NULL, 10);
                if(number <=0) break;

                while(number--){
                    res = sector_read(dev_no, sector);
                    if(res ==-2){
                        printf("MSC Device Unready\n");
                        break;
                    }else if(res <= 0){
                        printf("Fail Read Sector\n");
                        break;
                    }
                    sector ++;
                }
                break;

            case 3: // Write Sector
                if(last_sector ==0) last_sector = usb_msc_capacity_read(dev_no);
                if(last_sector ==0){
                    printf("Fail Read Capacity\n");
                    break;    
                }
                printf("Last Sector Number = %d\n", last_sector);
                printf("Write Sector =>");
                if(getstr(buf, 10)==NULL) return;
                sector = strtol((char *)buf, NULL, 10);
                if(sector > last_sector){
                    printf("illegal Write Sector.\n");
                    break;
                }

                printf("Write Data\n");
                memset(data, 0, sizeof(data));
                for(n=0;n<512;n++){
                    printf("=>0x");
                    if(getstr(buf, 10)==NULL) return;
                    if(buf[0]==0) break;
                    dt = strtol((char *)buf, NULL, 16);
                    data[n] = dt & 0xff;
                }
                n=0;
                do {
                    res = usb_msc_sector_write(dev_no, sector, data);
                    n++;
                }while(res == -1 && n < 3);
                
                if(res ==-1){
                    printf("Fail Write Sector\n");
                }else if(res == -2){
                    printf("MSC Device Unready\n");
                }
                break;

            case 4: // FatFileSystem
                test_fatfile();
                break;

            default:
                break;
        }
    }
}

int sector_read(int dev_no, uint32_t sector)
{
    uint8_t data[512];    
    int     size, row, col;
    int     to;
    to=0;
    do {
        memset(data, 0, sizeof(data));
        size=usb_msc_sector_read(dev_no, sector, data);
        to++;
    }while(size <=0 && to <=3);

    if(size <= 0) return size;
    
    printf("\n*** Sector %d Start Addr=%010llX \n", sector, (long long int)sector * 512);
    printf("-- addr --  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  ---- ASCII -----\n");

    for(row =0; row<32; row++){
        printf("%010llX  ", (long long int)sector * (long long int)512 + (long long)(row*16));
        for(col=0; col<16; col++){
            printf("%02X ", data[row*16+col]);
        }
        printf("  ");
        for(col=0; col<16; col++){
            if(data[row*16+col] < ' ' || data[row*16+col] >= 0x7e){
                printf(".");
            }else{
                printf("%c", data[row*16+col]);
            }
        }
        printf("\n");
    }
    return size;
}


/*==============================================================================
    USB CAMERA test
 =============================================================================*/

void  test_usb_camera(void)
{
    printf("This version does not yet support UVC class devices.\n");
}


/*==============================================================================
	InitializeSystem() 
==============================================================================*/

void InitializeSystem(void)
{
    __builtin_disable_interrupts();

    // enable multi-vector interrupts
    INTCONSET = _INTCON_MVEC_MASK;
    // select interrupt shadow register
    PRISS = 0x76543210;

    __builtin_enable_interrupts();


	// Analog input off
    ANSELA = 0x0000;
    ANSELB = 0x0000;
    ANSELC = 0x0000;
    ANSELD = 0x0000;
    ANSELE = 0x0000;
    ANSELF = 0x0000;
    ANSELG = 0x0000;

    // all port digital out, and output level low
    TRISA = 0; LATA = 0;
    TRISB = 0; LATB = 0;
    TRISC = 0; LATC = 0;
    TRISD = 0; LATD = 0;
    TRISE = 0; LATE = 0;
    TRISF = 0; LATF = 0;
    TRISG = 0; LATG = 0;
    
    // *** LED control bit *******************************
    TRISGbits.TRISG6 = 0;     // RG6 output
    LATGbits.LATG6 = 1;       // led turn off

    // *** UART2 DEBUG-OUT port setting ****************** 
    // U2TX  -- RF2
    TRISFbits.TRISF2=0;    // RF2 output
    LATFbits.LATF2=1;      // level high
    RPF2R = 0b0010;        // assign U2TX(RF2))
    // U2RX -- RF8
    TRISFbits.TRISF8=1;    // RF8 input
    U2RXR = 0b1011;        // aasign U2RXR(RF8))

} // InitializeSystem


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