2020-03-06 19:24:03 +00:00
|
|
|
// -*- mode: C; tab-width: 2 ; indent-tabs-mode: nil -*-
|
|
|
|
//
|
|
|
|
// Unified Keyboard Project
|
|
|
|
// ASDF keyboard firmware
|
|
|
|
//
|
|
|
|
// asdf_virtual_outputs.c
|
|
|
|
//
|
|
|
|
// This file contains code that maps "virtual" LEDs and outputs referenced by
|
|
|
|
// the code to actual LEDs and outputs in hardware. This keeps keymap-specific
|
|
|
|
// details out of the architecture-dependent files, and provides a flexible way
|
|
|
|
// for the keymap definitions to specify the LED and output functions for
|
|
|
|
// different keymaps or keyboard layouts.
|
|
|
|
//
|
|
|
|
// Copyright 2019 David Fenyes
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify it under
|
|
|
|
// the terms of the GNU General Public License as published by the Free Software
|
|
|
|
// Foundation, either version 3 of the License, or (at your option) any later
|
|
|
|
// version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
// details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License along with
|
|
|
|
// this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
|
|
|
2020-03-08 19:45:40 +00:00
|
|
|
#include <stdint.h>
|
2020-03-11 06:52:47 +00:00
|
|
|
#include <stdio.h>
|
2020-03-06 19:24:03 +00:00
|
|
|
#include "asdf_virtual.h"
|
2020-03-08 19:45:40 +00:00
|
|
|
#include "asdf_keymap_defs.h"
|
|
|
|
#include "asdf_config.h"
|
2020-03-06 19:24:03 +00:00
|
|
|
#include "asdf_arch.h"
|
|
|
|
|
|
|
|
// For each virtual out, maintain a "shadow" register for the output value. This
|
|
|
|
// permits machine independent implementations of the toggle and pulse functions
|
|
|
|
// to be implemented in this module, requiring only a "set" function for each
|
|
|
|
// virtual output in the architecture-dependent layer. This implementation is
|
|
|
|
// not as efficient, but the timing is not critical, and the events are so
|
|
|
|
// infrequent that the benefits of the refactoring far outweigh any performance
|
|
|
|
// penalty.
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint8_t shadow;
|
|
|
|
asdf_virtual_real_dev_t next;
|
|
|
|
} real_dev_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
asdf_virtual_real_dev_t real_device; // Each virtual device points to a linked
|
|
|
|
// list of any number of real devices.
|
|
|
|
asdf_virtual_function_t function;
|
|
|
|
} virtual_dev_t;
|
|
|
|
|
|
|
|
|
|
|
|
static real_dev_t real_device_table[NUM_REAL_OUTPUTS];
|
|
|
|
|
|
|
|
|
|
|
|
// vout_set[] contains the set() function for each real output device.
|
2020-03-08 19:45:40 +00:00
|
|
|
static void (*const vout_set[])(uint8_t) = {
|
|
|
|
[VMAP_NO_OUT] = &asdf_arch_null_output, //
|
2020-03-06 19:24:03 +00:00
|
|
|
[VMAP_OUT1] = &asdf_arch_out1_set, //
|
|
|
|
[VMAP_OUT2] = &asdf_arch_out2_set, //
|
|
|
|
[VMAP_OUT3] = &asdf_arch_out3_set, //
|
|
|
|
[VMAP_OUT1_OC] = &asdf_arch_out1_hi_z_set, //
|
|
|
|
[VMAP_OUT2_OC] = &asdf_arch_out2_hi_z_set, //
|
|
|
|
[VMAP_OUT3_OC] = &asdf_arch_out3_hi_z_set, //
|
2020-03-11 06:52:47 +00:00
|
|
|
[VMAP_LED1] = &asdf_arch_led1_set, //
|
|
|
|
[VMAP_LED2] = &asdf_arch_led2_set, //
|
|
|
|
[VMAP_LED3] = &asdf_arch_led3_set //
|
2020-03-06 19:24:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// virtual_out[] contains all the virtual outputs. An asdf_virtual_output_t
|
|
|
|
// value is used to identify each element. Each element is a virtual device,
|
|
|
|
// containing an asdf_virtual_real_dev_t value indicating the real device (if
|
|
|
|
// any) assigned to the virtual device.
|
|
|
|
static virtual_dev_t virtual_device_table[NUM_VIRTUAL_OUTPUTS];
|
|
|
|
|
|
|
|
|
|
|
|
// PROCEDURE: asdf_virtual_real_set
|
|
|
|
// INPUTS: (asdf_virtual_real_dev_t) real_out: which real output to set or clear
|
|
|
|
// INPUTS: (uint8_t) value
|
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: If the real output is valid, set to high if value is true, low
|
|
|
|
// if false.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above
|
|
|
|
//
|
|
|
|
// NOTES: No bounds checking. The caller must ensure a valid device
|
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 1
|
|
|
|
//
|
|
|
|
static void asdf_virtual_real_set(asdf_virtual_real_dev_t real_out, uint8_t value)
|
|
|
|
{
|
|
|
|
vout_set[real_out](value);
|
2020-03-08 19:45:40 +00:00
|
|
|
real_device_table[real_out].shadow = value;
|
2020-03-06 19:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PROCEDURE: asdf_virtual_real_assert
|
|
|
|
// INPUTS: (asdf_virtual_real_dev_t) real_out: which real output to set or clear
|
|
|
|
// INPUTS: none
|
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: Assert the value of the real output shadow register on the output.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above
|
|
|
|
//
|
|
|
|
// NOTES: No bounds checking. Only called from initialization code.
|
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 1
|
|
|
|
//
|
2020-03-08 19:45:40 +00:00
|
|
|
static void asdf_virtual_real_assert(asdf_virtual_real_dev_t real_out)
|
2020-03-06 19:24:03 +00:00
|
|
|
{
|
|
|
|
uint8_t value = real_device_table[real_out].shadow;
|
|
|
|
vout_set[real_out](value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PROCEDURE: asdf_virtual_real_toggle
|
|
|
|
// INPUTS: (asdf_virtual_real_dev_t) real_out: which real output to toggle
|
|
|
|
// INPUTS: none
|
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: Toggle the value of the real output.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above
|
|
|
|
//
|
|
|
|
// NOTES: No bounds checking. Only called from initialization code.
|
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 1
|
|
|
|
//
|
2020-03-08 19:45:40 +00:00
|
|
|
static void asdf_virtual_real_toggle(asdf_virtual_real_dev_t real_out)
|
2020-03-06 19:24:03 +00:00
|
|
|
{
|
|
|
|
uint8_t value = real_device_table[real_out].shadow;
|
|
|
|
|
2020-03-09 10:16:52 +00:00
|
|
|
asdf_virtual_real_set(real_out, !value);
|
2020-03-06 19:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PROCEDURE: asdf_virtual_action
|
|
|
|
// INPUTS: (asdf_virtual_output_t) virtual_out: which virtual output to modify
|
|
|
|
// INPUTS: (asdf_virtual_function_t) function: what function to apply to the virtual output
|
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: for each real output mapped to the virtual output, apply the
|
|
|
|
// specified function.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above
|
|
|
|
//
|
|
|
|
// NOTES: The virtual output points to a linked list of real devices.
|
|
|
|
//
|
|
|
|
// SCOPE: public
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 7
|
|
|
|
//
|
|
|
|
void asdf_virtual_action(asdf_virtual_output_t virtual_out, asdf_virtual_function_t function)
|
|
|
|
{
|
2020-03-08 19:45:40 +00:00
|
|
|
asdf_virtual_real_dev_t device = virtual_device_table[virtual_out].real_device;
|
2020-03-06 19:24:03 +00:00
|
|
|
|
|
|
|
while (VMAP_NO_OUT != device) {
|
|
|
|
switch (function) {
|
|
|
|
|
|
|
|
case V_PULSE: {
|
|
|
|
asdf_virtual_real_toggle(device);
|
|
|
|
asdf_arch_pulse_delay();
|
|
|
|
// yes we could omit the next two lines and fall through, but we will
|
|
|
|
// spend a few bytes of redundant code for the sake of consistency and
|
|
|
|
// readability.
|
|
|
|
asdf_virtual_real_toggle(device);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case V_TOGGLE: {
|
|
|
|
asdf_virtual_real_toggle(device);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case V_SET_HI: {
|
|
|
|
asdf_virtual_real_set(device, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case V_SET_LO: {
|
|
|
|
asdf_virtual_real_set(device, 0);
|
|
|
|
}
|
|
|
|
case V_NOFUNC:
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
device = real_device_table[device].next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 19:45:40 +00:00
|
|
|
// PROCEDURE: asdf_virtual_activate
|
|
|
|
// INPUTS: asdf_virtual_output_t: The virtual device to be activated
|
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: for each real output mapped to the virtual output, apply the
|
|
|
|
// function assigned to the virtual output at initialization.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above
|
|
|
|
//
|
|
|
|
// NOTES: The virtual output points to a linked list of real devices.
|
|
|
|
//
|
|
|
|
// SCOPE: public
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 1
|
|
|
|
//
|
|
|
|
void asdf_virtual_activate(asdf_virtual_output_t virtual_out)
|
|
|
|
{
|
|
|
|
asdf_virtual_action(virtual_out, virtual_device_table[virtual_out].function);
|
|
|
|
}
|
|
|
|
|
2020-03-11 06:52:47 +00:00
|
|
|
// PROCEDURE: valid_virtual_device
|
|
|
|
// INPUTS: (asdf_virtual_output_t) device
|
|
|
|
// OUTPUTS: returns true (1) if the device is valid, false (0) if not valid.
|
|
|
|
//
|
|
|
|
// DESCRIPTION: test to see if device is a valid device value.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS:
|
|
|
|
//
|
|
|
|
// NOTES:
|
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 1
|
|
|
|
//
|
|
|
|
static uint8_t valid_virtual_device(asdf_virtual_output_t device)
|
|
|
|
{
|
|
|
|
return (device > V_NULL && device < NUM_VIRTUAL_OUTPUTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PROCEDURE: valid_real_device
|
|
|
|
// INPUTS: (asdf_virtual_real_dev_t) device
|
|
|
|
// OUTPUTS: returns true (1) if the device is valid, false (0) if not valid.
|
|
|
|
//
|
|
|
|
// DESCRIPTION: test to see if device is a valid device value.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS:
|
|
|
|
//
|
|
|
|
// NOTES:
|
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 1
|
|
|
|
//
|
|
|
|
static uint8_t valid_real_device(asdf_virtual_real_dev_t device)
|
|
|
|
{
|
|
|
|
return (device > VMAP_NO_OUT && device < NUM_REAL_OUTPUTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PROCEDURE: real_device_is_available
|
|
|
|
// INPUTS: asdf_virtual_real_dev_t requiested_device
|
|
|
|
// OUTPUTS: returns VMAP_NO_OUT if device is alreay allocated. If not yet allocated,
|
|
|
|
// returns the index of the device in the available list before the requested
|
|
|
|
// device.
|
|
|
|
//
|
|
|
|
// DESCRIPTION: iterates through the linked list of available devices. If the
|
|
|
|
// requested_device is encountered, return the element before the requested
|
|
|
|
// device in the list. If the end of the list is reached, return VMAP_NO_OUT.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: none
|
|
|
|
//
|
|
|
|
// NOTES:
|
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 3
|
|
|
|
//
|
|
|
|
static uint8_t real_device_is_available(asdf_virtual_real_dev_t device)
|
|
|
|
{
|
|
|
|
asdf_virtual_real_dev_t current_out = VMAP_NO_OUT;
|
|
|
|
asdf_virtual_real_dev_t next_out = real_device_table[current_out].next;
|
|
|
|
|
|
|
|
while (next_out != VMAP_NO_OUT && next_out != device) {
|
|
|
|
current_out = next_out;
|
|
|
|
next_out = real_device_table[current_out].next;
|
|
|
|
}
|
|
|
|
return (VMAP_NO_OUT == next_out) ? NUM_REAL_OUTPUTS : current_out;
|
|
|
|
}
|
|
|
|
|
2020-03-06 19:24:03 +00:00
|
|
|
// PROCEDURE: asdf_virtual_assign
|
|
|
|
// INPUTS: (asdf_vout_t) virtual_out
|
|
|
|
// (uint8_t) real_out
|
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: map the virtual output specified by new_vout to real_out, if
|
|
|
|
// both arguments are valid. Ignore if not valid.
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above.
|
|
|
|
//
|
2020-03-11 06:52:47 +00:00
|
|
|
// NOTES: if the virtual device is invalid, or the real device is invalid, or
|
|
|
|
// the real device is already assigned, then nothing happens.
|
2020-03-06 19:24:03 +00:00
|
|
|
//
|
|
|
|
// SCOPE: private
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 2
|
|
|
|
//
|
|
|
|
static void asdf_virtual_assign(asdf_virtual_output_t virtual_out, asdf_virtual_real_dev_t real_out,
|
2020-03-08 19:45:40 +00:00
|
|
|
asdf_virtual_function_t function, uint8_t initial_value)
|
2020-03-06 19:24:03 +00:00
|
|
|
{
|
2020-03-11 06:52:47 +00:00
|
|
|
asdf_virtual_real_dev_t predecessor = real_device_is_available(real_out);
|
|
|
|
|
|
|
|
if (valid_virtual_device(virtual_out)
|
|
|
|
&& valid_real_device(real_out)
|
|
|
|
&& (NUM_REAL_OUTPUTS != predecessor)) {
|
|
|
|
virtual_device_table[virtual_out].function = function;
|
2020-03-06 19:24:03 +00:00
|
|
|
|
2020-03-11 06:52:47 +00:00
|
|
|
// remove from available list:
|
|
|
|
real_device_table[predecessor].next = real_device_table[real_out].next;
|
2020-03-06 19:24:03 +00:00
|
|
|
|
2020-03-11 06:52:47 +00:00
|
|
|
// add real device to the list associated with the virtual device:
|
2020-03-06 19:24:03 +00:00
|
|
|
|
2020-03-11 06:52:47 +00:00
|
|
|
real_device_table[real_out].next = virtual_device_table[virtual_out].real_device;
|
|
|
|
virtual_device_table[virtual_out].real_device = real_out;
|
|
|
|
|
|
|
|
// The real device shadow value is set here. The shadow values are asserted to
|
|
|
|
// the outputs only after all the assignments have been performed.
|
|
|
|
real_device_table[real_out].shadow = initial_value;
|
|
|
|
}
|
2020-03-06 19:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PROCEDURE: asdf_virtual_init
|
2020-03-08 19:45:40 +00:00
|
|
|
// INPUTS: (asdf_virtual_initializer_t *) initializer_list - contains the
|
|
|
|
// initializer list for the selected keymap.
|
2020-03-06 19:24:03 +00:00
|
|
|
// OUTPUTS: none
|
|
|
|
//
|
|
|
|
// DESCRIPTION: Initializes the LED and output mapping
|
|
|
|
//
|
|
|
|
// SIDE EFFECTS: see above
|
|
|
|
//
|
2020-03-08 19:45:40 +00:00
|
|
|
// NOTES: 1) // 2) ASDF_VIRTUAL_OUT_DEFAULT_VALUE is defined in asdf_config.h
|
2020-03-06 19:24:03 +00:00
|
|
|
//
|
|
|
|
// SCOPE: public
|
|
|
|
//
|
|
|
|
// COMPLEXITY: 2
|
|
|
|
//
|
2020-03-08 19:45:40 +00:00
|
|
|
void asdf_virtual_init(asdf_virtual_initializer_t *const initializer_list)
|
2020-03-06 19:24:03 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
// initialize list of virtual outputs
|
|
|
|
for (uint8_t i = 0; i < NUM_VIRTUAL_OUTPUTS; i++) {
|
|
|
|
virtual_device_table[i].function = V_NOFUNC;
|
|
|
|
virtual_device_table[i].real_device = VMAP_NO_OUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize the linked list of free devices
|
|
|
|
for (uint8_t i = 0; i < NUM_REAL_OUTPUTS; i++) {
|
|
|
|
real_device_table[i].shadow = ASDF_VIRTUAL_OUT_DEFAULT_VALUE;
|
2020-03-08 19:45:40 +00:00
|
|
|
real_device_table[i].next = i + 1; // initialize pointer to next in table
|
2020-03-06 19:24:03 +00:00
|
|
|
}
|
|
|
|
// The last item in the table is left with a bogus next pointer (beyond the
|
|
|
|
// end of the array) after the above loop. Make the last element point to
|
2020-03-08 19:45:40 +00:00
|
|
|
// V_NULL.
|
2020-03-06 19:24:03 +00:00
|
|
|
real_device_table[NUM_REAL_OUTPUTS - 1].next = VMAP_NO_OUT; // end of list.
|
|
|
|
|
|
|
|
|
|
|
|
// run through the keymap specific setup
|
|
|
|
for (uint8_t i = 0; //
|
2020-03-08 19:45:40 +00:00
|
|
|
i < ASDF_KEYMAP_INITIALIZER_LENGTH && initializer_list[i].virtual_device != V_NULL; i++) {
|
2020-03-06 19:24:03 +00:00
|
|
|
|
2020-03-08 19:45:40 +00:00
|
|
|
asdf_virtual_assign(initializer_list[i].virtual_device, initializer_list[i].real_device,
|
2020-03-06 19:24:03 +00:00
|
|
|
initializer_list[i].function, initializer_list[i].initial_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now set all the initial LED and output values
|
|
|
|
for (uint8_t i = 0; i < NUM_REAL_OUTPUTS; i++) {
|
|
|
|
asdf_virtual_real_assert((asdf_virtual_real_dev_t) i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------|---------|---------+---------+---------+---------+---------+---------+
|
|
|
|
// Above line is 80 columns, and should display completely in the editor.
|