source: ReferenceDesigns/w3_802.11/c/wlan_mac_high_framework/wlan_mac_schedule.c

Last change on this file was 6319, checked in by chunter, 5 years ago

1.8.0 release wlan-mac-se

File size: 13.2 KB
Line 
1/** @file wlan_mac_schedule.c
2 *  @brief Scheduler subsystem for high MAC framework
3 *
4 *  This set of functions allows upper-level MAC implementations
5 *  to schedule the execution of a provided callback for some point
6 *  in the future.
7 *
8 *  @copyright Copyright 2014-2019, Mango Communications. All rights reserved.
9 *          Distributed under the Mango Communications Reference Design License
10 *              See LICENSE.txt included in the design archive or
11 *              at http://mangocomm.com/802.11/license
12 *
13 *  This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11)
14 */
15
16/***************************** Include Files *********************************/
17#include "wlan_mac_high_sw_config.h"
18
19#include "xil_types.h"
20#include "xil_exception.h"
21
22#include "wlan_platform_high.h"
23#include "wlan_platform_common.h"
24#include "wlan_platform_timer.h"
25
26#include "wlan_mac_high.h"
27#include "wlan_mac_dl_list.h"
28#include "wlan_mac_schedule.h"
29
30
31/*************************** Variable Definitions ****************************/
32// Global counter for assigning unique event IDs
33static volatile u32 sched_event_id_cntr;
34
35// Global to count number of currently-defined scheduled events
36static int total_num_scheduled_events;
37
38// Global array of schedules; reference code defines two by default
39#define NUM_SCHEDULES 2
40static wlan_schedule_t wlan_schedules[NUM_SCHEDULES];
41
42/******************************** Functions **********************************/
43
44/*****************************************************************************/
45/**
46 * Initializes the scheduler subsystem.
47 *
48 * @param    None
49 *
50 * @return   WLAN_SUCCESS
51 *
52 *****************************************************************************/
53int wlan_mac_schedule_init(){
54    int i;
55
56    // Initialize globals
57    total_num_scheduled_events = 0;
58
59    // Counter increments as events are assigned unique IDs upon creation
60    // Event IDs are globally unique and do not need to be densely packed
61    //  First event assigned ID 1 by convention
62    sched_event_id_cntr = 1;
63
64    // Initialize each schedule's event list
65    for(i=0; i<NUM_SCHEDULES; i++) {
66        dl_list_init(&(wlan_schedules[i].event_list));
67    }
68
69    // Define the default schedules
70    // By convention schedules are ordered most-frequent (#0) to least-frequent
71
72    // Fine schedule - executes on every timer ISR
73    wlan_schedules[SCHEDULE_ID_FINE].exec_interval = 0;
74    wlan_schedules[SCHEDULE_ID_FINE].polls_to_next_exec = 0;
75
76    // Coarse schedule - executes every 4096 timer ISRs (2.6msec for 64usec timer interval)
77    wlan_schedules[SCHEDULE_ID_COARSE].exec_interval = 4096;
78    wlan_schedules[SCHEDULE_ID_COARSE].polls_to_next_exec = 4095;
79
80    return WLAN_SUCCESS;
81}
82
83/*****************************************************************************/
84/**
85 * Schedules the execution of a callback for some time in the future
86 *
87 * @param   scheduler_sel    - SCHEDULE_ID_COARSE or SCHEDULE_ID_FINE
88 * @param   interval_us      - Interval (in microseconds) until callback should be called
89 * @param   num_calls        - Number of repetitions or SCHEDULE_REPEAT_FOREVER for permanent periodic
90 * @param   callback         - Function pointer to callback
91 *
92 * @return  id               - ID of scheduled event or SCHEDULE_FAILURE if error
93 *
94 *****************************************************************************/
95u32 wlan_mac_schedule_add_event(u8 schedule_sel, u32 interval_us, u32 num_calls, void(*callback)()) {
96    u32 id;
97    dl_entry* event_entry_ptr;
98    wlan_sched_event_t* event_ptr;
99    u64 curr_system_time;
100
101    if(schedule_sel >= NUM_SCHEDULES) {
102        xil_printf("WARNING: invalid schedule selection %d\n", schedule_sel);
103        return SCHEDULE_FAILURE;
104    }
105
106    // Allocate memory for data structures
107    event_entry_ptr = wlan_mac_high_malloc(sizeof(dl_entry));
108    if (event_entry_ptr == NULL) {
109        return SCHEDULE_FAILURE;
110    }
111
112    event_ptr = wlan_mac_high_malloc(sizeof(wlan_sched_event_t));
113    if (event_ptr == NULL) {
114        wlan_mac_high_free(event_entry_ptr);
115        return SCHEDULE_FAILURE;
116    }
117
118    // Attach the schedule struct to this dl_entry
119    event_entry_ptr->data = event_ptr;
120
121    // Get unique scheduled event ID from global counter
122    // TODO: handle (very unlikely) case of exhausting all scheduled event IDs; this while loop would run forever as-is
123    do{
124        id = (sched_event_id_cntr++);
125
126        // Check if we hit the section of reserved IDs; Wrap back to 0 and start again
127        if ((id >= SCHEDULE_ID_RESERVED_MIN) && (id <= SCHEDULE_ID_RESERVED_MAX)) { id = 1; }
128
129        // Check if this candidate id is already allocated
130    } while(wlan_mac_schedule_find_event(id, NULL) != NULL);
131
132    // Get the current system time
133    curr_system_time = get_system_time_usec();
134
135    // Initialize the scheduled event struct
136    event_ptr->enabled     = 1;
137    event_ptr->id          = id;
138    event_ptr->interval_us = interval_us;
139    event_ptr->num_calls   = num_calls;
140    event_ptr->target_us   = curr_system_time + (u64)(interval_us);
141    event_ptr->callback    = (function_ptr_t)callback;
142
143    // Add new scheduled event to the schedule's list of events
144    total_num_scheduled_events++;
145    dl_entry_insertEnd(&(wlan_schedules[schedule_sel].event_list), event_entry_ptr);
146
147    // Check if this new scheduled event will be the only scheduled event
148    // If so the timer must be started - the timer is stopped when there are
149    //  no scheduled events to avoid unnecessary interrupts
150    if(total_num_scheduled_events == 1) {
151        wlan_timer_start();
152    }
153
154    return id;
155}
156
157
158int wlan_mac_schedule_disable_event(u32 event_id) {
159    dl_entry* event_entry;
160
161    // Check if event_id is valid
162    event_entry = wlan_mac_schedule_find_event(event_id, NULL);
163
164    if(event_entry == NULL) {
165        xil_printf("WARNING: attempted to disable unknown event ID %d\n", event_id);
166        return WLAN_FAILURE;
167    }
168
169    ((wlan_sched_event_t*)(event_entry->data))->enabled = 0;
170
171    return WLAN_SUCCESS;
172}
173
174int wlan_mac_schedule_enable_event(u32 event_id) {
175    dl_entry* event_entry;
176
177    // Check if event_id is valid
178    event_entry = wlan_mac_schedule_find_event(event_id, NULL);
179
180    if(event_entry == NULL) {
181        xil_printf("WARNING: attempted to enable unknown event ID %d\n", event_id);
182        return WLAN_FAILURE;
183    }
184
185    // Enable the event and schedule its next execution
186    ((wlan_sched_event_t*)(event_entry->data))->enabled = 1;
187    ((wlan_sched_event_t*)(event_entry->data))->target_us = get_system_time_usec() + ((wlan_sched_event_t*)(event_entry->data))->interval_us;
188
189    return WLAN_SUCCESS;
190}
191
192
193/*****************************************************************************/
194/**
195 * @brief  Deletes a scheduled event
196 *
197 * @param   event_id - ID of scheduled event to remove
198 *
199 * @return  int status - WLAN SUCCESS or WLAN_FAILURE
200 *
201 *****************************************************************************/
202int wlan_mac_schedule_remove_event(u32 event_id) {
203    wlan_sched_event_t* event_ptr = NULL;
204    dl_entry* event_entry_ptr = NULL;
205    dl_list* event_list = NULL;
206
207    // Lookup the event list entry from the event ID
208    event_entry_ptr = wlan_mac_schedule_find_event(event_id, &event_list);
209
210    if(event_entry_ptr == NULL) {
211        xil_printf("WARNING: attempted to remove unknown schedule ID %d\n", event_id);
212        return WLAN_FAILURE;
213    }
214
215    if(event_list == NULL) {
216        xil_printf("WARNING: wlan_mac_schedule_find_event() returned NULL list for event ID %d\n", event_id);
217        return WLAN_FAILURE;
218    }
219
220    // Extract the scheduled event struct from the dl_entry
221    event_ptr = (wlan_sched_event_t*)(event_entry_ptr->data);
222
223    if(event_ptr == NULL) {
224        xil_printf("WARNING: schedule entry for ID %d had NULL data\n", event_id);
225        return WLAN_FAILURE;
226    }
227
228    // Remove the list entry and free the entry/data memory allocations
229    dl_entry_remove(event_list, event_entry_ptr);
230    wlan_mac_high_free(event_entry_ptr);
231    wlan_mac_high_free(event_ptr);
232
233    total_num_scheduled_events--;
234
235    // Stop the timer if there are now zero scheduled events
236    if(total_num_scheduled_events == 0) {
237        wlan_timer_stop();
238    }
239
240    return WLAN_SUCCESS;
241}
242
243
244/*****************************************************************************/
245/**
246 * This function is called periodically (typically by a timer ISR) to
247 * evaluate all schedules and scheduled events.
248 *
249 * @return  Returns WLAN_SUCCESS
250 *
251 *****************************************************************************/
252int wlan_mac_schedule_poll() {
253
254    wlan_schedule_t* sched;
255    dl_list* event_list;
256
257    dl_entry* event_entry_ptr;
258    wlan_sched_event_t* event_ptr;
259    int event_id;
260    function_ptr_t event_callback;
261
262    int sched_id;
263    int l_iter;
264
265    // Record the current system time once here to avoid race conditions
266    //  when evaluating multiple schedules/events below
267    u64 curr_system_time = get_system_time_usec();
268
269    // Evaluate every schedule
270    for(sched_id=0; sched_id < NUM_SCHEDULES; sched_id++) {
271        sched = &(wlan_schedules[sched_id]);
272        event_list = &(wlan_schedules[sched_id].event_list);
273
274        // Check if this schedule is due for evaluation
275        //  Scheduled events are only considered (via the while loop below)
276        //   when their parent schedule is evaluated. This saves loop iterations
277        //   and u64 comparisons for groups of infrequently-executed events
278        if(sched->polls_to_next_exec > 0) {
279            // Schedule not due for evaluation - decrement its poll counter and continue
280            sched->polls_to_next_exec--;
281
282        } else {
283            // Schedule is due for evaluation - iterate over all its events
284
285            // Initialize the dl_entry pointer to the head of the event_list
286            event_entry_ptr = event_list->first;
287            l_iter = event_list->length;
288
289            while((event_entry_ptr != NULL) && (l_iter-- > 0)) {
290                event_ptr = (wlan_sched_event_t*)event_entry_ptr->data;
291
292                // Update the dl_entry pointer for the next iteration
293                //  This happens before the callback in case the callback removes the
294                //  event (callbacks shouldn't do this, but just in case)
295                // TODO: consider making this even safer - what happens if callback N
296                //  removes scheduled event N+1? This pointer would be bogus. Perhaps
297                //  record the next pointer here then again below, punting if they differ?
298                event_entry_ptr = dl_entry_next(event_entry_ptr);
299
300                // Call the event's callback if it's enabled and its next execution time has passed
301                //  Evaluate 'enabled' first to avoid unnecessary u64 comparisons
302                if((event_ptr->enabled == 1) && (curr_system_time >= (event_ptr->target_us))) {
303                    // Decrement the number of remaining calls for this event only if event
304                    //  has finite and non-zero number of remaining calls
305                    if ((event_ptr->num_calls != SCHEDULE_REPEAT_FOREVER) &&
306                        (event_ptr->num_calls != 0)) {
307
308                        (event_ptr->num_calls)--;
309                    }
310
311                    // Call the scheduled event's callback
312                    event_id       = event_ptr->id;
313                    event_callback = event_ptr->callback;
314                    event_callback(event_id);
315
316                    // Update or remove the scheduled event *after* the callback in case the
317                    //  callback changed the event's parameters. For example the callback might
318                    //  reset the num_calls value, indicating the event should not be removed
319                    if(event_ptr->num_calls == 0) {
320                        // Remove event after its final execution
321                        wlan_mac_schedule_remove_event(event_id);
322                    } else {
323                        // Update target time for next execution
324                        event_ptr->target_us = curr_system_time + (u64)(event_ptr->interval_us);
325                    }
326                } //END if(curr_system_time > event.target)
327            }
328
329            // Update the schedule after evaluating all its scheduled events
330            //  The fastest schedule always has exec_interval=polls_to_next_exec=0,
331            if(sched->exec_interval != 0) {
332                sched->polls_to_next_exec = sched->exec_interval;
333            }
334
335        } //END else of if(polls_to_next_exec > 0)
336    } //END for(sched_id in wlan_schedules)
337
338    return WLAN_SUCCESS;
339}
340
341
342
343/*****************************************************************************/
344/**
345 * Find schedule that corresponds to a given ID
346 *
347 * @param   event_id        - ID of the scheduled event to find
348 * @param   dl_list**   - Return param for list containing the scheduled event,
349 *                        if event_id is found. Pass NULL if list pointer is
350 *                        not required by calling context.
351 *
352 * @return  dl_entry*   - Pointer to the list entry for the scheduled event
353 *
354 *****************************************************************************/
355dl_entry* wlan_mac_schedule_find_event(u32 event_id, dl_list** sched_list_ret) {
356    int i, iter;
357
358    dl_list*  event_list;
359    dl_entry* curr_dl_entry;
360    dl_entry* next_dl_entry;
361    wlan_sched_event_t* curr_event;
362
363    // Check all event lists for the matching ID
364    for(i=0; i < NUM_SCHEDULES; i++) {
365        event_list = &(wlan_schedules[i].event_list);
366
367        // Initialize the loop variables
368        iter = event_list->length;
369        next_dl_entry = event_list->first;
370
371        // Search the event_list for this event_id
372        while ((next_dl_entry != NULL) && (iter-- > 0)) {
373            curr_dl_entry   = next_dl_entry;
374            next_dl_entry   = dl_entry_next(next_dl_entry);
375            curr_event      = (wlan_sched_event_t*)(curr_dl_entry->data);
376
377            if(curr_event->id == event_id) {
378                // Found a matching event - populate the return list argument
379                //  if the caller asked for it
380                if(sched_list_ret != NULL) {
381                    *sched_list_ret = event_list;
382                }
383                return curr_dl_entry;
384            }
385        }
386    }
387
388    // Matching event not found
389    return NULL;
390}
Note: See TracBrowser for help on using the repository browser.