mirror of
https://github.com/gdsports/USBHost_t36
synced 2024-11-21 08:35:03 -05:00
(bare bones) Interrupt transfer support
This commit is contained in:
parent
448910a94f
commit
252b9a2f24
@ -139,7 +139,7 @@ public:
|
||||
static void begin();
|
||||
protected:
|
||||
static Pipe_t * new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
|
||||
uint32_t direction, uint32_t max_packet_len);
|
||||
uint32_t direction, uint32_t maxlen, uint32_t interval=0);
|
||||
static bool queue_Control_Transfer(Device_t *dev, setup_t *setup,
|
||||
void *buf, USBDriver *driver);
|
||||
static bool queue_Data_Transfer(Pipe_t *pipe, void *buffer,
|
||||
@ -232,6 +232,8 @@ protected:
|
||||
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors);
|
||||
virtual bool control(const Transfer_t *transfer);
|
||||
void poweron(uint32_t port);
|
||||
static void callback(const Transfer_t *transfer);
|
||||
void status_change(const Transfer_t *transfer);
|
||||
setup_t setup;
|
||||
uint8_t hub_desc[16];
|
||||
uint8_t endpoint;
|
||||
|
93
ehci.cpp
93
ehci.cpp
@ -24,7 +24,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "USBHost.h"
|
||||
|
||||
#define PERIODIC_LIST_SIZE 64
|
||||
#define PERIODIC_LIST_SIZE 32
|
||||
|
||||
static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used));
|
||||
static uint8_t port_state;
|
||||
@ -47,6 +47,9 @@ static void add_to_async_followup_list(Transfer_t *first, Transfer_t *last);
|
||||
static void remove_from_async_followup_list(Transfer_t *transfer);
|
||||
static void add_to_periodic_followup_list(Transfer_t *first, Transfer_t *last);
|
||||
static void remove_from_periodic_followup_list(Transfer_t *transfer);
|
||||
static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen,
|
||||
uint32_t interval, uint32_t direction, uint32_t *offset, uint32_t *smask,
|
||||
uint32_t *cmask);
|
||||
|
||||
void USBHost::begin()
|
||||
{
|
||||
@ -132,7 +135,7 @@ void USBHost::begin()
|
||||
USBHS_FRINDEX = 0;
|
||||
USBHS_ASYNCLISTADDR = 0;
|
||||
USBHS_USBCMD = USBHS_USBCMD_ITC(8) | USBHS_USBCMD_RS |
|
||||
USBHS_USBCMD_ASP(3) | USBHS_USBCMD_ASPE |
|
||||
USBHS_USBCMD_ASP(3) | USBHS_USBCMD_ASPE | USBHS_USBCMD_PSE |
|
||||
#if PERIODIC_LIST_SIZE == 8
|
||||
USBHS_USBCMD_FS2 | USBHS_USBCMD_FS(3);
|
||||
#elif PERIODIC_LIST_SIZE == 16
|
||||
@ -342,15 +345,23 @@ static uint32_t QH_capabilities2(uint32_t high_bw_mult, uint32_t hub_port_number
|
||||
(split_completion_mask << 8) | (interrupt_schedule_mask << 0) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Create a new pipe. It's QH is added to the async or periodic schedule,
|
||||
// and a halt qTD is added to the QH, so we can grow the qTD list later.
|
||||
// dev: device owning this pipe/endpoint
|
||||
// type: 0=control, 2=bulk, 3=interrupt
|
||||
// endpoint: 0 for control, 1-15 for bulk or interrupt
|
||||
// direction: 0=OUT, 1=IN (unused for control)
|
||||
// maxlen: maximum packet size
|
||||
// interval: polling interval for interrupt, power of 2, unused if control or bulk
|
||||
//
|
||||
Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
|
||||
uint32_t direction, uint32_t max_packet_len)
|
||||
uint32_t direction, uint32_t maxlen, uint32_t interval)
|
||||
{
|
||||
Pipe_t *pipe;
|
||||
Transfer_t *halt;
|
||||
uint32_t c=0, dtc=0;
|
||||
uint32_t c=0, dtc=0, smask=0, cmask=0, offset=0;
|
||||
|
||||
Serial.println("new_Pipe");
|
||||
pipe = allocate_Pipe();
|
||||
@ -360,6 +371,17 @@ Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
|
||||
free_Pipe(pipe);
|
||||
return NULL;
|
||||
}
|
||||
if (type == 3) {
|
||||
// interrupt transfers require bandwidth & microframe scheduling
|
||||
if (interval > PERIODIC_LIST_SIZE*8) interval = PERIODIC_LIST_SIZE*8;
|
||||
if (dev->speed < 2 && interval < 8) interval = 8;
|
||||
if (!allocate_interrupt_pipe_bandwidth(dev->speed,
|
||||
maxlen, interval, direction, &offset, &smask, &cmask)) {
|
||||
free_Transfer(halt);
|
||||
free_Pipe(pipe);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memset(pipe, 0, sizeof(Pipe_t));
|
||||
memset(halt, 0, sizeof(Transfer_t));
|
||||
halt->qtd.next = 1;
|
||||
@ -378,10 +400,10 @@ Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
|
||||
} else if (type == 3) {
|
||||
// interrupt
|
||||
}
|
||||
pipe->qh.capabilities[0] = QH_capabilities1(15, c, max_packet_len, 0,
|
||||
pipe->qh.capabilities[0] = QH_capabilities1(15, c, maxlen, 0,
|
||||
dtc, dev->speed, endpoint, 0, dev->address);
|
||||
pipe->qh.capabilities[1] = QH_capabilities2(1, dev->hub_port,
|
||||
dev->hub_address, 0, 0);
|
||||
dev->hub_address, cmask, smask);
|
||||
|
||||
if (type == 0 || type == 2) {
|
||||
// control or bulk: add to async queue
|
||||
@ -401,6 +423,18 @@ Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
|
||||
} else if (type == 3) {
|
||||
// interrupt: add to periodic schedule
|
||||
// TODO: link it into the periodic table
|
||||
|
||||
// TODO: built tree...
|
||||
//uint32_t finterval = interval >> 3;
|
||||
//for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += finterval) {
|
||||
// uint32_t list = periodictable[i];
|
||||
//}
|
||||
|
||||
// quick hack for testing, just put it into the first table entry
|
||||
pipe->qh.horizontal_link = periodictable[0];
|
||||
periodictable[0] = (uint32_t)&(pipe->qh) | 2; // 2=QH
|
||||
Serial.print("init periodictable with ");
|
||||
Serial.println(periodictable[0], HEX);
|
||||
}
|
||||
return pipe;
|
||||
}
|
||||
@ -668,3 +702,50 @@ static void remove_from_periodic_followup_list(Transfer_t *transfer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Allocate bandwidth for an interrupt pipe. Given the packet size
|
||||
// and other parameters, find the best place to schedule this pipe.
|
||||
// Returns true if enough bandwidth is available, and the best
|
||||
// frame offset, smask and cmask. Or returns false if no group
|
||||
// of microframes has enough bandwidth available.
|
||||
//
|
||||
// speed: [in] 0=full speed, 1=low speed, 2=high speed
|
||||
// maxlen: [in] maximum packet length
|
||||
// interval: [in] polling interval, in 125 us micro frames
|
||||
// direction: [in] 0=OUT, 1=IN
|
||||
// offset: [out] frame offset, 0 to PERIODIC_LIST_SIZE-1
|
||||
// smask: [out] Start Mask
|
||||
// cmask: [out] Complete Mask
|
||||
//
|
||||
static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen,
|
||||
uint32_t interval, uint32_t direction, uint32_t *offset, uint32_t *smask,
|
||||
uint32_t *cmask)
|
||||
{
|
||||
// TODO: actual bandwidth planning needs to go here... but for
|
||||
// now we'll just always pile up everything at the same offset
|
||||
// and same microframe schedule for split transactions, without
|
||||
// even the slighest check whether it all fits.
|
||||
|
||||
if (speed == 2) {
|
||||
// high speed 480 Mbit/sec
|
||||
if (interval == 1) {
|
||||
*smask = 0xFF;
|
||||
} else if (interval == 2) {
|
||||
*smask = 0x55;
|
||||
} else if (interval <= 4) {
|
||||
*smask = 0x11;
|
||||
} else {
|
||||
*smask = 0x01;
|
||||
}
|
||||
*cmask = 0;
|
||||
*offset = 0;
|
||||
} else {
|
||||
// full speed 12 Mbit/sec or low speed 1.5 Mbit/sec
|
||||
*smask = 0x01;
|
||||
*cmask = 0x3C;
|
||||
*offset = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -113,6 +113,7 @@ void USBHost::enumeration(const Transfer_t *transfer)
|
||||
dev->enum_state = 1;
|
||||
return;
|
||||
case 1: // request all 18 bytes of device descriptor
|
||||
dev->address = enumsetup.wValue;
|
||||
pipe_set_addr(dev->control_pipe, enumsetup.wValue);
|
||||
mk_setup(enumsetup, 0x80, 6, 0x0100, 0, 18); // 6=GET_DESCRIPTOR
|
||||
queue_Control_Transfer(dev, &enumsetup, enumbuf, NULL);
|
||||
|
23
hub.cpp
23
hub.cpp
@ -114,7 +114,13 @@ bool USBHub::control(const Transfer_t *transfer)
|
||||
} else if (state == numports) {
|
||||
Serial.println("power turned on to all ports");
|
||||
// TODO: create interrupt pipe for status change notifications
|
||||
changepipe = new_Pipe(device, 3, endpoint, 1, 1);
|
||||
Serial.print("device addr = ");
|
||||
Serial.println(device->address);
|
||||
changepipe = new_Pipe(device, 3, endpoint, 1, 1, 512);
|
||||
Serial.print("pipe cap1 = ");
|
||||
Serial.println(changepipe->qh.capabilities[0], HEX);
|
||||
changepipe->callback_function = callback;
|
||||
queue_Data_Transfer(changepipe, &changebits, 1, this);
|
||||
state = 255;
|
||||
} else if (state == 255) {
|
||||
// parse a status response
|
||||
@ -122,6 +128,21 @@ bool USBHub::control(const Transfer_t *transfer)
|
||||
return true;
|
||||
}
|
||||
|
||||
void USBHub::callback(const Transfer_t *transfer)
|
||||
{
|
||||
Serial.println("HUB Callback (static)");
|
||||
if (transfer->driver) ((USBHub *)(transfer->driver))->status_change(transfer);
|
||||
}
|
||||
|
||||
void USBHub::status_change(const Transfer_t *transfer)
|
||||
{
|
||||
Serial.println("HUB Callback (member)");
|
||||
Serial.print("status = ");
|
||||
Serial.println(changebits, HEX);
|
||||
// TODO: do something with the status change info
|
||||
queue_Data_Transfer(changepipe, &changebits, 1, this);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
config descriptor from a Multi-TT hub
|
||||
|
Loading…
Reference in New Issue
Block a user