first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(TEST)
|
||||
# include <stdio.h>
|
||||
# define DEBUG_INFO 0
|
||||
# define DEBUG_SPEW 0
|
||||
#endif
|
||||
|
||||
#include "cobs.h"
|
||||
|
||||
bool cobs_encode(void *out_buf, size_t out_buf_bytes,
|
||||
const void *in_buf, size_t in_buf_bytes,
|
||||
size_t *out_buf_used)
|
||||
{
|
||||
// Output buffer iterator, skipping first code byte.
|
||||
uint8_t *restrict out = (uint8_t *) out_buf;
|
||||
size_t out_pos = 1;
|
||||
|
||||
// Input buffer iterator, starting at beginning of buffer.
|
||||
const uint8_t *restrict in = (const uint8_t *) in_buf;
|
||||
size_t in_pos = 0;
|
||||
|
||||
// Position of the code prefixing the current non-zero run of data.
|
||||
size_t code_pos = 0;
|
||||
uint8_t code = 1;
|
||||
if (out_buf_bytes < 1) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"COBS-encoding always has at least one byte output\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy non-zero runs of data
|
||||
while (in_pos < in_buf_bytes) {
|
||||
uint8_t byte = in[in_pos++];
|
||||
if (byte != 0) {
|
||||
// Copy non-0x00 byte across.
|
||||
if (out_pos >= out_buf_bytes - 1) {
|
||||
// Must be space for this data byte,
|
||||
// and then at least one more code byte.
|
||||
dprintf(DEBUG_INFO,
|
||||
"COBS-encode ran out of space\n");
|
||||
return false;
|
||||
}
|
||||
out[out_pos++] = byte;
|
||||
// Increment code. 8 bit limit checked below.
|
||||
++code;
|
||||
}
|
||||
if (byte == 0 || code >= 0xff) {
|
||||
// Input is 0x00 byte, or we just hit the max code.
|
||||
// Fill in the skipped code byte before the run.
|
||||
out[code_pos] = code;
|
||||
code = 1;
|
||||
// Already checked for code byte space above.
|
||||
code_pos = out_pos++;
|
||||
}
|
||||
}
|
||||
// Output final code assuming an implicit 0x00 terminator.
|
||||
// Invariant: code_pos < out_buf_bytes.
|
||||
out[code_pos] = code;
|
||||
*out_buf_used = out_pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cobs_decode(void *out_buf, size_t out_buf_bytes,
|
||||
const void *in_buf, size_t in_buf_bytes,
|
||||
size_t *out_buf_used)
|
||||
{
|
||||
// Output buffer iterator.
|
||||
uint8_t *restrict out = (uint8_t *) out_buf;
|
||||
size_t out_pos = 0;
|
||||
|
||||
// Input buffer iterator.
|
||||
const uint8_t *restrict in = (const uint8_t *) in_buf;
|
||||
size_t in_pos = 0;
|
||||
|
||||
while (in_pos < in_buf_bytes) {
|
||||
uint8_t code = in[in_pos++];
|
||||
if (code == 0) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"COBS-decode found illegal 0x00 byte\n");
|
||||
return false;
|
||||
}
|
||||
// Copy one less data bytes than the code.
|
||||
for (uint8_t i = 1; i < code; ++i) {
|
||||
if (in_pos >= in_buf_bytes) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"COBS-decode found truncated input\n");
|
||||
return false;
|
||||
} else if (out_pos >= out_buf_bytes) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"COBS-decode ran out of space %d/%d\n",
|
||||
(int) out_pos, (int) out_buf_bytes);
|
||||
return false;
|
||||
}
|
||||
out[out_pos++] = in[in_pos++];
|
||||
}
|
||||
// Output trailing 0x00, unless it's the implicit terminator,
|
||||
// or it was the max value code.
|
||||
if (code != 0xff && in_pos < in_buf_bytes) {
|
||||
if (out_pos >= out_buf_bytes) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"COBS-decode ran out of space\n");
|
||||
return false;
|
||||
}
|
||||
out[out_pos++] = 0x00;
|
||||
}
|
||||
}
|
||||
*out_buf_used = out_pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void dump_buf(const uint8_t *buf, size_t bytes) {
|
||||
for (size_t i = 0; i < bytes; ++i) {
|
||||
printf(" %02x", buf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void test_case(const uint8_t *in_buf, size_t in_buf_size,
|
||||
const uint8_t *expect_buf, size_t expect_buf_size)
|
||||
{
|
||||
uint8_t out_buf[1024];
|
||||
uint8_t out_buf2[1024];
|
||||
assert(sizeof(out_buf) == sizeof(out_buf2));
|
||||
assert(in_buf_size <= sizeof(out_buf) &&
|
||||
expect_buf_size <= sizeof(out_buf));
|
||||
size_t out_used = 0;
|
||||
|
||||
// Test forward encoding matches expected output.
|
||||
bool ok = cobs_encode(out_buf, sizeof(out_buf),
|
||||
in_buf, in_buf_size,
|
||||
&out_used);
|
||||
assert(ok);
|
||||
if (out_used != expect_buf_size) {
|
||||
printf("Encode generated %d bytes instead of %d\n",
|
||||
(int) out_used, (int) expect_buf_size);
|
||||
dump_buf(out_buf, out_used);
|
||||
abort();
|
||||
}
|
||||
assert(memcmp(out_buf, expect_buf, expect_buf_size) == 0);
|
||||
|
||||
// Test round-trip decoding matches original input.
|
||||
ok = cobs_decode(out_buf2, sizeof(out_buf2),
|
||||
out_buf, expect_buf_size,
|
||||
&out_used);
|
||||
assert(ok);
|
||||
if (out_used != in_buf_size) {
|
||||
printf("Decode generated %d bytes instead of %d\n",
|
||||
(int) out_used, (int) in_buf_size);
|
||||
dump_buf(out_buf2, out_used);
|
||||
abort();
|
||||
}
|
||||
assert(memcmp(out_buf2, in_buf, in_buf_size) == 0);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("COBS test\n");
|
||||
|
||||
{
|
||||
// Empty packet encodes a single 0x01.
|
||||
static const uint8_t expect[1] = { 0x01 };
|
||||
test_case(NULL, 0, expect, sizeof(expect));
|
||||
}
|
||||
|
||||
{
|
||||
// Short run, no zeroes.
|
||||
static const uint8_t in_buf[3] = { 0x01, 0x02, 0x03 };
|
||||
static const uint8_t expect[4] = { 0x04, 0x01, 0x02, 0x03 };
|
||||
test_case(in_buf, sizeof(in_buf), expect, sizeof(expect));
|
||||
}
|
||||
|
||||
{
|
||||
// Single zero encodes to a double 0x01.
|
||||
static const uint8_t in_buf[1] = { 0x00 };
|
||||
static const uint8_t expect[2] = { 0x01, 0x01 };
|
||||
test_case(in_buf, sizeof(in_buf), expect, sizeof(expect));
|
||||
}
|
||||
|
||||
{
|
||||
// 254 bytes encode to 0xff <bytes> 0x01
|
||||
uint8_t in_buf[254];
|
||||
memset(in_buf, 0xff, sizeof(in_buf));
|
||||
uint8_t expect[256];
|
||||
expect[0] = 0xff;
|
||||
memset(expect + 1, 0xff, sizeof(expect) - 2);
|
||||
expect[sizeof(expect) - 1] = 0x01;
|
||||
test_case(in_buf, sizeof(in_buf), expect, sizeof(expect));
|
||||
}
|
||||
|
||||
{
|
||||
// 254 bytes and a zero encode to 0xff <bytes> 0x01 0x01
|
||||
uint8_t in_buf[255];
|
||||
memset(in_buf, 0xff, sizeof(in_buf));
|
||||
in_buf[sizeof(in_buf) - 1] = 0x00;
|
||||
uint8_t expect[257];
|
||||
expect[0] = 0xff;
|
||||
memset(expect + 1, 0xff, sizeof(expect) - 3);
|
||||
expect[sizeof(expect) - 2] = 0x01;
|
||||
expect[sizeof(expect) - 1] = 0x01;
|
||||
test_case(in_buf, sizeof(in_buf), expect, sizeof(expect));
|
||||
}
|
||||
|
||||
printf("COBS test passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(TEST)
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef COBS_H
|
||||
#define COBS_H 1
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// COBS-encode from an input buffer to an output buffer.
|
||||
// Input and output must not be the same or overlap.
|
||||
// The byte stuffing algorithm may grow the data size.
|
||||
// Returns true if the output fits in out_buf_bytes, and sets
|
||||
// *out_buf_used to the new length.
|
||||
// Returns false if the output does not fit. Output buffer is clobbered.
|
||||
bool cobs_encode(void *out_buf, size_t out_buf_bytes,
|
||||
const void *in_buf, size_t in_buf_bytes,
|
||||
size_t *out_buf_used);
|
||||
|
||||
// COBS-decode from an input buffer to an output buffer.
|
||||
// Input and output must not be the same or overlap.
|
||||
// The byte stuffing algorithm may shrink the data size.
|
||||
// Returns true if properly COBS-encoded data was decoded successfully,
|
||||
// and sets *out_buf_used to the new length.
|
||||
// Returns false if illegal COBS codes are present, or are inconsistent
|
||||
// with the input buffer size. Output buffer clobbered.
|
||||
bool cobs_decode(void *out_buf, size_t out_buf_bytes,
|
||||
const void *in_buf, size_t in_buf_bytes,
|
||||
size_t *out_buf_used);
|
||||
|
||||
#endif // defined(COBS_H)
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <cbuffer.h>
|
||||
#include <platform.h>
|
||||
#include <platform/dockfifo_config.h>
|
||||
#include <platform/int.h>
|
||||
#include <sys.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
#include "cobs.h"
|
||||
#include "dockfifo_regs.h"
|
||||
#include "endian_tools.h"
|
||||
#include "lib/cksum.h"
|
||||
|
||||
// #undef DEBUG_LEVEL
|
||||
// #define DEBUG_LEVEL DEBUG_SPEW
|
||||
|
||||
// Slowest rate of polling write fifo when full.
|
||||
#define MAX_CHECK_INTERVAL 100000
|
||||
|
||||
// Maximum size of frame before stuffing bytes inserted, not including CRC.
|
||||
#define MAX_UNSTUFFED_FRAME (DOCKFIFO_BULK_MRU + 4)
|
||||
|
||||
// Maximum size of frame after stuffing bytes inserted (growing its size).
|
||||
// Worst case need 1 extra coding byte per 254 byte segment.
|
||||
#define MAX_STUFFED_FRAME (MAX_UNSTUFFED_FRAME + \
|
||||
(MAX_UNSTUFFED_FRAME + 253) / 254)
|
||||
|
||||
// Small buffer to help speed up RX transfers.
|
||||
#define RX_CBUF_SIZE 256
|
||||
|
||||
struct PacketBuffer {
|
||||
uint8_t stuffed_buf[MAX_STUFFED_FRAME];
|
||||
uint8_t unstuffed_buf[MAX_UNSTUFFED_FRAME];
|
||||
};
|
||||
|
||||
static struct task_event dockfifo_bulk_rx_event;
|
||||
|
||||
static struct PacketBuffer rx_packet;
|
||||
static struct PacketBuffer tx_packet;
|
||||
|
||||
// When not in sync, we need a framing byte before accepting input.
|
||||
static bool rx_sync = true;
|
||||
|
||||
// Small cbuffer to accelerate reading from the DBGFIFO.
|
||||
static CBUFFER rx_cbuf;
|
||||
|
||||
|
||||
//=======================
|
||||
// Local funtions
|
||||
//=======================
|
||||
|
||||
static void dockfifo_bulk_fill_rx_cbuf(void)
|
||||
{
|
||||
size_t min_occupancy = 0;
|
||||
size_t bytes = cb_free_space(&rx_cbuf);
|
||||
while (bytes > 0) {
|
||||
if ((min_occupancy >= 4) && (bytes >= 4)) {
|
||||
dprintf(DEBUG_SPEW,
|
||||
"dockfifo_bulk_fill_rx_cbuf 4B %d %d\n",
|
||||
(int) min_occupancy, (int) bytes);
|
||||
// At least 4 bytes available and 4 bytes to read.
|
||||
uint32_t rdata_4 =
|
||||
rDBGFIFO_R_DATA(DOCKFIFO_BULK_READ, 4);
|
||||
cb_write_unsafe(&rx_cbuf,
|
||||
(const uint8_t *) &rdata_4, 4);
|
||||
bytes -= 4;
|
||||
min_occupancy -= 4;
|
||||
} else {
|
||||
// Read between 1 and 3 bytes, including FIFO status.
|
||||
uint32_t to_read = bytes;
|
||||
if (to_read > 3) {
|
||||
to_read = 3;
|
||||
}
|
||||
uint32_t rdata_n =
|
||||
rDBGFIFO_R_DATA(DOCKFIFO_BULK_READ, to_read);
|
||||
// Get FIFO status *before* read took place.
|
||||
uint32_t occupancy = rdata_n & 0x7f;
|
||||
dprintf(DEBUG_SPEW,
|
||||
"dockfifo_bulk_fill_rx_cbuf occ:%d %d %d\n",
|
||||
(int) occupancy,
|
||||
(int) min_occupancy,
|
||||
(int) bytes);
|
||||
if (occupancy == 0) {
|
||||
// If no bytes were available, break
|
||||
// the loop instead of busy polling.
|
||||
break;
|
||||
}
|
||||
// Between 1 and 3 inclusive bytes were pumped
|
||||
// from the FIFO.
|
||||
uint32_t got_bytes = to_read;
|
||||
if (got_bytes > occupancy) {
|
||||
got_bytes = occupancy;
|
||||
}
|
||||
cb_write_unsafe(&rx_cbuf,
|
||||
((const uint8_t *) &rdata_n) + 1,
|
||||
got_bytes);
|
||||
bytes -= got_bytes;
|
||||
min_occupancy = occupancy - got_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t dockfifo_bulk_read_byte(void)
|
||||
{
|
||||
// Grab a byte from the rx cbuffer if possible.
|
||||
int c = cb_getc_unsafe(&rx_cbuf);
|
||||
if (c >= 0) {
|
||||
return (uint8_t) c;
|
||||
}
|
||||
// Otherwise, try to fill up the cbuffer.
|
||||
for (;;) {
|
||||
// Fill cbuffer from DBGFIFO.
|
||||
dockfifo_bulk_fill_rx_cbuf();
|
||||
// Try again.
|
||||
c = cb_getc_unsafe(&rx_cbuf);
|
||||
if (c >= 0) {
|
||||
return (uint8_t) c;
|
||||
}
|
||||
// Still nothing - wait for interrupt.
|
||||
dprintf(DEBUG_SPEW,
|
||||
"dbgfifo_bulk_read_byte wait IRQ avail %d\n",
|
||||
(int) (rDBGFIFO_R_STAT(DOCKFIFO_BULK_WRITE) & 0xffff));
|
||||
unmask_int(dockfifo_configs[DOCKFIFO_BULK_READ].irq);
|
||||
event_wait(&dockfifo_bulk_rx_event);
|
||||
dprintf(DEBUG_SPEW,
|
||||
"dbgfifo_bulk_read_byte got IRQ avail %d\n",
|
||||
(int) (rDBGFIFO_R_STAT(DOCKFIFO_BULK_WRITE) & 0xffff));
|
||||
}
|
||||
}
|
||||
|
||||
static void dockfifo_bulk_write_byte(uint8_t byte)
|
||||
{
|
||||
// Wait until the FIFO has at least one byte available.
|
||||
uint32_t check_interval = 1;
|
||||
while ((rDBGFIFO_W_STAT(DOCKFIFO_BULK_WRITE) & 0xffff) == 0) {
|
||||
task_sleep(check_interval);
|
||||
check_interval *= 2;
|
||||
if (check_interval > MAX_CHECK_INTERVAL) {
|
||||
check_interval = MAX_CHECK_INTERVAL;
|
||||
}
|
||||
}
|
||||
rDBGFIFO_W_DATA(DOCKFIFO_BULK_WRITE, 1) = byte;
|
||||
}
|
||||
|
||||
static void dockfifo_bulk_write_bytes(const uint8_t *data, size_t count)
|
||||
{
|
||||
while (count > 0) {
|
||||
// Wait until the FIFO has available space.
|
||||
uint32_t check_interval = 1;
|
||||
uint16_t avail;
|
||||
for (;;) {
|
||||
avail = rDBGFIFO_W_STAT(DOCKFIFO_BULK_WRITE) & 0xffff;
|
||||
if (avail > 0) {
|
||||
break;
|
||||
}
|
||||
task_sleep(check_interval);
|
||||
check_interval *= 2;
|
||||
if (check_interval > MAX_CHECK_INTERVAL) {
|
||||
check_interval = MAX_CHECK_INTERVAL;
|
||||
}
|
||||
}
|
||||
// Fill the available space up to the remaining count.
|
||||
size_t fill = (avail < count) ? avail : count;
|
||||
// Use 32 bit writes as much as possible.
|
||||
for (size_t i = 0; i < fill / sizeof(uint32_t); ++i) {
|
||||
uint32_t word;
|
||||
memcpy(&word, data, sizeof(word));
|
||||
data += sizeof(word);
|
||||
rDBGFIFO_W_DATA(DOCKFIFO_BULK_WRITE, 4) = word;
|
||||
}
|
||||
// Write remaining bits out in a single transaction.
|
||||
size_t remainder = fill % sizeof(uint32_t);
|
||||
if (remainder > 0) {
|
||||
uint32_t word = 0;
|
||||
memcpy(&word, data, remainder);
|
||||
data += remainder;
|
||||
rDBGFIFO_W_DATA(DOCKFIFO_BULK_WRITE, remainder) = word;
|
||||
}
|
||||
count -= fill;
|
||||
}
|
||||
}
|
||||
|
||||
static void dockfifo_bulk_interrupt_handler(void *arg)
|
||||
{
|
||||
int32_t which_bulk = (int32_t) arg;
|
||||
if (which_bulk != DOCKFIFO_BULK_READ) {
|
||||
panic("dockfifo_bulk_interrupt_handler wrong arg: %d",
|
||||
(int) which_bulk);
|
||||
}
|
||||
// Just mask the interrupt and defer to a task. The hardware
|
||||
// FIFO is large and has pacing.
|
||||
uint16_t avail = rDBGFIFO_R_STAT(DOCKFIFO_BULK_READ) & 0xffff;
|
||||
if (avail > 0) {
|
||||
//dprintf(DEBUG_SPEW, "irq with %d data\n", (int) avail);
|
||||
mask_int(dockfifo_configs[DOCKFIFO_BULK_READ].irq);
|
||||
event_signal(&dockfifo_bulk_rx_event);
|
||||
} else {
|
||||
dprintf(DEBUG_INFO, "Spurious DBGFIFO bulk irq\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=======================
|
||||
// Global funtions
|
||||
//=======================
|
||||
|
||||
int32_t dockfifo_bulk_read_frame(void *buf, size_t bytes, size_t *received)
|
||||
{
|
||||
*received = 0;
|
||||
|
||||
// Get into sync if necessary.
|
||||
while (!rx_sync) {
|
||||
uint8_t byte = dockfifo_bulk_read_byte();
|
||||
if (byte == 0) {
|
||||
dprintf(DEBUG_INFO, "Found sync byte\n");
|
||||
rx_sync = true;
|
||||
} else {
|
||||
dprintf(DEBUG_INFO, "Bulk re-sync, dropping 0x%02x\n",
|
||||
byte);
|
||||
}
|
||||
}
|
||||
|
||||
// Block reading bytes until we have a complete frame.
|
||||
size_t pos = 0;
|
||||
for (;;) {
|
||||
uint8_t byte = dockfifo_bulk_read_byte();
|
||||
if (byte == 0) {
|
||||
if (pos > 0) {
|
||||
// Got non-empty frame.
|
||||
dprintf(DEBUG_SPEW, "Got frame, %d bytes\n",
|
||||
(int) pos);
|
||||
break;
|
||||
} else {
|
||||
// Allowed to have EOF-SOF combinations.
|
||||
dprintf(DEBUG_SPEW, "Ignoring empty frame\n");
|
||||
}
|
||||
} else if (pos < MAX_STUFFED_FRAME) {
|
||||
// Got a data byte, and room to insert it.
|
||||
dprintf(DEBUG_SPEW, "Data[%d] = 0x%02x\n",
|
||||
(int) pos, byte);
|
||||
rx_packet.stuffed_buf[pos] = byte;
|
||||
++pos;
|
||||
} else {
|
||||
// MRU overflow.
|
||||
dprintf(DEBUG_INFO, "Bulk overflow 0x%02x\n", byte);
|
||||
rx_sync = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!rx_sync) {
|
||||
// Bad framing.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// COBS decode.
|
||||
size_t unstuffed_bytes = 0;
|
||||
if (!cobs_decode(rx_packet.unstuffed_buf,
|
||||
sizeof(rx_packet.unstuffed_buf),
|
||||
rx_packet.stuffed_buf, pos,
|
||||
&unstuffed_bytes)) {
|
||||
// Abandon dispatch if the COBS encoding is bad.
|
||||
dprintf(DEBUG_INFO, "Bad COBS data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Must be at least enough space for a CRC.
|
||||
if (unstuffed_bytes < 4) {
|
||||
dprintf(DEBUG_INFO, "Runt packet len %d\n",
|
||||
(int) unstuffed_bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t payload_size = unstuffed_bytes - 4;
|
||||
|
||||
// Must be enough room to receive the buffer up to the CRC.
|
||||
if (payload_size > bytes) {
|
||||
dprintf(DEBUG_INFO, "Frame payload too large: %d vs %d\n",
|
||||
(int) payload_size, (int) bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check CRC-32 is good.
|
||||
uint32_t computed_crc = crc32(rx_packet.unstuffed_buf, payload_size);
|
||||
uint32_t sent_crc = read_le_32(rx_packet.unstuffed_buf, payload_size);
|
||||
if (computed_crc != sent_crc) {
|
||||
dprintf(DEBUG_INFO,
|
||||
"Bad CRC %d: computed 0x%08x received 0x%08x\n",
|
||||
(int) payload_size, computed_crc, sent_crc);
|
||||
return -1;
|
||||
}
|
||||
dprintf(DEBUG_SPEW, "Received frame with CRC 0x%08x\n", sent_crc);
|
||||
|
||||
// Success - we have a correctly received frame.
|
||||
memcpy(buf, rx_packet.unstuffed_buf, payload_size);
|
||||
*received = payload_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t dockfifo_bulk_write_frame(const void *buf, size_t bytes)
|
||||
{
|
||||
// Need room for pipe number and CRC-32.
|
||||
if (bytes > sizeof(tx_packet.unstuffed_buf) - 4) {
|
||||
dprintf(DEBUG_INFO, "No room to serialize packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy packet to unstuffed packet buffer, append with CRC-32.
|
||||
memcpy(tx_packet.unstuffed_buf, buf, bytes);
|
||||
uint32_t crc = crc32(tx_packet.unstuffed_buf, bytes);
|
||||
write_le_32(tx_packet.unstuffed_buf, bytes, crc);
|
||||
|
||||
// COBS byte-stuffing.
|
||||
size_t stuffed_size = 0;
|
||||
if (!cobs_encode(tx_packet.stuffed_buf, sizeof(tx_packet.stuffed_buf),
|
||||
tx_packet.unstuffed_buf, bytes + 4,
|
||||
&stuffed_size)) {
|
||||
dprintf(DEBUG_INFO, "No room to serialize framing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dprintf(DEBUG_SPEW, "Send frame with CRC 0x%08x\n", crc);
|
||||
// SOF.
|
||||
dprintf(DEBUG_SPEW, "Send SOF\n");
|
||||
dockfifo_bulk_write_byte(0x00);
|
||||
// COBS stuffed data.
|
||||
dprintf(DEBUG_SPEW, "Send %d data\n", (int) stuffed_size);
|
||||
dockfifo_bulk_write_bytes(tx_packet.stuffed_buf, stuffed_size);
|
||||
// EOF.
|
||||
dprintf(DEBUG_SPEW, "Send EOF\n");
|
||||
dockfifo_bulk_write_byte(0x00);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t dockfifo_bulk_init()
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "dockfifo_bulk_init\n");
|
||||
|
||||
// Initialize rx cbuffer.
|
||||
cb_create(&rx_cbuf, RX_CBUF_SIZE);
|
||||
|
||||
// Setup hardware.
|
||||
clock_gate(CLK_SPU, true);
|
||||
clock_gate(CLK_DOCKFIFO, true);
|
||||
|
||||
// Reset fifos.
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_READ) = (1 << 31);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_WRITE) = (1 << 31);
|
||||
spin(1);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_READ) = (0 << 31);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_WRITE) = (0 << 31);
|
||||
|
||||
// Initialize local events.
|
||||
event_init(&dockfifo_bulk_rx_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
dprintf(DEBUG_SPEW, "interrupt: %d\n",
|
||||
(int) dockfifo_configs[DOCKFIFO_BULK_READ].irq);
|
||||
install_int_handler(dockfifo_configs[DOCKFIFO_BULK_READ].irq,
|
||||
&dockfifo_bulk_interrupt_handler,
|
||||
(void *)DOCKFIFO_BULK_READ);
|
||||
|
||||
// Enable access to fifo
|
||||
platform_dockfifo_access_enable(DOCKFIFO_BULK_READ, true);
|
||||
platform_dockfifo_access_enable(DOCKFIFO_BULK_WRITE, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t dockfifo_bulk_quiesce()
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "dockfifo_bulk_quiesce\n");
|
||||
|
||||
mask_int(dockfifo_configs[DOCKFIFO_BULK_READ].irq);
|
||||
|
||||
// Disable access to fifo
|
||||
platform_dockfifo_access_enable(DOCKFIFO_BULK_READ, false);
|
||||
platform_dockfifo_access_enable(DOCKFIFO_BULK_WRITE, false);
|
||||
|
||||
// Reset rx cbuffer.
|
||||
cb_reset(&rx_cbuf);
|
||||
|
||||
// Reset fifos.
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_READ) = (1 << 31);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_WRITE) = (1 << 31);
|
||||
spin(1);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_READ) = (0 << 31);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_BULK_WRITE) = (0 << 31);
|
||||
|
||||
clock_gate(CLK_DOCKFIFO, false);
|
||||
clock_gate(CLK_SPU, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#ifndef __APPLE_DOCKFIFO_H
|
||||
#define __APPLE_DOCKFIFO_H
|
||||
|
||||
#include <platform/soc/hwregbase.h>
|
||||
|
||||
#define DBGFIFO_W_SPACING (0x1000)
|
||||
#define DBGFIFO_CNFG_CG_ENA (0x1)
|
||||
|
||||
#define rDBGFIFO_R_DATA(_f, _n) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + ((_n) * 4)))
|
||||
#define rDBGFIFO_R_STAT(_f) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + 0x14))
|
||||
#define rDBGFIFO_W_DATA(_f, _n) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + DBGFIFO_W_SPACING + ((_n) * 4)))
|
||||
#define rDBGFIFO_W_STAT(_f) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + DBGFIFO_W_SPACING + 0x14))
|
||||
#define rDBGFIFO_CNFG(_f) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + 0x2000))
|
||||
#define rDBGFIFO_DRAIN(_f) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + 0x2004))
|
||||
#define rDBGFIFO_INTMASK(_f) (*(volatile uint32_t *)(DBGFIFO_0_BASE_ADDR + ((_f) * DBGFIFO_SPACING) + 0x2008))
|
||||
|
||||
#endif /* __APPLE_DOCKFIFO_H */
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <cbuffer.h>
|
||||
#include <drivers/power.h>
|
||||
#include <platform.h>
|
||||
#include <platform/dockfifo_config.h>
|
||||
#include <platform/int.h>
|
||||
#include <sys.h>
|
||||
#include <sys/task.h>
|
||||
#include <platform/timer.h>
|
||||
|
||||
#include "dockfifo_regs.h"
|
||||
|
||||
extern int DebugUartReady;
|
||||
|
||||
#define DOCKFIFO_UART_RX_LEN (16)
|
||||
|
||||
// Allow a 30ms stall of wall clock time before DockFIFO starts dropping characters
|
||||
#define DOCKFIFO_WR_MAX_STALL_US (30*1000)
|
||||
|
||||
static CBUFFER dockfifo_uart_rx_cbuf;
|
||||
static struct task_event dockfifo_uart_rx_event;
|
||||
|
||||
static uint64_t prev_dockfifo_drained_time; // Last time we've seen the DockFIFO drained by an external agent
|
||||
static uint64_t prev_dockfifo_spaces; // Previous w_stat level of the DockFIFO.
|
||||
static uint32_t dockfifo_capacity;
|
||||
|
||||
|
||||
#define INSTRUMENT_DOCKFIFO_DRAIN_COUNTER 1
|
||||
|
||||
#if INSTRUMENT_DOCKFIFO_DRAIN_COUNTER
|
||||
|
||||
#define DOCKFIFO_STAT_INCR(x) ((x)++)
|
||||
|
||||
static unsigned int dockfifo_stat_active_stall_loops;
|
||||
static unsigned int dockfifo_stat_dropped_characters;
|
||||
|
||||
#else
|
||||
#define DOCKFIFO_STAT_INCR(x)
|
||||
#endif
|
||||
|
||||
//=======================
|
||||
// Local funtions
|
||||
//=======================
|
||||
|
||||
static int dockfifo_drain_on_stall()
|
||||
{
|
||||
// Called when DockFIFO runs out of spaces.
|
||||
// Check if the DockFIFO reader has stalled. If so, empty the DockFIFO ourselves.
|
||||
// Return number of bytes drained.
|
||||
|
||||
if (timer_ticks_to_usecs(timer_get_ticks() - prev_dockfifo_drained_time) >= DOCKFIFO_WR_MAX_STALL_US) {
|
||||
// It's been more than DOCKFIFO_WR_MAX_STALL_US and nobody read from the FIFO
|
||||
// Drop a character.
|
||||
DOCKFIFO_STAT_INCR(dockfifo_stat_dropped_characters);
|
||||
|
||||
(void)rDBGFIFO_R_DATA(DOCKFIFO_UART_READ, 1);
|
||||
prev_dockfifo_spaces++;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t dockfifo_uart_write_byte(uint8_t byte)
|
||||
{
|
||||
|
||||
/**
|
||||
* DockFIFO Draining algorithm:
|
||||
*
|
||||
* We want DockFIFO UART to try hard to preserve characters as long as someone is
|
||||
* listening to the DockFIFO. But we also want DockFIFO to drop characters rapidly
|
||||
* when the FIFO is full and nobody is listening.
|
||||
*
|
||||
* So, when the DockFIFO is full, we will hang and poll for a max of DOCKFIFO_WR_MAX_STALL_US
|
||||
* If this time expires, we will begin instantly dropping the oldest character when we notice
|
||||
* the DockFIFO to be full (e.g. nobody is listening to DockFIFO).
|
||||
*
|
||||
* But, the moment we see someone start emptying the DockFIFO, the timer resets.
|
||||
* This will achieve a good balance between making sure we don't drop chars when the host is listening,
|
||||
* and making sure UART write overhead is low when nobody is attached to it.
|
||||
*
|
||||
**/
|
||||
|
||||
// Atomically check for free space in write fifo and enqueue a byte.
|
||||
enter_critical_section();
|
||||
for (;;) {
|
||||
uint32_t spaces = rDBGFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
|
||||
|
||||
if (spaces >= dockfifo_capacity || spaces > prev_dockfifo_spaces) {
|
||||
// More spaces showed up. That can only mean someone read the FIFO.
|
||||
// Note that if the DockFIFO is empty we cannot tell if someone is listening,
|
||||
// we can only give them the benefit of the doubt.
|
||||
|
||||
prev_dockfifo_drained_time = timer_get_ticks();
|
||||
}
|
||||
prev_dockfifo_spaces = spaces;
|
||||
|
||||
if (spaces > 0 || dockfifo_drain_on_stall()) {
|
||||
// We either had spaces, or just kicked out a stale byte on a stalled DockFIFO.
|
||||
break;
|
||||
}
|
||||
exit_critical_section();
|
||||
|
||||
// If we reached here, the DockFIFO is still full, probably due to heavy UART
|
||||
// traffic with an active reader.
|
||||
DOCKFIFO_STAT_INCR(dockfifo_stat_active_stall_loops);
|
||||
|
||||
enter_critical_section();
|
||||
}
|
||||
rDBGFIFO_W_DATA(DOCKFIFO_UART_WRITE, 1) = byte;
|
||||
prev_dockfifo_spaces--; // After writing a byte we have one fewer space than previously expected.
|
||||
exit_critical_section();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t dockfifo_uart_write(const uint8_t *data, size_t count, bool wait)
|
||||
{
|
||||
RELEASE_ASSERT(data != NULL);
|
||||
int32_t ret = 0;
|
||||
for (size_t i = 0; ret == 0 && i < count; ++i) {
|
||||
ret = dockfifo_uart_write_byte(data[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t dockfifo_uart_read(uint8_t *data, size_t count, bool wait)
|
||||
{
|
||||
int32_t bytes_read = 0;
|
||||
|
||||
RELEASE_ASSERT(data != NULL);
|
||||
|
||||
retry_read:
|
||||
if (wait && (!cb_readable_size(&dockfifo_uart_rx_cbuf)))
|
||||
event_wait(&dockfifo_uart_rx_event);
|
||||
|
||||
/* disable rx interrupt */
|
||||
mask_int(dockfifo_configs[DOCKFIFO_UART_READ].irq);
|
||||
|
||||
bytes_read += cb_read_unsafe(&dockfifo_uart_rx_cbuf, data + bytes_read, count - bytes_read);
|
||||
|
||||
/* enable rx interrupt */
|
||||
unmask_int(dockfifo_configs[DOCKFIFO_UART_READ].irq);
|
||||
|
||||
if (((size_t)bytes_read < count) && wait)
|
||||
goto retry_read;
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static int dockfifo_uart_reader_task(void *arg)
|
||||
{
|
||||
for(;;) {
|
||||
char c;
|
||||
int32_t len;
|
||||
|
||||
len = dockfifo_uart_read((uint8_t *)&c, 1, true);
|
||||
if ((len > 0) && ((DebugUartReady & kPowerNVRAMiBootDebugIAPSerial) != 0))
|
||||
debug_pushchar(c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dockfifo_uart_interrupt(void *arg)
|
||||
{
|
||||
int32_t which_uart = (int32_t)arg;
|
||||
|
||||
if (which_uart == DOCKFIFO_UART_READ) { // Rx
|
||||
while (((rDBGFIFO_R_STAT(DOCKFIFO_UART_READ) & 0xffff) != 0) && cb_free_space(&dockfifo_uart_rx_cbuf))
|
||||
cb_putc_unsafe(&dockfifo_uart_rx_cbuf, (rDBGFIFO_R_DATA(DOCKFIFO_UART_READ, 1) >> 8) & 0xff);
|
||||
|
||||
/* signal reader */
|
||||
event_signal(&dockfifo_uart_rx_event);
|
||||
}
|
||||
}
|
||||
|
||||
//=======================
|
||||
// Global funtions
|
||||
//=======================
|
||||
|
||||
int32_t dockfifo_uart_init()
|
||||
{
|
||||
// Setup hardware
|
||||
clock_gate(CLK_SPU, true);
|
||||
clock_gate(CLK_DOCKFIFO, true);
|
||||
|
||||
// reset fifos
|
||||
rDBGFIFO_CNFG(DOCKFIFO_UART_READ) = (1 << 31);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_UART_WRITE) = (1 << 31);
|
||||
spin(1);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_UART_READ) = (0 << 31);
|
||||
rDBGFIFO_CNFG(DOCKFIFO_UART_WRITE) = (0 << 31);
|
||||
|
||||
// Disable autodraining of the FIFO. We now purely manage it in software.
|
||||
rDBGFIFO_DRAIN(DOCKFIFO_UART_WRITE) = 0;
|
||||
|
||||
// Empty the DockFIFO by draining it until OCCUPANCY is 0, then measure its capacity
|
||||
while (rDBGFIFO_R_DATA(DOCKFIFO_UART_WRITE, 3) & 0x7F);
|
||||
|
||||
dockfifo_capacity = rDBGFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
|
||||
|
||||
cb_create(&dockfifo_uart_rx_cbuf, DOCKFIFO_UART_RX_LEN);
|
||||
event_init(&dockfifo_uart_rx_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
install_int_handler(dockfifo_configs[DOCKFIFO_UART_READ].irq, &dockfifo_uart_interrupt, (void *)DOCKFIFO_UART_READ);
|
||||
unmask_int(dockfifo_configs[DOCKFIFO_UART_READ].irq);
|
||||
|
||||
task_start(task_create("dockfifo_uart reader", dockfifo_uart_reader_task, NULL, 0x200));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t dockfifo_uart_putc(char c)
|
||||
{
|
||||
return dockfifo_uart_write((uint8_t *)&c, 1, true);
|
||||
}
|
||||
|
||||
int32_t dockfifo_uart_getc(bool wait) /* returns -1 if no data available */
|
||||
{
|
||||
char c;
|
||||
int32_t len;
|
||||
|
||||
len = dockfifo_uart_read((uint8_t *)&c, 1, wait);
|
||||
if (len == 0)
|
||||
return -1;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void dockfifo_enable_clock_gating(int num)
|
||||
{
|
||||
rDBGFIFO_CNFG(num) |= DBGFIFO_CNFG_CG_ENA;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#ifndef __DOCKFIFO_H
|
||||
#define __DOCKFIFO_H
|
||||
|
||||
struct dockfifo_config {
|
||||
uint8_t type;
|
||||
uint8_t clk;
|
||||
uint8_t irq;
|
||||
const char fifo_name[256];
|
||||
};
|
||||
|
||||
#define DOCKFIFO_W (1)
|
||||
#define DOCKFIFO_R (0)
|
||||
|
||||
#define DOCKFIFO_0 (0)
|
||||
#define DOCKFIFO_1 (1)
|
||||
#define DOCKFIFO_2 (2)
|
||||
#define DOCKFIFO_3 (3)
|
||||
#define DOCKFIFO_4 (4)
|
||||
#define DOCKFIFO_5 (5)
|
||||
#define DOCKFIFO_6 (6)
|
||||
#define DOCKFIFO_7 (7)
|
||||
|
||||
// Maximum frame size that can be handled by dockfifo_bulk_read_frame.
|
||||
#define DOCKFIFO_BULK_MRU 576
|
||||
|
||||
int32_t dockfifo_uart_init();
|
||||
int32_t dockfifo_uart_putc(char c);
|
||||
int32_t dockfifo_uart_getc(bool wait);
|
||||
|
||||
int32_t dockfifo_bulk_init();
|
||||
int32_t dockfifo_bulk_quiesce();
|
||||
int32_t dockfifo_bulk_read_frame(void *buf, size_t bytes, size_t *received);
|
||||
int32_t dockfifo_bulk_write_frame(const void *buf, size_t bytes);
|
||||
|
||||
void dockfifo_enable_clock_gating(int num);
|
||||
|
||||
#endif /* __DOCKFIFO_H */
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
ifneq ($(filter $(DOCKFIFOS), uart),)
|
||||
OPTIONS += \
|
||||
WITH_HW_DOCKFIFO_UART=1 \
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/dockfifo_uart.o
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(DOCKFIFOS), bulk),)
|
||||
OPTIONS += \
|
||||
WITH_HW_DOCKFIFO_BULK=1 \
|
||||
|
||||
OPTIONS += \
|
||||
WITH_HW_USB=1
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/cobs.o \
|
||||
$(LOCAL_DIR)/dockfifo_bulk.o
|
||||
endif
|
||||
|
||||
ifneq ($(DOCKFIFOS),)
|
||||
ALL_OBJS += \
|
||||
|
||||
GLOBAL_INCLUDES += $(LOCAL_DIR)/include
|
||||
endif
|
||||
Reference in new issue
Block a user