+/* Copyright (C) 2012 by Jacob Alexander
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// ----- Includes -----
+
+// AVR Includes
+#include <avr/interrupt.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+// Project Includes
+#include <led.h>
+#include <print.h>
+
+// Local Includes
+#include "scan_loop.h"
+
+
+
+// ----- Defines -----
+
+// - Pinout Defines -
+
+// Scan Bit Pins (from keyboard)
+// - Reads in the ASCII scancode
+// - Shift and ShiftLock are handled internally
+#define READSCAN_PORT PORTC
+#define READSCAN_DDR DDRC
+#define READSCAN_PIN PINC
+
+
+// Interrupt Pins (from keyboard)
+// - CODEINT (Code key signal interrupt/press)
+// - Normally high, low when "Code" key is pressed; separate from all other key presses
+// - PRESSINT (Press signal interrupt/press)
+// - Normally high, low when any key (or multiple except "Code") is pressed, returns to high once all keys are released
+// - Signal is changed BEFORE the Scan Bits are updated
+// - PULSEINT (Key action pulse interrupt/press)
+// - Normally high, low pulses of 147us on key presses (depending on the combination of mode control pins)
+// - Pulse is guarranteed to sent after the Scan Bits are updated
+#define CODEINT_PORT PORTE
+#define CODEINT_DDR DDRE
+#define CODEINT_PIN PINE
+#define CODEINT_POS 7
+
+#define PRESSINT_PORT PORTE
+#define PRESSINT_DDR DDRE
+#define PRESSINT_POS 6
+
+#define PULSEINT_PORT PORTD
+#define PULSEINT_DDR DDRD
+#define PULSEINT_POS 3
+
+
+// LED Pins (to keyboard)
+// - 1 disable LED
+// - 1 enable LED
+#define LED1_PORT PORTF // [Pin 19]
+#define LED1_DDR DDRF
+#define LED1_POS 6
+
+#define LED2_PORT PORTF // [Pin 20]
+#define LED2_DDR DDRF
+#define LED2_POS 7
+
+
+// Mode Control Pins (to keyboard)
+// - Repeat [Pin 14]
+// - 1 Single pulse mode (PULSEINT)
+// - 0 Repeated pulse mode (PULSEINT) (1 pulse, pause, then constant pulses)
+// - Multi [Pin 15]
+// - 1 1KRO mode (typewriter compatibility mode)
+// - 0 NKRO mode (new pulse on each keypress - PULSEINT)
+// - Signal [Pin 18]
+// - 1 disables pulse interrupt (PULSEINT)
+// - 0 enables pulse interrupt (PULSEINT)
+#define REPEAT_PORT PORTF
+#define REPEAT_DDR DDRF
+#define REPEAT_POS 3
+
+#define MULTI_PORT PORTF
+#define MULTI_DDR DDRF
+#define MULTI_POS 4
+
+#define SIGNAL_PORT PORTF
+#define SIGNAL_DDR DDRF
+#define SIGNAL_POS 5
+
+
+// Manually Scanned Keys
+// Keys that the controller screws up, requiring a separate wire to be brought to the controller
+// Note: Safer to route these through a NOT gate to boost the signal strength
+// Values below are AFTER NOT gate
+// - Shift (both shift keys are on the same scan line)
+// - 0 Released
+// - 1 Pressed
+// - Shift Lock
+// - 0 Released
+// - 1 Pressed
+#define MANUAL_SCAN_KEYS 2
+
+#define SHIFT_KEY 0
+#define SHIFT_PORT PORTF
+#define SHIFT_DDR DDRF
+#define SHIFT_PIN PINF
+#define SHIFT_POS 0
+
+#define SHIFTLOCK_KEY 1
+#define SHIFTLOCK_PORT PORTF
+#define SHIFTLOCK_DDR DDRF
+#define SHIFTLOCK_PIN PINF
+#define SHIFTLOCK_POS 1
+
+
+// ----- Macros -----
+
+// Make sure we haven't overflowed the buffer
+#define bufferAdd(byte) \
+ if ( KeyIndex_BufferUsed < KEYBOARD_BUFFER ) \
+ KeyIndex_Buffer[KeyIndex_BufferUsed++] = byte
+
+
+
+// ----- Variables -----
+
+// Buffer used to inform the macro processing module which keys have been detected as pressed
+volatile uint8_t KeyIndex_Buffer[KEYBOARD_BUFFER];
+volatile uint8_t KeyIndex_BufferUsed;
+volatile uint8_t KeyIndex_Add_InputSignal; // Used to pass the (click/input value) to the keyboard for the clicker
+
+volatile uint8_t KeyScan_Table[MANUAL_SCAN_KEYS]; // Used for tracking key status of manually scanned keys
+volatile uint8_t KeyScan_Prev [MANUAL_SCAN_KEYS]; // Keeps track of key state changes
+volatile uint8_t KeyScan_Count;
+
+
+
+// ----- Functions -----
+
+// Pre-declarations
+void processKeyValue( uint8_t keyValue );
+
+
+
+// Setup
+inline void scan_setup()
+{
+ // Setup the external interrupts for
+ // - General keypresses (INT6/E6) -> rising edge (to detect key release)
+ // - "Code" key (INT7/E7) -> falling/rising edge (to detect key press/release)
+ // - General keypress pulse (INT3/D3) -> falling edge (to detect key press )
+ EICRA = 0x80;
+ EICRB = 0x70;
+ EIMSK = 0xC8;
+
+
+ // Setup Interrupt Pins
+ CODEINT_PORT |= (1 << CODEINT_POS );
+ CODEINT_DDR &= ~(1 << CODEINT_POS );
+
+ PRESSINT_PORT |= (1 << PRESSINT_POS);
+ PRESSINT_DDR &= ~(1 << PRESSINT_POS);
+
+ PULSEINT_PORT |= (1 << PULSEINT_POS);
+ PULSEINT_DDR &= ~(1 << PULSEINT_POS);
+
+
+ // Setup LED Pins (default off)
+ LED1_PORT |= (1 << LED1_POS);
+ LED1_DDR |= (1 << LED1_POS);
+
+ LED2_PORT |= (1 << LED2_POS);
+ LED2_DDR |= (1 << LED2_POS);
+
+
+ // Setup READSCAN pins to read out scancode
+ READSCAN_PORT = 0xFF;
+ READSCAN_DDR = 0x00;
+
+
+ // Setup Mode Control Pins
+ // Note: These can be changed at any time, but there is no real reason too for a USB converter
+ REPEAT_PORT |= (1 << REPEAT_POS); // Setting high for single press mode
+ REPEAT_DDR |= (1 << REPEAT_POS);
+
+ MULTI_PORT &= ~(1 << MULTI_POS ); // Setting low for multi press mode (NKRO)
+ MULTI_DDR |= (1 << MULTI_POS );
+
+ SIGNAL_PORT &= ~(1 << SIGNAL_POS); // Setting low to enable PULSEINT
+ SIGNAL_DDR |= (1 << SIGNAL_POS);
+
+
+ // Setup Troublesome Key Pins
+ SHIFT_PORT &= ~(1 << SHIFT_POS );
+ SHIFT_DDR &= ~(1 << SHIFT_POS );
+
+ SHIFTLOCK_PORT &= ~(1 << SHIFTLOCK_POS);
+ SHIFTLOCK_DDR &= ~(1 << SHIFTLOCK_POS);
+
+
+ // Reset the keyboard before scanning, we might be in a wierd state
+ scan_resetKeyboard();
+}
+
+// Main Detection Loop
+// Not needed for the Sony OA-S3400 as signals are interrupt based, thus this is a busy loop
+// XXX Function is used for scanning troublesome keys, technically this is not needed for a pure converter
+// I just want proper use of the shift and shift lock keys, without having to do major rework to attach to the entire matrix
+inline uint8_t scan_loop()
+{
+ // Loop through known keys
+ for ( uint8_t key = 0; key < MANUAL_SCAN_KEYS; key++ ) switch ( key )
+ {
+ case SHIFT_KEY:
+ if ( SHIFT_PIN & (1 << SHIFT_POS) )
+ {
+ KeyScan_Table[SHIFT_KEY]++;
+ }
+ break;
+ case SHIFTLOCK_KEY:
+ if ( SHIFTLOCK_PIN & (1 << SHIFTLOCK_POS) )
+ {
+ KeyScan_Table[SHIFTLOCK_KEY]++;
+ }
+ break;
+ default:
+ erro_print("Invalid key scan index");
+ break;
+ }
+
+ // Increment vote instance
+ KeyScan_Count++;
+
+ // Loop function again if not enough votes have been tallied
+ if ( KeyScan_Count < 255 )
+ return 1;
+
+ // Clear vote data
+ KeyScan_Count = 0;
+
+ // Loop through known keys
+ for ( uint8_t key = 0; key < MANUAL_SCAN_KEYS; key++ )
+ {
+ // Key scanned as pressed (might have been held from a previous vote)
+ if ( KeyScan_Table[key] > 127 )
+ {
+ // Keypress detected
+ if ( !KeyScan_Prev[key] )
+ {
+ processKeyValue( 0x90 + key ); // Arbitrary key mapping starts at 0x90
+ KeyScan_Prev[key] = 1;
+ }
+ }
+ // Key scanned as released
+ else
+ {
+ // Keypress detected
+ if ( KeyScan_Prev[key] )
+ {
+ processKeyValue( 0xA0 + key ); // Arbitrary key mapping release starts at 0xA0
+ KeyScan_Prev[key] = 0;
+ }
+ }
+
+ // Clear votes
+ KeyScan_Table[key] = 0;
+ }
+
+ // End loop, process macros and USB data
+ return 0;
+}
+
+void processKeyValue( uint8_t keyValue )
+{
+ // - Convert Shifted Value to non-shifted ASCII code -
+
+ // Alphabetic
+ if ( keyValue >= 0x61 && keyValue <= 0x7A )
+ {
+ keyValue -= 0x20;
+ }
+ // Other keys with ASCII shift codes
+ else
+ {
+ switch ( keyValue )
+ {
+ case 0x21: // 1
+ case 0x23: // 3
+ case 0x24: // 4
+ case 0x25: // 5
+ keyValue += 0x10;
+ break;
+ case 0x26: // 7
+ case 0x28: // 9
+ keyValue += 0x11;
+ break;
+ case 0x81: // 1/2
+ case 0x3A: // ;
+ keyValue += 0x01;
+ break;
+ case 0x29: // 0
+ keyValue = 0x30;
+ break;
+ case 0x40: // 2
+ keyValue = 0x32;
+ break;
+ case 0x80: // 6
+ keyValue = 0x36;
+ break;
+ case 0x2A: // 8
+ keyValue = 0x38;
+ break;
+ case 0x5F: // -
+ keyValue = 0x2D;
+ break;
+ case 0x2B: // +
+ keyValue = 0x3D;
+ break;
+ case 0x22: // "
+ keyValue = 0x27;
+ break;
+ case 0x3F: // ?
+ keyValue = 0x2F;
+ break;
+ }
+ }
+
+ // Scan code is now finalized, and ready to add to buffer
+ // Note: Scan codes come from 3 different interrupts and a manual key scan into this function
+
+ // Debug
+ char tmpStr[6];
+ hexToStr( keyValue, tmpStr );
+ dPrintStrs( tmpStr, " " );
+
+ // Detect release condition
+ uint8_t release = 0;
+ switch ( keyValue )
+ {
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ keyValue -= 0x10;
+ case 0xB0:
+ release = 1;
+ break;
+ }
+
+ // Key Release
+ if ( release )
+ {
+ // Check for the released key, and shift the other keys lower on the buffer
+ uint8_t c;
+ for ( c = 0; c < KeyIndex_BufferUsed; c++ )
+ {
+ // General key buffer clear
+ if ( keyValue == 0xB0 )
+ {
+ switch ( KeyIndex_Buffer[c] )
+ {
+ // Ignore these keys on general key release (have their own release codes)
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ break;
+
+ // Remove key from buffer
+ default:
+ // Shift keys from c position
+ for ( uint8_t k = c; k < KeyIndex_BufferUsed - 1; k++ )
+ KeyIndex_Buffer[k] = KeyIndex_Buffer[k + 1];
+
+ // Decrement Buffer
+ KeyIndex_BufferUsed--;
+
+ break;
+ }
+ }
+ // Key to release found
+ else if ( KeyIndex_Buffer[c] == keyValue )
+ {
+ // Shift keys from c position
+ for ( uint8_t k = c; k < KeyIndex_BufferUsed - 1; k++ )
+ KeyIndex_Buffer[k] = KeyIndex_Buffer[k + 1];
+
+ // Decrement Buffer
+ KeyIndex_BufferUsed--;
+
+ break;
+ }
+ }
+
+ // Error case (no key to release)
+ if ( c == KeyIndex_BufferUsed + 1 )
+ {
+ errorLED( 1 );
+ char tmpStr[6];
+ hexToStr( keyValue, tmpStr );
+ erro_dPrint( "Could not find key to release: ", tmpStr );
+ }
+ }
+ // Press or Repeated Key
+ else
+ {
+ // Make sure the key isn't already in the buffer
+ for ( uint8_t c = 0; c < KeyIndex_BufferUsed + 1; c++ )
+ {
+ // Key isn't in the buffer yet
+ if ( c == KeyIndex_BufferUsed )
+ {
+ bufferAdd( keyValue );
+ break;
+ }
+
+ // Key already in the buffer
+ if ( KeyIndex_Buffer[c] == keyValue )
+ break;
+ }
+ }
+}
+
+// Key Press Detected Interrupt
+ISR(INT3_vect)
+{
+ cli(); // Disable Interrupts
+
+ uint8_t keyValue = 0x00;
+
+ // Bits are flipped coming in from the keyboard
+ keyValue = ~READSCAN_PIN;
+
+ // Process the scancode
+ processKeyValue( keyValue );
+
+ sei(); // Re-enable Interrupts
+}
+
+// Key Release Detected Interrupt
+ISR(INT6_vect)
+{
+ cli(); // Disable Interrupts
+
+ // Send release code for general keys, 0xB0
+ processKeyValue( 0xB0 );
+
+ sei(); // Re-enable Interrupts
+}
+
+// Code Key Interrupt
+ISR(INT7_vect)
+{
+ cli(); // Disable Interrupts
+
+ // Code Key Released (send scancode)
+ if ( CODEINT_PIN & (1 << CODEINT_POS) )
+ {
+ processKeyValue( 0xA2 );
+ }
+ // Code Key Pressed (send scancode)
+ else
+ {
+ processKeyValue( 0x92 );
+ }
+
+ sei(); // Re-enable Interrupts
+}
+
+
+// Send data to keyboard
+// Sony OA-S3400 has no serial/parallel dataport to send data too...
+uint8_t scan_sendData( uint8_t dataPayload )
+{
+ return 0;
+}
+
+// Signal KeyIndex_Buffer that it has been properly read
+// Not needed as a signal is sent to remove key-presses
+void scan_finishedWithBuffer( void )
+{
+ return;
+}
+
+// Reset/Hold keyboard
+// Sony OA-S3400 has no locking signals
+void scan_lockKeyboard( void )
+{
+}
+
+void scan_unlockKeyboard( void )
+{
+}
+
+// Reset Keyboard
+void scan_resetKeyboard( void )
+{
+ // Empty buffer, now that keyboard has been reset
+ KeyIndex_BufferUsed = 0;
+
+ // Clear the KeyScan table and count
+ for ( uint8_t key = 0; key < MANUAL_SCAN_KEYS; key++ )
+ {
+ KeyScan_Table[key] = 0;
+ KeyScan_Prev [key] = 0;
+ }
+ KeyScan_Count = 0;
+}
+
+// USB module is finished with buffer
+// Not needed as a signal is sent to remove key-presses
+void scan_finishedWithUSBBuffer( void )
+{
+ return;
+}
+