/*---------------------------------------------------------------------------*/ int cc1100_send_csmaca(radio_address_t address, protocol_t protocol, int priority, char *payload, radio_packet_length_t payload_len) { uint16_t min_window_size; uint16_t max_window_size; uint16_t difs; uint16_t slottime; switch(priority) { case PRIORITY_ALARM: min_window_size = PRIO_ALARM_MIN_WINDOW_SIZE; max_window_size = PRIO_ALARM_MAX_WINDOW_SIZE; difs = PRIO_ALARM_DIFS; slottime = PRIO_ALARM_SLOTTIME; break; case PRIORITY_WARNING: min_window_size = PRIO_WARN_MIN_WINDOW_SIZE; max_window_size = PRIO_WARN_MAX_WINDOW_SIZE; difs = PRIO_WARN_DIFS; slottime = PRIO_WARN_SLOTTIME; break; default: min_window_size = PRIO_DATA_MIN_WINDOW_SIZE; max_window_size = PRIO_DATA_MAX_WINDOW_SIZE; difs = PRIO_DATA_DIFS; slottime = PRIO_DATA_SLOTTIME; } /* Calculate collisions per second */ if (collision_state == COLLISION_STATE_INITIAL) { vtimer_now(&collision_measurement_start); collision_count = 0; collisions_per_sec = 0; collision_state = COLLISION_STATE_MEASURE; } else if (collision_state == COLLISION_STATE_MEASURE) { timex_t now; vtimer_now(&now); timex_t timespan = timex_sub(now, collision_measurement_start); if (timex_cmp(timespan, timex_set(1, 0)) > 0) { collisions_per_sec = (collision_count * 1000000) / (double) timex_uint64(timespan); if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) { timex_t now; vtimer_now(&now); collision_measurement_start = now; collision_state = COLLISION_STATE_KEEP; } else if (collisions_per_sec > 2.2) { timex_t now; vtimer_now(&now); collision_measurement_start = now; collision_state = COLLISION_STATE_KEEP; } else { collision_state = COLLISION_STATE_INITIAL; } } } else if (collision_state == COLLISION_STATE_KEEP) { timex_t now; vtimer_now(&now); timex_t timespan = timex_sub(now, collision_measurement_start); if (timex_cmp(timespan, timex_set(5, 0)) > 0) { collision_state = COLLISION_STATE_INITIAL; } } /* Adjust initial window size according to collision rate */ if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) { min_window_size *= 2; } else if (collisions_per_sec > 2.2) { min_window_size *= 4; } uint16_t windowSize = min_window_size; /* Start with window size of PRIO_XXX_MIN_WINDOW_SIZE */ uint16_t backoff = 0; /* Backoff between 1 and windowSize */ uint32_t total; /* Holds the total wait time before send try */ uint32_t cs_timeout; /* Current carrier sense timeout value */ if (protocol == 0) { return RADIO_INVALID_PARAM; /* Not allowed, protocol id must be greater zero */ } cc1100_phy_mutex_lock(); /* Lock radio for exclusive access */ /* Get carrier sense timeout based on overall error rate till now */ send_csmaca_calls++; int fail_percentage = (send_csmaca_calls_cs_timeout * 100) / send_csmaca_calls; if (fail_percentage == 0) { fail_percentage = 1; } cs_timeout = CARRIER_SENSE_TIMEOUT / fail_percentage; if (cs_timeout < CARRIER_SENSE_TIMEOUT_MIN) { cs_timeout = CARRIER_SENSE_TIMEOUT_MIN; } cc1100_cs_init(); /* Initialize carrier sensing */ window: if (backoff != 0) { goto cycle; /* If backoff was 0 */ } windowSize *= 2; /* ...double the current window size */ if (windowSize > max_window_size) { windowSize = max_window_size; /* This is the maximum size allowed */ } backoff = rand() % windowSize; /* ...and choose new backoff */ backoff += (uint16_t) 1; cycle: cs_timeout_flag = 0; /* Carrier sense timeout flag */ cs_hwtimer_id = hwtimer_set(cs_timeout, /* Set hwtimer to set CS timeout flag */ cs_timeout_cb, NULL); while (cc1100_cs_read()) { /* Wait until air is free */ if (cs_timeout_flag) { send_csmaca_calls_cs_timeout++; #ifndef CSMACA_MAC_AGGRESSIVE_MODE cc1100_phy_mutex_unlock(); cc1100_go_after_tx(); /* Go from RX to default mode */ return RADIO_CS_TIMEOUT; /* Return immediately */ #endif #ifdef CSMACA_MAC_AGGRESSIVE_MODE goto send; /* Send anyway */ #endif } } hwtimer_remove(cs_hwtimer_id); /* Remove hwtimer */ cc1100_cs_write_cca(1); /* Air is free now */ cc1100_cs_set_enabled(true); if (cc1100_cs_read()) { goto window; /* GDO0 triggers on rising edge, so */ } /* test once after interrupt is enabled */ if (backoff > 0) { backoff--; /* Decrement backoff counter */ } total = slottime; /* Calculate total wait time */ total *= (uint32_t)backoff; /* Slot vector set */ total += difs; /* ...and standard DIFS wait time */ cs_timeout_flag = 0; /* Carrier sense timeout flag */ cs_hwtimer_id = hwtimer_set(total, /* Set hwtimer to set CS timeout flag */ cs_timeout_cb, NULL); while (!cs_timeout_flag || !cc1100_cs_read_cca()) { /* Wait until timeout is finished */ if (cc1100_cs_read_cca() == 0) { /* Is the air still free? */ hwtimer_remove(cs_hwtimer_id); goto window; /* No. Go back to new wait period. */ } } cc1100_cs_set_enabled(false); #ifdef CSMACA_MAC_AGGRESSIVE_MODE send: #endif int res = cc1100_send(address, protocol, priority, payload, payload_len); if (res < 0) { collision_count++; } return res; }
int cc1100_send(radio_address_t addr, protocol_t protocol, int priority, char *payload, int payload_len) { bool result; int return_code; uint8_t address; uint8_t retries; /* Lock mutex, nobody else should send now */ cc1100_phy_mutex_lock(); /* TX state machine lock -> no timers (WOR), no packets (only ACKs) */ rflags.TX = true; /* Set chip to idle state */ cc1100_set_idle(); /* CC1100 radio layer only supports 8-bit addresses */ address = addr; /* Loopback not supported */ if (address == cc1100_get_address()) { return_code = RADIO_ADDR_OUT_OF_RANGE; goto mode_before_final; } /* Check address */ if (address > MAX_UID) { return_code = RADIO_ADDR_OUT_OF_RANGE; goto mode_before_final; } /* Packet too long */ if (payload_len > MAX_DATA_LENGTH) { return_code = RADIO_PAYLOAD_TOO_LONG; goto mode_before_final; } if (radio_state == RADIO_PWD) { return_code = RADIO_WRONG_MODE; goto mode_before_final; } /* Set number of transmission retries */ retries = (address == CC1100_BROADCAST_ADDRESS) ? cc1100_retransmission_count_bc : cc1100_retransmission_count_uc; memset(tx_buffer.data, 0, MAX_DATA_LENGTH); /* Clean data */ /* TODO: If packets are shorter than max packet size, WOR interval is too long. * This must be solved in some way. */ tx_buffer.length = 3 + payload_len; /* 3 bytes (A&PS&F) + data length */ tx_buffer.address = address; /* Copy destination address */ tx_buffer.flags = 0x00; /* Set clean state */ tx_buffer.flags = W_FLAGS_PROTOCOL(protocol); /* Copy protocol identifier */ tx_buffer.phy_src = (uint8_t) cc1100_get_address(); /* Copy sender address */ /* Set identification number of packet */ tx_buffer.flags |= rflags.SEQ; /* Set flags.identification (bit 0) */ rflags.SEQ = !rflags.SEQ; /* Toggle value of layer 0 sequence number bit */ memcpy(tx_buffer.data, payload, payload_len); /* Copy data */ /* Send the packet */ cc1100_spi_write_reg(CC1100_MCSM0, 0x08); /* Turn off FS-Autocal */ result = send_burst(&tx_buffer, retries, 0); /* Send raw burst */ return_code = result ? payload_len : RADIO_OP_FAILED; /* Collect statistics */ if (address != CC1100_BROADCAST_ADDRESS) { cc1100_statistic.packets_out++; if (result) { cc1100_statistic.packets_out_acked++; } } else { cc1100_statistic.packets_out_broadcast++; } goto final; mode_before_final: rflags.TX = false; /* Definitely set secure mode (CONST_RX -> RX, WOR -> WOR) */ cc1100_go_after_tx(); final:
/*---------------------------------------------------------------------------*/ int cc1100_send_csmaca(radio_address_t address, protocol_t protocol, int priority, char *payload, int payload_len) { uint16_t min_window_size; uint16_t max_window_size; uint16_t difs; uint16_t slottime; switch (priority) { case PRIORITY_ALARM: min_window_size = PRIO_ALARM_MIN_WINDOW_SIZE; max_window_size = PRIO_ALARM_MAX_WINDOW_SIZE; difs = PRIO_ALARM_DIFS; slottime = PRIO_ALARM_SLOTTIME; break; case PRIORITY_WARNING: min_window_size = PRIO_WARN_MIN_WINDOW_SIZE; max_window_size = PRIO_WARN_MAX_WINDOW_SIZE; difs = PRIO_WARN_DIFS; slottime = PRIO_WARN_SLOTTIME; break; default: min_window_size = PRIO_DATA_MIN_WINDOW_SIZE; max_window_size = PRIO_DATA_MAX_WINDOW_SIZE; difs = PRIO_DATA_DIFS; slottime = PRIO_DATA_SLOTTIME; } // Calculate collisions per second if (collision_state == COLLISION_STATE_INITIAL) { timex_t now = vtimer_now(); collision_measurement_start = now.microseconds; collision_count = 0; collisions_per_sec = 0; collision_state = COLLISION_STATE_MEASURE; } else if (collision_state == COLLISION_STATE_MEASURE) { timex_t now = vtimer_now(); uint64_t timespan = now.microseconds - collision_measurement_start; if (timespan > 1000000) { collisions_per_sec = (collision_count * 1000000) / (double) timespan; if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) { timex_t now = vtimer_now(); collision_measurement_start = now.microseconds; collision_state = COLLISION_STATE_KEEP; } else if (collisions_per_sec > 2.2) { timex_t now = vtimer_now(); collision_measurement_start = now.microseconds; collision_state = COLLISION_STATE_KEEP; } else { collision_state = COLLISION_STATE_INITIAL; } } } else if (collision_state == COLLISION_STATE_KEEP) { timex_t now = vtimer_now(); uint64_t timespan = now.microseconds - collision_measurement_start; if (timespan > 5000000) { collision_state = COLLISION_STATE_INITIAL; } } // Adjust initial window size according to collision rate if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) { min_window_size *= 2; } else if (collisions_per_sec > 2.2) { min_window_size *= 4; } uint16_t windowSize = min_window_size; // Start with window size of PRIO_XXX_MIN_WINDOW_SIZE uint16_t backoff = 0; // Backoff between 1 and windowSize uint32_t total; // Holds the total wait time before send try uint32_t cs_timeout; // Current carrier sense timeout value if (protocol == 0) { return RADIO_INVALID_PARAM; // Not allowed, protocol id must be greater zero } cc1100_phy_mutex_lock(); // Lock radio for exclusive access // Get carrier sense timeout based on overall error rate till now send_csmaca_calls++; int fail_percentage = (send_csmaca_calls_cs_timeout * 100) / send_csmaca_calls; if (fail_percentage == 0) fail_percentage = 1; cs_timeout = CARRIER_SENSE_TIMEOUT / fail_percentage; if (cs_timeout < CARRIER_SENSE_TIMEOUT_MIN) cs_timeout = CARRIER_SENSE_TIMEOUT_MIN; cc1100_cs_init(); // Initialize carrier sensing window: if (backoff != 0) goto cycle; // If backoff was 0 windowSize *= 2; // ...double the current window size if (windowSize > max_window_size) { windowSize = max_window_size; // This is the maximum size allowed } backoff = rand() % windowSize; // ...and choose new backoff if (backoff < 0) backoff *= -1; backoff += (uint16_t) 1; cycle: cs_timeout_flag = 0; // Carrier sense timeout flag cs_hwtimer_id = hwtimer_set(cs_timeout, // Set hwtimer to set CS timeout flag cs_timeout_cb, NULL); while (cc1100_cs_read()) // Wait until air is free { if (cs_timeout_flag) { send_csmaca_calls_cs_timeout++; #ifndef CSMACA_MAC_AGGRESSIVE_MODE cc1100_phy_mutex_unlock(); cc1100_go_after_tx(); // Go from RX to default mode return RADIO_CS_TIMEOUT; // Return immediately #endif #ifdef CSMACA_MAC_AGGRESSIVE_MODE goto send; // Send anyway #endif } } hwtimer_remove(cs_hwtimer_id); // Remove hwtimer cc1100_cs_write_cca(1); // Air is free now cc1100_cs_set_enabled(true); if (cc1100_cs_read()) goto window; // GDO0 triggers on rising edge, so // test once after interrupt is enabled if (backoff > 0) backoff--; // Decrement backoff counter total = slottime; // Calculate total wait time total *= (uint32_t)backoff; // Slot vector set total += difs; // ...and standard DIFS wait time cs_timeout_flag = 0; // Carrier sense timeout flag cs_hwtimer_id = hwtimer_set(total, // Set hwtimer to set CS timeout flag cs_timeout_cb, NULL); while (!cs_timeout_flag || !cc1100_cs_read_cca()) // Wait until timeout is finished { if (cc1100_cs_read_cca() == 0) // Is the air still free? { hwtimer_remove(cs_hwtimer_id); goto window; // No. Go back to new wait period. } } cc1100_cs_set_enabled(false); #ifdef CSMACA_MAC_AGGRESSIVE_MODE send: #endif int res = cc1100_send(address, protocol, priority, payload, payload_len); if (res < 0) { collision_count++; } return res; }