error_t hw_radio_send_packet(hw_radio_packet_t* packet, tx_packet_callback_t tx_cb, uint16_t eta, uint8_t dll_header_bg_frame[2])
{
  assert(eta == 0); // advertising not implemented on si4460 for now

	// TODO error handling EINVAL, ESIZE, EOFF
	if(current_state == HW_RADIO_STATE_TX)
		return EBUSY;

	uint8_t data_length = packet->length + 1;

	if (packet->tx_meta.tx_cfg.channel_id.channel_header.ch_coding == PHY_CODING_FEC_PN9)
	{

		DPRINT("Original packet: %d", data_length);
		DPRINT_DATA(packet->data, data_length);

		data_length = fec_encode(packet->data, data_length);
		DPRINT("Encoded packet: %d", data_length);
		DPRINT_DATA(packet->data, data_length);

	} else {
		if (has_hardware_crc)
			data_length -= 2;
	}

	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;


	DPRINT("Data to TX Fifo:");
	DPRINT_DATA(packet->data, data_length);
        DPRINT("TX ch header=%x, ind=%i",
               packet->tx_meta.tx_cfg.channel_id.channel_header_raw,
               packet->tx_meta.tx_cfg.channel_id.center_freq_index);

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

	DEBUG_TX_START();
	DEBUG_RX_END();

	ezradioStartTx(packet, ez_channel_id, should_rx_after_tx_completed, data_length);
	return SUCCESS;
}
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;

	uint8_t data_length = packet->length + 1;

	if (packet->tx_meta.tx_cfg.channel_id.channel_header.ch_coding == PHY_CODING_FEC_PN9)
	{

		DPRINT(LOG_STACK_PHY, "Original packet: %d", data_length);
		DPRINT_DATA(packet->data, data_length);

		data_length = fec_encode(packet->data, data_length);
		DPRINT(LOG_STACK_PHY, "Encoded packet: %d", data_length);
		DPRINT_DATA(packet->data, data_length);

	} else {
		if (has_hardware_crc)
			data_length -= 2;
	}

	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;


	DPRINT(LOG_STACK_PHY, "Data to TX Fifo:");
	DPRINT_DATA(packet->data, data_length);


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

	DEBUG_TX_START();
	DEBUG_RX_END();

	ezradioStartTx(packet, ez_channel_id, should_rx_after_tx_completed, data_length);
	return SUCCESS;
}
static void process_serial_frame(fifo_t* fifo) {
  bool command_completed = false;
  bool completed_with_error = false;
  while(fifo_get_size(fifo)) {
    alp_action_t action;
    alp_parse_action(fifo, &action);

    switch(action.operation) {
      case ALP_OP_RETURN_TAG:
        if(action.tag_response.tag_id == command.tag_id) {
          command_completed = action.tag_response.completed;
          completed_with_error = action.tag_response.error;
        } else {
          DPRINT("received resp with unexpected tag_id (%i vs %i)", action.tag_response.tag_id, command.tag_id);
          // TODO unsolicited responses
        }
        break;
      case ALP_OP_WRITE_FILE_DATA:
        if(callbacks->write_file_data_callback)
          callbacks->write_file_data_callback(action.file_data_operand.file_offset.file_id,
                                               action.file_data_operand.file_offset.offset,
                                               action.file_data_operand.provided_data_length,
                                               action.file_data_operand.data);
        break;
      case ALP_OP_RETURN_FILE_DATA:
        if(callbacks->return_file_data_callback)
          callbacks->return_file_data_callback(action.file_data_operand.file_offset.file_id,
                                               action.file_data_operand.file_offset.offset,
                                               action.file_data_operand.provided_data_length,
                                               action.file_data_operand.data);
        break;
      case ALP_OP_RETURN_STATUS:
        if(action.status.type==ALP_ITF_ID_D7ASP)
        {
          d7ap_session_result_t interface_status = *((d7ap_session_result_t*)action.status.data);
          uint8_t addressee_len = d7ap_addressee_id_length(interface_status.addressee.ctrl.id_type);
          DPRINT("received resp from: ");
          DPRINT_DATA(interface_status.addressee.id, addressee_len);
        }
        if(callbacks->modem_interface_status_callback)
          callbacks->modem_interface_status_callback(action.status.type,&action.status.data);
        break;
      default:
        assert(false);
    }
  }


  if(command_completed) {
    DPRINT("command with tag %i completed @ %i", command.tag_id, timer_get_counter_value());
    if(callbacks->command_completed_callback)
      callbacks->command_completed_callback(completed_with_error,command.tag_id);

    command.is_active = false;
  }

}
static void process_rx_fifo(void *arg) {
  if(!parsed_header) {
    // <sync byte (0xC0)><version (0x00)><length of ALP command (1 byte)><ALP command> // TODO CRC
    if(fifo_get_size(&rx_fifo) > SERIAL_ALP_FRAME_HEADER_SIZE) {
        uint8_t header[SERIAL_ALP_FRAME_HEADER_SIZE];
        fifo_peek(&rx_fifo, header, 0, SERIAL_ALP_FRAME_HEADER_SIZE);
        DPRINT_DATA(header, 3); // TODO tmp

        if(header[0] != SERIAL_ALP_FRAME_SYNC_BYTE || header[1] != SERIAL_ALP_FRAME_VERSION) {
          fifo_skip(&rx_fifo, 1);
          DPRINT("skip");
          parsed_header = false;
          payload_len = 0;
          if(fifo_get_size(&rx_fifo) > SERIAL_ALP_FRAME_HEADER_SIZE)
            sched_post_task(&process_rx_fifo);

          return;
        }

        parsed_header = true;
        fifo_skip(&rx_fifo, SERIAL_ALP_FRAME_HEADER_SIZE);
        payload_len = header[2];
        DPRINT("found header, payload size = %i", payload_len);
        sched_post_task(&process_rx_fifo);
    }
  } else {
    if(fifo_get_size(&rx_fifo) < payload_len) {
      DPRINT("payload not complete yet");
      return;
    }

    // payload complete, start parsing
    // rx_fifo can be bigger than the current serial packet, init a subview fifo
    // which is restricted to payload_len so we can't parse past this packet.
    fifo_t payload_fifo;
    fifo_init_subview(&payload_fifo, &rx_fifo, 0, payload_len);
    process_serial_frame(&payload_fifo);

    // pop parsed bytes from original fifo
    fifo_skip(&rx_fifo, payload_len - fifo_get_size(&payload_fifo));
    parsed_header = false;
  }
}
static void ezradio_handle_end_of_packet()
{
	// fill rx_meta
	rx_packet->rx_meta.rssi = hw_radio_get_latched_rssi();
	rx_packet->rx_meta.lqi = 0;
	memcpy(&(rx_packet->rx_meta.rx_cfg.channel_id), &current_channel_id, sizeof(channel_id_t));
	if (has_hardware_crc && current_rx_cfg.channel_id.channel_header.ch_coding != PHY_CODING_FEC_PN9)
		rx_packet->rx_meta.crc_status = HW_CRC_VALID;
	else
		rx_packet->rx_meta.crc_status = HW_CRC_UNAVAILABLE;

	rx_packet->rx_meta.timestamp = timer_get_counter_value();
	//memcpy((void*)rx_packet->rx_meta.rx_cfg, (void*)&current_rx_cfg, sizeof(hw_rx_metadata_t));

	ezradio_fifo_info(EZRADIO_CMD_FIFO_INFO_ARG_FIFO_RX_BIT, NULL);


	DPRINT_DATA(rx_packet->data, rx_packet->length+1);


	if (current_rx_cfg.channel_id.channel_header.ch_coding == PHY_CODING_FEC_PN9)
	{
		fec_decode_packet(rx_packet->data, rx_packet->length, rx_packet->length);
		//assert length and data[0] can only differ 1
		rx_packet->length =  rx_packet->data[0];
	}

	DPRINT_PACKET(rx_packet, false);


	DEBUG_RX_END();

//					if(rx_packet_callback != NULL) // TODO this can happen while doing CCA but we should not be interrupting here (disable packet handler?)
	rx_packet_callback(rx_packet);
//					else
//						release_packet_callback(packet);

	if(current_state == HW_RADIO_STATE_RX)
	{
		start_rx(&current_rx_cfg);
	}
}
static void ezradio_int_callback()
{
	//DPRINT("ezradio ISR");

	ezradio_cmd_reply_t ezradioReply;
	ezradio_cmd_reply_t radioReplyLocal;
	ezradio_get_int_status(0x0, 0x0, 0x0, &ezradioReply);
	//ezradio_frr_a_read(3, &ezradioReply);

	//DPRINT(" - INT_PEND     %s", byte_to_binary(ezradioReply.FRR_A_READ.FRR_A_VALUE));
  DPRINT(" - INT_PEND     %s", byte_to_binary(ezradioReply.GET_INT_STATUS.INT_PEND));
//	DPRINT(" - INT_STATUS   %s", byte_to_binary(ezradioReply.GET_INT_STATUS.INT_STATUS));
//	DPRINT(" - PH_PEND      %s", byte_to_binary(ezradioReply.GET_INT_STATUS.PH_PEND));
	//DPRINT(" - PH_STATUS    %s", byte_to_binary(ezradioReply.GET_INT_STATUS.PH_STATUS));
	//DPRINT(" - PH_STATUS    %s", byte_to_binary(ezradioReply.FRR_A_READ.FRR_B_VALUE));
//	DPRINT(" - MODEM_PEND   %s", byte_to_binary(ezradioReply.GET_INT_STATUS.MODEM_PEND));
//	DPRINT(" - MODEM_STATUS %s", byte_to_binary(ezradioReply.GET_INT_STATUS.MODEM_STATUS));
//	DPRINT(" - CHIP_PEND    %s", byte_to_binary(ezradioReply.GET_INT_STATUS.CHIP_PEND));
//	DPRINT(" - CHIP_STATUS  %s", byte_to_binary(ezradioReply.GET_INT_STATUS.CHIP_STATUS));

	//if (ezradioReply.FRR_A_READ.FRR_A_VALUE & EZRADIO_CMD_GET_INT_STATUS_REP_INT_PEND_PH_INT_PEND_BIT)
	if (ezradioReply.GET_INT_STATUS.INT_PEND & EZRADIO_CMD_GET_INT_STATUS_REP_INT_PEND_PH_INT_PEND_BIT)
	{

		//DPRINT("PH ISR");
		switch(current_state)
		{
			case HW_RADIO_STATE_RX:
				if ((ezradioReply.GET_INT_STATUS.PH_STATUS & EZRADIO_CMD_GET_INT_STATUS_REP_PH_STATUS_PACKET_RX_BIT) ||
						(ezradioReply.GET_INT_STATUS.PH_STATUS & EZRADIO_CMD_GET_INT_STATUS_REP_PH_STATUS_RX_FIFO_ALMOST_FULL_BIT))
				{
					//DPRINT("PACKET_RX IRQ");

					if(rx_packet_callback != NULL)
					{
						/* Check how many bytes we received. */
						ezradio_fifo_info(0, &radioReplyLocal);

						DPRINT("RX ISR packetLength: %d", radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT);

						if (rx_fifo_data_lenght == 0)
						{
							DPRINT("RX FIFO: %d", radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT);
							uint8_t buffer[4];
							ezradio_read_rx_fifo(4, buffer);
							if (current_rx_cfg.channel_id.channel_header.ch_coding == PHY_CODING_FEC_PN9)
							{
								uint8_t fec_buffer[4];
								memcpy(fec_buffer, buffer, 4);
								fec_decode_packet(fec_buffer, 4, 4);
								expected_data_length = fec_calculated_decoded_length(fec_buffer[0]+1);
								DPRINT("RX Packet Length: %d / %d", fec_buffer[0], expected_data_length);
							} else {
								expected_data_length = buffer[0] + 1;
							}
							rx_packet = alloc_packet_callback(expected_data_length);
							memcpy(rx_packet->data, buffer, 4);
							rx_fifo_data_lenght += 4;
							radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT-=4;
						}

						if (ezradioReply.GET_INT_STATUS.PH_STATUS & EZRADIO_CMD_GET_INT_STATUS_REP_PH_STATUS_PACKET_RX_BIT)
						{

							/* Read out the RX FIFO content. */
							ezradio_read_rx_fifo(radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT, &(rx_packet->data[rx_fifo_data_lenght]));
							//ezradio_read_rx_fifo(radioReplyLocal2.PACKET_INFO.LENGTH, packet->data);

							ezradio_handle_end_of_packet();
							return;
						}

						if (ezradioReply.GET_INT_STATUS.PH_STATUS & EZRADIO_CMD_GET_INT_STATUS_REP_PH_STATUS_RX_FIFO_ALMOST_FULL_BIT)
						{
							while (radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT > 0)
							{
								DPRINT("RX FIFO: %d", radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT);
								/* Read out the FIFO Count bytes of RX FIFO */
								ezradio_read_rx_fifo(radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT, &(rx_packet->data[rx_fifo_data_lenght]));
								rx_fifo_data_lenght += radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT;
								//DPRINT("%d of %d bytes collected", rx_fifo_data_lenght, rx_packet->data[0]+1);
								ezradio_fifo_info(0, &radioReplyLocal);

								if (rx_fifo_data_lenght >= expected_data_length)
								{
									ezradio_handle_end_of_packet();
									return;
								}
							}

							ezradio_int_callback();

							return;
						}

					}
				} else if (ezradioReply.GET_INT_STATUS.PH_STATUS & EZRADIO_CMD_GET_INT_STATUS_REP_PH_STATUS_CRC_ERROR_BIT)

				//} else if ( ezradioReply.FRR_A_READ.FRR_B_VALUE & EZRADIO_CMD_GET_INT_STATUS_REP_PH_STATUS_CRC_ERROR_BIT)
				{
					DPRINT("- PACKET_RX CRC_ERROR IRQ");
					/* Check how many bytes we received. */
					ezradio_fifo_info(0, &radioReplyLocal);
					DPRINT("RX ISR packetLength: %d", radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT);

					//ezradio_cmd_reply_t radioReplyLocal2;
					//ezradio_get_packet_info(0, 0, 0, &radioReplyLocal2);

					if (rx_fifo_data_lenght == 0)
					{
						rx_packet = alloc_packet_callback(radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT);
					}

					/* Read out the RX FIFO content. */
					ezradio_read_rx_fifo(radioReplyLocal.FIFO_INFO.RX_FIFO_COUNT, &(rx_packet->data[rx_fifo_data_lenght]));

					rx_packet->rx_meta.crc_status = HW_CRC_INVALID;
					rx_packet->length = rx_packet->data[0] + 1;


          DPRINT_DATA(rx_packet->data, rx_packet->length);


					DEBUG_RX_END();

					if(rx_packet_callback != NULL)
						rx_packet_callback(rx_packet);
					else
						release_packet_callback(rx_packet);

					if(current_state == HW_RADIO_STATE_RX)
					{
						start_rx(&current_rx_cfg);
					}

				} else {
					DPRINT((" - OTHER RX IRQ"));
				}



				break;
			case HW_RADIO_STATE_TX:
				if (ezradioReply.GET_INT_STATUS.PH_PEND & EZRADIO_CMD_GET_INT_STATUS_REP_PH_PEND_PACKET_SENT_PEND_BIT)
				//if (ezradioReply.FRR_A_READ.FRR_C_VALUE & EZRADIO_CMD_GET_INT_STATUS_REP_PH_PEND_PACKET_SENT_PEND_BIT)
				{
					DPRINT("PACKET_SENT IRQ");
					DEBUG_TX_END();

					if(tx_packet_callback != 0)
          {
              current_packet->tx_meta.timestamp = timer_get_counter_value();
              DPRINT_DATA(current_packet->data, current_packet->length);
              tx_packet_callback(current_packet);
          }

          /* We can't switch back to Rx since the Rx callbacks are modified
           * during CCA, so we systematically go to idle
          */
          switch_to_idle_mode();

//				} else if (ezradioReply.FRR_A_READ.FRR_C_VALUE & EZRADIO_CMD_GET_INT_STATUS_REP_PH_PEND_TX_FIFO_ALMOST_EMPTY_PEND_BIT)
//				{
//					DPRINT(" - TX FIFO Almost empty IRQ ");
//
//					ezradio_fifo_info(0, &radioReplyLocal);
//					DPRINT("TX FIFO Space: %d", radioReplyLocal.FIFO_INFO.TX_FIFO_SPACE);
//
//					//Fill fifo
//					#ifdef HAL_RADIO_USE_HW_CRC
//						int16_t new_length = current_packet->length-1 - tx_fifo_data_length;
//					#else
//						int16_t new_length = current_packet->length+1 - tx_fifo_data_length;
//					#endif
//					if (new_length > radioReplyLocal.FIFO_INFO.TX_FIFO_SPACE) new_length = radioReplyLocal.FIFO_INFO.TX_FIFO_SPACE;
//
//					while (new_length > 0)
//					{
//						ezradio_write_tx_fifo(new_length, &(current_packet->data[tx_fifo_data_length]));
//						tx_fifo_data_length += new_length;
//						DPRINT ("%d added -> %d", new_length, tx_fifo_data_length);
//
//					#ifdef HAL_RADIO_USE_HW_CRC
//						new_length = current_packet->length-1 - tx_fifo_data_length;
//					#else
//						new_length = current_packet->length+1 - tx_fifo_data_length;
//					#endif
//						if (new_length > radioReplyLocal.FIFO_INFO.TX_FIFO_SPACE) new_length = radioReplyLocal.FIFO_INFO.TX_FIFO_SPACE;
//					}
//
//					if (new_length == 0)
//					{
//						DPRINT("reprocess callback");
//						ezradio_int_callback();
//						return;
//					}
//
//					DPRINT ("%d added -> %d", new_length, tx_fifo_data_length);
				} else {
					DPRINT(" - OTHER IRQ");
				}
				break;
			default:
				//assert(false);
				DPRINT("State: %d", current_state);
		}
	}

//	if (ezradioReply.FRR_A_READ.FRR_A_VALUE & EZRADIO_CMD_GET_INT_STATUS_REP_INT_PEND_MODEM_INT_PEND_BIT)
//	{
//		DPRINT("MODEM ISR");
//	}

//	if (ezradioReply.FRR_A_READ.FRR_A_VALUE & EZRADIO_CMD_GET_INT_STATUS_REP_INT_PEND_CHIP_INT_PEND_BIT)
//	{
//		DPRINT("CHIP ISR");
//
//		if (current_state != HW_RADIO_STATE_IDLE)
//		{
//			if (ezradioReply.GET_INT_STATUS.CHIP_STATUS & EZRADIO_CMD_GET_INT_STATUS_REP_CHIP_STATUS_STATE_CHANGE_BIT)
//			{
//				ezradio_request_device_state(&radioReplyLocal);
//				DPRINT(" - Current State %d", radioReplyLocal.REQUEST_DEVICE_STATE.CURR_STATE);
//				DPRINT(" - Current channel %d", radioReplyLocal.REQUEST_DEVICE_STATE.CURRENT_CHANNEL);
//			}else {
//				DPRINT(" - OTHER IRQ");
//			}
//		}
//	}


}