mirror of
https://github.com/gdsports/USBHost_t36
synced 2025-02-16 06:50:14 -05:00
Add documentation (in source code comments)
This commit is contained in:
parent
5935deb8ce
commit
3355eab2a2
@ -26,6 +26,33 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
|
||||||
/************************************************/
|
/************************************************/
|
||||||
|
46
ehci.cpp
46
ehci.cpp
@ -24,25 +24,69 @@
|
|||||||
#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;
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,21 +25,43 @@
|
|||||||
#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) {
|
||||||
@ -49,6 +71,12 @@ void USBHost::Task()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
@ -107,7 +135,12 @@ Device_t * USBHost::new_Device(uint32_t speed, uint32_t hub_addr, uint32_t hub_p
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
34
memory.cpp
34
memory.cpp
@ -25,16 +25,42 @@
|
|||||||
#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...
|
||||||
|
|
||||||
static Device_t memory_Device[1];
|
|
||||||
static Pipe_t memory_Pipe[1] __attribute__ ((aligned(32)));
|
|
||||||
static Transfer_t memory_Transfer[4] __attribute__ ((aligned(32)));
|
|
||||||
|
|
||||||
|
// Lists of "free" memory
|
||||||
static Device_t * free_Device_list = NULL;
|
static Device_t * free_Device_list = NULL;
|
||||||
static Pipe_t * free_Pipe_list = NULL;
|
static Pipe_t * free_Pipe_list = NULL;
|
||||||
static Transfer_t * free_Transfer_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 Pipe_t memory_Pipe[1] __attribute__ ((aligned(32)));
|
||||||
|
static Transfer_t memory_Transfer[4] __attribute__ ((aligned(32)));
|
||||||
|
|
||||||
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));
|
||||||
|
@ -24,6 +24,11 @@
|
|||||||
#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)
|
||||||
|
Loading…
Reference in New Issue
Block a user