modified to compile for me

Dependents:   N64_Output_XPAD

Fork of USBHostXpad by Suga koubou

USBHostXpad.cpp

Committer:
Ownasaurus
Date:
2017-04-03
Revision:
9:2b1ba1e2eb70
Parent:
8:f5f3dd9b97b6

File content as of revision 9:2b1ba1e2eb70:

/*
 * Xbox 360 Wireless Controller for Windows library
 * for mbed USBHost library
 * Copyright (c) 2013 Hiroshi Suga
 *
 * VID=0x045e PID=0x0719
 *
 * Reference:
 *  https://github.com/Grumbel/xboxdrv/blob/master/PROTOCOL
 *  http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller
 */

#include "USBHostXpad.h"

#if 1 or USBHOST_XPAD

#include "dbg.h"

#define DEVICE_TO_HOST  0x80
#define HOST_TO_DEVICE  0x00

USBHostXpad::USBHostXpad()
{
    host = USBHost::getHostInst();
    init();
}

void USBHostXpad::init() {
    dev_connected = false;
    dev = NULL;
    int_in = NULL;
    int_out = NULL;
    xpad_intf = -1;
    xpad_device_found = false;
    nb_ep = 0;
    dev_type = TYPE_UNKNOWN;
    dev_started = false;
}

bool USBHostXpad::connected()
{
    return dev_connected;
}

bool USBHostXpad::connect()
{

    if (dev_connected) {
        return true;
    }

    for (int i = 0; i < MAX_DEVICE_CONNECTED; i++) {
        if ((dev = host->getDevice(i)) != NULL) {
            
            USB_DBG("Trying to connect MSD device\r\n");
            
            if(host->enumerate(dev, this)) break;

            if (xpad_device_found) {
                int_in = dev->getEndpoint(xpad_intf, INTERRUPT_ENDPOINT, IN);
                int_out = dev->getEndpoint(xpad_intf, INTERRUPT_ENDPOINT, OUT);
                
                if (!int_in || !int_out) continue;

                USB_INFO("New Xpad device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, xpad_intf);
                dev->setName("XPAD", xpad_intf);
                host->registerDriver(dev, xpad_intf, this, &USBHostXpad::init);
                int_in->attach(this, &USBHostXpad::rxHandler);
                host->interruptRead(dev, int_in, report, int_in->getSize(), false);

                serial = 0;
                wait_ms(100);
                led(LED_OFF);
               
                dev_connected = true;
                return true;
            }

        }
    }
    init();
    return false;
}

void USBHostXpad::rxHandler() {
    //USB_INFO("Message received: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \r\n",report[0],report[1],report[2],report[3],report[4]);
    int len_listen = int_in->getSize();
    int len = int_in->getLengthTransferred();

    if (dev_type == TYPE_XBOX) {
        parseMessage();
    } 
    else if(dev_type == TYPE_XBOXONE) {
        if(report[0] == 0x02) // auth
        {
            if(report[1] == 0x20) // request
            {
                start();
                dev_started = true;
                //return true;
            }
        }
        else if(report[0] == 0x03)
        {
            // heartbeat
        }
        else if(report[0] == 0x07)
        {
            // guide button
        }
        else if(report[0] == 0x20)
        {
            // buttons update
            parseMessage();
        }
        else if(report[0] == 0x01)
        {
            // error packet?
        }
    } else
    if (report[0] == 0x08) {
        if (report[1] == 0x80 || /*x360 headset status*/report[1] == 0x80) {
            // Connection Status Messages
            start();
        } else
        if (report[1] == 0x00) {
            // Disconnected Status Messages
            dev_started = false;
        }
    } else
    if (report[0] == 0x01) { // X360 led
        ; // no need to do anything?
    } else
    if (report[0] == 0x02) { // X360 unknown
        ; // no need to do anything?
    } else
    if (report[0] == 0x03) { // X360 rumble
        ; // no need to do anything?
    } else
    if (report[0] == 0x00) {
        if ((report[1] == 0x14) ||
            (report[1] == 0x01 && report[2] == 0x00 && report[3] == 0xf0)) {
            // Event data
            parseMessage();
        } else
        if (report[1] == 0x0f) {
            // On connection
            led(LED1_ON);
            start();
            dev_started = true;
        } else
        if (report[1] == 0x00) {
            if (report[3] == 0x13) {
                // Battery charge level
                battery = report[4];
            } else
            if (report[3] == 0xf0) {
                // Device low power mode (HID updates will stop until controller inputs change)
//                restart();
            }
        } else
        if (report[1] == 0xf8) {
            // Status Messages ?
        }
    } else {
        // Unknown command received
        USB_INFO("rxHandler len:%d data:%02x %02x %02x %02x", len, report[0], report[1], report[2], report[3]);
    }

    if (dev) {
        host->interruptRead(dev, int_in, report, len_listen, false);
    }
}

/*virtual*/ void USBHostXpad::setVidPid(uint16_t vid, uint16_t pid)
{
    if (vid == 0x045e && (pid == 0x0202 || pid == 0x0285 || pid == 0x0287 || pid == 0x0289)) {
        dev_type = TYPE_XBOX;
    } else
    if ((vid == 0x045e && pid == 0x028e) || /*my offbrand additions!*/(vid == 0x0e6f && pid == 0x0213) || (vid == 0x24c6 && pid == 0xfafc)) {
        dev_type = TYPE_XBOX360;
    } else
    if (vid == 0x045e && pid == 0x0719) {
        dev_type = TYPE_XBOX360W;
    } else
    if (vid == 0x045e && (pid == 0x02ea || pid == 0x02e3 || pid == 0x02d1 || pid == 0x02dd)) { // also includes XBOX ONE ELITE
        dev_type = TYPE_XBOXONE;
    }
    USB_INFO("setVidPid vid:%04x pid:%04x xbox:%d", vid, pid, dev_type);
}

/*virtual*/ bool USBHostXpad::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
{
    USB_INFO("parseInterface class:%02x subclass:%02x protocol:%02x [nb: %d - intf: %d]", intf_class, intf_subclass, intf_protocol, intf_nb, xpad_intf);
    if ((xpad_intf == -1) && (intf_class == 0x58) && (intf_subclass == 0x42) && (intf_protocol == 0x00)) {
        xpad_intf = intf_nb; // XBOX ?
        return true;
    }
    if ((xpad_intf == -1) && (intf_class == 0xff) && (intf_subclass == 0x5d) && (intf_protocol == 0x81)) {
        xpad_intf = intf_nb; // XBOX360W
        return true;
    }
    if ((xpad_intf == -1) && (intf_class == 0xff) && (intf_subclass == 0x5d) && (intf_protocol == 0x01)) {
        xpad_intf = intf_nb; // XBOX360 WIRED
        return true;
    }
    if ((xpad_intf == -1) && (intf_class == 0xff) && (intf_subclass == 0x47) && (intf_protocol == 0xd0)) {
        xpad_intf = intf_nb; // XBOXONE WIRED
        return true;
    }
    return false;
}

/*virtual*/ bool USBHostXpad::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
{
    USB_INFO("useEndpoint nb:%d type:%d dir:%d", intf_nb, type, dir);
    if (intf_nb == xpad_intf) {
        if (type == INTERRUPT_ENDPOINT) {
            nb_ep ++;
            if (nb_ep == 2) {
                xpad_device_found = true;
            }
            return true;
        }
    }
    return false;
}


void USBHostXpad::parseMessage()
{
    uint8_t *data = report;

    switch (dev_type) {
    case TYPE_XBOX:
        buttons = ((uint32_t)report[3] << 8) | report[2];
        if (report[4]) buttons |= XPAD_PAD_A;
        if (report[5]) buttons |= XPAD_PAD_B;
        if (report[6]) buttons |= XPAD_PAD_X;
        if (report[7]) buttons |= XPAD_PAD_Y;
        trigger_l = data[10];
        trigger_r = data[11];

        stick_lx = ((int16_t)report[13] << 8) | report[12];
        stick_ly = ((int16_t)report[15] << 8) | report[14];
        stick_rx = ((int16_t)report[17] << 8) | report[16];
        stick_ry = ((int16_t)report[19] << 8) | report[18];
        break;
    case TYPE_XBOX360W:
        data += 4;
    case TYPE_XBOX360:
        buttons = ((uint32_t)data[3] << 8) | data[2];
        trigger_l = data[4];
        trigger_r = data[5];

        stick_lx = ((int16_t)data[7] << 8) | data[6];
        stick_ly = ((int16_t)data[9] << 8) | data[8];
        stick_rx = ((int16_t)data[11] << 8) | data[10];
        stick_ry = ((int16_t)data[13] << 8) | data[12];
        break;
    case TYPE_XBOXONE:
        buttons = 0;
        buttons = (report[4] & 0x0C) << 2; // correctly place start and back
        buttons |= (report[4] & 0xF0) << 8; // correctly place AXYB
        buttons |= report[5] & 0x0F; // correctly place DPad
        buttons |= (report[5] & 0x30) << 4; // correctly place bumpers
        buttons |= (report[5] & 0xC0); // correctly analog stick buttons
        
        //TODO: FIX TRIGGERS, PROBABLY OVERFLOWING
        trigger_l = ((uint32_t)report[7] << 8) | report[6]; //6 and 7 
        trigger_r = ((uint32_t)report[9] << 8) | report[8]; // 8 and 9
        
        stick_lx = ((int16_t)data[11] << 8) | data[10];
        stick_ly = ((int16_t)data[13] << 8) | data[12];
        stick_rx = ((int16_t)data[15] << 8) | data[14];
        stick_ry = ((int16_t)data[17] << 8) | data[16];
        
        break;
    default:
        return;
    }
    
    if (onUpdate) {
        (*onUpdate)(buttons, stick_lx, stick_ly, stick_rx, stick_ry, trigger_l, trigger_r);
    }
}

bool USBHostXpad::restart ()
{
    unsigned char odata[32];
    memset(odata, 0, sizeof(odata));
    odata[0] = 0x08;
    odata[2] = 0x0f;
    odata[3] = 0xc0;
    if (dev) {
        for (int i = 0; i < 4; i ++) {
            host->interruptWrite(dev, int_out, odata, 12);
        }
    }
    return true;
}

int USBHostXpad::read (PAD pad)
{
    switch (pad) {
    case XPAD_BUTTONS:
        return buttons;
    case XPAD_STICK_LX:
        return stick_lx;
    case XPAD_STICK_LY:
        return stick_ly;
    case XPAD_STICK_RX:
        return stick_rx;
    case XPAD_STICK_RY:
        return stick_ry;
    case XPAD_TRIGGER_L:
        return trigger_l;
    case XPAD_TRIGGER_R:
        return trigger_r;
    case XPAD_BATTERY:
        return battery;
    default:
        return (buttons & pad) ? 1 : 0;
    }
}

bool USBHostXpad::start()
{
    if(dev_type == TYPE_XBOXONE)
    {
        unsigned char odata[32];
        memset(odata, 0, sizeof(odata));
        odata[0] = 0x05;
        odata[1] = 0x20;
        odata[2] = serial++;
        odata[3] = 0x09;
        odata[4] = 0x06;
        odata[11] = 0x55;
        odata[12] = 0x53;
        if (dev) {
            if (host->interruptWrite(dev, int_out, odata, 13) != USB_TYPE_OK) {
                return false;
            }
        }
        
        memset(odata, 0, sizeof(odata));
        odata[0] = 0x05;
        odata[1] = 0x20;
        odata[2] = serial++;
        odata[3] = 0x01;
        odata[4] = 0x00;
        if (dev) {
            if (host->interruptWrite(dev, int_out, odata, 5) != USB_TYPE_OK) {
                return false;
            }
        }
        return true;
    }
    
    // other controller init
    unsigned char odata[32];
    memset(odata, 0, sizeof(odata));
    odata[3] = 0x40;
    if (dev) {
        if (host->interruptWrite(dev, int_out, odata, 12) != USB_TYPE_OK) {
            return false;
        }
    }
    return true;
}

bool USBHostXpad::stop()
{
    unsigned char odata[32];
    memset(odata, 0, sizeof(odata));
    odata[2] = 0x08;
    odata[3] = 0xc0;
    if (dev) {
        if (host->interruptWrite(dev, int_out, odata, 12) != USB_TYPE_OK) {
            return false;
        }
    }
    return true;
}

bool USBHostXpad::led(LED cmd)
{
    unsigned char odata[32];
    memset(odata, 0, sizeof(odata));
    switch (dev_type) {
    case TYPE_XBOX:
        return true;
    case TYPE_XBOX360:
        odata[0] = 0x01;
        odata[1] = 0x03;
        odata[2] = cmd;
        break;
    case TYPE_XBOX360W:
        odata[2] = 0x08;
        odata[3] = 0x40 + (cmd % 0x0e);
        break;
    case TYPE_XBOXONE:
        return true; // no LEDs to change
    default:
        return false;
    }
    if (dev) {
        if(dev_type == TYPE_XBOX360)
        {
            if (host->interruptWrite(dev, int_out, odata, 3) != USB_TYPE_OK) {
                return false;
            }
        }
        else if(dev_type == TYPE_XBOX360W)
        {
            if (host->interruptWrite(dev, int_out, odata, 4) != USB_TYPE_OK) {
                return false;
            }
        }
    }
    return true;
}

bool USBHostXpad::rumble(uint8_t large, uint8_t small)
{
    unsigned char odata[32];
    memset(odata, 0, sizeof(odata));
    switch (dev_type) {
    case TYPE_XBOX:
        odata[1] = 0x06;
        odata[3] = small;
        odata[5] = large;
        break;
    case TYPE_XBOX360:
        odata[1] = 0x08;
        odata[3] = large;
        odata[4] = small;
        break;
    case TYPE_XBOX360W:
        odata[1] = 0x01;
        odata[2] = 0x0f;
        odata[3] = 0xc0;
        odata[5] = large;
        odata[6] = small;
        break;
    case TYPE_XBOXONE:
        return true;
    default:
        return false;
    }
    if (dev) {
        if (host->interruptWrite(dev, int_out, odata, 12) != USB_TYPE_OK) {
            return false;
        }
    }
    return true;
}

#endif