/** * @file * * @brief This module handles the functionality required for Association. * * $Id: associate.c,v 1.97 2007/05/15 08:34:38 sschneid Exp $ */ /** * \author * Atmel Corporation: http://www.atmel.com * Support email: avr@atmel.com */ /* * Copyright (c) 2006, Atmel Corporation All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of ATMEL may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* === Includes ============================================================ */ #include #include "mac.h" #include "phy.h" #include "timer_const.h" #if APP_TYPE >= APP_L2 || defined(DOXYGEN) /* === Globals ============================================================= */ /* Current capability information of potential coordinator */ static uint8_t capability_info; /* Current address mode of potential coordinator */ static uint8_t coord_addr_mode; /* === Prototypes ========================================================== */ /* === Implementation ====================================================== */ /* * Internal function to initiate mlme associate confirm message */ static void gen_mlme_associate_conf(uint8_t status, uint16_t AssocShortAddress) { mlme_associate_conf_t mac; mac.size = sizeof(mlme_associate_conf_t) - sizeof(mac.size); mac.cmdcode = MLME_ASSOCIATE_CONFIRM; mac.status = status; mac.AssocShortAddress = AssocShortAddress; bios_pushback_event((uint8_t *)&mac); } /** * @brief Handles the MLME_Associate_request command from the NWK layer * * The MLME-ASSOCIATE.request primitive is generated by the next higher layer * of an unassociated device and issued to its MLME to request an association * with a coordinator. If the device wishes to associate with a coordinator * on a beacon-enabled PAN, the MLME may optionally track the beacon of that * coordinator prior to issuing this primitive. * * @param m A pointer to association request parameters. */ void mlme_associate_request(uint8_t *m) { mlme_associate_req_t* msg = (mlme_associate_req_t *)m; // MLME_Associate_Request can only be processed after scan if ((mac_state != MAC_Ya) && (mac_state != MAC_Zp)) { gen_mlme_associate_conf(MAC_INVALID_PARAMETER, BROADCAST); return; } coord_addr_mode = msg->CoordAddrMode; if ((coord_addr_mode != FCF_SHORT_ADDR) && (coord_addr_mode != FCF_LONG_ADDR)) { gen_mlme_associate_conf(MAC_INVALID_PARAMETER, BROADCAST); return; } if (coord_addr_mode == FCF_SHORT_ADDR) { mac_pib_macCoordShortAddress = msg->CoordAddress; } else { mac_pib_macCoordExtendedAddress = msg->CoordAddress; } // Set the PAN ID in the PIB to the PAN ID passed as a parameter mac_set_panid(msg->CoordPANId); capability_info = msg->CapabilityInformation; mac_current_channel = msg->LogicalChannel; mac_original_state = mac_state; // do we need to wake up the radio first? if (mac_radio_sleep_state == RADIO_SLEEPING) { mac_state = MAC_WAKEUP_ASSOC; // wake up radio first mac_phy_wakeup(); } else { mac_awake_associate(); } } /** * @brief Continues handling of MLME_ASSOCIATE.request once the radio is awake */ void mac_awake_associate(void) { uint8_t psr_buf[sizeof(plme_set_req_t) + sizeof(uint8_t)]; plme_set_req_t* psr = (plme_set_req_t *) psr_buf; frame_buffer_t* fbuf; uint8_t index; uint16_t fcf; // set the MAC to it's original state mac_state = mac_original_state; /* Call buffer request to get a buffer pointer. */ fbuf = mac_buffer_request(); if (fbuf == NULL) { gen_mlme_associate_conf(MAC_TRANSACTION_OVERFLOW, BROADCAST); // go to sleep again? if (!mac_pib_macRxOnWhenIdle) { // Send the radio to sleep mac_phy_init_sleep(); } return; } if (coord_addr_mode == FCF_SHORT_ADDR) { fcf = FCF_SET_FRAMETYPE(FCF_FRAMETYPE_MAC_CMD) | FCF_SET_DEST_ADDR_MODE(FCF_SHORT_ADDR) | FCF_SET_SOURCE_ADDR_MODE(FCF_LONG_ADDR) | FCF_ACK_REQUEST; } else { fcf = FCF_SET_FRAMETYPE(FCF_FRAMETYPE_MAC_CMD) | FCF_SET_DEST_ADDR_MODE(FCF_LONG_ADDR) | FCF_SET_SOURCE_ADDR_MODE(FCF_LONG_ADDR) | FCF_ACK_REQUEST; } index = 1; // Build the Frame Control Field fbuf->pd_data.data[index++] = fcf & 0xff; fbuf->pd_data.data[index++] = (fcf >> 8) & 0xff; // Build the sequence number fbuf->pd_data.data[index++] = mac_pib_macDSN++; // Build the Destination PAN ID fbuf->pd_data.data[index++] = mac_pib_macPANId & 0xFF; fbuf->pd_data.data[index++] = (mac_pib_macPANId >> 8) & 0xFF; // Build the Destination address if (coord_addr_mode == FCF_SHORT_ADDR) { memcpy(&fbuf->pd_data.data[index], &mac_pib_macCoordShortAddress, sizeof(uint16_t)); index += sizeof(uint16_t); } else { memcpy(&fbuf->pd_data.data[index], &mac_pib_macCoordExtendedAddress, sizeof(uint64_t)); index += sizeof(uint64_t); } // Source PAN ID is broadcast PAN ID fbuf->pd_data.data[index++] = BROADCAST & 0xFF; fbuf->pd_data.data[index++] = (BROADCAST >> 8) & 0xFF; // Build the Source address memcpy(&fbuf->pd_data.data[index], &mac_ieee_address, sizeof(uint64_t)); index += sizeof(uint64_t); // Build the command frame id fbuf->pd_data.data[index++] = fbuf->msgtype = ASSOCIATIONREQUEST; // Build the capability info. fbuf->pd_data.data[index++] = capability_info; /* begin MFR */ index += CRC_SIZE - sizeof(fbuf->pd_data.size); fbuf->pd_data.data[0] = fbuf->pd_data.psduLength = index; /* end MFR */ fbuf->pd_data.size = sizeof(pd_data_req_t) - sizeof(fbuf->pd_data.size) + index; // Set the radio to the desired channel plme_set_conf_t cm; psr->size = sizeof(plme_set_req_t) - sizeof(psr->size) + 1; psr->cmdcode = PLME_SET_REQUEST; psr->PIBAttribute = phyCurrentChannel; psr->data[0] = 1; psr->data[1] = mac_current_channel; /* We need to do this without the queue, otherwise we would start CSMA-CA without knowing whether the channel has been switched successfully */ plme_set_request_internal(psr, &cm); if (cm.status != PHY_SUCCESS) { // return buffer mac_buffer_free(fbuf); // Report unsuccessful setting of channel gen_mlme_associate_conf(MAC_INVALID_PARAMETER, BROADCAST); // go to sleep again? if (!mac_pib_macRxOnWhenIdle) { // Send the radio to sleep mac_phy_init_sleep(); } return; } ASSERT(mac_message == NULL); mac_message = fbuf; /* Start CSMA-CA using backoff and retry (direct transmission) */ mac_csma_ca(true, true); return; } #if DEVICE_TYPE == FD1 || DEVICE_TYPE == FD1_NOGTS || defined(DOXYGEN) /** * @brief Processes a received association request command frame. * * This function will process a received association request command frame. The * result will be a MAC to Stack MLME_Associate_indication call. * */ void mac_process_associate_request(void) { mlme_associate_ind_t mai; /* * 7.5.3.1: "If a coordinator with macAssociationPermit set to * false receives an association request command from a device, * the command shall be ignored." */ if (!mac_pib_macAssociationPermit) { return; } // Build the MLME_ASSOCIATE_indication parameters mai.DeviceAddress = mac_parse_data.src_addr.long_address; mai.CapabilityInformation = mac_parse_data.payload_data.assoc_req_data.capability_info; mai.SecurityUse = false; mai.ACLEntry = MAC_NOACLENTRYFOUND; // Send an associate indication to the stack mai.size = sizeof(mlme_associate_ind_t) - sizeof(mai.size); mai.cmdcode = MLME_ASSOCIATE_INDICATION; bios_pushback_event((uint8_t *) &mai); } /** * @brief Entry point from the stack for MLME_Associate_response. * * The MLME-ASSOCIATE.response primitive is used to initiate a response to an * MLMEASSOCIATE.indication primitive. * * @param m A pointer to association response parameters. * */ void mlme_associate_response(uint8_t *m) { mlme_associate_resp_t *msg = (mlme_associate_resp_t *)m; frame_buffer_t *fbuf; uint8_t index; uint16_t fcf; // A MLME_Associate_Response cannot be processed unless in one of the following states // MAC_Za and MAC_A. if ((mac_state != MAC_Za) && (mac_state != MAC_A)) { return; } // Only a PAN coordinator can receive this primitive from its stack if (!mac_i_pan_coordinator) { return; } /* Call buffer request indirect to get a buffer pointer. */ fbuf = mac_buffer_request_indirect(); if (fbuf == NULL) { mac_mlme_comm_status(FCF_LONG_ADDR, &mac_ieee_address, FCF_LONG_ADDR, &msg->DeviceAddress, MAC_TRANSACTION_OVERFLOW); return; } // If we are in non-beaconing mode and the coordinator does have pending data // the MAC persistence timer needs to be started, which is realized by means of // the beacon timer mac_check_persistence_timer(); fcf = FCF_SET_FRAMETYPE(FCF_FRAMETYPE_MAC_CMD) | FCF_SET_DEST_ADDR_MODE(FCF_LONG_ADDR) | FCF_SET_SOURCE_ADDR_MODE(FCF_LONG_ADDR) | FCF_ACK_REQUEST; index = 1; // Build the Frame Control Field fbuf->pd_data.data[index++] = fcf & 0xFF; fbuf->pd_data.data[index++] = (fcf >> 8) & 0xFF; // Build the sequence number fbuf->pd_data.data[index++] = mac_pib_macDSN++; // Build the Destination PAN ID memcpy(&fbuf->pd_data.data[index], &mac_pib_macPANId, sizeof(uint16_t)); index += sizeof(uint16_t); // Build the Destination address memcpy(&fbuf->pd_data.data[index], &msg->DeviceAddress, sizeof(uint64_t)); index += sizeof(uint64_t); // Build the Source PAN ID memcpy(&fbuf->pd_data.data[index], &mac_pib_macPANId, sizeof(uint16_t)); index += sizeof(uint16_t); // Build the Source address memcpy(&fbuf->pd_data.data[index], &mac_ieee_address, sizeof(uint64_t)); index += sizeof(uint64_t); // Build the command frame id fbuf->pd_data.data[index++] = fbuf->msgtype = ASSOCIATIONRESPONSE; // Build the short address memcpy(&fbuf->pd_data.data[index], &msg->AssocShortAddress, sizeof(uint16_t)); index += sizeof(uint16_t); // Build the association status fbuf->pd_data.data[index++] = msg->status; /* begin MFR */ index += CRC_SIZE - sizeof(fbuf->pd_data.size); fbuf->pd_data.data[0] = fbuf->pd_data.psduLength = index; /* end MFR */ fbuf->transactiontime = mac_pib_macTransactionPersistenceTime; fbuf->pd_data.size = sizeof(pd_data_req_t) - sizeof(fbuf->pd_data.size) + index; fbuf->pd_data.cmdcode = PD_DATA_REQUEST; } #endif /* DEVICE_TYPE == FD1 || DEVICE_TYPE == FD1_NOGTS || defined(DOXYGEN) */ /** * @brief Processing of an associaton reponse command frame. */ void mac_process_associate_response(void) { uint8_t status = mac_parse_data.payload_data.assoc_response_data.assoc_status; if (status == ASSOCIATION_SUCCESSFUL) { // Set our newly assigned short address in the PIB mac_set_shortaddr(mac_parse_data.payload_data.assoc_response_data.short_addr); memcpy(&mac_pib_macCoordExtendedAddress,&mac_parse_data.src_addr,sizeof(uint64_t)); gen_mlme_associate_conf(status, mac_pib_macShortAddress); mac_state = MAC_A; #if POWER_SAVE_BEACON_OFF == 1 /* Since power save for devices in beacon mode is currently not supported the device needs to stay awake if it has associated successfully with a coordinator in beacon mode */ if (mac_pib_macBeaconOrder != NON_BEACON_NETWORK) { mac_pib_macRxOnWhenIdle = true; } #endif } else { gen_mlme_associate_conf(status, BROADCAST); mac_state = mac_original_state; mac_pib_macPANId = macPANId_def; mac_pib_macCoordShortAddress = macCoordShortAddress_def; mac_pib_macCoordExtendedAddress = CLEAR_ADDR_64; } // Check and see if the frame pending bit was set on the received data frame if (mac_parse_data.fcf & FCF_FRAME_PENDING) { // Build command frame due to implicit poll request mac_build_data_req_cmd(false, false); } else { // go to sleep again? if (!mac_pib_macRxOnWhenIdle) { // Send the radio to sleep mac_phy_init_sleep(); } } } /* Handle T_Response_Wait timer */ void mac_t_response_wait(void) { ASSERT(mac_state == MAC_Wait_Response); mac_build_data_req_cmd(false, false); if (!bios_starttimer(T_AssocResponseTime, aResponseWaitTime)) { // Timer could not be started, so we need to make sure we do not get stuck mac_t_assocresponsetime(); } } /* Handle T_Assoc_Response timer */ void mac_t_assocresponsetime(void) { gen_mlme_associate_conf(MAC_NO_DATA, BROADCAST); mac_state = mac_original_state; mac_pib_macPANId = macPANId_def; mac_pib_macCoordShortAddress = macCoordShortAddress_def; mac_pib_macCoordExtendedAddress = CLEAR_ADDR_64; // go to sleep again? if (!mac_pib_macRxOnWhenIdle) { // Send the radio to sleep mac_phy_init_sleep(); } } #endif /* APP_TYPE >= APP_L2 || defined(DOXYGEN) */ /* EOF */