[[Include(wiki:802.11/wlan_exp/app_notes/tutorial_token_mac/TOC)]] [[TracNav(802.11/TOC)]] = Alterations to CPU_HIGH = In this section, we will describe and discuss the changes needed to the high-level MAC code to realize the design. The main concept of TokenMAC is that the token should rotate among members of the network. This inherently requires knowledge of the membership of the network so that that it can be determined who should be given the token and for how long they should have it. In the 802.11 Reference Design, knowledge of network membership (or "association") belongs to the AP. As such, the AP project is well-suited to being modified with the behavior of managing token passing. In a sense, the AP becomes a sort of "traffic cop" and halts traffic from certain associations while enabling traffic to others. Additionally, we will need to make some small changes to the MAC High Framework to deal with some new inter-processor communication (IPC) messages with CPU_LOW. == Specific Changes == === Code Common to CPU_HIGH and CPU_LOW === Changes should be made to {{{wlan_mac_ipc_util.h}}} in the project SDK workspace zip. ---- We need to define two new types of IPC messages between CPU_LOW and CPU_HIGH. The first, which we will call {{{TOKEN_NEW_RESERVATION}}}, will primarily be used by an AP when it decrees that a token reservation period now belongs to one of the devices on its network. The second, which we will call {{{TOKEN_END_RESERVATION}}}, will primarily be used by an AP's CPU_LOW project to indicate to the AP that the current reservation period has ended and it should move on to the next device in its association table. Add the following snippet of code to {{{wlan_mac_ipc_util.h}}}: {{{ #!c #define IPC_MBOX_TOKEN_NEW_RESERVATION 20 #define IPC_MBOX_TOKEN_END_RESERVATION 21 typedef struct{ u32 res_duration; u8 addr[6]; u16 reserved; } ipc_token_new_reservation; typedef enum {TOKEN_DURATION_COMPLETE, TOKEN_TIMEOUT, TOKEN_OFFER_REJECTION} token_end_reservation_reason_t; typedef struct{ token_end_reservation_reason_t reason; } ipc_token_end_reservation; }}} The values of 20 and 21 are arbitrary. The key part is that you choose numbers that do not overlap with any of the other definitions that are prepended by {{{IPC_MBOX_}}}. It is these definitions that the processors use to determine what type of IPC message is currently being received. Next, {{{ipc_token_new_reservation}}} and {{{ipc_token_end_reservation}}} are structs that we will use to more easily fill in and process IPC payloads. ---- === Access Point (AP) === Changes should be made to {{{wlan_mac_ap.c}}} in the project SDK workspace zip. ---- We need to create a function that, when called, knows how to issue a new token for a reservation period to a device on the network. Note: the AP itself needs to be able to transmit, so it should include itself as one of the devices that get issued a token. Add the following function to the AP code: {{{ #!c void set_new_reservation(){ #define DEFAULT_RESERVATION_DURATION_USEC 20000 interrupt_state_t curr_interrupt_state; static dl_entry* next_station_info_entry = NULL; dl_entry* curr_station_info_entry; station_info* curr_station_info; wlan_ipc_msg ipc_msg_to_low; ipc_token_new_reservation ipc_payload; ipc_msg_to_low.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_TOKEN_NEW_RESERVATION); //The below chunk of code is just to make sure that we pad an extra u32 word if the //ipc_token_new_reservation struct is not 32-bit aligned. if( (sizeof(u32)*(sizeof(ipc_token_new_reservation)/sizeof(u32))) == sizeof(ipc_token_new_reservation) ){ ipc_msg_to_low.num_payload_words = (sizeof(ipc_token_new_reservation)/sizeof(u32)); } else { ipc_msg_to_low.num_payload_words = (sizeof(ipc_token_new_reservation)/sizeof(u32)) + 1; } ipc_msg_to_low.payload_ptr = (u32*)(&ipc_payload); //For safety, we will go ahead and disable interrupts while we are processing this function. This function //iterates through the doubly-linked list of associated stations. If that list were to be modified by an interrupt //context while this function is running, there may be unexpected results. By disabling interrupts, we know //that we can trust the doubly-linked list for this entire function context curr_interrupt_state = wlan_mac_high_interrupt_stop(); curr_station_info_entry = next_station_info_entry; // Loop through all associated stations if(curr_station_info_entry == NULL){ // It's the AP's reservation next_station_info_entry = my_bss_info->associated_stations.first; //SEND IPC for AP memcpy( ipc_payload.addr, my_bss_info->bssid, 6 ); ipc_payload.res_duration = DEFAULT_RESERVATION_DURATION_USEC; ipc_mailbox_write_msg(&ipc_msg_to_low); curr_station_info_entry = next_station_info_entry; } else { // It's a STA's reservation curr_station_info = (station_info*)(curr_station_info_entry->data); if( wlan_mac_high_is_valid_association(&my_bss_info->associated_stations, curr_station_info) ){ if(curr_station_info_entry == my_bss_info->associated_stations.last){ // We've reached the end of the table, so we wrap around to the beginning next_station_info_entry = NULL; } else { next_station_info_entry = dl_entry_next(curr_station_info_entry); } //SEND IPC for curr_station_info memcpy( ipc_payload.addr, curr_station_info->addr, 6 ); ipc_payload.res_duration = DEFAULT_RESERVATION_DURATION_USEC; ipc_mailbox_write_msg(&ipc_msg_to_low); curr_station_info_entry = next_station_info_entry; } else { // This curr_station_info is invalid. Perhaps it was removed from // the association table next_station_info_entry = NULL; } // END if(is_valid_association) } wlan_mac_high_interrupt_restore_state(curr_interrupt_state); } }}} Take a minute and read through what that code snippet is doing. Every time it is called, it will move on to the next member of the association table doubly-linked list and send an IPC message to CPU_LOW indicating the address of that member along with a duration of time the token reservation for that member should last. The #define for {{{DEFAULT_RESERVATION_DURATION_USEC}}} indicates this duration. After every loop through of the association table, it will issue an IPC message to CPU_LOW using its own BSSID as the address in the IPC payload. This indicates that the medium is now reserved for the AP itself. ---- The next thing we need to do is figure out when we should call our new {{{set_new_reservation()}}} function. We should definitely call it once at boot in {{{main()}}} to get the process started (at which point, no stations will be associated and that function would simply formally start the first token reservation period for the AP). But when should we call it next? We should call it whenever the current reservation period is complete. We'd expect this time to come {{{DEFAULT_RESERVATION_DURATION_USEC}}} microseconds after the previous call, so we could set up a periodic schedule and call our function at regular intervals. However, its entirely possible that a station is unable to accept a token for a reservation period, so we may wish to terminate the current reservation period early and move on to the next station's reservation. The AP itself has no idea when this even occurs since it is within the domain of CPU_LOW. So, rather than worry about scheduling the next call of {{{set_new_reservation()}}}, we can instead teach the MAC High Framework how to call that function when it receives an IPC message from CPU_LOW indicating that the current reservation period is over. We'll do this with a simple function callback. Add the following two lines to the {{{main()}}} function of the AP: {{{ #!c wlan_mac_high_set_token_new_reservation_callback((function_ptr_t)set_new_reservation); set_new_reservation(); }}} The safest place to add these lines is just before the {{{while(1)}}} clause at the end of the {{{main()}}} function. {{{wlan_mac_high_set_token_new_reservation_callback}}} does not exist yet; we will add it in the next section with our changes to the MAC High Framework. Basically, this line of code lets the AP delegate responsibility for calling {{{set_new_reservation}}} to the MAC High Framework. ---- === MAC High Framework === Changes should be made to {{{wlan_mac_high.c}}} in the project SDK workspace zip. ---- The first thing we need to do implement the callback structure that we assumed in our AP modifications. First, we should add a new top-level global to {{{wlan_mac_high.c}}} at the top of the file: {{{ #!c volatile function_ptr_t token_new_reservation_callback; }}} This variable will serve to hold the pointer to a function that should be called when the framework wants the AP to issue a new token reservation. For safety, we should explicitly set this function pointer to the nullCallback at boot so calling this function pointer does not crash the CPU. To do this, add the following line to {{{wlan_mac_high_init()}}}: {{{ #!c token_new_reservation_callback = (function_ptr_t)nullCallback; }}} Finally, we should create a function that allows the AP code (or whatever other high-level project) assign the new callback. Add this function to the MAC High Framework: {{{ #!c void wlan_mac_high_set_token_new_reservation_callback(function_ptr_t callback){ token_new_reservation_callback = callback; } }}} We have now implemented the {{{wlan_mac_high_set_token_new_reservation_callback()}}} function that we called in our AP modifications. ---- Finally, we need to actually call the {{{token_new_reservation_callback()}}} function pointer. We need to establish a contract with CPU_LOW. Specifically, we will assume that every time the AP sends down a {{{TOKEN_NEW_RESERVATION}}} IPC message to CPU_LOW, there will be a response of a {{{TOKEN_END_RESERVATION}}} IPC message at some point after that event. In other words, CPU_HIGH will not be responsible for determining when any given reservation period is over. We will assume CPU_LOW will tell CPU_HIGH when a new token reservation period should be issued. As such, we just need to call {{{token_new_reservation_callback()}}} whenever we get a {{{TOKEN_END_RESERVATION}}} IPC message from CPU_LOW. This is an easy modification to make. In {{{wlan_mac_high_process_ipc_msg()}}}, there is a big switch statement that handled every type of IPC message ID. All we need to do as add new cases to this switch: {{{ #!c case IPC_MBOX_TOKEN_NEW_RESERVATION: // Do nothing - To remove warning about "Unknown IPC message type" break; case IPC_MBOX_TOKEN_END_RESERVATION: token_new_reservation_callback(); break; }}} ----