// -*- mode: C; tab-width: 2 ; indent-tabs-mode: nil -*- // // Unified Keyboard Project // ASDF keyboard firmware // // asdf_physical.c // // This file contains code to manage physical resources and serves as an API // between the virtual layer and the architecture specific code. // // 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 . // #include #include "asdf_physical.h" #include "asdf_config.h" #include "asdf_arch.h" // For each physical resource, there is a handler and a "shadow" register for the output value. // // For line outputs, the shadow register permits machine independent // implementations of the toggle and pulse functions to be implemented in this // module, requiring only a "set" function for each physical resource 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 { void (*handler)(uint8_t); uint8_t shadow; asdf_physical_dev_t next; } physical_device_table_entry_t; // physical_handler[] contains the set() function for each real output device. static physical_device_table_entry_t physical_device_table[ASDF_PHYSICAL_NUM_RESOURCES] = { [PHYSICAL_NO_OUT] = {.handler = &asdf_arch_null_output,}, [PHYSICAL_OUT1] = {.handler = &asdf_arch_out1_set,}, [PHYSICAL_OUT2] = {.handler = &asdf_arch_out2_set,}, [PHYSICAL_OUT3] = {.handler = &asdf_arch_out3_set,}, [PHYSICAL_OUT1_OPEN_HI] = {.handler = &asdf_arch_out1_open_hi_set,}, [PHYSICAL_OUT2_OPEN_HI] = {.handler = &asdf_arch_out2_open_hi_set,}, [PHYSICAL_OUT3_OPEN_HI] = {.handler = &asdf_arch_out3_open_hi_set,}, [PHYSICAL_OUT1_OPEN_LO] = {.handler = &asdf_arch_out1_open_lo_set,}, [PHYSICAL_OUT2_OPEN_LO] = {.handler = &asdf_arch_out2_open_lo_set,}, [PHYSICAL_OUT3_OPEN_LO] = {.handler = &asdf_arch_out3_open_lo_set,}, [PHYSICAL_LED1] = {.handler = &asdf_arch_led1_set,}, [PHYSICAL_LED2] = {.handler = &asdf_arch_led2_set,}, [PHYSICAL_LED3] = {.handler = &asdf_arch_led3_set,}, }; // PROCEDURE: asdf_physical_set // INPUTS: (asdf_physical_dev_t) physical_out: which real output to set or clear // INPUTS: (uint8_t) value // OUTPUTS: none // // DESCRIPTION: If the physical resource 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: public // // COMPLEXITY: 1 // void asdf_physical_set(asdf_physical_dev_t physical_out, uint8_t value) { physical_device_table[physical_out].handler(value); physical_device_table[physical_out].shadow = value; } // PROCEDURE: asdf_physical_on // INPUTS: (asdf_physical_dev_t) physical_out: which real output to set to ON // OUTPUTS: none // // DESCRIPTION: If the physical resource is valid, set to high // // SIDE EFFECTS: see above // // NOTES: No bounds checking. The caller must ensure a valid device // // SCOPE: public // // COMPLEXITY: 1 // void asdf_physical_on(asdf_physical_dev_t physical_out) { physical_device_table[physical_out].handler(1); physical_device_table[physical_out].shadow = 1; } // PROCEDURE: asdf_physical_off // INPUTS: (asdf_physical_dev_t) physical_out: which real output to set to OFF // OUTPUTS: none // // DESCRIPTION: If the physical resource is valid, set to low // // SIDE EFFECTS: see above // // NOTES: No bounds checking. The caller must ensure a valid device // // SCOPE: public // // COMPLEXITY: 1 // void asdf_physical_off(asdf_physical_dev_t physical_out) { physical_device_table[physical_out].handler(0); physical_device_table[physical_out].shadow = 0; } // PROCEDURE: asdf_physical_assert // INPUTS: (asdf_physical_dev_t) physical_out: which physical resource to set or clear // INPUTS: none // OUTPUTS: none // // DESCRIPTION: Assert the value of the physical resource shadow register on the output. // // SIDE EFFECTS: see above // // NOTES: No bounds checking. Only called from initialization code. // // SCOPE: public // // COMPLEXITY: 1 // void asdf_physical_assert(asdf_physical_dev_t physical_out) { uint8_t value = physical_device_table[physical_out].shadow; physical_device_table[physical_out].handler(value); } // PROCEDURE: asdf_physical_toggle // INPUTS: (asdf_physical_dev_t) physical_out: which physical resource to toggle // INPUTS: none // OUTPUTS: none // // DESCRIPTION: Toggle the value of the physical resource. // // SIDE EFFECTS: see above // // NOTES: No bounds checking. Only called from initialization code. // // SCOPE: public // // COMPLEXITY: 1 // void asdf_physical_toggle(asdf_physical_dev_t physical_out) { uint8_t value = physical_device_table[physical_out].shadow; asdf_physical_set(physical_out, !value); } // PROCEDURE: valid_physical_device // INPUTS: (asdf_physical_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_physical_device(asdf_physical_dev_t device) { return (device > PHYSICAL_NO_OUT && device < ASDF_PHYSICAL_NUM_RESOURCES); } // PROCEDURE: physical_device_is_available // INPUTS: asdf_physical_dev_t requiested_device // OUTPUTS: returns PHYSICAL_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 PHYSICAL_NO_OUT. // // SIDE EFFECTS: none // // NOTES: // // SCOPE: public // // COMPLEXITY: 3 // uint8_t physical_device_is_available(asdf_physical_dev_t device) { asdf_physical_dev_t current_out = PHYSICAL_NO_OUT; asdf_physical_dev_t next_out = physical_device_table[current_out].next; while (next_out != PHYSICAL_NO_OUT && next_out != device) { current_out = next_out; next_out = physical_device_table[current_out].next; } return (PHYSICAL_NO_OUT == next_out) ? ASDF_PHYSICAL_NUM_RESOURCES : current_out; } // PROCEDURE: asdf_physical_next_device // INPUTS: (asdf_physical_dev_t) device - the current physical resource attached // to the virtual output being operated on // // OUTPUTS: (asdf_physical_dev_t) returns the next physical resource assigned to // the virtual output. // // DESCRIPTION: See above. // // SIDE EFFECTS: None. // // NOTES: // // SCOPE: public // // COMPLEXITY: 1 // asdf_physical_dev_t asdf_physical_next_device(asdf_physical_dev_t device) { return physical_device_table[device].next; } // PROCEDURE: asdf_physical_allocate // // INPUTS: (asdf_physical_out_t) physical_out - the desired physical resource to allocate. // (asdf_physical_out_t) tail - the list of physical resources to tack on // to the requested resource, if available. // // OUTPUTS: (asdf_physical_out_t) returns TRUE if the allocation is succesful, // FALSE (0) otherwise. // // DESCRIPTION: Check that the requested physical resource is valid and // available. If so, then remove the resource from the physical resource table // and assign an initial value, then return a TRUE (1). Return FALSE (0) if // allocation was not successful. // // SIDE EFFECTS: see above. // // SCOPE: public // // COMPLEXITY: 2 // uint8_t asdf_physical_allocate(asdf_physical_dev_t physical_out, asdf_physical_dev_t tail, uint8_t initial_value) { uint8_t success = 0; asdf_physical_dev_t predecessor = physical_device_is_available(physical_out); if (valid_physical_device(physical_out) && (ASDF_PHYSICAL_NUM_RESOURCES != predecessor)) { // remove from available list: physical_device_table[predecessor].next = physical_device_table[physical_out].next; // tack the tail on to the physical resource physical_device_table[physical_out].next = tail; // The physical resource shadow value is set here. The shadow values are // asserted to the outputs only after all the assignments have been // performed. physical_device_table[physical_out].shadow = initial_value; success = 1; } return success; } // PROCEDURE: asdf_physical_init // INPUTS: none // OUTPUTS: none // // DESCRIPTION: Initialize physical resource table // // SIDE EFFECTS: see above // // NOTES: // // SCOPE: public // // COMPLEXITY: 2 // void asdf_physical_init(void) { // initialize the linked list of free devices for (uint8_t i = 0; i < ASDF_PHYSICAL_NUM_RESOURCES; i++) { physical_device_table[i].shadow = ASDF_VIRTUAL_OUT_DEFAULT_VALUE; physical_device_table[i].next = i + 1; // initialize pointer to next in table } // 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 // PHYSICAL_NO_OUT. physical_device_table[ASDF_PHYSICAL_NUM_RESOURCES - 1].next = PHYSICAL_NO_OUT; // end of list. } //-------|---------|---------+---------+---------+---------+---------+---------+ // Above line is 80 columns, and should display completely in the editor.