/* 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 #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((char *)adkbuf, 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=",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; uint8_t *p = (uint8_t *)transfer->buffer; if (p != NULL && len != 0) { do { if (++head >= RX_QUEUE_SIZE) head = 0; rx_queue[head] = *p++; } while (--len); } rx_head = head; rx_packet_queued = false; rx_queue_packets(rx_head, rx_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 >= rx_size) { // 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; }