Browse Source

Adding Android Accessory ADK mode.

main
MatthewBergman 5 years ago
parent
commit
a11f69b0a8
2 changed files with 501 additions and 0 deletions
  1. +48
    -0
      USBHost_t36.h
  2. +453
    -0
      adk.cpp

+ 48
- 0
USBHost_t36.h View File

@@ -1851,4 +1851,52 @@ private:

};

class ADK: public USBDriver {
public:
ADK(USBHost &host) { init(); }
bool ready();
void begin(char *adk_manufacturer, char *adk_model, char *adk_desc, char *adk_version, char *adk_uri, char *adk_serial);
void end();
int available(void);
int peek(void);
int read(void);
size_t write(size_t len, uint8_t *buf);
protected:
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len);
virtual void disconnect();
virtual void control(const Transfer_t *transfer);
static void rx_callback(const Transfer_t *transfer);
static void tx_callback(const Transfer_t *transfer);
void rx_data(const Transfer_t *transfer);
void tx_data(const Transfer_t *transfer);
void init();
void rx_queue_packets(uint32_t head, uint32_t tail);
void sendStr(Device_t *dev, uint8_t index, char *str);
private:
int state = 0;
Pipe_t *rxpipe;
Pipe_t *txpipe;
enum { MAX_PACKET_SIZE = 512 };
enum { RX_QUEUE_SIZE = 160 }; // must be more than MAX_PACKET_SIZE/4
uint32_t rx_buffer[MAX_PACKET_SIZE/4];
uint32_t tx_buffer[MAX_PACKET_SIZE/4];
uint16_t rx_size;
uint16_t tx_size;
uint32_t rx_queue[RX_QUEUE_SIZE];
bool rx_packet_queued;
uint16_t rx_head;
uint16_t rx_tail;
uint8_t rx_ep;
uint8_t tx_ep;
char *manufacturer;
char *model;
char *desc;
char *version;
char *uri;
char *serial;
Pipe_t mypipes[3] __attribute__ ((aligned(32)));
Transfer_t mytransfers[7] __attribute__ ((aligned(32)));
};


#endif

+ 453
- 0
adk.cpp View File

@@ -0,0 +1,453 @@
/* USB EHCI Host for Teensy 3.6
* Copyright 2017 Paul Stoffregen (paul@pjrc.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/

#include <Arduino.h>
#include "USBHost_t36.h" // Read this header first for key info

#define print USBHost::print_
#define println USBHost::println_

#define ADK_VID 0x18D1
#define ADK_PID 0x2D00
#define ADB_PID 0x2D01
#define USB_SETUP_DEVICE_TO_HOST 0x80
#define USB_SETUP_HOST_TO_DEVICE 0x00
#define USB_SETUP_TYPE_VENDOR 0x40
#define USB_SETUP_RECIPIENT_DEVICE 0x00
#define UHS_ADK_bmREQ_GET USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE
#define UHS_ADK_bmREQ_SEND USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE
#define UHS_ADK_GETPROTO 51 // check USB accessory protocol version
#define UHS_ADK_SENDSTR 52 // send identifying string
#define UHS_ADK_ACCSTART 53 // start device in accessory mode
#define UHS_ADK_ID_MANUFACTURER 0
#define UHS_ADK_ID_MODEL 1
#define UHS_ADK_ID_DESCRIPTION 2
#define UHS_ADK_ID_VERSION 3
#define UHS_ADK_ID_URI 4
#define UHS_ADK_ID_SERIAL 5

static uint8_t adkbuf[256] __attribute__ ((aligned(16)));
static setup_t adksetup __attribute__ ((aligned(16)));

/************************************************************/
// Initialization and claiming of devices & interfaces
/************************************************************/

void ADK::init()
{
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t));
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t));
rx_head = 0;
rx_tail = 0;
driver_ready_for_device(this);
state = 0;
}

bool ADK::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len)
{
// only claim at interface level
if (type != 1)
return false;
// Check for ADK or ADB PIDs
if (dev->idVendor == ADK_VID && (dev->idProduct == ADK_PID || dev->idProduct == ADB_PID))
{
const uint8_t *p = descriptors;
const uint8_t *end = p + len;

// Interface descriptor
if (p[0] != 9 || p[1] != 4)
return false;

// bInterfaceClass: 255 Vendor Specific
if (p[5] != 255)
{
return false;
}
// bInterfaceSubClass: 255
if (p[6] != 255)
{
return false;
}
println("ADK claim this=", (uint32_t)this, HEX);
print("vid=", dev->idVendor, HEX);
print(", pid=", dev->idProduct, HEX);
print(", bDeviceClass = ", dev->bDeviceClass);
print(", bDeviceSubClass = ", dev->bDeviceSubClass);
println(", bDeviceProtocol = ", dev->bDeviceProtocol);
println(" bInterfaceClass=", p[5]);
println(" bInterfaceSubClass=", p[6]);
print_hexbytes(descriptors, len);
p += 9;
rx_ep = 0;
tx_ep = 0;

while (p < end)
{
len = *p;
if (len < 4)
return false; // all desc are at least 4 bytes
if (p + len > end)
return false; // reject if beyond end of data
uint32_t type = p[1];
if (type == 5)
{
// endpoint descriptor
if (p[0] < 7)
return false; // at least 7 bytes
if (p[3] != 2)
return false; // must be bulk type
println(" Endpoint: ", p[2], HEX);
switch (p[2] & 0xF0)
{
case 0x80:
// IN endpoint
if (rx_ep == 0)
{
rx_ep = p[2] & 0x0F;
rx_size = p[4] | (p[5] << 8);
println(" rx_size = ", rx_size);
println(" rx_ep = ", rx_ep);
}
break;
case 0x00:
// OUT endpoint
if (tx_ep == 0)
{
tx_ep = p[2];
tx_size = p[4] | (p[5] << 8);
println(" tx_size = ", tx_size);
println(" tx_ep = ", tx_ep);
}
break;
default:
return false;
}
}
// ADK uses the first two endpoints for communication
if (rx_ep && tx_ep)
{
println("Found both rx and tx EPs");
break;
}
p += len;
}
// if an IN endpoint was found, create its pipe
if (rx_ep && rx_size <= MAX_PACKET_SIZE)
{
rxpipe = new_Pipe(dev, 2, rx_ep, 1, rx_size);
if (rxpipe)
{
rxpipe->callback_function = rx_callback;
queue_Data_Transfer(rxpipe, rx_buffer, rx_size, this);
rx_packet_queued = true;
println("Done creating RX pipe");
}
}
else
{
rxpipe = NULL;
}
// if an OUT endpoint was found, create its pipe
if (tx_ep && tx_size <= MAX_PACKET_SIZE)
{
txpipe = new_Pipe(dev, 2, tx_ep, 0, tx_size);
if (txpipe)
{
txpipe->callback_function = tx_callback;
println("Done creating TX pipe");
}
}
else
{
txpipe = NULL;
}
rx_head = 0;
rx_tail = 0;

// claim if either pipe created
bool created = (rxpipe || txpipe);
if (created)
{
println("Done with init.");
state = 8;
}
return created;
}
else
{
state = 0;
println("Not in accessory mode.");
// Kick off switch to Accessory Mode
mk_setup(adksetup, UHS_ADK_bmREQ_GET, UHS_ADK_GETPROTO, 0, 0, 2);
queue_Control_Transfer(dev, &adksetup, adkbuf, this);
return true;
}
return false;
}
void ADK::sendStr(Device_t *dev, uint8_t index, char *str)
{
strcpy(adkbuf, (unsigned char*)str);
mk_setup(adksetup, UHS_ADK_bmREQ_SEND, UHS_ADK_SENDSTR, 0, index, strlen(str));
queue_Control_Transfer(dev, &adksetup, adkbuf, this);
}

void ADK::control(const Transfer_t *transfer)
{
println("Control callback state=%i",state);
switch (state)
{
case 0:
print_hexbytes(transfer->buffer, transfer->length);
state++;
sendStr(device, UHS_ADK_ID_MANUFACTURER, manufacturer);
break;
case 1:
print_hexbytes(transfer->buffer, transfer->length);
state++;
sendStr(device, UHS_ADK_ID_MODEL, model);
break;
case 2:
print_hexbytes(transfer->buffer, transfer->length);
state++;
sendStr(device, UHS_ADK_ID_DESCRIPTION, desc);
break;
case 3:
print_hexbytes(transfer->buffer, transfer->length);
state++;
sendStr(device, UHS_ADK_ID_VERSION, version);
break;
case 4:
print_hexbytes(transfer->buffer, transfer->length);
state++;
sendStr(device, UHS_ADK_ID_URI, uri);
break;
case 5:
print_hexbytes(transfer->buffer, transfer->length);
state++;
sendStr(device, UHS_ADK_ID_SERIAL, serial);
break;
case 6:
print_hexbytes(transfer->buffer, transfer->length);
state++;

// Send ADK switch command
mk_setup(adksetup, UHS_ADK_bmREQ_SEND, UHS_ADK_ACCSTART, 0, 0, 0);
queue_Control_Transfer(device, &adksetup, NULL, this);
break;
case 7:
println("After ACC switch command. Device should re-enumerate.");
print_hexbytes(transfer->buffer, transfer->length);
state++;
break;
default:
break;
}
}

void ADK::rx_callback(const Transfer_t *transfer)
{
if (transfer->driver) {
((ADK *)(transfer->driver))->rx_data(transfer);
}
}

void ADK::tx_callback(const Transfer_t *transfer)
{
if (transfer->driver) {
((ADK *)(transfer->driver))->tx_data(transfer);
}
}

void ADK::rx_data(const Transfer_t *transfer)
{
uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
println("ADK Receive rx_data");
print("Len: ");
print(len);
print(" Data: ");
print_hexbytes(transfer->buffer, len);
uint32_t head = rx_head;
uint32_t tail = rx_tail;
for (uint32_t i=0; i < ceil(len/4); i++)
{
uint32_t msg = rx_buffer[i];
if (msg) {
if (++head >= RX_QUEUE_SIZE)
head = 0;
rx_queue[head] = msg;
}
}
rx_head = head;
rx_packet_queued = false;
rx_queue_packets(head, tail);
}

void ADK::rx_queue_packets(uint32_t head, uint32_t tail)
{
if (rx_packet_queued)
return;
uint32_t avail = (head < tail) ? tail - head - 1 : RX_QUEUE_SIZE - 1 - head + tail;
println("rx_size = ", rx_size);
println("avail = ", avail);
if (avail >= (uint32_t)(rx_size>>2)) {
// enough space to accept another full packet
println("queue another receive packet");
queue_Data_Transfer(rxpipe, rx_buffer, rx_size, this);
rx_packet_queued = true;
} else {
// queue can't accept another packet's data, so leave
// the data waiting on the device until we can accept it
println("wait to receive more packets");
rx_packet_queued = false;
}
}

void ADK::tx_data(const Transfer_t *transfer)
{
println("ADK tx_data transmit complete");
print(" Data: ");
print_hexbytes(transfer->buffer, tx_size);
}


void ADK::disconnect()
{
println("Disconnect");

rxpipe = NULL;
txpipe = NULL;
state = 0;
}

void ADK::begin(char *adk_manufacturer, char *adk_model, char *adk_desc, char *adk_version, char *adk_uri, char *adk_serial)
{
manufacturer = adk_manufacturer;
model = adk_model;
desc = adk_desc;
version = adk_version;
uri = adk_uri;
serial = adk_serial;
}

void ADK::end(void)
{
// TODO: add end code
}

int ADK::available(void)
{
if (!device)
return 0;
uint32_t head = rx_head;
uint32_t tail = rx_tail;
if (head >= tail)
return head - tail;
return RX_QUEUE_SIZE + head - tail;
}

int ADK::peek(void)
{
if (!device)
return -1;
uint32_t head = rx_head;
uint32_t tail = rx_tail;
if (head == tail)
return -1;
if (++tail >= RX_QUEUE_SIZE)
tail = 0;
return rx_queue[tail];
}

int ADK::read(void)
{
if (!device)
return -1;
uint32_t head = rx_head;
uint32_t tail = rx_tail;
if (head == tail)
return -1;
if (++tail >= RX_QUEUE_SIZE)
tail = 0;
int c = rx_queue[tail];
rx_tail = tail;
rx_queue_packets(head, tail);
return c;
}

size_t ADK::write(size_t len, uint8_t *buf)
{
memcpy(tx_buffer, buf, len);
__disable_irq();
queue_Data_Transfer(txpipe, tx_buffer, len, this);
__enable_irq();
return len;
}

bool ADK::ready()
{
if (state > 7)
return true;
return false;
}

Loading…
Cancel
Save