// --------------------------------------------------------------------
// Copyright (c) 2009 by Terasic Technologies Inc. 
// --------------------------------------------------------------------
//
// Permission:
//
//   Terasic grants permission to use and modify this code for use
//   in synthesis for all Terasic Development Boards and Altera Development 
//   Kits made by Terasic.  Other use of this code, including the selling 
//   ,duplication, or modification of any portion is strictly prohibited.
//
// Disclaimer:
//
//   This VHDL/Verilog or C/C++ source code is intended as a design reference
//   which illustrates how these types of functions can be implemented.
//   It is the user's responsibility to verify their design for
//   consistency and functionality through the use of formal
//   verification methods.  Terasic provides no warranty regarding the use 
//   or functionality of this code.
//
// --------------------------------------------------------------------
//           
//                     Terasic Technologies Inc
//                     356 Fu-Shin E. Rd Sec. 1. JhuBei City,
//                     HsinChu County, Taiwan
//                     302
//
//                     web: http://www.terasic.com/
//                     email: support@terasic.com
//
// --------------------------------------------------------------------
// Last Built by: Quartus 9.1, build 222


/*  Function Description:
 *      Demo usb host class driver for the following two types of devices:
 *          a. Bulk-Only Transport Usb-Storage, with file system FAT16/FAT32
 *          b. HID usb mosue.
 *
 *  Demo Operation:
 *      1. Configure FPAG with .sof
 *      2. Download this .elf to board. 
 *      3. Wait the usb hosts ready by checking the usb led.
 *      4. Plug usb-storage and/or usb-mouse to the usb port in the board.
 *      5. If usb-storage is detected, the file/folder name in the root directory 
 *         will be display on the on the console windows of NIOS II IDE. 
 *         If the root directory contains a file, called as test.txt, the content of 
 *         this file will also be displayed on the console window.
 *      6. The mouse x/y and status will be display on the console window if mouse 
 *         status is changed.
 * 
 *       
 *  
 *  Author:
 *      Richard Change
 *  
 *  Revision:
 *      V1.0, init 03/11/2008
 * 
 *  0709_2008
 *  USB Controller Update: 
 *      hold time: 10 ns
 *      setup time: 10 ns
 *      write time: 20 ns
 *      read time: 30 ns
 *      clock: 100 MHZ
 *  Add USB SCRATCH register read/write test
 * 
 * */

 

#include <stdio.h>
#include <io.h>
#include "terasic_includes.h"
#include "FatFileSystem.h"
#include "usb_hub.h"
#include "usb_class.h"
#include "usb_mouse.h"
#include "usb_disk.h"

#define err(x)  printf(x);


#define APP_DEBUG(x)   {printf("[APPDBG]"); printf x;}
#define APP_PRINTF(x)  {printf("[APP]"); printf x;}

typedef struct{
    bool bDeviceAttached;
    USB_DEVICE_TYPE DeviceType;
    alt_u32 DriverHandle;
}PORT_STATUS;

typedef struct{
    HUB_HANDLE hHub;
    PORT_STATUS PortStatus[3];  
}HUB_STATUS;


void welcome_led(void){
    
    const alt_u32 led_delay = 500*1000;    
    IOWR(PIO_LED_BASE,0, 0xFFFF00); // blue
    usleep(led_delay);
    IOWR(PIO_LED_BASE,0, 0xFF00FF); // green
    usleep(led_delay);
    IOWR(PIO_LED_BASE,0, 0x00FFFF); // red
    usleep(led_delay);
    IOWR(PIO_LED_BASE,0, 0xFFFFFF); // black
    usleep(led_delay);
    IOWR(PIO_LED_BASE,0, 0x000000); // while
    usleep(led_delay);
}    

void HubCallback(HUB_EVENT_TYPE HubEvent, alt_u8 Port, USB_SPEED DeviceSpeed, void *pPrivData){
    char szSpeed[32], szDeviceType[32];
    HUB_STATUS *pHubStatus = (HUB_STATUS *)pPrivData;
    if (HubEvent == HUB_EVENT_DEVICE_ATTACH){
        //===== determine device type
        USB_DEVICE_TYPE DeviceType;
        DeviceType = USBCLASS_GetDeviceType(pHubStatus->hHub, Port, DeviceSpeed);
        USBCLASS_GetDeviceTypeName(DeviceType, szDeviceType, sizeof(szDeviceType));
        switch(DeviceSpeed){
            case USB_SPEED_LOW: strcpy(szSpeed, "Low Speed"); break;
            case USB_SPEED_FULL: strcpy(szSpeed, "Full Speed"); break;
            case USB_SPEED_HIGH: strcpy(szSpeed, "High Speed"); break;
            default: strcpy(szSpeed, "Unknown Speed"); break;
        }
        APP_PRINTF(("Device attach at port %d, Speed=%s, DeviceType=%s\n", Port, szSpeed, szDeviceType));
        pHubStatus->PortStatus[Port-1].bDeviceAttached = TRUE;
        //===== action for varius device type
        alt_u8 AssignedAddress = Port + 1; // address 1 is reserved for hub
        if (DeviceType == USB_DEVICE_TYPE_DISK){
            pHubStatus->PortStatus[Port-1].DeviceType = USB_DEVICE_TYPE_DISK; 
            // show items in root directory
            USBDISK_HANDLE hDisk;
            hDisk = USBDISK_Open(pHubStatus->hHub, Port, DeviceSpeed, AssignedAddress);
            if (hDisk){
                FAT_HANDLE hFat;
                hFat = Fat_Mount(FAT_USB_DISK, hDisk);
                if (hFat){
                    Fat_Test(hFat, "test.txt");
                    Fat_Unmount(hFat);
                }
                USBDISK_Close(hDisk);
            }
            
        }else if (DeviceType == USB_DEVICE_TYPE_MOUSE){
            USBMS_HANDLE hMouse;
            hMouse = USBMS_Open(pHubStatus->hHub, Port, DeviceSpeed, AssignedAddress);
            pHubStatus->PortStatus[Port-1].DeviceType = USB_DEVICE_TYPE_MOUSE; 
            pHubStatus->PortStatus[Port-1].DriverHandle = (alt_32)hMouse; 
        }
    }else if (HubEvent == HUB_EVENT_DEVICE_DETACH){
        pHubStatus->PortStatus[Port-1].bDeviceAttached = FALSE;
        if (pHubStatus->PortStatus[Port-1].DriverHandle){
            if (pHubStatus->PortStatus[Port].DeviceType == USB_DEVICE_TYPE_MOUSE){
                USBMS_HANDLE hMouse = (USBMS_HANDLE)pHubStatus->PortStatus[Port-1].DriverHandle;
                USBMS_Close(hMouse);
            }
            pHubStatus->PortStatus[Port-1].DriverHandle = 0;
        }
        APP_PRINTF(("Device detach at port %d\n", Port));
    } 
}

void MouseTrace(HUB_STATUS *pHubStatus){
    int Port;
    for(Port=0;Port<3;Port++){
        if (pHubStatus->PortStatus[Port].bDeviceAttached &&
            pHubStatus->PortStatus[Port].DeviceType == USB_DEVICE_TYPE_MOUSE &&
            pHubStatus->PortStatus[Port].DriverHandle){
            // get device    
            USBMS_HANDLE hMouse = (USBMS_HANDLE)pHubStatus->PortStatus[Port].DriverHandle; 
            int x, y, status;
            if (USBMS_GetStatus(hMouse, &x, &y, &status)){
                char szStatus[128];
                strcpy(szStatus, "");
                if (status & 0x01)
                    strcat(szStatus, "Left");
                if (status & 0x04)
                    strcat(szStatus, (strlen(szStatus))?"/Middle":"Middle");
                if (status & 0x02)
                    strcat(szStatus, (strlen(szStatus))?"/Right":"Right");
                if (strlen(szStatus))                    
                    strcat(szStatus, " Button Pressed ");
                    
                if (strlen(szStatus)){
                    APP_PRINTF(("Port[%d] Mouse:x=%d, y=%d, %s\n", Port+1, x, y, szStatus));
                }else{    
                    APP_PRINTF(("Port[%d] Mouse:x=%d, y=%d\n", Port+1, x, y));
                }                    
            }
        }
    }
}


int main()
{
    HUB_STATUS HubStatus;



    welcome_led();
    APP_PRINTF(("===== DE3 ISP1761 USB Host Demo [12/16/2009]=====\n"));

    memset(&HubStatus, 0, sizeof(HubStatus));    
    HubStatus.hHub = Hub_Start(HubCallback, (void *)&HubStatus);
    if (!HubStatus.hHub){
        printf("Failed to start USB Hub\n");
        return 0;
    }
    
    APP_PRINTF(("Please insert USB-Storage or/and HID USB-Mouse\n"));
    while(1){
   
        Hub_Run(HubStatus.hHub);
        MouseTrace(&HubStatus);
    }    
    
    
    return 0;
   
    
}
