Add AndroidAccessory library

Change-Id: I1e47598ea3a47ea0545dc2ce4b375b0c461c02f2
diff --git a/AndroidAccessory/AndroidAccessory.cpp b/AndroidAccessory/AndroidAccessory.cpp
new file mode 100644
index 0000000..d063910
--- /dev/null
+++ b/AndroidAccessory/AndroidAccessory.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <Max3421e.h>
+#include <Usb.h>
+#include <AndroidAccessory.h>
+
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+
+#define ACCESSORY_GET_PROTOCOL 51
+#define ACCESSORY_SEND_STRING 52
+#define ACCESSORY_START 53
+
+
+AndroidAccessory::AndroidAccessory(const char *manufacturer,
+                                   const char *model,
+                                   const char *description,
+                                   const char *version,
+                                   const char *uri,
+                                   const char *serial) : manufacturer(manufacturer),
+                                                         model(model),
+                                                         description(description),
+                                                         version(version),
+                                                         uri(uri),
+                                                         serial(serial),
+                                                         connected(false)
+{
+
+}
+
+void AndroidAccessory::powerOn(void)
+{
+    max.powerOn();
+    delay(200);
+}
+
+int AndroidAccessory::getProtocol(byte addr)
+{
+    uint16_t protocol = -1;
+    usb.ctrlReq(addr, 0, USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
+                ACCESSORY_GET_PROTOCOL, 0, 0, 0, 2, (char *)&protocol);
+    return protocol;
+}
+
+void AndroidAccessory::sendString(byte addr, int index, const char *str)
+{
+    usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
+                ACCESSORY_SEND_STRING, 0, 0, index, strlen(str) + 1, (char *)str);
+}
+
+
+bool AndroidAccessory::switchDevice(byte addr)
+{
+    int protocol = getProtocol(addr);
+
+    if (protocol == 1) {
+        Serial.print("device supports protcol 1\n");
+    } else {
+        Serial.print("could not read device protocol version\n");
+        return false;
+    }
+
+    sendString(addr, ACCESSORY_STRING_MANUFACTURER, manufacturer);
+    sendString(addr, ACCESSORY_STRING_MODEL, model);
+    sendString(addr, ACCESSORY_STRING_DESCRIPTION, description);
+    sendString(addr, ACCESSORY_STRING_VERSION, version);
+    sendString(addr, ACCESSORY_STRING_URI, uri);
+    sendString(addr, ACCESSORY_STRING_SERIAL, serial);
+
+    usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
+                ACCESSORY_START, 0, 0, 0, 0, NULL);
+    return true;
+}
+
+bool AndroidAccessory::findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp)
+{
+    int len;
+    byte err;
+    uint8_t *p;
+
+    err = usb.getConfDescr(addr, 0, 4, 0, (char *)descBuff);
+    if (err) {
+        Serial.print("Can't get config descriptor length\n");
+        return false;
+    }
+
+
+    len = descBuff[2] | ((int)descBuff[3] << 8);
+    if (len > sizeof(descBuff)) {
+        Serial.print("config descriptor too large\n");
+            /* might want to truncate here */
+        return false;
+    }
+
+    err = usb.getConfDescr(addr, 0, len, 0, (char *)descBuff);
+    if (err) {
+        Serial.print("Can't get config descriptor\n");
+        return false;
+    }
+
+    p = descBuff;
+    inEp->epAddr = 0;
+    outEp->epAddr = 0;
+    while (p < (descBuff + len)){
+        uint8_t descLen = p[0];
+        uint8_t descType = p[1];
+        USB_ENDPOINT_DESCRIPTOR *epDesc;
+        EP_RECORD *ep;
+
+        switch (descType) {
+        case USB_DESCRIPTOR_CONFIGURATION:
+            Serial.print("config desc\n");
+            break;
+
+        case USB_DESCRIPTOR_INTERFACE:
+            Serial.print("interface desc\n");
+            break;
+
+        case USB_DESCRIPTOR_ENDPOINT:
+            epDesc = (USB_ENDPOINT_DESCRIPTOR *)p;
+            if (!inEp->epAddr && (epDesc->bEndpointAddress & 0x80))
+                ep = inEp;
+            else if (!outEp->epAddr)
+                ep = outEp;
+            else
+                ep = NULL;
+
+            if (ep) {
+                ep->epAddr = epDesc->bEndpointAddress & 0x7f;
+                ep->Attr = epDesc->bmAttributes;
+                ep->MaxPktSize = epDesc->wMaxPacketSize;
+                ep->sndToggle = bmSNDTOG0;
+                ep->rcvToggle = bmRCVTOG0;
+            }
+            break;
+
+        default:
+            Serial.print("unkown desc type ");
+            Serial.println( descType, HEX);
+            break;
+        }
+
+        p += descLen;
+    }
+
+    if (!(inEp->epAddr && outEp->epAddr))
+        Serial.println("can't find accessory endpoints");
+
+    return inEp->epAddr && outEp->epAddr;
+}
+
+bool AndroidAccessory::configureAndroid(void)
+{
+    byte err;
+    EP_RECORD inEp, outEp;
+
+    if (!findEndpoints(1, &inEp, &outEp))
+        return false;
+
+    memset(&epRecord, 0x0, sizeof(epRecord));
+
+    epRecord[inEp.epAddr] = inEp;
+    if (outEp.epAddr != inEp.epAddr)
+        epRecord[outEp.epAddr] = outEp;
+
+    in = inEp.epAddr;
+    out = outEp.epAddr;
+
+    Serial.print("inEp: ");
+    Serial.println(inEp.epAddr, HEX);
+    Serial.print("outEp: ");
+    Serial.println(outEp.epAddr, HEX);
+
+    epRecord[0] = *(usb.getDevTableEntry(0,0));
+    usb.setDevTableEntry(1, epRecord);
+
+    err = usb.setConf( 1, 0, 1 );
+    if (err) {
+        Serial.print("Can't set config to 1\n");
+        return false;
+    }
+
+    usb.setUsbTaskState( USB_STATE_RUNNING );
+
+    return true;
+}
+
+bool AndroidAccessory::isConnected(void)
+{
+    USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) descBuff;
+    byte err;
+
+    max.Task();
+    usb.Task();
+
+    if (!connected &&
+        usb.getUsbTaskState() >= USB_STATE_CONFIGURING &&
+        usb.getUsbTaskState() != USB_STATE_RUNNING) {
+        Serial.print("\nDevice addressed... ");
+        Serial.print("Requesting device descriptor.");
+
+        err = usb.getDevDescr(1, 0, 0x12, (char *) devDesc);
+        if (err) {
+            Serial.print("\nDevice descriptor cannot be retrieved. Program Halted\n");
+            while(1);
+        }
+
+        if (isAccessoryDevice(devDesc)) {
+            Serial.print("found android acessory device\n");
+
+            connected = configureAndroid();
+        } else {
+            Serial.print("found possible device. swithcing to serial mode\n");
+            switchDevice(1);
+        }
+    } else if (usb.getUsbTaskState() == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {
+        connected = false;
+    }
+
+    return connected;
+}
+
+int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit)
+{
+    return usb.newInTransfer(1, in, len, (char *)buff, nakLimit);
+}
+
+int AndroidAccessory::write(void *buff, int len)
+{
+    usb.outTransfer(1, out, len, (char *)buff);
+    return len;
+}
+
diff --git a/AndroidAccessory/AndroidAccessory.h b/AndroidAccessory/AndroidAccessory.h
new file mode 100644
index 0000000..1d4542d
--- /dev/null
+++ b/AndroidAccessory/AndroidAccessory.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AndroidAccessory_h__
+#define __AndroidAccessory_h__
+
+#include "WProgram.h"
+
+class AndroidAccessory {
+private:
+    const char *manufacturer;
+    const char *model;
+    const char *description;
+    const char *version;
+    const char *uri;
+    const char *serial;
+
+    MAX3421E max;
+    USB usb;
+    bool connected;
+    uint8_t in;
+    uint8_t out;
+
+    EP_RECORD epRecord[8];
+
+    uint8_t descBuff[256];
+
+    bool isAccessoryDevice(USB_DEVICE_DESCRIPTOR *desc)
+    {
+        return desc->idVendor == 0x18d1 &&
+            (desc->idProduct == 0x2D00 || desc->idProduct == 0x2D01);
+    }
+
+    int getProtocol(byte addr);
+    void sendString(byte addr, int index, const char *str);
+    bool switchDevice(byte addr);
+    bool findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp);
+    bool configureAndroid(void);
+
+public:
+    AndroidAccessory(const char *manufacturer,
+                     const char *model,
+                     const char *description,
+                     const char *version,
+                     const char *uri,
+                     const char *serial);
+
+    void powerOn(void);
+
+    bool isConnected(void);
+    int read(void *buff, int len, unsigned int nakLimit = USB_NAK_LIMIT);
+    int write(void *buff, int len);
+};
+
+
+#endif /* __AndroidAccessory_h__ */