static void switch_to_idle_mode()
{
    //Flush FIFOs and go to sleep, ensure interrupts are disabled
    current_state = HW_RADIO_STATE_IDLE;
    cc1101_interface_set_interrupts_enabled(false);
    cc1101_interface_strobe(RF_SFRX); // TODO cc1101 datasheet : Only issue SFRX in IDLE or RXFIFO_OVERFLOW states
    cc1101_interface_strobe(RF_SFTX); // TODO cc1101 datasheet : Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states.
    cc1101_interface_strobe(RF_SIDLE);
    cc1101_interface_strobe(RF_SPWD);
}
void start_radio(){
#if defined USE_SI4460
#ifdef HAS_LCD
    lcd_write_string("Start sending \n");
#endif
#elif defined USE_CC1101
    cc1101_interface_strobe(RF_SCAL);
    cc1101_interface_strobe(RF_STX);
#endif
}
void configure_radio(modulation_t mod){
#if defined USE_SI4460 || defined USE_SX127X
    if(mod == MODULATION_CW){
        hw_radio_continuous_tx(&tx_cfg, true);
    } else {
        hw_radio_continuous_tx(&tx_cfg, false);
    }

#elif defined USE_CC1101

    hw_radio_set_rx(&tx_cfg, NULL, NULL); // we 'misuse' hw_radio_set_rx to configure the channel (using the public API)
    hw_radio_set_idle(); // go straight back to idle

    /* Configure */
    cc1101_interface_write_single_patable(current_eirp_level);
    //cc1101_interface_write_single_reg(0x08, 0x22); // PKTCTRL0 random PN9 mode + disable data whitening
    cc1101_interface_write_single_reg(0x08, 0x22); // PKTCTRL0 disable data whitening, continious preamble
    if(mod == MODULATION_CW) {
      cc1101_interface_write_single_reg(0x12, 0x30); // MDMCFG2
    } else {
      cc1101_interface_write_single_reg(0x12, 0x12); // MDMCFG2
    }

    cc1101_interface_strobe(0x32); // strobe calibrate
#endif
}
void stop_radio(){
#if defined USE_SI4460
    // stop sending signal
    ezradio_change_state(EZRADIO_CMD_CHANGE_STATE_ARG_NEXT_STATE1_NEW_STATE_ENUM_READY);
#elif defined USE_CC1101
    cc1101_interface_strobe(RF_SIDLE);
#endif
}
void main()
{
	system_init(buffer, sizeof(buffer), buffer, sizeof(buffer));
	phy_init();

	int p = cc1101_interface_read_single_reg(PARTNUM);
	p = cc1101_interface_read_single_reg(VERSION);

    cc1101_interface_strobe(RF_STX);

	while(1);
}
static void start_rx(hw_rx_cfg_t const* rx_cfg)
{
    current_state = HW_RADIO_STATE_RX;

    cc1101_interface_strobe(RF_SFRX);
    configure_channel(&(rx_cfg->channel_id));
    configure_syncword_class(rx_cfg->syncword_class);
    cc1101_interface_write_single_reg(PKTLEN, 0xFF);

    if(rx_packet_callback != 0) // when rx callback not set we ignore received packets
        cc1101_interface_set_interrupts_enabled(true);

    cc1101_interface_strobe(RF_SRX);

    if(rssi_valid_callback != 0)
    {
        // TODO calculate/predict rssi response time (see DN505)
        // and wait until valid. For now we wait 200 us.

        hw_busy_wait(200);
        rssi_valid_callback(hw_radio_get_rssi());
    }
}
error_t hw_radio_send_packet(hw_radio_packet_t* packet, tx_packet_callback_t tx_cb)
{
    // TODO error handling EINVAL, ESIZE, EOFF
    if(current_state == HW_RADIO_STATE_TX)
        return EBUSY;

    assert(packet->length < 63); // long packets not yet supported

    tx_packet_callback = tx_cb;

    if(current_state == HW_RADIO_STATE_RX)
    {
        pending_rx_cfg.channel_id = current_channel_id;
        pending_rx_cfg.syncword_class = current_syncword_class;
        should_rx_after_tx_completed = true;
    }

    current_state = HW_RADIO_STATE_TX;
    current_packet = packet;
    cc1101_interface_strobe(RF_SIDLE);
    cc1101_interface_strobe(RF_SFTX);

#ifdef FRAMEWORK_LOG_ENABLED // TODO more granular
    log_print_stack_string(LOG_STACK_PHY, "Data to TX Fifo:");
    log_print_data(packet->data, packet->length + 1);
#endif

    configure_channel((channel_id_t*)&(current_packet->tx_meta.tx_cfg.channel_id));
    configure_eirp(current_packet->tx_meta.tx_cfg.eirp);
    configure_syncword_class(current_packet->tx_meta.tx_cfg.syncword_class);

    cc1101_interface_write_burst_reg(TXFIFO, packet->data, packet->length + 1);
    cc1101_interface_set_interrupts_enabled(true);
    cc1101_interface_strobe(RF_STX);
    return SUCCESS;
}
void start()
{
    hw_rx_cfg_t rx_cfg;
    rx_cfg.channel_id.channel_header.ch_coding = PHY_CODING_PN9;
    rx_cfg.channel_id.channel_header.ch_class = current_channel_class;
    rx_cfg.channel_id.channel_header.ch_freq_band = current_channel_band;
    rx_cfg.channel_id.center_freq_index = channel_indexes[current_channel_indexes_index];

#ifdef HAS_LCD
    char string[10] = "";
    char rate;
    char band[3];
    switch(current_channel_class)
    {
        case PHY_CLASS_LO_RATE: rate = 'L'; break;
        case PHY_CLASS_NORMAL_RATE: rate = 'N'; break;
        case PHY_CLASS_HI_RATE: rate = 'H'; break;
    }

    switch(current_channel_band)
    {
        case PHY_BAND_433: strncpy(band, "433", sizeof(band)); break;
        case PHY_BAND_868: strncpy(band, "868", sizeof(band)); break;
        case PHY_BAND_915: strncpy(band, "915", sizeof(band)); break;
    }

    sprintf(string, "%.3s%c-%i", band, rate, rx_cfg.channel_id.center_freq_index),
    lcd_write_string(string);
#endif

    hw_radio_set_rx(&rx_cfg, NULL, &rssi_valid_cb); // we 'misuse' hw_radio_set_rx to configure the channel (using the public API)
    hw_radio_set_idle(); // go straight back to idle

    cc1101_interface_write_single_reg(0x08, 0x22); // PKTCTRL0 random PN9 mode + disable data whitening
    //cc1101_interface_write_single_reg(0x12, 0x30); // MDMCFG2: use OOK modulation to clearly view centre freq on spectrum analyzer, comment for GFSK
    cc1101_interface_write_single_patable(0xc0); // 10dBm TX EIRP
    cc1101_interface_strobe(0x35); // strobe TX
}
static void configure_channel(const channel_id_t* channel_id)
{
    // only change settings if channel_id changed compared to current config
    if(!hw_radio_channel_ids_equal(channel_id, &current_channel_id))
    {
        assert(channel_id->channel_header.ch_coding == PHY_CODING_PN9); // TODO implement other codings
        // TODO assert valid center freq index

        memcpy(&current_channel_id, channel_id, sizeof(channel_id_t)); // cache new settings

        // TODO preamble size depends on channel class

        // set freq band
        DPRINT("Set frequency band index: %d", channel_id->channel_header.ch_freq_band);

        // TODO validate
        switch(channel_id->channel_header.ch_freq_band)
        {
        // TODO calculate depending on rate and channr
        case PHY_BAND_433:
            cc1101_interface_write_single_reg(FREQ2, RADIO_FREQ2(RADIO_FREQ_433_NORMAL_RATE));
            cc1101_interface_write_single_reg(FREQ1, RADIO_FREQ1(RADIO_FREQ_433_NORMAL_RATE));
            cc1101_interface_write_single_reg(FREQ0, RADIO_FREQ0(RADIO_FREQ_433_NORMAL_RATE));
            break;
        case PHY_BAND_868_1: // TODO 868_2
            cc1101_interface_write_single_reg(FREQ2, RADIO_FREQ2(RADIO_FREQ_868_NORMAL_RATE));
            cc1101_interface_write_single_reg(FREQ1, RADIO_FREQ2(RADIO_FREQ_868_NORMAL_RATE));
            cc1101_interface_write_single_reg(FREQ0, RADIO_FREQ2(RADIO_FREQ_868_NORMAL_RATE));
            break;
        case PHY_BAND_915_1: // TODO 915_x
            assert(false);
//            WriteSingleReg(RADIO_FREQ2, (uint8_t)(RADIO_FREQ_915>>16 & 0xFF));
//            WriteSingleReg(RADIO_FREQ1, (uint8_t)(RADIO_FREQ_915>>8 & 0xFF));
//            WriteSingleReg(RADIO_FREQ0, (uint8_t)(RADIO_FREQ_915 & 0xFF));
            break;
        }

        // set channel center frequency
        DPRINT("Set channel freq index: %d", channel_id->center_freq_index);
        cc1101_interface_write_single_reg(CHANNR, channel_id->center_freq_index); // TODO validate

        // set modulation, symbol rate and deviation
        switch(channel_id->channel_header.ch_class)
        {
            case PHY_CLASS_NORMAL_RATE:
                // TODO validate
                cc1101_interface_write_single_reg(MDMCFG3, RADIO_MDMCFG3_DRATE_M_NORMAL_RATE);
                cc1101_interface_write_single_reg(MDMCFG4, RADIO_MDMCFG4_NORMAL_RATE);
                cc1101_interface_write_single_reg(DEVIATN, RADIO_DEVIATN_NORMAL_RATE);
                break;
            case PHY_CLASS_LO_RATE:
                cc1101_interface_write_single_reg(MDMCFG3, RADIO_MDMCFG3_DRATE_M_LOW_RATE);
                cc1101_interface_write_single_reg(MDMCFG4, RADIO_MDMCFG4_LOW_RATE);
                cc1101_interface_write_single_reg(DEVIATN, RADIO_DEVIATN_LOW_RATE);
                break;
            default:
                assert(false);
                // TODO: other classes
        }
    }

    cc1101_interface_strobe(RF_SCAL); // TODO is this the right case?
}
static void end_of_packet_isr()
{
    cc1101_interface_set_interrupts_enabled(false);
    DPRINT("end of packet ISR");
    switch(current_state)
    {
        case HW_RADIO_STATE_RX: ;
            uint8_t packet_len = cc1101_interface_read_single_reg(RXFIFO);
            DPRINT("EOP ISR packetLength: %d", packet_len);
            if(packet_len >= 63)
            {
            	// long packets not yet supported or bit error in length byte, don't assert but flush rx
                DPRINT("Packet size too big, flushing RX");
                uint8_t status = (cc1101_interface_strobe(RF_SNOP) & 0xF0);
                if(status == 0x60)
                {
                    // RX overflow
                    cc1101_interface_strobe(RF_SFRX);
                }
                else if(status == 0x10)
                {
                    // still in RX, switch to idle first
                    cc1101_interface_strobe(RF_SIDLE);
                }

                while(cc1101_interface_strobe(RF_SNOP) != 0x0F); // wait until in idle state
                cc1101_interface_strobe(RF_SRX);
                while(cc1101_interface_strobe(RF_SNOP) != 0x1F); // wait until in RX state
                cc1101_interface_set_interrupts_enabled(true);
                return;
            }

            hw_radio_packet_t* packet = alloc_packet_callback(packet_len);
            packet->length = packet_len;
            cc1101_interface_read_burst_reg(RXFIFO, packet->data + 1, packet->length);

            // fill rx_meta
            packet->rx_meta.rssi = convert_rssi(cc1101_interface_read_single_reg(RXFIFO));
            packet->rx_meta.lqi = cc1101_interface_read_single_reg(RXFIFO) & 0x7F;
            memcpy(&(packet->rx_meta.rx_cfg.channel_id), &current_channel_id, sizeof(channel_id_t));
            packet->rx_meta.crc_status = HW_CRC_UNAVAILABLE; // TODO
            packet->rx_meta.timestamp = timer_get_counter_value();

#ifdef FRAMEWORK_LOG_ENABLED
            log_print_raw_phy_packet(packet, false);
#endif

            rx_packet_callback(packet);
            if(current_state == HW_RADIO_STATE_RX) // check still in RX, could be modified by upper layer while in callback
            {
                uint8_t status = (cc1101_interface_strobe(RF_SNOP) & 0xF0);
                if(status == 0x60) // RX overflow
                {
                    cc1101_interface_strobe(RF_SFRX);
                    while(cc1101_interface_strobe(RF_SNOP) != 0x0F); // wait until in idle state
                    cc1101_interface_strobe(RF_SRX);
                    while(cc1101_interface_strobe(RF_SNOP) != 0x1F); // wait until in RX state
                }

                cc1101_interface_set_interrupts_enabled(true);
                assert(cc1101_interface_strobe(RF_SNOP) == 0x1F); // expect to be in RX mode
            }
            break;
        case HW_RADIO_STATE_TX:
        	if(!should_rx_after_tx_completed)
        		switch_to_idle_mode();

            current_packet->tx_meta.timestamp = timer_get_counter_value();

#ifdef FRAMEWORK_LOG_ENABLED
            log_print_raw_phy_packet(current_packet, true);
#endif

            if(tx_packet_callback != 0)
                tx_packet_callback(current_packet);

            if(should_rx_after_tx_completed)
            {
                // RX requested while still in TX ...
                // TODO this could probably be further optimized by not going into IDLE
                // after RX by setting TXOFF_MODE to RX (if the cfg is the same at least)
                should_rx_after_tx_completed = false;
                start_rx(&pending_rx_cfg);
            }
            break;
        default:
            assert(false);
    }
}