-/* Copyright (C) 2014-2015 by Jacob Alexander
+/* Copyright (C) 2014-2016 by Jacob Alexander
*
* This file is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
-// ----- Macros -----
+// ----- Defines -----
+
+#define UART_Num_Interfaces 2
+#define UART_Master 1
+#define UART_Slave 0
+#define UART_Buffer_Size UARTConnectBufSize_define
+
-// Macro for adding to each uart Tx ring buffer
-#define uart_addTxBuffer( uartNum ) \
-case uartNum: \
- while ( uart##uartNum##_buffer_items + count > uart_buffer_size ) \
- { \
- warn_msg("Too much data to send on UART0, waiting..."); \
- delay( 1 ); \
- } \
- for ( uint8_t c = 0; c < count; c++ ) \
- { \
- if ( Connect_debug ) \
- { \
- printHex( buffer[ c ] ); \
- print( " +" #uartNum NL ); \
- } \
- uart##uartNum##_buffer[ uart##uartNum##_buffer_tail++ ] = buffer[ c ]; \
- uart##uartNum##_buffer_items++; \
- if ( uart##uartNum##_buffer_tail >= uart_buffer_size ) \
- uart##uartNum##_buffer_tail = 0; \
- if ( uart##uartNum##_buffer_head == uart##uartNum##_buffer_tail ) \
- uart##uartNum##_buffer_head++; \
- if ( uart##uartNum##_buffer_head >= uart_buffer_size ) \
- uart##uartNum##_buffer_head = 0; \
- } \
- break
+
+// ----- Macros -----
// Macro for popping from Tx ring buffer
#define uart_fillTxFifo( uartNum ) \
uint8_t fifoSize = ( ( UART##uartNum##_PFIFO & UART_PFIFO_TXFIFOSIZE ) >> 2 ); \
if ( fifoSize == 0 ) \
fifoSize = 1; \
- while ( UART##uartNum##_TCFIFO < fifoSize ) \
- { \
- if ( uart##uartNum##_buffer_items == 0 ) \
- break; \
- UART##uartNum##_D = uart##uartNum##_buffer[ uart##uartNum##_buffer_head++ ]; \
- uart##uartNum##_buffer_items--; \
- if ( uart##uartNum##_buffer_head >= uart_buffer_size ) \
- uart##uartNum##_buffer_head = 0; \
- } \
-}
-
-// Macro for processing UART Rx
-#define uart_processRx( uartNum ) \
-{ \
- if ( !( UART##uartNum##_S1 & UART_S1_RDRF ) ) \
- return; \
- uint8_t available = UART##uartNum##_RCFIFO; \
- if ( available == 0 ) \
+ if ( Connect_debug ) \
{ \
- available = UART##uartNum##_D; \
- UART##uartNum##_CFIFO = UART_CFIFO_RXFLUSH; \
- return; \
+ print( "TxFIFO " #uartNum " - " ); \
+ printHex( fifoSize ); \
+ print("/"); \
+ printHex( UART##uartNum##_TCFIFO ); \
+ print("/"); \
+ printHex( uart_tx_buf[ uartNum ].items ); \
+ print( NL ); \
} \
- while ( available-- > 0 ) \
+ /* XXX Doesn't work well */ \
+ /* while ( UART##uartNum##_TCFIFO < fifoSize ) */ \
+ /* More reliable, albeit slower */ \
+ fifoSize -= UART##uartNum##_TCFIFO; \
+ while ( fifoSize-- != 0 ) \
{ \
- uint8_t byteRead = UART##uartNum##_D; \
- if ( Connect_debug ) \
- { \
- printHex( byteRead ); \
- print( "(" ); \
- printInt8( available ); \
- print( ") <-" ); \
- } \
- switch ( uart##uartNum##_rx_status ) \
- { \
- case UARTStatus_Wait: \
- if ( Connect_debug ) \
- { \
- print(" SYN "); \
- } \
- uart##uartNum##_rx_status = byteRead == 0x16 ? UARTStatus_SYN : UARTStatus_Wait; \
- break; \
- case UARTStatus_SYN: \
- if ( Connect_debug ) \
- { \
- print(" SOH "); \
- } \
- uart##uartNum##_rx_status = byteRead == 0x01 ? UARTStatus_SOH : UARTStatus_Wait; \
- break; \
- case UARTStatus_SOH: \
- { \
- if ( Connect_debug ) \
- { \
- print(" CMD "); \
- } \
- uint8_t byte = byteRead; \
- if ( byte <= Animation ) \
- { \
- uart##uartNum##_rx_status = UARTStatus_Command; \
- uart##uartNum##_rx_command = byte; \
- uart##uartNum##_rx_bytes_waiting = 0xFFFF; \
- } \
- else \
- { \
- uart##uartNum##_rx_status = UARTStatus_Wait; \
- } \
- switch ( uart##uartNum##_rx_command ) \
- { \
- case IdRequest: \
- Connect_receive_IdRequest( 0, (uint16_t*)&uart##uartNum##_rx_bytes_waiting, uartNum ); \
- uart##uartNum##_rx_status = UARTStatus_Wait; \
- break; \
- default: \
- if ( Connect_debug ) \
- { \
- print("###"); \
- } \
- break; \
- } \
- break; \
- } \
- case UARTStatus_Command: \
- { \
- if ( Connect_debug ) \
- { \
- print(" CMD "); \
- } \
- uint8_t (*rcvFunc)(uint8_t, uint16_t(*), uint8_t) = (uint8_t(*)(uint8_t, uint16_t(*), uint8_t))(Connect_receiveFunctions[ uart##uartNum##_rx_command ]); \
- if ( rcvFunc( byteRead, (uint16_t*)&uart##uartNum##_rx_bytes_waiting, uartNum ) ) \
- uart##uartNum##_rx_status = UARTStatus_Wait; \
+ if ( uart_tx_buf[ uartNum ].items == 0 ) \
break; \
- } \
- default: \
- erro_msg("Invalid UARTStatus..."); \
- uart##uartNum##_rx_status = UARTStatus_Wait; \
- available++; \
- continue; \
- } \
- if ( Connect_debug ) \
- { \
- print( NL ); \
- } \
+ UART##uartNum##_D = uart_tx_buf[ uartNum ].buffer[ uart_tx_buf[ uartNum ].head++ ]; \
+ uart_tx_buf[ uartNum ].items--; \
+ if ( uart_tx_buf[ uartNum ].head >= UART_Buffer_Size ) \
+ uart_tx_buf[ uartNum ].head = 0; \
} \
}
// Macros for locking/unlock Tx buffers
#define uart_lockTx( uartNum ) \
{ \
- while ( uart##uartNum##_tx_status == UARTStatus_Wait ); \
- uart##uartNum##_tx_status = UARTStatus_Wait; \
+ /* First, secure place in line for the resource */ \
+ while ( uart_tx_status[ uartNum ].lock ); \
+ uart_tx_status[ uartNum ].lock = 1; \
+ /* Next, wait unit the UART is ready */ \
+ while ( uart_tx_status[ uartNum ].status != UARTStatus_Ready ); \
+ uart_tx_status[ uartNum ].status = UARTStatus_Wait; \
+}
+
+#define uart_lockBothTx( uartNum1, uartNum2 ) \
+{ \
+ /* First, secure place in line for the resource */ \
+ while ( uart_tx_status[ uartNum1 ].lock || uart_tx_status[ uartNum2 ].lock ); \
+ uart_tx_status[ uartNum1 ].lock = 1; \
+ uart_tx_status[ uartNum2 ].lock = 1; \
+ /* Next, wait unit the UARTs are ready */ \
+ while ( uart_tx_status[ uartNum1 ].status != UARTStatus_Ready || uart_tx_status[ uartNum2 ].status != UARTStatus_Ready ); \
+ uart_tx_status[ uartNum1 ].status = UARTStatus_Wait; \
+ uart_tx_status[ uartNum2 ].status = UARTStatus_Wait; \
}
#define uart_unlockTx( uartNum ) \
{ \
- uart##uartNum##_tx_status = UARTStatus_Ready; \
+ /* Ready the UART */ \
+ uart_tx_status[ uartNum ].status = UARTStatus_Ready; \
+ /* Unlock the resource */ \
+ uart_tx_status[ uartNum ].lock = 0; \
}
// CLI Functions
void cliFunc_connectCmd ( char *args );
+void cliFunc_connectDbg ( char *args );
void cliFunc_connectIdl ( char *args );
void cliFunc_connectLst ( char *args );
void cliFunc_connectMst ( char *args );
+// ----- Structs -----
+
+typedef struct UARTRingBuf {
+ uint8_t head;
+ uint8_t tail;
+ uint8_t items;
+ uint8_t buffer[UART_Buffer_Size];
+} UARTRingBuf;
+
+typedef struct UARTDMABuf {
+ uint8_t buffer[UART_Buffer_Size];
+ uint16_t last_read;
+} UARTDMABuf;
+
+typedef struct UARTStatusRx {
+ UARTStatus status;
+ Command command;
+ uint16_t bytes_waiting;
+} UARTStatusRx;
+
+typedef struct UARTStatusTx {
+ UARTStatus status;
+ uint8_t lock;
+} UARTStatusTx;
+
+
+
// ----- Variables -----
// Connect Module command dictionary
CLIDict_Entry( connectCmd, "Sends a command via UART Connect, first arg is which uart, next arg is the command, rest are the arguments." );
+CLIDict_Entry( connectDbg, "Toggle UARTConnect debug mode." );
CLIDict_Entry( connectIdl, "Sends N number of Idle commands, 2 is the default value, and should be sufficient in most cases." );
CLIDict_Entry( connectLst, "Lists available UARTConnect commands and index id" );
CLIDict_Entry( connectMst, "Sets the device as master. Use argument of s to set as slave." );
CLIDict_Entry( connectSts, "UARTConnect status." );
CLIDict_Def( uartConnectCLIDict, "UARTConnect Module Commands" ) = {
CLIDict_Item( connectCmd ),
+ CLIDict_Item( connectDbg ),
CLIDict_Item( connectIdl ),
CLIDict_Item( connectLst ),
CLIDict_Item( connectMst ),
// -- Connect Device Id Variables --
uint8_t Connect_id = 255; // Invalid, unset
uint8_t Connect_master = 0;
+uint8_t Connect_maxId = 0;
// -- Control Variables --
uint32_t Connect_lastCheck = 0; // Cable Check scheduler
-uint8_t Connect_debug = 0; // Set 1 for debug
+uint8_t Connect_debug = 0; // Set 1 for debug
+uint8_t Connect_override = 0; // Prevents master from automatically being set
-
-// -- Rx Status Variables --
-
-volatile UARTStatus uart0_rx_status;
-volatile UARTStatus uart1_rx_status;
-volatile uint16_t uart0_rx_bytes_waiting;
-volatile uint16_t uart1_rx_bytes_waiting;
-volatile Command uart0_rx_command;
-volatile Command uart1_rx_command;
+volatile uint8_t uarts_configured = 0;
-// -- Tx Status Variables --
+// -- Rx Variables --
-volatile UARTStatus uart0_tx_status;
-volatile UARTStatus uart1_tx_status;
+volatile UARTDMABuf uart_rx_buf[UART_Num_Interfaces];
+volatile UARTStatusRx uart_rx_status[UART_Num_Interfaces];
-// -- Ring Buffer Variables --
+// -- Tx Variables --
-#define uart_buffer_size UARTConnectBufSize_define
-volatile uint8_t uart0_buffer_head;
-volatile uint8_t uart0_buffer_tail;
-volatile uint8_t uart0_buffer_items;
-volatile uint8_t uart0_buffer[uart_buffer_size];
-volatile uint8_t uart1_buffer_head;
-volatile uint8_t uart1_buffer_tail;
-volatile uint8_t uart1_buffer_items;
-volatile uint8_t uart1_buffer[uart_buffer_size];
-
-volatile uint8_t uarts_configured = 0;
+UARTRingBuf uart_tx_buf [UART_Num_Interfaces];
+UARTStatusTx uart_tx_status[UART_Num_Interfaces];
// -- Ring Buffer Convenience Functions --
void Connect_addBytes( uint8_t *buffer, uint8_t count, uint8_t uart )
{
// Too big to fit into buffer
- if ( count > uart_buffer_size )
+ if ( count > UART_Buffer_Size )
{
erro_msg("Too big of a command to fit into the buffer...");
return;
}
- // Choose the uart
- switch ( uart )
+ // Invalid UART
+ if ( uart >= UART_Num_Interfaces )
{
- uart_addTxBuffer( 0 );
- uart_addTxBuffer( 1 );
- default:
- erro_msg("Invalid UART to send from...");
- break;
+ erro_print("Invalid UART to send from...");
+ return;
+ }
+
+ // Delay UART copy until there's some space left
+ while ( uart_tx_buf[ uart ].items + count > UART_Buffer_Size )
+ {
+ warn_msg("Too much data to send on UART");
+ printInt8( uart );
+ print( ", waiting..." NL );
+ delay( 1 );
+ }
+
+ // Append data to ring buffer
+ for ( uint8_t c = 0; c < count; c++ )
+ {
+ if ( Connect_debug )
+ {
+ printHex( buffer[ c ] );
+ print(" +");
+ printInt8( uart );
+ print( NL );
+ }
+
+ uart_tx_buf[ uart ].buffer[ uart_tx_buf[ uart ].tail++ ] = buffer[ c ];
+ uart_tx_buf[ uart ].items++;
+ if ( uart_tx_buf[ uart ].tail >= UART_Buffer_Size )
+ uart_tx_buf[ uart ].tail = 0;
+ if ( uart_tx_buf[ uart ].head == uart_tx_buf[ uart ].tail )
+ uart_tx_buf[ uart ].head++;
+ if ( uart_tx_buf[ uart ].head >= UART_Buffer_Size )
+ uart_tx_buf[ uart ].head = 0;
}
}
void Connect_send_CableCheck( uint8_t patternLen )
{
// Wait until the Tx buffers are ready, then lock them
- uart_lockTx( 0 );
- uart_lockTx( 1 );
+ uart_lockBothTx( UART_Master, UART_Slave );
// Prepare header
uint8_t header[] = { 0x16, 0x01, CableCheck, patternLen };
// Send header
- Connect_addBytes( header, sizeof( header ), 1 ); // Master
- Connect_addBytes( header, sizeof( header ), 0 ); // Slave
+ Connect_addBytes( header, sizeof( header ), UART_Master );
+ Connect_addBytes( header, sizeof( header ), UART_Slave );
// Send 0xD2 (11010010) for each argument
uint8_t value = 0xD2;
for ( uint8_t c = 0; c < patternLen; c++ )
{
- Connect_addBytes( &value, 1, 1 ); // Master
- Connect_addBytes( &value, 1, 0 ); // Slave
+ Connect_addBytes( &value, 1, UART_Master );
+ Connect_addBytes( &value, 1, UART_Slave );
}
// Release Tx buffers
- uart_unlockTx( 0 );
- uart_unlockTx( 1 );
+ uart_unlockTx( UART_Master );
+ uart_unlockTx( UART_Slave );
}
void Connect_send_IdRequest()
{
// Lock master bound Tx
- uart_lockTx( 1 );
+ uart_lockTx( UART_Master );
// Prepare header
uint8_t header[] = { 0x16, 0x01, IdRequest };
// Send header
- Connect_addBytes( header, sizeof( header ), 1 ); // Master
+ Connect_addBytes( header, sizeof( header ), UART_Master );
// Unlock Tx
- uart_unlockTx( 1 );
+ uart_unlockTx( UART_Master );
}
// id is the value the next slave should enumerate as
void Connect_send_IdEnumeration( uint8_t id )
{
// Lock slave bound Tx
- uart_lockTx( 0 );
+ uart_lockTx( UART_Slave );
// Prepare header
uint8_t header[] = { 0x16, 0x01, IdEnumeration, id };
// Send header
- Connect_addBytes( header, sizeof( header ), 0 ); // Slave
+ Connect_addBytes( header, sizeof( header ), UART_Slave );
// Unlock Tx
- uart_unlockTx( 0 );
+ uart_unlockTx( UART_Slave );
}
// id is the currently assigned id to the slave
void Connect_send_IdReport( uint8_t id )
{
// Lock master bound Tx
- uart_lockTx( 1 );
+ uart_lockTx( UART_Master );
// Prepare header
uint8_t header[] = { 0x16, 0x01, IdReport, id };
// Send header
- Connect_addBytes( header, sizeof( header ), 1 ); // Master
+ Connect_addBytes( header, sizeof( header ), UART_Master );
// Unlock Tx
- uart_unlockTx( 1 );
+ uart_unlockTx( UART_Master );
}
// id is the currently assigned id to the slave
void Connect_send_ScanCode( uint8_t id, TriggerGuide *scanCodeStateList, uint8_t numScanCodes )
{
// Lock master bound Tx
- uart_lockTx( 1 );
+ uart_lockTx( UART_Master );
// Prepare header
uint8_t header[] = { 0x16, 0x01, ScanCode, id, numScanCodes };
// Send header
- Connect_addBytes( header, sizeof( header ), 1 ); // Master
+ Connect_addBytes( header, sizeof( header ), UART_Master );
// Send each of the scan codes
- Connect_addBytes( (uint8_t*)scanCodeStateList, numScanCodes * TriggerGuideSize, 1 ); // Master
+ Connect_addBytes( (uint8_t*)scanCodeStateList, numScanCodes * TriggerGuideSize, UART_Master );
// Unlock Tx
- uart_unlockTx( 1 );
+ uart_unlockTx( UART_Master );
}
// id is the currently assigned id to the slave
void Connect_send_Animation( uint8_t id, uint8_t *paramList, uint8_t numParams )
{
// Lock slave bound Tx
- uart_lockTx( 0 );
+ uart_lockTx( UART_Slave );
// Prepare header
uint8_t header[] = { 0x16, 0x01, Animation, id, numParams };
// Send header
- Connect_addBytes( header, sizeof( header ), 0 ); // Slave
+ Connect_addBytes( header, sizeof( header ), UART_Slave );
// Send each of the scan codes
- Connect_addBytes( paramList, numParams, 0 ); // Slave
+ Connect_addBytes( paramList, numParams, UART_Slave );
// Unlock Tx
- uart_unlockTx( 0 );
+ uart_unlockTx( UART_Slave );
+}
+
+// Send a remote capability command using capability index
+// This may not be what's expected (especially if the firmware is not the same on each node)
+// To broadcast to all slave nodes, set id to 255 instead of a specific id
+void Connect_send_RemoteCapability( uint8_t id, uint8_t capabilityIndex, uint8_t state, uint8_t stateType, uint8_t numArgs, uint8_t *args )
+{
+ // Prepare header
+ uint8_t header[] = { 0x16, 0x01, RemoteCapability, id, capabilityIndex, state, stateType, numArgs };
+
+ // Ignore current id
+ if ( id == Connect_id )
+ return;
+
+ // Send towards slave node
+ if ( id > Connect_id )
+ {
+ // Lock slave bound Tx
+ uart_lockTx( UART_Slave );
+
+ // Send header
+ Connect_addBytes( header, sizeof( header ), UART_Slave );
+
+ // Send arguments
+ Connect_addBytes( args, numArgs, UART_Slave );
+
+ // Unlock Tx
+ uart_unlockTx( UART_Slave );
+ }
+
+ // Send towards master node
+ if ( id < Connect_id || id == 255 )
+ {
+ // Lock slave bound Tx
+ uart_lockTx( UART_Master );
+
+ // Send header
+ Connect_addBytes( header, sizeof( header ), UART_Master );
+
+ // Send arguments
+ Connect_addBytes( args, numArgs, UART_Master );
+
+ // Unlock Tx
+ uart_unlockTx( UART_Master );
+ }
}
void Connect_send_Idle( uint8_t num )
{
// Wait until the Tx buffers are ready, then lock them
- uart_lockTx( 0 );
- uart_lockTx( 1 );
+ uart_lockBothTx( UART_Slave, UART_Master );
// Send n number of idles to reset link status (if in a bad state)
uint8_t value = 0x16;
for ( uint8_t c = 0; c < num; c++ )
{
- Connect_addBytes( &value, 1, 1 ); // Master
- Connect_addBytes( &value, 1, 0 ); // Slave
+ Connect_addBytes( &value, 1, UART_Master );
+ Connect_addBytes( &value, 1, UART_Slave );
}
// Release Tx buffers
- uart_unlockTx( 0 );
- uart_unlockTx( 1 );
+ uart_unlockTx( UART_Master );
+ uart_unlockTx( UART_Slave );
}
// - Cable Check variables -
uint32_t Connect_cableFaultsMaster = 0;
-uint32_t Connect_cableFaultsSlave = 0;
+uint32_t Connect_cableFaultsSlave = 0;
+uint32_t Connect_cableChecksMaster = 0;
+uint32_t Connect_cableChecksSlave = 0;
uint8_t Connect_cableOkMaster = 0;
-uint8_t Connect_cableOkSlave = 0;
+uint8_t Connect_cableOkSlave = 0;
-uint8_t Connect_receive_CableCheck( uint8_t byte, uint16_t *pending_bytes, uint8_t to_master )
+uint8_t Connect_receive_CableCheck( uint8_t byte, uint16_t *pending_bytes, uint8_t uart_num )
{
// Check if this is the first byte
if ( *pending_bytes == 0xFFFF )
warn_print("Cable Fault!");
// Check which side of the chain
- if ( to_master )
- {
- Connect_cableFaultsMaster++;
- Connect_cableOkMaster = 0;
- print(" Master ");
- }
- else
+ if ( uart_num == UART_Slave )
{
Connect_cableFaultsSlave++;
Connect_cableOkSlave = 0;
print(" Slave ");
}
+ else
+ {
+ // Lower current requirement during errors
+ // USB minimum
+ // Only if this is not the master node
+ if ( Connect_id != 0 )
+ {
+ Output_update_external_current( 100 );
+ }
+
+ Connect_cableFaultsMaster++;
+ Connect_cableOkMaster = 0;
+ print(" Master ");
+ }
printHex( byte );
print( NL );
// Signal that the command should wait for a SYN again
return 1;
}
+ else
+ {
+ // Check which side of the chain
+ if ( uart_num == UART_Slave )
+ {
+ Connect_cableChecksSlave++;
+ }
+ else
+ {
+ // If we already have an Id, then set max current again
+ if ( Connect_id != 255 && Connect_id != 0 )
+ {
+ // TODO reset to original negotiated current
+ Output_update_external_current( 500 );
+ }
+ Connect_cableChecksMaster++;
+ }
+ }
}
// If cable check was successful, set cable ok
if ( *pending_bytes == 0 )
{
- if ( to_master )
+ if ( uart_num == UART_Slave )
{
- Connect_cableOkMaster = 1;
+ Connect_cableOkSlave = 1;
}
else
{
- Connect_cableOkSlave = 1;
+ Connect_cableOkMaster = 1;
}
}
return *pending_bytes == 0 ? 1 : 0;
}
-uint8_t Connect_receive_IdRequest( uint8_t byte, uint16_t *pending_bytes, uint8_t to_master )
+uint8_t Connect_receive_IdRequest( uint8_t byte, uint16_t *pending_bytes, uint8_t uart_num )
{
dbug_print("IdRequest");
// Check the directionality
- if ( to_master )
+ if ( uart_num == UART_Master )
{
erro_print("Invalid IdRequest direction...");
}
return 1;
}
-uint8_t Connect_receive_IdEnumeration( uint8_t id, uint16_t *pending_bytes, uint8_t to_master )
+uint8_t Connect_receive_IdEnumeration( uint8_t id, uint16_t *pending_bytes, uint8_t uart_num )
{
dbug_print("IdEnumeration");
// Check the directionality
- if ( !to_master )
+ if ( uart_num == UART_Slave )
{
erro_print("Invalid IdEnumeration direction...");
}
// Send reponse back to master
Connect_send_IdReport( id );
+ // Node now enumerated, set external power to USB Max
+ // Only set if this is not the master node
+ // TODO Determine power slice for each node as part of protocol
+ if ( Connect_id != 0 )
+ {
+ Output_update_external_current( 500 );
+ }
+
// Propogate next Id if the connection is ok
if ( Connect_cableOkSlave )
{
return 1;
}
-uint8_t Connect_receive_IdReport( uint8_t id, uint16_t *pending_bytes, uint8_t to_master )
+uint8_t Connect_receive_IdReport( uint8_t id, uint16_t *pending_bytes, uint8_t uart_num )
{
dbug_print("IdReport");
// Check the directionality
- if ( to_master )
+ if ( uart_num == UART_Master )
{
erro_print("Invalid IdRequest direction...");
}
// Track Id response if master
if ( Connect_master )
{
- // TODO, setup id's
info_msg("Id Reported: ");
printHex( id );
print( NL );
+
+ // Check if this is the highest ID
+ if ( id > Connect_maxId )
+ Connect_maxId = id;
return 1;
}
// Propagate id if yet another slave
uint8_t Connect_receive_ScanCodeBufferPos;
uint8_t Connect_receive_ScanCodeDeviceId;
-uint8_t Connect_receive_ScanCode( uint8_t byte, uint16_t *pending_bytes, uint8_t to_master )
+uint8_t Connect_receive_ScanCode( uint8_t byte, uint16_t *pending_bytes, uint8_t uart_num )
{
- dbug_print("ScanCode");
// Check the directionality
- if ( !to_master )
+ if ( uart_num == UART_Master )
{
erro_print("Invalid ScanCode direction...");
}
// Master node, trigger scan codes
if ( Connect_master ) switch ( (*pending_bytes)-- )
{
+ // Byte count always starts at 0xFFFF
case 0xFFFF: // Device Id
Connect_receive_ScanCodeDeviceId = byte;
break;
case 0xFFFE: // Number of TriggerGuides in bytes (byte * 3)
- *pending_bytes = byte * 3;
+ *pending_bytes = byte * sizeof( TriggerGuide );
Connect_receive_ScanCodeBufferPos = 0;
break;
// Set the specific TriggerGuide entry
((uint8_t*)&Connect_receive_ScanCodeBuffer)[ Connect_receive_ScanCodeBufferPos++ ] = byte;
- // Reset the BufferPos if higher than 3
+ // Reset the BufferPos if higher than sizeof TriggerGuide
// And send the TriggerGuide to the Macro Module
- if ( Connect_receive_ScanCodeBufferPos > 3 )
+ if ( Connect_receive_ScanCodeBufferPos >= sizeof( TriggerGuide ) )
{
Connect_receive_ScanCodeBufferPos = 0;
- Macro_triggerState( &Connect_receive_ScanCodeBuffer, 1 );
+
+ // Adjust ScanCode offset
+ if ( Connect_receive_ScanCodeDeviceId > 0 )
+ {
+ // Check if this node is too large
+ if ( Connect_receive_ScanCodeDeviceId >= InterconnectNodeMax )
+ {
+ warn_msg("Not enough interconnect layout nodes configured: ");
+ printHex( Connect_receive_ScanCodeDeviceId );
+ print( NL );
+ break;
+ }
+
+ // This variable is in generatedKeymaps.h
+ extern uint8_t InterconnectOffsetList[];
+ Connect_receive_ScanCodeBuffer.scanCode = Connect_receive_ScanCodeBuffer.scanCode + InterconnectOffsetList[ Connect_receive_ScanCodeDeviceId - 1 ];
+ }
+
+ // ScanCode receive debug
+ if ( Connect_debug )
+ {
+ dbug_msg("");
+ printHex( Connect_receive_ScanCodeBuffer.type );
+ print(" ");
+ printHex( Connect_receive_ScanCodeBuffer.state );
+ print(" ");
+ printHex( Connect_receive_ScanCodeBuffer.scanCode );
+ print( NL );
+ }
+
+ // Send ScanCode to macro module
+ Macro_interconnectAdd( &Connect_receive_ScanCodeBuffer );
}
break;
}
// Propagate ScanCode packet
+ // XXX It would be safer to buffer the scancodes first, before transmitting the packet -Jacob
+ // The current method is the more efficient/aggressive, but could cause issues if there were errors during transmission
else switch ( (*pending_bytes)-- )
{
+ // Byte count always starts at 0xFFFF
case 0xFFFF: // Device Id
{
Connect_receive_ScanCodeDeviceId = byte;
// Lock the master Tx buffer
- uart_lockTx( 1 );
+ uart_lockTx( UART_Master );
// Send header + Id byte
uint8_t header[] = { 0x16, 0x01, ScanCode, byte };
- Connect_addBytes( header, sizeof( header ), 1 ); // Master
+ Connect_addBytes( header, sizeof( header ), UART_Master );
break;
}
- case 0xFFFE: // Number of TriggerGuides in bytes (byte * 3)
- *pending_bytes = byte * 3;
+ case 0xFFFE: // Number of TriggerGuides in bytes
+ *pending_bytes = byte * sizeof( TriggerGuide );
Connect_receive_ScanCodeBufferPos = 0;
// Pass through byte
- Connect_addBytes( &byte, 1, 1 ); // Master
+ Connect_addBytes( &byte, 1, UART_Master );
break;
default:
// Pass through byte
- Connect_addBytes( &byte, 1, 1 ); // Master
+ Connect_addBytes( &byte, 1, UART_Master );
// Unlock Tx Buffer after sending last byte
if ( *pending_bytes == 0 )
- uart_unlockTx( 1 );
+ uart_unlockTx( UART_Master );
break;
}
return *pending_bytes == 0 ? 1 : 0;
}
-uint8_t Connect_receive_Animation( uint8_t byte, uint16_t *pending_bytes, uint8_t to_master )
+uint8_t Connect_receive_Animation( uint8_t byte, uint16_t *pending_bytes, uint8_t uart_num )
{
dbug_print("Animation");
return 1;
}
+// - Remote Capability Variables -
+#define Connect_receive_RemoteCapabilityMaxArgs 25 // XXX Calculate the max using kll
+RemoteCapabilityCommand Connect_receive_RemoteCapabilityBuffer;
+uint8_t Connect_receive_RemoteCapabilityArgs[Connect_receive_RemoteCapabilityMaxArgs];
+
+uint8_t Connect_receive_RemoteCapability( uint8_t byte, uint16_t *pending_bytes, uint8_t uart_num )
+{
+ // Check which byte in the packet we are at
+ switch ( (*pending_bytes)-- )
+ {
+ // Byte count always starts at 0xFFFF
+ case 0xFFFF: // Device Id
+ Connect_receive_RemoteCapabilityBuffer.id = byte;
+ break;
+
+ case 0xFFFE: // Capability Index
+ Connect_receive_RemoteCapabilityBuffer.capabilityIndex = byte;
+ break;
+
+ case 0xFFFD: // State
+ Connect_receive_RemoteCapabilityBuffer.state = byte;
+ break;
+
+ case 0xFFFC: // StateType
+ Connect_receive_RemoteCapabilityBuffer.stateType = byte;
+ break;
+
+ case 0xFFFB: // Number of args
+ Connect_receive_RemoteCapabilityBuffer.numArgs = byte;
+ *pending_bytes = byte;
+ break;
+
+ default: // Args (# defined by previous byte)
+ Connect_receive_RemoteCapabilityArgs[
+ Connect_receive_RemoteCapabilityBuffer.numArgs - *pending_bytes + 1
+ ] = byte;
+
+ // If entire packet has been fully received
+ if ( *pending_bytes == 0 )
+ {
+ // Determine if this is the node to run the capability on
+ // Conditions: Matches or broadcast (0xFF)
+ if ( Connect_receive_RemoteCapabilityBuffer.id == 0xFF
+ || Connect_receive_RemoteCapabilityBuffer.id == Connect_id )
+ {
+ extern const Capability CapabilitiesList[]; // See generatedKeymap.h
+ void (*capability)(uint8_t, uint8_t, uint8_t*) = (void(*)(uint8_t, uint8_t, uint8_t*))(
+ CapabilitiesList[ Connect_receive_RemoteCapabilityBuffer.capabilityIndex ].func
+ );
+ capability(
+ Connect_receive_RemoteCapabilityBuffer.state,
+ Connect_receive_RemoteCapabilityBuffer.stateType,
+ &Connect_receive_RemoteCapabilityArgs[2]
+ );
+ }
+
+ // If this is not the correct node, keep sending it in the same direction (doesn't matter if more nodes exist)
+ // or if this is a broadcast
+ if ( Connect_receive_RemoteCapabilityBuffer.id == 0xFF
+ || Connect_receive_RemoteCapabilityBuffer.id != Connect_id )
+ {
+ // Prepare outgoing packet
+ Connect_receive_RemoteCapabilityBuffer.command = RemoteCapability;
+
+ // Send to the other UART (not the one receiving the packet from
+ uint8_t uart_direction = uart_num == UART_Master ? UART_Slave : UART_Master;
+
+ // Lock Tx UART
+ switch ( uart_direction )
+ {
+ case UART_Master: uart_lockTx( UART_Master ); break;
+ case UART_Slave: uart_lockTx( UART_Slave ); break;
+ }
+
+ // Send header
+ uint8_t header[] = { 0x16, 0x01 };
+ Connect_addBytes( header, sizeof( header ), uart_direction );
+
+ // Send Remote Capability and arguments
+ Connect_addBytes( (uint8_t*)&Connect_receive_RemoteCapabilityBuffer, sizeof( RemoteCapabilityCommand ), uart_direction );
+ Connect_addBytes( Connect_receive_RemoteCapabilityArgs, Connect_receive_RemoteCapabilityBuffer.numArgs, uart_direction );
+
+ // Unlock Tx UART
+ switch ( uart_direction )
+ {
+ case UART_Master: uart_unlockTx( UART_Master ); break;
+ case UART_Slave: uart_unlockTx( UART_Slave ); break;
+ }
+ }
+ }
+ break;
+ }
+
+ // Check whether the scan codes have finished sending
+ return *pending_bytes == 0 ? 1 : 0;
+}
+
// Baud Rate
// NOTE: If finer baud adjustment is needed see UARTx_C4 -> BRFA in the datasheet
Connect_receive_IdReport,
Connect_receive_ScanCode,
Connect_receive_Animation,
+ Connect_receive_RemoteCapability,
};
-// ----- Interrupt Functions -----
-
-// Master / UART0 ISR
-void uart0_status_isr()
-{
- // Process Rx buffer
- uart_processRx( 0 );
-}
-
-// Slave / UART1 ISR
-void uart1_status_isr()
-{
- // Process Rx buffer
- uart_processRx( 1 );
-}
-
-
-
// ----- Functions -----
// Resets the state of the UART buffers and state variables
void Connect_reset()
{
- // Rx Status Variables
- uart0_rx_status = UARTStatus_Wait;
- uart1_rx_status = UARTStatus_Wait;
- uart0_rx_bytes_waiting = 0;
- uart1_rx_bytes_waiting = 0;
+ // Reset Rx
+ memset( (void*)uart_rx_status, 0, sizeof( UARTStatusRx ) * UART_Num_Interfaces );
- // Tx Status Variables
- uart0_tx_status = UARTStatus_Ready;
- uart1_tx_status = UARTStatus_Ready;
+ // Reset Tx
+ memset( (void*)uart_tx_buf, 0, sizeof( UARTRingBuf ) * UART_Num_Interfaces );
+ memset( (void*)uart_tx_status, 0, sizeof( UARTStatusTx ) * UART_Num_Interfaces );
- // Ring Buffer Variables
- uart0_buffer_head = 0;
- uart0_buffer_tail = 0;
- uart0_buffer_items = 0;
- uart1_buffer_head = 0;
- uart1_buffer_tail = 0;
- uart1_buffer_items = 0;
+ // Set Rx/Tx buffers as ready
+ for ( uint8_t inter = 0; inter < UART_Num_Interfaces; inter++ )
+ {
+ uart_tx_status[ inter ].status = UARTStatus_Ready;
+ uart_rx_buf[ inter ].last_read = UART_Buffer_Size;
+ }
}
if ( Connect_master )
Connect_id = 0; // 0x00 is always the master Id
- // Master / UART0 setup
- // Slave / UART1 setup
+ // UART0 setup
+ // UART1 setup
// Setup the the UART interface for keyboard data input
SIM_SCGC4 |= SIM_SCGC4_UART0; // Disable clock gating
SIM_SCGC4 |= SIM_SCGC4_UART1; // Disable clock gating
UART0_C1 = UART_C1_M | UART_C1_PE | UART_C1_ILT;
UART1_C1 = UART_C1_M | UART_C1_PE | UART_C1_ILT;
- // Number of bytes in FIFO before TX Interrupt
- // TODO Set 0
- UART0_TWFIFO = 1;
- UART1_TWFIFO = 1;
+ // Only using Tx Fifos
+ UART0_PFIFO = UART_PFIFO_TXFE;
+ UART1_PFIFO = UART_PFIFO_TXFE;
+
+ // Setup DMA clocks
+ SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
+ SIM_SCGC7 |= SIM_SCGC7_DMA;
+
+ // Start with channels disabled first
+ DMAMUX0_CHCFG0 = 0;
+ DMAMUX0_CHCFG1 = 0;
+
+ // Configure DMA channels
+ //DMA_DSR_BCR0 |= DMA_DSR_BCR_DONE_MASK; // TODO What's this?
+ DMA_TCD0_CSR = 0;
+ DMA_TCD1_CSR = 0;
+
+ // Default control register
+ DMA_CR = 0;
+
+ // DMA Priority
+ DMA_DCHPRI0 = 0; // Ch 0, priority 0
+ DMA_DCHPRI1 = 1; // ch 1, priority 1
+
+ // Clear error interrupts
+ DMA_EEI = 0;
+
+ // Setup TCD
+ DMA_TCD0_SADDR = (uint32_t*)&UART0_D;
+ DMA_TCD1_SADDR = (uint32_t*)&UART1_D;
+ DMA_TCD0_SOFF = 0;
+ DMA_TCD1_SOFF = 0;
+
+ // No modulo, 8-bit transfer size
+ DMA_TCD0_ATTR = DMA_TCD_ATTR_SMOD(0) | DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DMOD(0) | DMA_TCD_ATTR_DSIZE(0);
+ DMA_TCD1_ATTR = DMA_TCD_ATTR_SMOD(0) | DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DMOD(0) | DMA_TCD_ATTR_DSIZE(0);
+
+ // One byte transferred at a time
+ DMA_TCD0_NBYTES_MLNO = 1;
+ DMA_TCD1_NBYTES_MLNO = 1;
- // Number of bytes in FIFO before RX Interrupt
- UART0_RWFIFO = 1;
- UART1_RWFIFO = 1;
+ // Source address does not change
+ DMA_TCD0_SLAST = 0;
+ DMA_TCD1_SLAST = 0;
- // Enable TX and RX FIFOs
- UART0_PFIFO = UART_PFIFO_TXFE | UART_PFIFO_RXFE;
- UART1_PFIFO = UART_PFIFO_TXFE | UART_PFIFO_RXFE;
+ // Destination buffer
+ DMA_TCD0_DADDR = (uint32_t*)uart_rx_buf[0].buffer;
+ DMA_TCD1_DADDR = (uint32_t*)uart_rx_buf[1].buffer;
- // Reciever Inversion Disabled, LSBF
- // UART_S2_RXINV UART_S2_MSBF
- UART0_S2 |= 0x00;
- UART1_S2 |= 0x00;
+ // Incoming byte, increment by 1 in the rx buffer
+ DMA_TCD0_DOFF = 1;
+ DMA_TCD1_DOFF = 1;
- // Transmit Inversion Disabled
- // UART_C3_TXINV
- UART0_C3 |= 0x00;
- UART1_C3 |= 0x00;
+ // Single major loop, must be the same value
+ DMA_TCD0_CITER_ELINKNO = UART_Buffer_Size;
+ DMA_TCD1_CITER_ELINKNO = UART_Buffer_Size;
+ DMA_TCD0_BITER_ELINKNO = UART_Buffer_Size;
+ DMA_TCD1_BITER_ELINKNO = UART_Buffer_Size;
+
+ // Reset buffer when full
+ DMA_TCD0_DLASTSGA = -( UART_Buffer_Size );
+ DMA_TCD1_DLASTSGA = -( UART_Buffer_Size );
+
+ // Enable DMA channels
+ DMA_ERQ |= DMA_ERQ_ERQ0 | DMA_ERQ_ERQ1;
+
+ // Setup DMA channel routing
+ DMAMUX0_CHCFG0 = DMAMUX_ENABLE | DMAMUX_SOURCE_UART0_RX;
+ DMAMUX0_CHCFG1 = DMAMUX_ENABLE | DMAMUX_SOURCE_UART1_RX;
+
+ // Enable DMA requests (requires Rx interrupts)
+ UART0_C5 = UART_C5_RDMAS;
+ UART1_C5 = UART_C5_RDMAS;
// TX Enabled, RX Enabled, RX Interrupt Enabled
- // UART_C2_TE UART_C2_RE UART_C2_RIE
UART0_C2 = UART_C2_TE | UART_C2_RE | UART_C2_RIE;
UART1_C2 = UART_C2_TE | UART_C2_RE | UART_C2_RIE;
}
+#define DMA_BUF_POS( x, pos ) \
+ case x: \
+ pos = DMA_TCD##x##_CITER_ELINKNO; \
+ break
+void Connect_rx_process( uint8_t uartNum )
+{
+ // Determine current position to read until
+ uint16_t bufpos = 0;
+ switch ( uartNum )
+ {
+ DMA_BUF_POS( 0, bufpos );
+ DMA_BUF_POS( 1, bufpos );
+ }
+
+ // Process each of the new bytes
+ // Even if we receive more bytes during processing, wait until the next check so we don't starve other tasks
+ while ( bufpos != uart_rx_buf[ uartNum ].last_read )
+ {
+ // If the last_read byte is at the buffer edge, roll back to beginning
+ if ( uart_rx_buf[ uartNum ].last_read == 0 )
+ {
+ uart_rx_buf[ uartNum ].last_read = UART_Buffer_Size;
+
+ // Check to see if we're at the boundary
+ if ( bufpos == UART_Buffer_Size )
+ break;
+ }
+
+ // Read the byte out of Rx DMA buffer
+ uint8_t byte = uart_rx_buf[ uartNum ].buffer[ UART_Buffer_Size - uart_rx_buf[ uartNum ].last_read-- ];
+
+ if ( Connect_debug )
+ {
+ printHex( byte );
+ print(" ");
+ }
+
+ // Process UART byte
+ switch ( uart_rx_status[ uartNum ].status )
+ {
+ // Every packet must start with a SYN / 0x16
+ case UARTStatus_Wait:
+ if ( Connect_debug )
+ {
+ print(" Wait ");
+ }
+ uart_rx_status[ uartNum ].status = byte == 0x16 ? UARTStatus_SYN : UARTStatus_Wait;
+ break;
+
+ // After a SYN, there must be a SOH / 0x01
+ case UARTStatus_SYN:
+ if ( Connect_debug )
+ {
+ print(" SYN ");
+ }
+ uart_rx_status[ uartNum ].status = byte == 0x01 ? UARTStatus_SOH : UARTStatus_Wait;
+ break;
+
+ // After a SOH the packet structure may diverge a bit
+ // This is the packet type field (refer to the Command enum)
+ // For very small packets (e.g. IdRequest) this is all that's required to take action
+ case UARTStatus_SOH:
+ {
+ if ( Connect_debug )
+ {
+ print(" SOH ");
+ }
+
+ // Check if this is actually a reserved CMD 0x16 (Error condition)
+ if ( byte == Command_SYN )
+ {
+ uart_rx_status[ uartNum ].status = UARTStatus_SYN;
+ break;
+ }
+
+ // Otherwise process the command
+ if ( byte < Command_TOP )
+ {
+ uart_rx_status[ uartNum ].status = UARTStatus_Command;
+ uart_rx_status[ uartNum ].command = byte;
+ uart_rx_status[ uartNum ].bytes_waiting = 0xFFFF;
+ }
+ // Invalid packet type, ignore
+ else
+ {
+ uart_rx_status[ uartNum ].status = UARTStatus_Wait;
+ }
+
+ // Check if this is a very short packet
+ switch ( uart_rx_status[ uartNum ].command )
+ {
+ case IdRequest:
+ Connect_receive_IdRequest( 0, (uint16_t*)&uart_rx_status[ uartNum ].bytes_waiting, uartNum );
+ uart_rx_status[ uartNum ].status = UARTStatus_Wait;
+ break;
+
+ default:
+ if ( Connect_debug )
+ {
+ print(" ### ");
+ printHex( uart_rx_status[ uartNum ].command );
+ }
+ break;
+ }
+ break;
+ }
+
+ // After the packet type has been deciphered do Command specific processing
+ // Until the Command has received all the bytes it requires the UART buffer stays in this state
+ case UARTStatus_Command:
+ {
+ if ( Connect_debug )
+ {
+ print(" CMD ");
+ }
+ /* Call specific UARTConnect command receive function */
+ uint8_t (*rcvFunc)(uint8_t, uint16_t(*), uint8_t) = (uint8_t(*)(uint8_t, uint16_t(*), uint8_t))(Connect_receiveFunctions[ uart_rx_status[ uartNum ].command ]);
+ if ( rcvFunc( byte, (uint16_t*)&uart_rx_status[ uartNum ].bytes_waiting, uartNum ) )
+ uart_rx_status[ uartNum ].status = UARTStatus_Wait;
+ break;
+ }
+
+ // Unknown status, should never get here
+ default:
+ erro_msg("Invalid UARTStatus...");
+ uart_rx_status[ uartNum ].status = UARTStatus_Wait;
+ continue;
+ }
+
+ if ( Connect_debug )
+ {
+ print( NL );
+ }
+ }
+}
+
+
// Scan for updates in the master/slave
// - Interrupts will deal with most input functions
// - Used to send queries
{
// Check if initially configured as a slave and usb comes up
// Then reconfigure as a master
- if ( !Connect_master && Output_Available )
+ if ( !Connect_master && Output_Available && !Connect_override )
{
Connect_setup( Output_Available );
}
// Limit how often we do cable checks
+ //uint32_t time_compare = 0x007; // Used for debugging cables -HaaTa
uint32_t time_compare = 0x7FF; // Must be all 1's, 0x3FF is valid, 0x4FF is not
uint32_t current_time = systick_millis_count;
if ( Connect_lastCheck != current_time
{
// Check if Tx Buffers are empty and the Tx Ring buffers have data to send
// This happens if there was previously nothing to send
- if ( uart0_buffer_items > 0 && UART0_TCFIFO == 0 )
+ if ( uart_tx_buf[ 0 ].items > 0 && UART0_TCFIFO == 0 )
uart_fillTxFifo( 0 );
- if ( uart1_buffer_items > 0 && UART1_TCFIFO == 0 )
+ if ( uart_tx_buf[ 1 ].items > 0 && UART1_TCFIFO == 0 )
uart_fillTxFifo( 1 );
+
+ // Process Rx Buffers
+ Connect_rx_process( 0 );
+ Connect_rx_process( 1 );
}
}
+// Called by parent Scan module whenever the available current changes
+void Connect_currentChange( unsigned int current )
+{
+ // TODO - Any potential power saving here?
+}
+
+
// ----- CLI Command Functions -----
}
}
+void cliFunc_connectDbg( char* args )
+{
+ print( NL );
+ info_msg("Connect Debug Mode Toggle");
+ Connect_debug = !Connect_debug;
+}
+
void cliFunc_connectIdl( char* args )
{
// Parse number from argument
print( NL );
+ // Set override
+ Connect_override = 1;
+
switch ( arg1Ptr[0] )
{
+ // Disable override
+ case 'd':
+ case 'D':
+ Connect_override = 0;
case 's':
case 'S':
info_msg("Setting device as slave.");
print( Connect_master ? "Master" : "Slave" );
print( NL "Device Id:\t" );
printHex( Connect_id );
+ print( NL "Max Id:\t" );
+ printHex( Connect_maxId );
print( NL "Master <=" NL "\tStatus:\t");
printHex( Connect_cableOkMaster );
print( NL "\tFaults:\t");
- printHex( Connect_cableFaultsMaster );
+ printHex32( Connect_cableFaultsMaster );
+ print("/");
+ printHex32( Connect_cableChecksMaster );
print( NL "\tRx:\t");
- printHex( uart1_rx_status );
+ printHex( uart_rx_status[UART_Master].status );
print( NL "\tTx:\t");
- printHex( uart1_tx_status );
+ printHex( uart_tx_status[UART_Master].status );
print( NL "Slave <=" NL "\tStatus:\t");
printHex( Connect_cableOkSlave );
print( NL "\tFaults:\t");
- printHex( Connect_cableFaultsSlave );
+ printHex32( Connect_cableFaultsSlave );
+ print("/");
+ printHex32( Connect_cableChecksSlave );
print( NL "\tRx:\t");
- printHex( uart0_rx_status );
+ printHex( uart_rx_status[UART_Slave].status );
print( NL "\tTx:\t");
- printHex( uart0_tx_status );
+ printHex( uart_tx_status[UART_Slave].status );
}