| #include <stdint.h> | #include <stdint.h> | ||||
| // Dear inquisitive reader, USB is a complex protocol defined with | |||||
| // very specific terminology. To have any chance of understand this | |||||
| // source code, you absolutely must have solid knowledge of specific | |||||
| // USB terms such as host, device, endpoint, pipe, enumeration.... | |||||
| // You really must also have at least a basic knowledge of the | |||||
| // different USB transfers: control, bulk, interrupt, isochronous. | |||||
| // | |||||
| // The USB 2.0 specification explains these in chapter 4 (pages 15 | |||||
| // to 24), and provides more detail in the first part of chapter 5 | |||||
| // (pages 25 to 55). The USB spec is published for free at | |||||
| // www.usb.org. Here is a convenient link to just the main PDF: | |||||
| // | |||||
| // https://www.pjrc.com/teensy/beta/usb20.pdf | |||||
| // | |||||
| // This is a huge file, but chapter 4 is short and easy to read. | |||||
| // If you're not familiar with the USB lingo, please do yourself | |||||
| // a favor by reading at least chapter 4 to get up to speed on the | |||||
| // meaning of these important USB concepts and terminology. | |||||
| // | |||||
| // If you wish to ask questions (which belong on the forum, not | |||||
| // github issues) or discuss development of this library, you | |||||
| // ABSOLUTELY MUST know the basic USB terminology from chapter 4. | |||||
| // Please repect other people's valuable time & effort by making | |||||
| // your best effort to read chapter 4 before asking USB questions! | |||||
| #define USBHOST_PRINT_DEBUG | #define USBHOST_PRINT_DEBUG | ||||
| /************************************************/ | /************************************************/ |
| #include <Arduino.h> | #include <Arduino.h> | ||||
| #include "USBHost_t36.h" // Read this header first for key info | #include "USBHost_t36.h" // Read this header first for key info | ||||
| // All USB EHCI controller hardware access is done from this file's code. | |||||
| // Hardware services are made available to the rest of this library by | |||||
| // three structures: | |||||
| // | |||||
| // Pipe_t: Every USB endpoint is accessed by a pipe. new_Pipe() | |||||
| // sets up the EHCI to support the pipe/endpoint, and delete_Pipe() | |||||
| // removes this configuration. | |||||
| // | |||||
| // Transfer_t: These are used for all communication. Data transfers | |||||
| // are placed into work queues, to be executed by the EHCI in | |||||
| // the future. Transfer_t only manages data. The actual data | |||||
| // is stored in a separate buffer (usually from a device driver) | |||||
| // which is referenced from Transfer_t. All data transfer is queued, | |||||
| // never done with blocking functions that wait. When transfers | |||||
| // complete, a driver-supplied callback function is called to notify | |||||
| // the driver. | |||||
| // | |||||
| // USBDriverTimer: Some drivers require timers. These allow drivers | |||||
| // to share the hardware timer, with each USBDriverTimer object | |||||
| // able to schedule a callback function a configurable number of | |||||
| // microseconds in the future. | |||||
| // | |||||
| // In addition to these 3 services, the EHCI interrupt also responds | |||||
| // to changes on the main port, creating and deleting the root device. | |||||
| // See enumeration.cpp for all device-level code. | |||||
| // Size of the periodic list, in milliseconds. This determines the | // Size of the periodic list, in milliseconds. This determines the | ||||
| // slowest rate we can poll interrupt endpoints. Each entry uses | // slowest rate we can poll interrupt endpoints. Each entry uses | ||||
| // 12 bytes (4 for a pointer, 8 for bandwidth management). | // 12 bytes (4 for a pointer, 8 for bandwidth management). | ||||
| // may be 8, 16, 32, 64, 128, 256, 512, 1024 | |||||
| // Supported values: 8, 16, 32, 64, 128, 256, 512, 1024 | |||||
| #define PERIODIC_LIST_SIZE 32 | #define PERIODIC_LIST_SIZE 32 | ||||
| // The EHCI periodic schedule, used for interrupt pipes/endpoints | |||||
| static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used)); | static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used)); | ||||
| static uint8_t uframe_bandwidth[PERIODIC_LIST_SIZE*8]; | static uint8_t uframe_bandwidth[PERIODIC_LIST_SIZE*8]; | ||||
| // State of the 1 and only physical USB host port on Teensy 3.6 | |||||
| static uint8_t port_state; | static uint8_t port_state; | ||||
| #define PORT_STATE_DISCONNECTED 0 | #define PORT_STATE_DISCONNECTED 0 | ||||
| #define PORT_STATE_DEBOUNCE 1 | #define PORT_STATE_DEBOUNCE 1 | ||||
| #define PORT_STATE_RESET 2 | #define PORT_STATE_RESET 2 | ||||
| #define PORT_STATE_RECOVERY 3 | #define PORT_STATE_RECOVERY 3 | ||||
| #define PORT_STATE_ACTIVE 4 | #define PORT_STATE_ACTIVE 4 | ||||
| // The device currently connected, or NULL when no device | |||||
| static Device_t *rootdev=NULL; | static Device_t *rootdev=NULL; | ||||
| // List of all queued transfers in the asychronous schedule (control & bulk). | |||||
| // When the EHCI completes these transfers, this list is how we locate them | |||||
| // in memory. | |||||
| static Transfer_t *async_followup_first=NULL; | static Transfer_t *async_followup_first=NULL; | ||||
| static Transfer_t *async_followup_last=NULL; | static Transfer_t *async_followup_last=NULL; | ||||
| // List of all queued transfers in the asychronous schedule (interrupt endpoints) | |||||
| // When the EHCI completes these transfers, this list is how we locate them | |||||
| // in memory. | |||||
| static Transfer_t *periodic_followup_first=NULL; | static Transfer_t *periodic_followup_first=NULL; | ||||
| static Transfer_t *periodic_followup_last=NULL; | static Transfer_t *periodic_followup_last=NULL; | ||||
| // List of all pending timers. This double linked list is stored in | |||||
| // chronological order. Each timer is stored with the number of | |||||
| // microseconds which need to elapsed from the prior timer on this | |||||
| // list, to allow efficient servicing from the timer interrupt. | |||||
| static USBDriverTimer *active_timers=NULL; | static USBDriverTimer *active_timers=NULL; | ||||
| #include "USBHost_t36.h" // Read this header first for key info | #include "USBHost_t36.h" // Read this header first for key info | ||||
| // USB devices are managed from this file. | |||||
| // List of all connected devices, regardless of their status. If | |||||
| // it's connected to the EHCI port or any port on any hub, it needs | |||||
| // to be linked into this list. | |||||
| static Device_t *devlist=NULL; | |||||
| // List of all inactive drivers. At the end of enumeration, when | |||||
| // drivers claim the device or its interfaces, they are removed | |||||
| // from this list and linked into the list of active drivers on | |||||
| // that device. When devices disconnect, the drivers are returned | |||||
| // to this list, making them again available for enumeration of new | |||||
| // devices. | |||||
| static USBDriver *available_drivers = NULL; | static USBDriver *available_drivers = NULL; | ||||
| // Static buffers used during enumeration. One a single USB device | |||||
| // may enumerate at once, because USB address zero is used, and | |||||
| // because this static buffer & state info can't be shared. | |||||
| static uint8_t enumbuf[256] __attribute__ ((aligned(16))); | static uint8_t enumbuf[256] __attribute__ ((aligned(16))); | ||||
| static setup_t enumsetup __attribute__ ((aligned(16))); | static setup_t enumsetup __attribute__ ((aligned(16))); | ||||
| static uint16_t enumlen; | static uint16_t enumlen; | ||||
| static Device_t *devlist=NULL; | |||||
| // True while any device is present but not yet fully configured. | // True while any device is present but not yet fully configured. | ||||
| // Only one USB device may be in this state at a time (responding | // Only one USB device may be in this state at a time (responding | ||||
| // to address zero) and using the enumeration static buffer. | // to address zero) and using the enumeration static buffer. | ||||
| volatile bool USBHost::enumeration_busy = false; | volatile bool USBHost::enumeration_busy = false; | ||||
| static void pipe_set_maxlen(Pipe_t *pipe, uint32_t maxlen); | static void pipe_set_maxlen(Pipe_t *pipe, uint32_t maxlen); | ||||
| static void pipe_set_addr(Pipe_t *pipe, uint32_t addr); | static void pipe_set_addr(Pipe_t *pipe, uint32_t addr); | ||||
| // The main user function to cause internal state to update. Since we do | |||||
| // almost everything with DMA and interrupts, the only work to do here is | |||||
| // call all the active driver Task() functions. | |||||
| void USBHost::Task() | void USBHost::Task() | ||||
| { | { | ||||
| for (Device_t *dev = devlist; dev; dev = dev->next) { | for (Device_t *dev = devlist; dev; dev = dev->next) { | ||||
| } | } | ||||
| } | } | ||||
| // Drivers call this after they've completed initialization, so get themselves | |||||
| // added to the list of inactive drivers available for new devices during | |||||
| // enumeraton. Typically this is called from constructors, so hardware access | |||||
| // or even printing debug messages should be avoided here. Just initialize | |||||
| // lists and return. | |||||
| // | |||||
| void USBHost::driver_ready_for_device(USBDriver *driver) | void USBHost::driver_ready_for_device(USBDriver *driver) | ||||
| { | { | ||||
| driver->device = NULL; | driver->device = NULL; | ||||
| } | } | ||||
| // Control transfer callback function. ALL control transfers from all | |||||
| // devices call this function when they complete. When control transfers | |||||
| // are created by drivers, the driver is called to handle the result. | |||||
| // Otherwise, the control transfer is part of the enumeration process, | |||||
| // which is implemented here. | |||||
| // | |||||
| void USBHost::enumeration(const Transfer_t *transfer) | void USBHost::enumeration(const Transfer_t *transfer) | ||||
| { | { | ||||
| Device_t *dev; | Device_t *dev; |
| #include "USBHost_t36.h" // Read this header first for key info | #include "USBHost_t36.h" // Read this header first for key info | ||||
| // Memory allocation | |||||
| // Memory allocation for Device_t, Pipe_t and Transfer_t structures. | |||||
| // | |||||
| // To provide an Arduino-friendly experience, the memory allocation of | |||||
| // these item is primarily done by the instances of device driver objects, | |||||
| // which are typically created as static objects near the beginning of | |||||
| // the Arduino sketch. Static allocation allows Arduino's memory usage | |||||
| // summary to accurately show the amount of RAM this library is using. | |||||
| // Users can choose which devices they wish to support and how many of | |||||
| // each by creating more object instances. | |||||
| // | |||||
| // Device driver objects "contribute" their copies of these structures. | |||||
| // When ehci.cpp allocates Pipe_t and Transfer_t, or enumeration.cpp | |||||
| // allocates Device_t, the memory actually comes from these structures | |||||
| // physically located within the device driver instances. The usage | |||||
| // model looks like traditional malloc/free dynamic memory on the heap, | |||||
| // but in fact it's a simple memory pool from the drivers. | |||||
| // | |||||
| // Timing is deterministic and fast, because each pool allocates only | |||||
| // a single fixed size object. In theory, each driver should contribute | |||||
| // the number of items it will use, so we should not ever end up with | |||||
| // a situation where an item can't be allocated when it's needed. Well, | |||||
| // unless there's a bug or oversight... | |||||
| // Lists of "free" memory | |||||
| static Device_t * free_Device_list = NULL; | |||||
| static Pipe_t * free_Pipe_list = NULL; | |||||
| static Transfer_t * free_Transfer_list = NULL; | |||||
| // A small amount of non-driver memory, just to get things started | |||||
| // TODO: is this really necessary? Can these be eliminated, so we | |||||
| // use only memory from the drivers? | |||||
| static Device_t memory_Device[1]; | static Device_t memory_Device[1]; | ||||
| static Pipe_t memory_Pipe[1] __attribute__ ((aligned(32))); | static Pipe_t memory_Pipe[1] __attribute__ ((aligned(32))); | ||||
| static Transfer_t memory_Transfer[4] __attribute__ ((aligned(32))); | static Transfer_t memory_Transfer[4] __attribute__ ((aligned(32))); | ||||
| static Device_t * free_Device_list = NULL; | |||||
| static Pipe_t * free_Pipe_list = NULL; | |||||
| static Transfer_t * free_Transfer_list = NULL; | |||||
| void USBHost::init_Device_Pipe_Transfer_memory(void) | void USBHost::init_Device_Pipe_Transfer_memory(void) | ||||
| { | { | ||||
| contribute_Devices(memory_Device, sizeof(memory_Device)/sizeof(Device_t)); | contribute_Devices(memory_Device, sizeof(memory_Device)/sizeof(Device_t)); |
| #include <Arduino.h> | #include <Arduino.h> | ||||
| #include "USBHost_t36.h" // Read this header first for key info | #include "USBHost_t36.h" // Read this header first for key info | ||||
| // Printing of specific data structures. When this is enabled, | |||||
| // a tremendous amount of debug printing occurs. It's done all | |||||
| // from interrupt context, so this should never normally be | |||||
| // enabled for regular programs that print from the Arduino sketch. | |||||
| #ifdef USBHOST_PRINT_DEBUG | #ifdef USBHOST_PRINT_DEBUG | ||||
| void USBHost::print(const Transfer_t *transfer) | void USBHost::print(const Transfer_t *transfer) |