Преглед изворни кода

(bare bones) Interrupt transfer support

main
PaulStoffregen пре 8 година
родитељ
комит
252b9a2f24
4 измењених фајлова са 113 додато и 8 уклоњено
  1. +3
    -1
      USBHost.h
  2. +87
    -6
      ehci.cpp
  3. +1
    -0
      enumeration.cpp
  4. +22
    -1
      hub.cpp

+ 3
- 1
USBHost.h Прегледај датотеку

static void begin(); static void begin();
protected: protected:
static Pipe_t * new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint, 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, static bool queue_Control_Transfer(Device_t *dev, setup_t *setup,
void *buf, USBDriver *driver); void *buf, USBDriver *driver);
static bool queue_Data_Transfer(Pipe_t *pipe, void *buffer, static bool queue_Data_Transfer(Pipe_t *pipe, void *buffer,
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors); virtual bool claim(Device_t *device, int type, const uint8_t *descriptors);
virtual bool control(const Transfer_t *transfer); virtual bool control(const Transfer_t *transfer);
void poweron(uint32_t port); void poweron(uint32_t port);
static void callback(const Transfer_t *transfer);
void status_change(const Transfer_t *transfer);
setup_t setup; setup_t setup;
uint8_t hub_desc[16]; uint8_t hub_desc[16];
uint8_t endpoint; uint8_t endpoint;

+ 87
- 6
ehci.cpp Прегледај датотеку

#include <Arduino.h> #include <Arduino.h>
#include "USBHost.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 uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used));
static uint8_t port_state; static uint8_t port_state;
static void remove_from_async_followup_list(Transfer_t *transfer); 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 add_to_periodic_followup_list(Transfer_t *first, Transfer_t *last);
static void remove_from_periodic_followup_list(Transfer_t *transfer); 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() void USBHost::begin()
{ {
USBHS_FRINDEX = 0; USBHS_FRINDEX = 0;
USBHS_ASYNCLISTADDR = 0; USBHS_ASYNCLISTADDR = 0;
USBHS_USBCMD = USBHS_USBCMD_ITC(8) | USBHS_USBCMD_RS | 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 #if PERIODIC_LIST_SIZE == 8
USBHS_USBCMD_FS2 | USBHS_USBCMD_FS(3); USBHS_USBCMD_FS2 | USBHS_USBCMD_FS(3);
#elif PERIODIC_LIST_SIZE == 16 #elif PERIODIC_LIST_SIZE == 16
(split_completion_mask << 8) | (interrupt_schedule_mask << 0) ); (split_completion_mask << 8) | (interrupt_schedule_mask << 0) );
} }




// Create a new pipe. It's QH is added to the async or periodic schedule, // 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. // 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, 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; Pipe_t *pipe;
Transfer_t *halt; 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"); Serial.println("new_Pipe");
pipe = allocate_Pipe(); pipe = allocate_Pipe();
free_Pipe(pipe); free_Pipe(pipe);
return NULL; 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(pipe, 0, sizeof(Pipe_t));
memset(halt, 0, sizeof(Transfer_t)); memset(halt, 0, sizeof(Transfer_t));
halt->qtd.next = 1; halt->qtd.next = 1;
} else if (type == 3) { } else if (type == 3) {
// interrupt // 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); dtc, dev->speed, endpoint, 0, dev->address);
pipe->qh.capabilities[1] = QH_capabilities2(1, dev->hub_port, 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) { if (type == 0 || type == 2) {
// control or bulk: add to async queue // control or bulk: add to async queue
} else if (type == 3) { } else if (type == 3) {
// interrupt: add to periodic schedule // interrupt: add to periodic schedule
// TODO: link it into the periodic table // 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; return pipe;
} }
} }
} }



// 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;
}



+ 1
- 0
enumeration.cpp Прегледај датотеку

dev->enum_state = 1; dev->enum_state = 1;
return; return;
case 1: // request all 18 bytes of device descriptor case 1: // request all 18 bytes of device descriptor
dev->address = enumsetup.wValue;
pipe_set_addr(dev->control_pipe, enumsetup.wValue); pipe_set_addr(dev->control_pipe, enumsetup.wValue);
mk_setup(enumsetup, 0x80, 6, 0x0100, 0, 18); // 6=GET_DESCRIPTOR mk_setup(enumsetup, 0x80, 6, 0x0100, 0, 18); // 6=GET_DESCRIPTOR
queue_Control_Transfer(dev, &enumsetup, enumbuf, NULL); queue_Control_Transfer(dev, &enumsetup, enumbuf, NULL);

+ 22
- 1
hub.cpp Прегледај датотеку

} else if (state == numports) { } else if (state == numports) {
Serial.println("power turned on to all ports"); Serial.println("power turned on to all ports");
// TODO: create interrupt pipe for status change notifications // 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; state = 255;
} else if (state == 255) { } else if (state == 255) {
// parse a status response // parse a status response
return true; 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 config descriptor from a Multi-TT hub

Loading…
Откажи
Сачувај