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

Last change on this file since 5743 was 5689, checked in by chunter, 7 years ago

1) Fixed a typo in the last commit that made a compilation error
2) Continued cleanup of DCF
3) Changed dates to 2017 in all files

File size: 21.1 KB
Line 
1/** @file wlan_mac_schedule.c
2 *  @brief Scheduler
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-2017, 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 "xparameters.h"
23#include "xtmrctr.h"
24#include "xintc.h"
25
26#include "wlan_mac_time_util.h"
27#include "wlan_mac_high.h"
28#include "wlan_mac_dl_list.h"
29#include "wlan_mac_schedule.h"
30
31
32/*************************** Constant Definitions *****************************/
33
34#define WLAN_SCHED_EXEC_MONITOR                            0
35#define WLAN_SCHED_EXEC_MONITOR_DEFAULT_THRESHOLD          100000         // 100 ms
36
37
38/*************************** Variable Definitions ****************************/
39static XTmrCtr               timer_instance;
40
41volatile static u32          schedule_count;
42
43
44static wlan_sched_state_t    wlan_sched_coarse;
45static wlan_sched_state_t    wlan_sched_fine;
46static dl_list               disabled_list;
47
48#if WLAN_SCHED_EXEC_MONITOR
49static u64                   last_exec_timestamp;
50static u64                   schedule_exec_monitor;
51static u32                   monitor_threshold;
52#endif
53
54
55/*************************** Function Prototypes *****************************/
56
57void          timer_interrupt_handler(void * instancePtr);
58void          schedule_handler(void * callback_ref, u8 timer_number);
59
60
61/******************************** Functions **********************************/
62
63/*****************************************************************************/
64/**
65 * Initializes the schedulers.
66 *
67 * @param    None
68 *
69 * @return   None
70 *
71 *****************************************************************************/
72int wlan_mac_schedule_init(){
73    int status;
74
75    // Initialize internal variables
76    schedule_count           = 1;
77
78#if WLAN_SCHED_EXEC_MONITOR
79    last_exec_timestamp      = 0;
80    schedule_exec_monitor    = 0;
81    monitor_threshold        = WLAN_SCHED_EXEC_MONITOR_DEFAULT_THRESHOLD;
82#endif
83
84    dl_list_init(&(wlan_sched_coarse.enabled_list));
85    wlan_sched_coarse.next = NULL;
86    dl_list_init(&(wlan_sched_fine.enabled_list));
87    wlan_sched_fine.next = NULL;
88
89    dl_list_init(&disabled_list);
90
91    // Initialize the timer
92    // The driver for the timer does not handle a reboot of CPU_HIGH
93    // gracefully. Before initializing it, we should stop any running
94    // timers. Note: we cannot use the official XTmrCtr_Stop() function
95    // for this since it requires that the timer instance be initialized.
96    u32 ControlStatusReg;
97    u8  i;
98    XTmrCtr_Config *TmrCtrConfigPtr = XTmrCtr_LookupConfig(TMRCTR_DEVICE_ID);
99
100    if(TmrCtrConfigPtr != NULL){
101        for(i=0; i<2; i++){
102            //Loop over timer instances
103            /*
104             * Read the current register contents
105             */
106            ControlStatusReg = XTmrCtr_ReadReg(TmrCtrConfigPtr->BaseAddress,
107                                        i, XTC_TCSR_OFFSET);
108
109            /*
110             * Disable the timer counter such that it's not running
111             */
112            ControlStatusReg &= ~(XTC_CSR_ENABLE_TMR_MASK);
113
114            /*
115             * Write out the updated value to the actual register.
116             */
117            XTmrCtr_WriteReg(TmrCtrConfigPtr->BaseAddress, i,
118                      XTC_TCSR_OFFSET, ControlStatusReg);
119
120        }
121
122
123
124        status = XTmrCtr_Initialize(&timer_instance, TMRCTR_DEVICE_ID);
125        if ( (status != XST_SUCCESS) ) {
126            xil_printf("ERROR:  XTmrCtr failed to initialize\n");
127            return -1;
128        }
129
130        // Set the handler for Timer
131        XTmrCtr_SetHandler(&timer_instance, schedule_handler, &timer_instance);
132
133        // Enable interrupt of timer and auto-reload so it continues repeatedly
134        XTmrCtr_SetOptions(&timer_instance, TIMER_CNTR_FAST, XTC_DOWN_COUNT_OPTION | XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);
135        XTmrCtr_SetOptions(&timer_instance, TIMER_CNTR_SLOW, XTC_DOWN_COUNT_OPTION | XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);
136
137        return 0;
138    } else {
139        //Could not find XTmrCtr_Config for the timer
140        return -1;
141    }
142}
143
144
145
146/*****************************************************************************/
147/**
148 * Set up scheduler interrupt
149 *
150 * @param   intc             - pointer to instance of interrupt controller
151 *
152 * @return  status           - success or failure of attempt to connect
153 *
154 *****************************************************************************/
155int wlan_mac_schedule_setup_interrupt(XIntc* intc){
156    int status;
157
158    status =  XIntc_Connect(intc, TMRCTR_INTERRUPT_ID, (XInterruptHandler)timer_interrupt_handler, &timer_instance);
159    XIntc_Enable(intc, TMRCTR_INTERRUPT_ID);
160
161    return status;
162}
163
164
165
166/*****************************************************************************/
167/**
168 * Schedules the execution of a callback for some time in the future
169 *
170 * @param   scheduler_sel    - SCHEDULE_COARSE or SCHEDULE_FINE
171 * @param   delay            - Interval (in microseconds) until callback should be called
172 * @param   num_calls        - Number of repetitions or SCHEDULE_REPEAT_FOREVER for permanent periodic
173 * @param   callback         - Function pointer to callback
174 *
175 * @return  id               - ID of scheduled event or SCHEDULE_FAILURE if error
176 *
177 *****************************************************************************/
178u32 wlan_mac_schedule_event_repeated(u8 scheduler_sel, u32 delay, u32 num_calls, void(*callback)()){
179    u32            id;
180    dl_entry     * entry_ptr;
181    wlan_sched   * sched_ptr;
182    u64            curr_system_time;
183
184    // Allocate memory for data structures
185    entry_ptr = wlan_mac_high_malloc(sizeof(dl_entry));
186    if (entry_ptr == NULL) { return SCHEDULE_FAILURE; }
187
188    sched_ptr = wlan_mac_high_malloc(sizeof(wlan_sched));
189    if (sched_ptr == NULL) { wlan_mac_high_free(entry_ptr); return SCHEDULE_FAILURE; }
190
191    // Attach the schedule struct to this dl_entry
192    entry_ptr->data = sched_ptr;
193
194    do{
195        // Get Schedule ID from global counter
196        id = (schedule_count++);
197
198        // Check if we hit the section of reserved IDs; Wrap back to 0 and start again
199        if ((id >= SCHEDULE_ID_RESERVED_MIN) && (id <= SCHEDULE_ID_RESERVED_MAX)) { id = 1; }
200
201        // Check to make sure that this id has not been issued to any currently
202        // scheduled events
203    } while( (wlan_mac_schedule_find(&wlan_sched_coarse.enabled_list, id) != NULL) ||
204             (wlan_mac_schedule_find(&wlan_sched_fine.enabled_list, id) != NULL) ||
205             (wlan_mac_schedule_find(&disabled_list, id) != NULL));
206
207    // Get the current system time
208    curr_system_time     = get_system_time_usec();
209
210    // Initialize the schedule struct
211    sched_ptr->enabled   = 1;
212    sched_ptr->id        = id;
213    sched_ptr->delay_us  = delay;
214    sched_ptr->num_calls = num_calls;
215    sched_ptr->target_us = curr_system_time + (u64)(delay);
216    sched_ptr->callback  = (function_ptr_t)callback;
217
218    // Add schedule struct to the appropriate schedule list
219    switch(scheduler_sel){
220        // ------------------------------------------------
221        case SCHEDULE_COARSE:
222            // Start timer if the list goes from 0 -> 1 event
223            if (wlan_sched_coarse.enabled_list.length == 0) {
224                XTmrCtr_SetResetValue(&timer_instance, TIMER_CNTR_SLOW, (SLOW_TIMER_DUR_US * TIMER_CLKS_PER_US));
225                XTmrCtr_Start(&timer_instance, TIMER_CNTR_SLOW);
226            }
227
228            dl_entry_insertBeginning(&(wlan_sched_coarse.enabled_list), entry_ptr);
229        break;
230
231        // ------------------------------------------------
232        case SCHEDULE_FINE:
233            // Start timer if the list goes from 0 -> 1 event
234            if(wlan_sched_fine.enabled_list.length == 0){
235                XTmrCtr_SetResetValue(&timer_instance, TIMER_CNTR_FAST, (FAST_TIMER_DUR_US * TIMER_CLKS_PER_US));
236                XTmrCtr_Start(&timer_instance, TIMER_CNTR_FAST);
237            }
238
239            dl_entry_insertBeginning(&(wlan_sched_fine.enabled_list), entry_ptr);
240        break;
241
242        // ------------------------------------------------
243        default:
244            xil_printf("Unknown scheduler selection.  No event scheduled.\n");
245            return SCHEDULE_FAILURE;
246        break;
247    }
248
249    return id;
250}
251
252
253dl_entry* wlan_mac_schedule_disable_id(u8 scheduler_sel, u32 sched_id){
254    dl_entry* sched_entry;
255    dl_list* enabled_list;
256    u8 timer_sel;
257
258    switch(scheduler_sel){
259        case SCHEDULE_COARSE:
260            enabled_list = &(wlan_sched_coarse.enabled_list);
261            timer_sel = TIMER_CNTR_SLOW;
262        break;
263
264        case SCHEDULE_FINE:
265            enabled_list = &(wlan_sched_fine.enabled_list);
266            timer_sel = TIMER_CNTR_FAST;
267        break;
268
269        default:
270            return NULL;
271        break;
272    }
273
274    sched_entry = wlan_mac_schedule_find(enabled_list, sched_id);
275
276    if(sched_entry != NULL){
277        if( ((wlan_sched*)(sched_entry->data))->num_calls != 0 ){
278            dl_entry_remove(enabled_list, sched_entry);
279            dl_entry_insertEnd(&disabled_list, sched_entry);
280            ((wlan_sched*)(sched_entry->data))->enabled = 0;
281        } else {
282            // wlan_mac_schedule_disable_id() is not supported for a schedule whose num_calls
283            // is 0. The most common occurence of this scenario is trying to disable a schedule
284            // from the final execution of a callback of that schedule.
285            return NULL;
286        }
287    }
288
289    // Stop the timer if there are no more events
290    //     NOTE:  Will be restarted when an event is added or re-enabled
291    if (enabled_list->length == 0) {
292        XTmrCtr_Stop(&timer_instance, timer_sel);
293    }
294
295    return sched_entry;
296}
297
298int wlan_mac_schedule_enable(u8 scheduler_sel, dl_entry* sched_entry){
299
300    dl_list* enabled_list;
301    u8 timer_sel;
302    u32 reset_value;
303
304    if(sched_entry == NULL){
305        return -1;
306    }
307    switch(scheduler_sel){
308        case SCHEDULE_COARSE:
309            enabled_list = &(wlan_sched_coarse.enabled_list);
310            timer_sel = TIMER_CNTR_SLOW;
311            reset_value = (SLOW_TIMER_DUR_US * TIMER_CLKS_PER_US);
312        break;
313
314        case SCHEDULE_FINE:
315            enabled_list = &(wlan_sched_fine.enabled_list);
316            timer_sel = TIMER_CNTR_FAST;
317            reset_value = (FAST_TIMER_DUR_US * TIMER_CLKS_PER_US);
318        break;
319
320        default:
321            return -1;
322        break;
323    }
324
325    dl_entry_remove(&disabled_list, sched_entry);
326    dl_entry_insertEnd(enabled_list, sched_entry);
327
328    ((wlan_sched*)(sched_entry->data))->enabled = 1;
329    ((wlan_sched*)(sched_entry->data))->target_us = get_system_time_usec() + ((wlan_sched*)(sched_entry->data))->delay_us;
330
331    // Start timer if the list goes from 0 -> 1 event
332    if(wlan_sched_fine.enabled_list.length == 1){
333        XTmrCtr_SetResetValue(&timer_instance, timer_sel, reset_value);
334        XTmrCtr_Start(&timer_instance, timer_sel);
335    }
336
337    return 0;
338
339}
340
341
342/*****************************************************************************/
343/**
344 * @brief  Cancels the execution of a scheduled callback
345 *
346 * @param   scheduler_sel    - SCHEDULE_COARSE or SCHEDULE_FINE
347 * @param   id               - ID of schedule that should be removed
348 *
349 * @return  None
350 *
351 * @note    This function will fail silently if the ID parameter does not match
352 *          any currently running schedule event IDs.
353 *
354 *****************************************************************************/
355void wlan_mac_remove_schedule(u8 scheduler_sel, u32 id){
356    dl_entry     * curr_entry_ptr;
357    wlan_sched   * curr_sched_ptr;
358
359    dl_list* enabled_list;
360
361    switch(scheduler_sel){
362        case SCHEDULE_COARSE:
363            enabled_list = &(wlan_sched_coarse.enabled_list);
364        break;
365
366        case SCHEDULE_FINE:
367            enabled_list = &(wlan_sched_fine.enabled_list);
368        break;
369
370        default:
371            return;
372        break;
373    }
374
375    curr_entry_ptr = wlan_mac_schedule_find(enabled_list, id);
376
377    if (curr_entry_ptr != NULL) {
378
379        // Extract the schedule struct from dl_entry
380        curr_sched_ptr = (wlan_sched*)(curr_entry_ptr->data);
381
382        switch (scheduler_sel) {
383            // ------------------------------------------------
384            case SCHEDULE_COARSE:
385                if(curr_sched_ptr != NULL){
386                    if(wlan_sched_coarse.next == curr_entry_ptr){
387                        // The next dl_entry in the schedule_handler iteration
388                        // is the schedule we are about to remove. We need to advance
389                        // this pointer prior to removing so that we do not break
390                        // the execution of schedule_handler.
391                        wlan_sched_coarse.next = dl_entry_next(curr_entry_ptr);
392                    }
393                    dl_entry_remove(&(wlan_sched_coarse.enabled_list), curr_entry_ptr);
394                    wlan_mac_high_free(curr_entry_ptr);
395                    wlan_mac_high_free(curr_sched_ptr);
396                }
397
398                // Stop the timer if there are no more events
399                //     NOTE:  Will be restarted when new event is added
400                if (wlan_sched_coarse.enabled_list.length == 0) {
401                    XTmrCtr_Stop(&timer_instance, TIMER_CNTR_SLOW);
402                }
403            break;
404
405            // ------------------------------------------------
406            case SCHEDULE_FINE:
407                if(curr_sched_ptr != NULL){
408                    if(wlan_sched_fine.next == curr_entry_ptr){
409                        // The next dl_entry in the schedule_handler iteration
410                        // is the schedule we are about to remove. We need to advance
411                        // this pointer prior to removing so that we do not break
412                        // the execution of schedule_handler.
413                        wlan_sched_fine.next = dl_entry_next(curr_entry_ptr);
414                    }
415                    dl_entry_remove(&(wlan_sched_fine.enabled_list), curr_entry_ptr);
416                    wlan_mac_high_free(curr_entry_ptr);
417                    wlan_mac_high_free(curr_sched_ptr);
418                }
419
420                // Stop the timer if there are no more events
421                //     NOTE:  Will be restarted when new event is added
422                if (wlan_sched_fine.enabled_list.length == 0){
423                    XTmrCtr_Stop(&timer_instance, TIMER_CNTR_FAST);
424                }
425            break;
426
427            // ------------------------------------------------
428            default:
429                xil_printf("Unknown scheduler selection.  No event removed.\n");
430            break;
431        }
432
433#if WLAN_SCHED_EXEC_MONITOR
434        // If both timers are stopped, then we need to reset the last_exec_timestamp
435        //     NOTE:  This is so we do not get erroneous results when the timer is off
436        //         for extended periods of time.
437        if ((wlan_sched_coarse.length == 0) && (wlan_sched_fine.length == 0)) {
438            last_exec_timestamp = 0;
439        }
440#endif
441    }
442}
443
444
445
446/*****************************************************************************/
447/**
448 * Timer interrupt handler
449 *
450 * @param   instance_ptr     - Pointer to the timer instance
451 *
452 * @return  None
453 *
454 * @note    This function is a modified implementation of XTmrCtr_InterruptHandler
455 *          in the xtmrctr_intr.c driver. It has been modified to remove an explicit
456 *          reset of the timer.
457 *
458 *****************************************************************************/
459void timer_interrupt_handler(void * instance_ptr){
460
461    XTmrCtr      * timer_ptr;
462    u8             timer_number;
463    u32            base_addr;
464    u32            csr_reg;
465
466#ifdef _ISR_PERF_MON_EN_
467    wlan_mac_set_dbg_hdr_out(ISR_PERF_MON_GPIO_MASK);
468#endif
469
470    // Verify inputs are valid
471    if (instance_ptr == NULL) { return; }
472
473    timer_ptr = (XTmrCtr *) instance_ptr;
474    base_addr = timer_ptr->BaseAddress;
475
476    // Loop thru each timer counter in the device and call the callback
477    // function for each timer which has caused an interrupt
478    for (timer_number = 0; timer_number < XTC_DEVICE_TIMER_COUNT; timer_number++) {
479
480        // Read Control / Status register
481        csr_reg = XTmrCtr_ReadReg(base_addr, timer_number, XTC_TCSR_OFFSET);
482
483        // Check if interrupt is enabled
484        if (csr_reg & XTC_CSR_ENABLE_INT_MASK) {
485
486            // Check if timer expired and interrupt occured
487            if (csr_reg & XTC_CSR_INT_OCCURED_MASK) {
488                // Increment statistics for the number of interrupts
489                timer_ptr->Stats.Interrupts++;
490
491                // Call callback to handle timer processing
492                timer_ptr->Handler(timer_ptr->CallBackRef, timer_number);
493
494                // Read Control / Status register
495                csr_reg = XTmrCtr_ReadReg(base_addr, timer_number, XTC_TCSR_OFFSET);
496
497                // Acknowledge the interrupt
498                //     NOTE: It's very important that this ack occurs after the handler call. This
499                //         will prevent a scenario where a fine schedule callback can "crash" a
500                //         node by taking longer than whatever the interval is of the fast timer.
501                XTmrCtr_WriteReg(base_addr, timer_number, XTC_TCSR_OFFSET, (csr_reg | XTC_CSR_INT_OCCURED_MASK));
502            }
503        }
504    }
505
506#ifdef _ISR_PERF_MON_EN_
507    wlan_mac_clear_dbg_hdr_out(ISR_PERF_MON_GPIO_MASK);
508#endif
509}
510
511
512
513/*****************************************************************************/
514/**
515 * Internal callback used by the timer interrupt handler. This function should
516 * not be called by the upper-level MAC.
517 *
518 * @param   callback_ref   - Reserved (Not currently used)
519 * @param   timer_number   - ID of which timer has expired
520 *
521 * @return  None
522 *
523 *****************************************************************************/
524void schedule_handler(void * callback_ref, u8 timer_number){
525    dl_entry*           curr_entry_ptr;
526    wlan_sched*         curr_sched_ptr;
527
528    u8                  scheduler;
529    volatile wlan_sched_state_t* wlan_sched_state;
530    u32                 sched_id;
531    function_ptr_t      sched_callback;
532
533    u64            curr_system_time;
534
535    volatile static         u8 debug_print = 0;
536
537    // Get current system time
538    curr_system_time = get_system_time_usec();
539
540#if WLAN_SCHED_EXEC_MONITOR
541    // ----------------------------------------------------
542    // Scheduler Monitor Implementation
543    //
544    // Collect the "time between executions" of the schedule_handler function.
545    //
546    // The first time through the handler after the timer has been started,
547    // the last_exec_timestamp will be zero.  Therefore, we want to wait until
548    // subsequent loops to get the "time between executions" (ie the value
549    // recorded by the schedule_exec_monitor variable).
550    //
551    if (last_exec_timestamp != 0) {
552        schedule_exec_monitor = curr_system_time - last_exec_timestamp;
553    }
554
555    // If the monitor threshold is exceeded, print a warning.
556    //     NOTE:  Since prints take a long time, update the system time so the
557    //         print does not affect the collection of "time between executions"
558    if (schedule_exec_monitor > monitor_threshold) {
559        xil_printf("WARNING:  %d us between scheduler executions.\n", schedule_exec_monitor);
560        curr_system_time = get_system_time_usec();
561    }
562
563    // Store the current time in the last_timer_timestamp
564    last_exec_timestamp = curr_system_time;
565#endif
566
567    // Set any timer specific variables
568    if (timer_number == TIMER_CNTR_FAST) {
569        scheduler  = SCHEDULE_FINE;
570        wlan_sched_state = &(wlan_sched_fine);
571    } else {
572        // All other timers default to coarse scheduler
573        scheduler  = SCHEDULE_COARSE;
574        wlan_sched_state = &(wlan_sched_coarse);
575    }
576
577    // Get the first entry of the schedule dl_list
578    wlan_sched_state->next = wlan_sched_state->enabled_list.first;
579
580    // Update the appropriate scheduler
581    while (wlan_sched_state->next != NULL) {
582        curr_entry_ptr = wlan_sched_state->next;
583        wlan_sched_state->next = dl_entry_next(wlan_sched_state->next);
584        curr_sched_ptr = (wlan_sched*)(curr_entry_ptr->data);
585
586        if(debug_print){
587            xil_printf("curr_sched_ptr = 0x%08x\n", curr_sched_ptr);
588            xil_printf("curr_sched_ptr->callback = 0x%08x\n", curr_sched_ptr->callback);
589            xil_printf("curr_sched_ptr->id = %d\n", curr_sched_ptr->id);
590        }
591
592        // If the target time has passed, then process the event
593        if (curr_system_time >= (curr_sched_ptr->target_us)) {
594            sched_id       = curr_sched_ptr->id;
595            sched_callback = curr_sched_ptr->callback;
596
597            // Update the number of scheduled event calls
598            if ((curr_sched_ptr->num_calls != SCHEDULE_REPEAT_FOREVER) &&
599                (curr_sched_ptr->num_calls != 0)) {
600                (curr_sched_ptr->num_calls)--;
601            }
602
603            sched_callback(sched_id);
604
605            // The update of the target or removal of the schedule occurs after the callback so that the callback
606            // has the opportunity to "save" the schedule. It can do this by updating "num_calls" or by calling
607            // wlan_mac_schedule_disable
608            if(curr_sched_ptr->enabled == 1){
609                if(curr_sched_ptr->num_calls == 0){
610                    // Remove event (stops timer if necessary)
611                    wlan_mac_remove_schedule(scheduler, sched_id);
612                } else {
613                    // Update scheduled event for next execution
614                    curr_sched_ptr->target_us = curr_system_time + (u64)(curr_sched_ptr->delay_us);
615                }
616            }
617        }
618    }
619}
620
621
622
623/*****************************************************************************/
624/**
625 * Find schedule that corresponds to a given ID
626 *
627 * @param   dl_list*        - List to search through; typically the fine, coarse, and deactivated lists
628 * @param   id              - ID of the scheduler that should be returned
629 *
630 * @return  dl_entry*       - Pointer to the list entry that contains the schedule
631 *
632 *****************************************************************************/
633dl_entry* wlan_mac_schedule_find(dl_list* sched_list, u32 id){
634    int            iter;
635
636    dl_entry     * curr_dl_entry;
637    dl_entry     * next_dl_entry;
638    wlan_sched   * curr_wlan_sched;
639
640    // Initialize the loop variables
641    iter          = sched_list->length;
642    next_dl_entry = sched_list->first;
643
644    // Process the list
645    while ((next_dl_entry != NULL) && (iter-- > 0)) {
646        curr_dl_entry   = next_dl_entry;
647        next_dl_entry   = dl_entry_next(next_dl_entry);
648        curr_wlan_sched = (wlan_sched*)(curr_dl_entry->data);
649
650        if(curr_wlan_sched->id == id){
651            return curr_dl_entry;
652        }
653    }
654
655    return NULL;
656}
657
Note: See TracBrowser for help on using the repository browser.