Pārlūkot izejas kodu

Clearer bandwidth allocation code & store in Pipe_t

main
PaulStoffregen pirms 8 gadiem
vecāks
revīzija
1c5d403aba
2 mainītis faili ar 111 papildinājumiem un 70 dzēšanām
  1. +13
    -4
      USBHost.h
  2. +98
    -66
      ehci.cpp

+ 13
- 4
USBHost.h Parādīt failu

Device_t *device; Device_t *device;
uint8_t type; // 0=control, 1=isochronous, 2=bulk, 3=interrupt uint8_t type; // 0=control, 1=isochronous, 2=bulk, 3=interrupt
uint8_t direction; // 0=out, 1=in (changes for control, others fixed) uint8_t direction; // 0=out, 1=in (changes for control, others fixed)
uint8_t unusedbyte[2];
uint8_t start_mask;
uint8_t complete_mask;
Pipe_t *next; Pipe_t *next;
void (*callback_function)(const Transfer_t *); void (*callback_function)(const Transfer_t *);
uint16_t periodic_interval;
uint16_t periodic_offset;
uint32_t unused1;
uint32_t unused2;
uint32_t unused3;
uint32_t unused4;
uint32_t unused5;
uint32_t unused6;
uint32_t unused7;
}; };


// Transfer_t represents a single transaction on the USB bus. // Transfer_t represents a single transaction on the USB bus.
static void free_Pipe(Pipe_t *q); static void free_Pipe(Pipe_t *q);
static Transfer_t * allocate_Transfer(void); static Transfer_t * allocate_Transfer(void);
static void free_Transfer(Transfer_t *q); static void free_Transfer(Transfer_t *q);
static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen,
uint32_t interval, uint32_t direction, uint32_t *offset_out,
uint32_t *smask_out, uint32_t *cmask_out);
static bool allocate_interrupt_pipe_bandwidth(Pipe_t *pipe,
uint32_t maxlen, uint32_t interval);
protected: protected:
static void print(const Transfer_t *transfer); static void print(const Transfer_t *transfer);
static void print(const Transfer_t *first, const Transfer_t *last); static void print(const Transfer_t *first, const Transfer_t *last);

+ 98
- 66
ehci.cpp Parādīt failu

println("sizeof Device = ", sizeof(Device_t)); println("sizeof Device = ", sizeof(Device_t));
println("sizeof Pipe = ", sizeof(Pipe_t)); println("sizeof Pipe = ", sizeof(Pipe_t));
println("sizeof Transfer = ", sizeof(Transfer_t)); println("sizeof Transfer = ", sizeof(Transfer_t));
if ((sizeof(Pipe_t) & 0x1F) || (sizeof(Transfer_t) & 0x1F)) {
println("ERROR: Pipe_t & Transfer_t must be multiples of 32 bytes!");
while (1) ; // die here
}


// configure the MPU to allow USBHS DMA to access memory // configure the MPU to allow USBHS DMA to access memory
MPU_RGDAAC0 |= 0x30000000; MPU_RGDAAC0 |= 0x30000000;
{ {
Pipe_t *pipe; Pipe_t *pipe;
Transfer_t *halt; Transfer_t *halt;
uint32_t c=0, dtc=0, smask=0, cmask=0, offset=0;
uint32_t c=0, dtc=0;


println("new_Pipe"); println("new_Pipe");
pipe = allocate_Pipe(); pipe = allocate_Pipe();
free_Pipe(pipe); free_Pipe(pipe);
return NULL; return NULL;
} }
memset(pipe, 0, sizeof(Pipe_t));
memset(halt, 0, sizeof(Transfer_t));
halt->qtd.next = 1;
halt->qtd.token = 0x40;
pipe->device = dev;
pipe->qh.next = (uint32_t)halt;
pipe->qh.alt_next = 1;
pipe->direction = direction;
pipe->type = type;
if (type == 3) { if (type == 3) {
// interrupt transfers require bandwidth & microframe scheduling // 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)) {
if (!allocate_interrupt_pipe_bandwidth(pipe, maxlen, interval)) {
free_Transfer(halt); free_Transfer(halt);
free_Pipe(pipe); free_Pipe(pipe);
return NULL; return NULL;
} }
} }
memset(pipe, 0, sizeof(Pipe_t));
if (endpoint > 0) { if (endpoint > 0) {
// if non-control pipe, update dev->data_pipes list // if non-control pipe, update dev->data_pipes list
Pipe_t *p = dev->data_pipes; Pipe_t *p = dev->data_pipes;
p->next = pipe; p->next = pipe;
} }
} }
memset(halt, 0, sizeof(Transfer_t));
halt->qtd.next = 1;
halt->qtd.token = 0x40;
pipe->device = dev;
pipe->qh.next = (uint32_t)halt;
pipe->qh.alt_next = 1;
pipe->direction = direction;
pipe->type = type;
if (type == 0) { if (type == 0) {
// control // control
if (dev->speed < 2) c = 1; if (dev->speed < 2) c = 1;
pipe->qh.capabilities[0] = QH_capabilities1(15, c, maxlen, 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, cmask, smask);
dev->hub_address, pipe->complete_mask, pipe->start_mask);


if (type == 0 || type == 2) { if (type == 0 || type == 2) {
// control or bulk: add to async queue // control or bulk: add to async queue
// interrupt: add to periodic schedule // interrupt: add to periodic schedule
// TODO: link it into the periodic table // TODO: link it into the periodic table



//add_qh_to_periodic_schedule(pipe);

// TODO: built tree... // TODO: built tree...
//uint32_t finterval = interval >> 3; //uint32_t finterval = interval >> 3;
//for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += finterval) { //for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += finterval) {
return n4; return n4;
} }


static uint32_t round_to_power_of_two(uint32_t n, uint32_t maxnum)
{
for (uint32_t pow2num=1; pow2num < maxnum; pow2num <<= 1) {
if (n <= (pow2num | (pow2num >> 1))) return pow2num;
}
return maxnum;
}

// Allocate bandwidth for an interrupt pipe. Given the packet size // Allocate bandwidth for an interrupt pipe. Given the packet size
// and other parameters, find the best place to schedule this pipe. // and other parameters, find the best place to schedule this pipe.
// Returns true if enough bandwidth is available, and the best // Returns true if enough bandwidth is available, and the best
// frame offset, smask and cmask. Or returns false if no group // frame offset, smask and cmask. Or returns false if no group
// of microframes has enough bandwidth available. // 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
// pipe:
// device->speed [in] 0=full speed, 1=low speed, 2=high speed
// direction [in] 0=OUT, 1=IN
// start_mask [out] uframes to start transfer
// complete_mask [out] uframes to complete transfer (FS & LS only)
// periodic_interval [out] fream repeat level: 1, 2, 4, 8... PERIODIC_LIST_SIZE
// periodic_offset [out] frame repeat offset: 0 to periodic_interval-1
// maxlen: [in] maximum packet length
// interval: [in] polling interval: LS+FS: frames, HS: 2^(n-1) uframes
// //
bool USBHost::allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen,
uint32_t interval, uint32_t direction, uint32_t *offset_out,
uint32_t *smask_out, uint32_t *cmask_out)
bool USBHost::allocate_interrupt_pipe_bandwidth(Pipe_t *pipe, uint32_t maxlen, uint32_t interval)
{ {
println("allocate_interrupt_pipe_bandwidth"); println("allocate_interrupt_pipe_bandwidth");
if (interval == 0) interval = 1;
maxlen = (maxlen * 76459) >> 16; // worst case bit stuffing maxlen = (maxlen * 76459) >> 16; // worst case bit stuffing
if (speed == 2) {
if (pipe->device->speed == 2) {
// high speed 480 Mbit/sec // high speed 480 Mbit/sec
if (interval > 15) interval = 15;
interval = 1 << (interval - 1);
if (interval > PERIODIC_LIST_SIZE*8) interval = PERIODIC_LIST_SIZE*8;
uint32_t stime = (55 + 32 + maxlen) >> 5; // time units: 32 bytes or 533 ns uint32_t stime = (55 + 32 + maxlen) >> 5; // time units: 32 bytes or 533 ns
uint32_t min_offset = 0xFFFFFFFF;
uint32_t min_bw = 0xFFFFFFFF;
uint32_t best_offset = 0xFFFFFFFF;
uint32_t best_bandwidth = 0xFFFFFFFF;
for (uint32_t offset=0; offset < interval; offset++) { for (uint32_t offset=0; offset < interval; offset++) {
uint32_t max_bw = 0;
// for each possible uframe offset, find the worst uframe bandwidth
uint32_t max_bandwidth = 0;
for (uint32_t i=offset; i < PERIODIC_LIST_SIZE*8; i += interval) { for (uint32_t i=offset; i < PERIODIC_LIST_SIZE*8; i += interval) {
uint32_t bw = uframe_bandwidth[i] + stime;
if (bw > max_bw) max_bw = bw;
uint32_t bandwidth = uframe_bandwidth[i] + stime;
if (bandwidth > max_bandwidth) max_bandwidth = bandwidth;
} }
if (max_bw < min_bw) {
min_bw = max_bw;
min_offset = offset;
// remember which uframe offset is the best
if (max_bandwidth < best_bandwidth) {
best_bandwidth = max_bandwidth;
best_offset = offset;
} }
} }
print(" min_bw = ");
print(min_bw);
print(" best_bandwidth = ");
print(best_bandwidth);
print(", at offset = "); print(", at offset = ");
println(min_offset);
if (min_bw > 187) return false;
for (uint32_t i=min_offset; i < PERIODIC_LIST_SIZE*8; i += interval) {
println(best_offset);
// a 125 us micro frame can fit 7500 bytes, or 234 of our 32-byte units
// fail if the best found needs more than 80% (234 * 0.8) in any uframe
if (best_bandwidth > 187) return false;
for (uint32_t i=best_offset; i < PERIODIC_LIST_SIZE*8; i += interval) {
uframe_bandwidth[i] += stime; uframe_bandwidth[i] += stime;
} }
*offset_out = min_offset >> 3;
if (interval == 1) { if (interval == 1) {
*smask_out = 0xFF;
pipe->start_mask = 0xFF;
} else if (interval == 2) { } else if (interval == 2) {
*smask_out = 0x55 << (min_offset & 1);
pipe->start_mask = 0x55 << (best_offset & 1);
} else if (interval <= 4) { } else if (interval <= 4) {
*smask_out = 0x11 << (min_offset & 3);
pipe->start_mask = 0x11 << (best_offset & 3);
} else { } else {
*smask_out = 0x01 << (min_offset & 7);
pipe->start_mask = 0x01 << (best_offset & 7);
} }
*cmask_out = 0;
uint32_t poffset = best_offset >> 3;
pipe->periodic_offset = (poffset > 0) ? poffset : 1;
pipe->complete_mask = 0;
} else { } else {
// full speed 12 Mbit/sec or low speed 1.5 Mbit/sec // full speed 12 Mbit/sec or low speed 1.5 Mbit/sec
interval = round_to_power_of_two(interval, PERIODIC_LIST_SIZE);
pipe->periodic_interval = interval;
uint32_t stime, ctime; uint32_t stime, ctime;
if (direction == 0) {
if (pipe->direction == 0) {
// for OUT direction, SSPLIT will carry the data payload
// TODO: how much time to SSPLIT & CSPLIT actually take? // TODO: how much time to SSPLIT & CSPLIT actually take?
// they're not documented in 5.7 or 5.11.3. // they're not documented in 5.7 or 5.11.3.
stime = (100 + 32 + maxlen) >> 5; stime = (100 + 32 + maxlen) >> 5;
ctime = (55 + 32) >> 5; ctime = (55 + 32) >> 5;
} else { } else {
// for IN direction, data payload in CSPLIT
stime = (40 + 32) >> 5; stime = (40 + 32) >> 5;
ctime = (70 + 32 + maxlen) >> 5; ctime = (70 + 32 + maxlen) >> 5;
} }
interval = interval >> 3; // can't be zero, earlier check for interval >= 8
// TODO: should we take Single-TT hubs into account, avoid // TODO: should we take Single-TT hubs into account, avoid
// scheduling overlapping SSPLIT & CSPLIT to the same hub? // scheduling overlapping SSPLIT & CSPLIT to the same hub?
uint32_t min_shift = 0;
uint32_t min_offset = 0xFFFFFFFF;
uint32_t min_bw = 0xFFFFFFFF;
// TODO: even if Multi-TT, do we need to worry about packing
// too many into the same uframe?
uint32_t best_shift = 0;
uint32_t best_offset = 0xFFFFFFFF;
uint32_t best_bandwidth = 0xFFFFFFFF;
for (uint32_t offset=0; offset < interval; offset++) { for (uint32_t offset=0; offset < interval; offset++) {
uint32_t max_bw = 0;
// for each 1ms frame offset, compute the worst uframe usage
uint32_t max_bandwidth = 0;
for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += interval) { for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += interval) {
for (uint32_t j=0; j <= 3; j++) { // max 3 without FSTN for (uint32_t j=0; j <= 3; j++) { // max 3 without FSTN
// at each location, find worst uframe usage
// for SSPLIT+CSPLITs
uint32_t n = (i << 3) + j; uint32_t n = (i << 3) + j;
uint32_t bw1 = uframe_bandwidth[n+0] + stime; uint32_t bw1 = uframe_bandwidth[n+0] + stime;
uint32_t bw2 = uframe_bandwidth[n+2] + ctime; uint32_t bw2 = uframe_bandwidth[n+2] + ctime;
uint32_t bw3 = uframe_bandwidth[n+3] + ctime; uint32_t bw3 = uframe_bandwidth[n+3] + ctime;
uint32_t bw4 = uframe_bandwidth[n+4] + ctime; uint32_t bw4 = uframe_bandwidth[n+4] + ctime;
max_bw = max4(bw1, bw2, bw3, bw4);
if (max_bw < min_bw) {
min_bw = max_bw;
min_offset = i;
min_shift = j;
max_bandwidth = max4(bw1, bw2, bw3, bw4);
// remember the best usage found
if (max_bandwidth < best_bandwidth) {
best_bandwidth = max_bandwidth;
best_offset = i;
best_shift = j;
} }
} }
} }
} }
print(" min_bw = ");
println(min_bw);
print(" best_bandwidth = ");
println(best_bandwidth);
print(", at offset = "); print(", at offset = ");
print(min_offset);
print(best_offset);
print(", shift= "); print(", shift= ");
println(min_shift);
if (min_bw > 187) return false;
for (uint32_t i=min_offset; i < PERIODIC_LIST_SIZE; i += interval) {
uint32_t n = (i << 3) + min_shift;
println(best_shift);
// a 125 us micro frame can fit 7500 bytes, or 234 of our 32-byte units
// fail if the best found needs more than 80% (234 * 0.8) in any uframe
if (best_bandwidth > 187) return false;
for (uint32_t i=best_offset; i < PERIODIC_LIST_SIZE; i += interval) {
uint32_t n = (i << 3) + best_shift;
uframe_bandwidth[n+0] += stime; uframe_bandwidth[n+0] += stime;
uframe_bandwidth[n+2] += ctime; uframe_bandwidth[n+2] += ctime;
uframe_bandwidth[n+3] += ctime; uframe_bandwidth[n+3] += ctime;
uframe_bandwidth[n+4] += ctime; uframe_bandwidth[n+4] += ctime;
} }
*smask_out = 0x01 << min_shift;
*cmask_out = 0x1C << min_shift;
*offset_out = min_offset;
pipe->start_mask = 0x01 << best_shift;
pipe->complete_mask = 0x1C << best_shift;
pipe->periodic_offset = best_offset;
} }
return true; return true;
} }

Notiek ielāde…
Atcelt
Saglabāt