Skip to content

Commit

Permalink
port: Add comments
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinmehall committed Aug 26, 2016
1 parent 2f734e8 commit 0351bf7
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
38 changes: 37 additions & 1 deletion firmware/firmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,25 +114,61 @@ typedef struct UartBuf {
} UartBuf;

typedef struct PortData {
/// Pin mappings
const TesselPort* port;

/// Buffers for data from the host
USB_ALIGN u8 cmd_buf[BRIDGE_BUF_SIZE];

/// Buffers for data to the host
USB_ALIGN u8 reply_buf[BRIDGE_BUF_SIZE];

/// Bridge channel
u8 chan;

/// DMA channel for TX
DmaChan dma_tx;

/// DMA channel for RX
DmaChan dma_rx;

/// Parser state (PortState in port.c)
u8 state;

/// Port mode (SPI/UART/etc, PortMode in port.c)
u8 mode;

/// Length of valid data in cmd_buf
u8 cmd_len;

/// Current position in cmd_buf
u8 cmd_pos;

/// Current write position in reply_buf (length of valid data written)
u8 reply_len;

/// Currently executing command (PortCmd in port.c)
u8 cmd;

/// Parsed arguments
u8 arg[BRIDGE_ARG_SIZE];

/// Length of arguments
u8 arg_len;

/// Position into arguments
u8 arg_pos;
u8 len;

/// GCLK channel for this port
u8 clock_channel;

/// TCC channel for this port
u8 tcc_channel;

/// True if the port is waiting for a packet from the host
bool pending_out;

/// True if the port is sending a packet to the host
bool pending_in;
UartBuf uart_buf;
} PortData;
Expand Down
33 changes: 32 additions & 1 deletion firmware/port.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
#include "firmware.h"

/// Signal an error on the port. The host must take action to reset the port to resume communication.
void port_error(PortData* p) {
bridge_disable_chan(p->chan);
}

/// Start a transfer from the host of size BRIDGE_BUF_SIZE into the passed buffer.
/// The buffer is owned by the bridge until it signals completion by calling port_bridge_out_completion.
void port_bridge_start_out(PortData* p, u8* buf) {
bridge_start_out(p->chan, buf);
}

/// Start a transfer to the host, with the specified buffer and size. The buffer must remain valid
/// until the bridge signals completion by calling port_bridge_in_completion.
void port_bridge_start_in(PortData* p, u8* buf, size_t len) {
bridge_start_in(p->chan, buf, len);
}
Expand Down Expand Up @@ -89,11 +94,13 @@ void port_enable_async_events(PortData *p);
void port_disable_async_events(PortData *p);
void uart_send_data(PortData *p);

/// Returns true of the specified pin index has interrupt capability
inline static bool port_pin_supports_interrupt(PortData* p, u8 i) {
u8 extint = pin_extint(p->port->gpio[i]);
return !!((1 << extint) & p->port->pin_interrupts);
}

/// Initialize the port. Call once on boot.
void port_init(PortData* p, u8 chan, const TesselPort* port,
u8 clock_channel, u8 tcc_channel, DmaChan dma_tx, DmaChan dma_rx) {
p->tcc_channel = tcc_channel;
Expand All @@ -109,6 +116,8 @@ void port_init(PortData* p, u8 chan, const TesselPort* port,
bridge_enable_chan(chan);
}

/// Enable the port. Call when switching into a mode where the port will be used.
/// Resets all port state.
void port_enable(PortData* p) {
bridge_start_out(p->chan, p->cmd_buf);
p->pending_in = false;
Expand All @@ -127,6 +136,7 @@ void port_enable(PortData* p) {
}
}

/// Disable the port.
void port_disable(PortData* p) {
p->state = PORT_DISABLE;
sercom_reset(p->port->spi);
Expand All @@ -150,6 +160,7 @@ void port_disable(PortData* p) {
bridge_enable_chan(p->chan);
}

/// Enqueue a byte on the reply buf. Requires that at least one byte of space is available.
void port_send_status(PortData* p, u8 d) {
if (p->reply_len >= BRIDGE_BUF_SIZE) {
port_error(p);
Expand All @@ -158,7 +169,7 @@ void port_send_status(PortData* p, u8 d) {
p->reply_buf[p->reply_len++] = d;
}

// returns number of arguments
/// Returns the number of argument bytes for the specified command
int port_cmd_args(PortCmd cmd) {
switch (cmd) {
case CMD_NOP:
Expand Down Expand Up @@ -213,6 +224,7 @@ int port_cmd_args(PortCmd cmd) {
return 0;
}

/// Calculate the number of bytes that can immediately be processed for a TX command
u32 port_tx_len(PortData* p) {
u32 size = p->arg[0];
u32 cmd_remaining = p->cmd_len - p->cmd_pos;
Expand All @@ -222,6 +234,7 @@ u32 port_tx_len(PortData* p) {
return size;
}

/// Calculate the number of bytes that can immediately be processed for a RX command
u32 port_rx_len(PortData* p) {
u32 size = p->arg[0];
u32 reply_remaining = BRIDGE_BUF_SIZE - p->reply_len;
Expand All @@ -231,6 +244,7 @@ u32 port_rx_len(PortData* p) {
return size;
}

/// Calculate the number of bytes that can immediately be processed for a TXRX command
u32 port_txrx_len(PortData *p) {
u32 size = p->arg[0];
u32 cmd_remaining = p->cmd_len - p->cmd_pos;
Expand All @@ -244,10 +258,12 @@ u32 port_txrx_len(PortData *p) {
return size;
}

/// Get the GPIO pin for a port pin index
Pin port_selected_pin(PortData* p) {
return p->port->gpio[p->arg[0] % 8];
}

/// Complete an asynchronous command and begin the next command.
void port_exec_async_complete(PortData* p, ExecStatus s) {
if (p->state != PORT_EXEC_ASYNC) {
port_error(p);
Expand All @@ -257,6 +273,7 @@ void port_exec_async_complete(PortData* p, ExecStatus s) {
port_step(p);
}

/// Flush pending received UART data to the reply buffer
void uart_send_data(PortData *p){
if (p->uart_buf.buf_len > 0) {
// pad 2 bytes at the beginning
Expand Down Expand Up @@ -286,6 +303,11 @@ void uart_send_data(PortData *p){
}
}

/// Begin execution of a command. This function performs the setup for commands with payloads,
/// or the entire execution for commands that do not have payloads.
/// EXEC_DONE: move on to the next command
/// EXEC_CONTINUE: schedule port_continue_command to be called with a part of the payload when
/// available
ExecStatus port_begin_cmd(PortData *p) {
switch (p->cmd) {
case CMD_NOP:
Expand Down Expand Up @@ -495,6 +517,8 @@ ExecStatus port_begin_cmd(PortData *p) {
return EXEC_DONE;
}

/// Called to process the payload of a command. It is not guaranteed that the full payload will
/// be available in one chunk, so this function is called on events until it returns EXEC_DONE.
ExecStatus port_continue_cmd(PortData *p) {
switch (p->cmd) {
case CMD_ECHO: {
Expand Down Expand Up @@ -574,6 +598,7 @@ bool port_rx_locked(PortData *p) {
}
}

/// Enable interrupts for async events
void port_enable_async_events(PortData *p) {
EIC->INTENSET.reg = p->port->pin_interrupts;

Expand All @@ -583,6 +608,7 @@ void port_enable_async_events(PortData *p) {
}
}

/// Disable interrupts for async events
void port_disable_async_events(PortData *p) {
EIC->INTENCLR.reg = p->port->pin_interrupts;

Expand All @@ -592,6 +618,7 @@ void port_disable_async_events(PortData *p) {
}
}

/// Return true if the port is in a state where it can handle asyncronous events
inline bool port_async_events_allowed(PortData* p) {
if (!p->pending_in) {
if (p->state == PORT_READ_CMD) return true;
Expand All @@ -603,6 +630,8 @@ inline bool port_async_events_allowed(PortData* p) {
return false;
}

/// Step the state machine. This is the main dispatch function of the port control logic.
/// This gets called after an event occurs to decide what happens next.
void port_step(PortData* p) {
if (p->state == PORT_DISABLE) {
port_error(p);
Expand Down Expand Up @@ -637,6 +666,7 @@ void port_step(PortData* p) {
};

if (p->state == PORT_READ_CMD) {
// Read a command byte and look up how many argument bytes it needs
p->cmd = p->cmd_buf[p->cmd_pos++];
p->arg_len = port_cmd_args(p->cmd);

Expand All @@ -647,6 +677,7 @@ void port_step(PortData* p) {
p->state = port_begin_cmd(p);
}
} else if (p->state == PORT_READ_ARG) {
// Read an argument byte
if (p->arg_len == 0) {
port_error(p);
return;
Expand Down

0 comments on commit 0351bf7

Please sign in to comment.