9a35536c2f60a73d3f4521cd3b81b239bb401de8
[firmerware] / src / kernel / cpu.c
1 /* CPU drivers
2  *
3  * This file is part of 0cpm Firmerware.
4  *
5  * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
6  *
7  * 0cpm Firmerware is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation, version 3.
10  *
11  * 0cpm Firmerware is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with 0cpm Firmerware.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <stdbool.h>
24 #include <stdarg.h>
25
26 #include <config.h>
27
28 #include <0cpm/cpu.h>
29 #include <0cpm/irq.h>
30 #include <0cpm/cons.h>
31
32
33 /* Round-robin queues with interrupts and closures, ordered by priority.
34  * To be fair, the elements pointed at have already been completed
35  * and are awaiting roll-over or removal.
36  * TODO: Make the array contents volatile
37  */
38 irq_t     *irqs     [CPU_PRIO_COUNT] = { NULL, NULL, NULL, NULL };
39 closure_t *closures [CPU_PRIO_COUNT] = { NULL, NULL, NULL, NULL };
40 #if CPU_PRIO_COUNT != 4
41 //TODO// #  error "Update the number of NULL value initialisations above"
42 #endif
43
44 /* The current priority is an optimisation; it tells what the highest
45  * active priority is, so as to avoid too much searching.  The priority
46  * starts at the low value CPU_PRIO_ZERO at which nothing runs; as soon
47  * as anything is scheduled, the value is increased to signal work is to
48  * be done.
49  */
50 volatile priority_t cur_prio = CPU_PRIO_ZERO;
51
52
53 /* Add an IRQ to the round-robin task queue to be scheduled.
54  * This routine must always be called with interrupts inactive;
55  * either as part of an interrupt routine, or when called in the
56  * course of the main program, from within a critical region.
57  */
58 void irq_fire (irq_t *irq) {
59         priority_t p = irq->irq_priority;
60 //TODO// if (irq->irq_next) return;
61         if (irqs [p]) {
62                 irq->irq_next = irqs [p]->irq_next;
63                 irqs [p]->irq_next = irq;
64         } else {
65                 irq->irq_next = irq;
66         }
67         irqs [p] = irq; // Newly added becomes last in the queue
68         if (p > cur_prio) {
69                 cur_prio = p;
70         }
71 //TODO:TEST// ht162x_audiolevel (1 << 5);
72 }
73
74
75 /* Hop jobs until there is nothing left to do.  This is actually the main
76  * procedure for the scheduler.  If it ends, then all the work has been
77  * enqueued somewhere, and is awaiting a response.  In other words, it
78  * is then time to sleep.  This assumes that no task or IRQ will ever
79  * be set to CPU_PRIO_ZERO.
80  */
81 void jobhopper (void) {
82 void ht162x_putchar (uint8_t idx, uint8_t ch, bool notify);
83         //OVERZEALOUSLOGGER// bottom_printf ("Jobhopper starts.\n");
84 //TODO:TEST// ht162x_putchar (0, '0', true);
85         while (cur_prio > CPU_PRIO_ZERO) {
86                 closure_t *here;
87 //TODO:TEST// ht162x_putchar (0, '1', true);
88                 while (irqs [cur_prio]) {
89 //TODO:TEST// ht162x_putchar (0, '2', true);
90                         //TODO:JUSTREMOVE// irqs [cur_prio] = irqs [cur_prio]->irq_next;
91                         while (true) {
92                                 irq_t *prev;
93                                 irq_t *todo;
94                                 bottom_critical_region_begin ();
95                                 prev = irqs [cur_prio];
96                                 if (prev == NULL) {
97                                         bottom_critical_region_end ();
98                                         break;
99                                 }
100                                 todo = prev->irq_next;
101                                 if (prev != todo) {
102 //TODO:TEST// ht162x_audiolevel (1 << 5);
103 //TODO:TEST// ht162x_putchar (0, '3', true);
104                                         prev->irq_next = todo->irq_next;
105                                 } else {
106 //TODO:TEST// ht162x_audiolevel (1 << 5);
107 //TODO:TEST// ht162x_putchar (0, '4', true);
108                                         irqs [cur_prio] = NULL;
109                                 }
110                                 todo->irq_next = NULL;
111                                 bottom_critical_region_end ();
112                                 //OVERZEALOUSLOGGER// bottom_printf ("Jobhopper calls interrupt handler.\n");
113 //TODO:TEST// ht162x_audiolevel (2 << 5);
114 //TODO:NAAAHHH// if (this->irq_handler)
115                                 (*todo->irq_handler) (todo);
116 //TODO:TEST// ht162x_putchar (0, '5', true);
117                         }
118                 }
119                 here = closures [cur_prio];
120                 if (here) {
121                         closures [cur_prio] = here->cls_next;
122                         here->cls_next = NULL;
123                         here = (*here->cls_handler) (here);
124                         if (here) {
125                                 closure_t **last = &closures [cur_prio];
126                                 while (*last != NULL) {
127 //TODO:TEST// ht162x_putchar (0, '6', true);
128                                         last = &(*last)->cls_next;
129                                 }
130 //TODO:TEST// ht162x_putchar (0, '7', true);
131                                 bottom_printf ("Jobhopper found closure -- TODO: call it!\n");
132                                 here->cls_next = NULL;
133                                 *last = here;
134                         }
135                 } else {
136 //TODO:TEST// ht162x_putchar (0, '8', true);
137                         cur_prio--;
138                 }
139         }
140 //TODO:TEST// ht162x_putchar (0, '9', true);
141         //OVERZEALOUSLOGGER// bottom_printf ("Jobhopper ends.\n");
142 }
143