kopia lustrzana https://github.com/DL7AD/pecanpico10
				
				
				
			
		
			
				
	
	
		
			1035 wiersze
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1035 wiersze
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
    Aerospace Decoder - Copyright (C) 2018 Bob Anderson (VK2GJ)
 | 
						|
 | 
						|
    Unless required by applicable law or agreed to in writing, software
 | 
						|
    distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
*/
 | 
						|
 | 
						|
#include "pktconf.h"
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*/
 | 
						|
/* Module local definitions.                                                 */
 | 
						|
/*===========================================================================*/
 | 
						|
 | 
						|
/*===========================================================================*/
 | 
						|
/* Module exported variables.                                                */
 | 
						|
/*===========================================================================*/
 | 
						|
 | 
						|
/*===========================================================================*/
 | 
						|
/* Module local types.                                                       */
 | 
						|
/*===========================================================================*/
 | 
						|
 | 
						|
/*===========================================================================*/
 | 
						|
/* Module local variables.                                                   */
 | 
						|
/*===========================================================================*/
 | 
						|
 | 
						|
/*===========================================================================*/
 | 
						|
/* Module local functions.                                                   */
 | 
						|
/*===========================================================================*/
 | 
						|
 | 
						|
/*===========================================================================*/
 | 
						|
/* Module exported functions.                                                */
 | 
						|
/*===========================================================================*/
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Initializes packet handlers and starts the radio manager.
 | 
						|
 * @note    The option to manage multiple radios is not yet implemented.
 | 
						|
 * @note    Once initialized the transmit service is available.
 | 
						|
 * @note    To activate receive requires an open to be made.
 | 
						|
 *
 | 
						|
 * @param[in]   radio unit ID.
 | 
						|
 *
 | 
						|
 *@return   result of operation.
 | 
						|
 *@retval   true    service was created.
 | 
						|
 *@retval   false   service creation failed or state was not idle.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
bool pktServiceCreate(radio_unit_t radio) {
 | 
						|
 | 
						|
  /*
 | 
						|
   * Get service object maps radio IDs to service objects
 | 
						|
   */
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if(handler->state != PACKET_IDLE)
 | 
						|
    return false;
 | 
						|
  /*
 | 
						|
   * Initialize the packet common event object.
 | 
						|
   */
 | 
						|
  chEvtObjectInit(pktGetEventSource(handler));
 | 
						|
 | 
						|
  memset(&handler->radio_rx_config, 0, sizeof(radio_task_object_t));
 | 
						|
  memset(&handler->radio_tx_config, 0, sizeof(radio_task_object_t));
 | 
						|
 | 
						|
  /* Set flags and radio ID. */
 | 
						|
  handler->rx_active = false;
 | 
						|
  handler->radio_init = false;
 | 
						|
  handler->radio = radio;
 | 
						|
 | 
						|
  /* Set service semaphore to idle state. */
 | 
						|
  chBSemObjectInit(&handler->close_sem, false);
 | 
						|
 | 
						|
  /* Set radio semaphore to free state. */
 | 
						|
  chBSemObjectInit(&handler->radio_sem, false);
 | 
						|
 | 
						|
  /*
 | 
						|
   * Create outgoing pool if it does not already exist.
 | 
						|
   * If it does exist the ref count is increased.
 | 
						|
   */
 | 
						|
/*  if(pktOutgoingBufferPoolCreate(radio) == NULL)
 | 
						|
    return false;*/
 | 
						|
 | 
						|
#if   USE_NEW_PKT_TX_ALLOC == TRUE
 | 
						|
  /*
 | 
						|
   * Create outgoing buffer sempahore if it does not already exist.
 | 
						|
   * If it does exist the ref count is increased.
 | 
						|
   * If we can't create it get false else true.
 | 
						|
   */
 | 
						|
  if(pktCommonBufferSemaphoreCreate(radio) == NULL)
 | 
						|
    return false;
 | 
						|
#endif
 | 
						|
  /* Send request to create radio manager. */
 | 
						|
  if (pktRadioManagerCreate(radio) == NULL)
 | 
						|
    return false;
 | 
						|
  handler->state = PACKET_READY;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Releases packet service.
 | 
						|
 * @note    The option to manage multiple radios is not yet implemented.
 | 
						|
 * @post    The packet service is no longer available for transmit or receive.
 | 
						|
 *
 | 
						|
 * @param[in] radio unit ID
 | 
						|
 *
 | 
						|
 *@return   result of operation.
 | 
						|
 *@retval   true    service was released.
 | 
						|
 *@retval   false   service state is incorrect or invalid radio ID.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
bool pktServiceRelease(radio_unit_t radio) {
 | 
						|
 | 
						|
  /*
 | 
						|
   * Lookup radio and assign handler (RPKTDx).
 | 
						|
   */
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if(handler->state != PACKET_READY)
 | 
						|
    return false;
 | 
						|
#if   USE_NEW_PKT_TX_ALLOC == TRUE
 | 
						|
  pktBufferSemaphoreRelease(radio);
 | 
						|
#endif
 | 
						|
  pktRadioManagerRelease(radio);
 | 
						|
  handler->state = PACKET_IDLE;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Initializes packet handlers and starts the radio manager.
 | 
						|
 * @note    The option to manage multiple radios is not yet implemented.
 | 
						|
 * @note    Once initialized the transmit service is available.
 | 
						|
 * @note    To activate receive requires an open to be made.
 | 
						|
 *
 | 
						|
 * @param[in]   radio unit ID.
 | 
						|
 *
 | 
						|
 *@return   result of operation.
 | 
						|
 *@retval   true    service was created.
 | 
						|
 *@retval   false   service creation failed or state was not idle.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
bool pktServiceHibernate(radio_unit_t radio) {
 | 
						|
 | 
						|
  /*
 | 
						|
   * Get service object maps radio IDs to service objects
 | 
						|
   */
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if(handler->state != PACKET_IDLE)
 | 
						|
    return false;
 | 
						|
  /*
 | 
						|
   * Initialize the packet common event object.
 | 
						|
   */
 | 
						|
  chEvtObjectInit(pktGetEventSource(handler));
 | 
						|
 | 
						|
  memset(&handler->radio_rx_config, 0, sizeof(radio_task_object_t));
 | 
						|
  memset(&handler->radio_tx_config, 0, sizeof(radio_task_object_t));
 | 
						|
 | 
						|
  /* Set flags and radio ID. */
 | 
						|
  handler->rx_active = false;
 | 
						|
  handler->radio_init = false;
 | 
						|
  handler->radio = radio;
 | 
						|
 | 
						|
  /* Set service semaphore to idle state. */
 | 
						|
  chBSemObjectInit(&handler->close_sem, false);
 | 
						|
 | 
						|
  /* Set radio semaphore to free state. */
 | 
						|
  chBSemObjectInit(&handler->radio_sem, false);
 | 
						|
 | 
						|
  /* Send request to create radio manager. */
 | 
						|
  if (pktRadioManagerCreate(radio) == NULL)
 | 
						|
    return false;
 | 
						|
  handler->state = PACKET_READY;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Releases packet service.
 | 
						|
 * @note    The option to manage multiple radios is not yet implemented.
 | 
						|
 * @post    The packet service is no longer available for transmit or receive.
 | 
						|
 *
 | 
						|
 * @param[in] radio unit ID
 | 
						|
 *
 | 
						|
 *@return   result of operation.
 | 
						|
 *@retval   true    service was released.
 | 
						|
 *@retval   false   service state is incorrect or invalid radio ID.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
bool pktServiceWakeup(radio_unit_t radio) {
 | 
						|
 | 
						|
  /*
 | 
						|
   * Lookup radio and assign handler (RPKTDx).
 | 
						|
   */
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if(handler->state != PACKET_READY)
 | 
						|
    return false;
 | 
						|
  pktRadioManagerRelease(radio);
 | 
						|
  handler->state = PACKET_IDLE;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Opens a packet receive service.
 | 
						|
 * @post    The packet service is initialized and ready to be started.
 | 
						|
 *
 | 
						|
 * @param[in] radio     radio unit identifier.
 | 
						|
 * @param[in] encoding  radio link level encoding.
 | 
						|
 * @param[in] frequency operating frequency (in Hz).
 | 
						|
 * @param[in] ch_step   frequency step per channel (in Hz).
 | 
						|
 *
 | 
						|
 * @return              status of operation.
 | 
						|
 * @retval MSG_OK       if the open request was processed.
 | 
						|
 * @retval MSG_TIMEOUT  if the open request timed out waiting for resources.
 | 
						|
 * @retval MSG_RESET    if state is invalid or bad parameter is submitted.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
msg_t pktOpenRadioReceive(radio_unit_t radio,
 | 
						|
                           encoding_type_t encoding,
 | 
						|
                           radio_freq_t frequency,
 | 
						|
                           channel_hz_t ch_step) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  chDbgCheck(handler->state == PACKET_READY);
 | 
						|
 | 
						|
  if(handler->state != PACKET_READY)
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  /* Wait for any prior session to complete closing. */
 | 
						|
  chBSemWait(&handler->close_sem);
 | 
						|
 | 
						|
  /* Save radio configuration. */
 | 
						|
  handler->radio_rx_config.type = encoding;
 | 
						|
  handler->radio_rx_config.base_frequency = frequency;
 | 
						|
  handler->radio_rx_config.step_hz = ch_step;
 | 
						|
 | 
						|
  /* Reset the statistics collection variables. */
 | 
						|
  handler->sync_count = 0;
 | 
						|
  handler->frame_count = 0;
 | 
						|
  handler->valid_count = 0;
 | 
						|
  handler->good_count = 0;
 | 
						|
 | 
						|
  radio_task_object_t rt = handler->radio_rx_config;
 | 
						|
 | 
						|
  /* Set parameters for radio command. */
 | 
						|
  rt.command = PKT_RADIO_RX_OPEN;
 | 
						|
 | 
						|
  /*
 | 
						|
   * Open (init) the radio (via submit radio task).
 | 
						|
   */
 | 
						|
  msg_t msg = pktSendRadioCommand(radio, &rt, NULL);
 | 
						|
 | 
						|
  if(msg != MSG_OK)
 | 
						|
    return msg;
 | 
						|
 | 
						|
  handler->state = PACKET_OPEN;
 | 
						|
  pktAddEventFlags(handler, EVT_PKT_CHANNEL_OPEN);
 | 
						|
 | 
						|
  return MSG_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Starts packet reception.
 | 
						|
 * @pre     The packet service must have been opened.
 | 
						|
 * @post    The radio is tuned to the specified channel.
 | 
						|
 * @post    The packet reception is running if it was stopped.
 | 
						|
 *
 | 
						|
 * @param[in]   handler pointer to a @p packet handler object.
 | 
						|
 * @param[in]   channel radio channel number to select
 | 
						|
 * @param[in]   sq      the RSSI setting to be used.
 | 
						|
 * @param[in]   cb      callback function called on receipt of packet.
 | 
						|
 *
 | 
						|
 * @return              Status of the operation.
 | 
						|
 * @retval MSG_OK       if the service was started.
 | 
						|
 * @retval MSG_RESET    parameter error or service not in correct state.
 | 
						|
 * @retval MSG_TIMEOUT  if the service could not be started.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
msg_t pktStartDataReception(radio_unit_t radio,
 | 
						|
                            radio_ch_t channel,
 | 
						|
                            radio_squelch_t sq,
 | 
						|
                            pkt_buffer_cb_t cb) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  if(!(handler->state == PACKET_OPEN || handler->state == PACKET_STOP))
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  handler->usr_callback = cb;
 | 
						|
 | 
						|
  handler->radio_rx_config.channel = channel;
 | 
						|
  handler->radio_rx_config.squelch = sq;
 | 
						|
 | 
						|
  radio_task_object_t rt = handler->radio_rx_config;
 | 
						|
 | 
						|
  rt.command = PKT_RADIO_RX_START;
 | 
						|
 | 
						|
  msg_t msg = pktSendRadioCommand(radio, &rt, NULL);
 | 
						|
  if(msg != MSG_OK)
 | 
						|
    return MSG_TIMEOUT;
 | 
						|
 | 
						|
  handler->state = PACKET_RUN;
 | 
						|
  pktAddEventFlags(handler, EVT_PKT_DECODER_START);
 | 
						|
  return MSG_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Starts a packet decoder.
 | 
						|
 * @pre     The packet channel must have been opened.
 | 
						|
 * @post    The packet decoder is running.
 | 
						|
 *
 | 
						|
 * @param[in]   radio unit ID.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
void pktStartDecoder(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
 | 
						|
  event_listener_t el;
 | 
						|
  event_source_t *esp;
 | 
						|
 | 
						|
  switch(handler->radio_rx_config.type) {
 | 
						|
    case MOD_AFSK: {
 | 
						|
 | 
						|
      esp = pktGetEventSource((AFSKDemodDriver *)handler->link_controller);
 | 
						|
 | 
						|
      pktRegisterEventListener(esp, &el, USR_COMMAND_ACK, DEC_START_EXEC);
 | 
						|
 | 
						|
      thread_t *the_decoder =
 | 
						|
          ((AFSKDemodDriver *)handler->link_controller)->decoder_thd;
 | 
						|
      chEvtSignal(the_decoder, DEC_COMMAND_START);
 | 
						|
      break;
 | 
						|
    } /* End case. */
 | 
						|
 | 
						|
    case MOD_2FSK: {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    default:
 | 
						|
      return;
 | 
						|
  } /* End switch. */
 | 
						|
 | 
						|
  /* Wait for the decoder to start. */
 | 
						|
  eventflags_t evt;
 | 
						|
  do {
 | 
						|
    /* In reality this is redundant as the only masked event is START. */
 | 
						|
    chEvtWaitAny(USR_COMMAND_ACK);
 | 
						|
 | 
						|
    /* Wait for correct event at source.
 | 
						|
     */
 | 
						|
    evt = chEvtGetAndClearFlags(&el);
 | 
						|
  } while (evt != DEC_START_EXEC);
 | 
						|
  pktUnregisterEventListener(esp, &el);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Stop reception.
 | 
						|
 * @notes   Decoding is stopped.
 | 
						|
 * @notes   Any packets out for processing remain in effect.
 | 
						|
 * @pre     The packet channel must be running.
 | 
						|
 * @post    The packet channel is stopped.
 | 
						|
 *
 | 
						|
 * @param[in] radio     radio unit ID..
 | 
						|
 *
 | 
						|
 * @return              Status of the operation.
 | 
						|
 * @retval MSG_OK       if the channel was stopped.
 | 
						|
 * @retval MSG_RESET    if the channel was not in the correct state.
 | 
						|
 * @retval MSG_TIMEOUT  if the channel could not be stopped or is invalid.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
msg_t pktStopDataReception(radio_unit_t radio) {
 | 
						|
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  if(handler == NULL)
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  if(handler->state != PACKET_RUN)
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  /* Stop the radio processing. */
 | 
						|
 | 
						|
  radio_task_object_t rt = handler->radio_rx_config;
 | 
						|
 | 
						|
  rt.command = PKT_RADIO_RX_STOP;
 | 
						|
 | 
						|
  msg_t msg = pktSendRadioCommand(radio, &rt, NULL);
 | 
						|
  if(msg != MSG_OK)
 | 
						|
    return msg;
 | 
						|
 | 
						|
  handler->state = PACKET_STOP;
 | 
						|
  pktAddEventFlags(handler, EVT_PKT_CHANNEL_STOP);
 | 
						|
  return MSG_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Stops a packet decoder.
 | 
						|
 * @pre     The packet channel must be running.
 | 
						|
 * @post    The packet decoder is stopped.
 | 
						|
 *
 | 
						|
 * @param[in]   radio unit ID.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
void pktStopDecoder(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  if(handler == NULL)
 | 
						|
    chDbgAssert(false, "invalid radio ID");
 | 
						|
 | 
						|
  event_listener_t el;
 | 
						|
  event_source_t *esp;
 | 
						|
 | 
						|
  switch(handler->radio_rx_config.type) {
 | 
						|
    case MOD_AFSK: {
 | 
						|
      esp = pktGetEventSource((AFSKDemodDriver *)handler->link_controller);
 | 
						|
 | 
						|
      pktRegisterEventListener(esp, &el, USR_COMMAND_ACK, DEC_STOP_EXEC);
 | 
						|
 | 
						|
      thread_t *the_decoder =
 | 
						|
          ((AFSKDemodDriver *)handler->link_controller)->decoder_thd;
 | 
						|
      chEvtSignal(the_decoder, DEC_COMMAND_STOP);
 | 
						|
      break;
 | 
						|
    } /* End case. */
 | 
						|
 | 
						|
    case MOD_2FSK: {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    default:
 | 
						|
      return;
 | 
						|
  } /* End switch. */
 | 
						|
 | 
						|
  /* Wait for the decoder to stop. */
 | 
						|
  eventflags_t evt;
 | 
						|
  do {
 | 
						|
    chEvtWaitAny(USR_COMMAND_ACK);
 | 
						|
 | 
						|
    /* Wait for correct event at source.
 | 
						|
     */
 | 
						|
    evt = chEvtGetAndClearFlags(&el);
 | 
						|
  } while (evt != DEC_STOP_EXEC);
 | 
						|
  pktUnregisterEventListener(esp, &el);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Closes a packet receive service.
 | 
						|
 * @pre     The packet service must have been stopped.
 | 
						|
 * @post    The packet service is closed and returned to ready state.
 | 
						|
 * @post    Memory used by the decoder thread is released.
 | 
						|
 *
 | 
						|
 * @param[in] radio     radio unit ID.
 | 
						|
 *
 | 
						|
 * @return              Status of the operation.
 | 
						|
 * @retval MSG_OK       if the service was closed successfully.
 | 
						|
 * @retval MSG_RESET    service not in the correct state or invalid parameter.
 | 
						|
 * @retval MSG_TIMEOUT  if the service could not be closed.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
msg_t pktCloseRadioReceive(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
  if(handler == NULL)
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  if(!(handler->state == PACKET_STOP || handler->state == PACKET_CLOSE))
 | 
						|
    return MSG_RESET;
 | 
						|
 | 
						|
  handler->state = PACKET_CLOSE;
 | 
						|
 | 
						|
  /* Set parameters for radio. */;
 | 
						|
 | 
						|
  radio_task_object_t rt = handler->radio_rx_config;
 | 
						|
 | 
						|
  rt.command = PKT_RADIO_RX_CLOSE;
 | 
						|
 | 
						|
  /* Submit command. A timeout can occur waiting for a command queue object. */
 | 
						|
  msg_t msg = pktSendRadioCommand(radio, &rt, NULL);
 | 
						|
  if(msg != MSG_OK)
 | 
						|
    return msg;
 | 
						|
 | 
						|
  pktAddEventFlags(handler, EVT_PKT_CHANNEL_CLOSE);
 | 
						|
  handler->state = PACKET_READY;
 | 
						|
  return MSG_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Stores data in a packet channel buffer.
 | 
						|
 * @notes   If the data is an HDLC value it will be escape encoded.
 | 
						|
 * @post    The character is stored and the internal buffer index is updated.
 | 
						|
 *
 | 
						|
 * @param[in] pkt_buffer    pointer to a @p packet buffer object.
 | 
						|
 * @param[in] data          the character to be stored
 | 
						|
 *
 | 
						|
 * @return              Status of the operation.
 | 
						|
 * @retval true         The data was stored.
 | 
						|
 * @retval false        The data could not be stored (buffer full).
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
bool pktStoreBufferData(pkt_data_object_t *pkt_buffer, ax25char_t data) {
 | 
						|
  if((pkt_buffer->packet_size + 1U) > pkt_buffer->buffer_size) {
 | 
						|
    /* Buffer full. */
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  /* Buffer space available. */
 | 
						|
  pkt_buffer->buffer[pkt_buffer->packet_size++] = data;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Dispatch a received buffer object.
 | 
						|
 * @notes   The buffer is checked to determine validity and CRC.
 | 
						|
 * @post    The buffer status is updated in the packet FIFO.
 | 
						|
 * @post    Packet quality statistics are updated.
 | 
						|
 * @post    Where no callback is used the buffer is posted to the FIFO mailbox.
 | 
						|
 * @post    Where a callback is used a thread is created to execute the callback.
 | 
						|
 *
 | 
						|
 * @param[in] pkt_buffer    pointer to a @p packet buffer object.
 | 
						|
 *
 | 
						|
 * @return  Status flags added after packet validity check.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
eventflags_t pktDispatchReceivedBuffer(pkt_data_object_t *pkt_buffer) {
 | 
						|
 | 
						|
  chDbgAssert(pkt_buffer != NULL, "no packet buffer");
 | 
						|
 | 
						|
  packet_svc_t *handler = pkt_buffer->handler;
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid handler");
 | 
						|
 | 
						|
  eventflags_t flags = EVT_NONE;
 | 
						|
  handler->frame_count++;
 | 
						|
  if(pktIsBufferValidAX25Frame(pkt_buffer)) {
 | 
						|
    handler->valid_count++;
 | 
						|
    uint16_t magicCRC =
 | 
						|
        calc_crc16(pkt_buffer->buffer, 0,
 | 
						|
                   pkt_buffer->packet_size);
 | 
						|
    if(magicCRC == CRC_INCLUSIVE_CONSTANT)
 | 
						|
        handler->good_count++;
 | 
						|
    flags |= (magicCRC == CRC_INCLUSIVE_CONSTANT)
 | 
						|
                ? EVT_AX25_FRAME_RDY
 | 
						|
                : EVT_AX25_CRC_ERROR;
 | 
						|
  } else {
 | 
						|
    flags |= EVT_PKT_INVALID_FRAME;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Update status in packet buffer object. */
 | 
						|
  pkt_buffer->status |= flags;
 | 
						|
 | 
						|
  objects_fifo_t *pkt_fifo = chFactoryGetObjectsFIFO(pkt_buffer->pkt_factory);
 | 
						|
 | 
						|
  chDbgAssert(pkt_fifo != NULL, "no packet FIFO");
 | 
						|
 | 
						|
  if(pkt_buffer->cb_func == NULL) {
 | 
						|
 | 
						|
    /* Send the packet buffer to the FIFO queue. */
 | 
						|
    chFifoSendObject(pkt_fifo, pkt_buffer);
 | 
						|
  } else {
 | 
						|
    /* Schedule a callback. */
 | 
						|
    thread_t *cb_thd = pktCreateBufferCallback(pkt_buffer);
 | 
						|
 | 
						|
    chDbgAssert(cb_thd != NULL, "failed to create callback thread");
 | 
						|
 | 
						|
    if(cb_thd == NULL) {
 | 
						|
      /* Failed to create CB thread. Release buffer. Flag event. */
 | 
						|
      chFifoReturnObject(pkt_fifo, pkt_buffer);
 | 
						|
      flags |= EVT_PKT_FAILED_CB_THD;
 | 
						|
 | 
						|
    } else {
 | 
						|
      /* Increase outstanding callback count. */
 | 
						|
      handler->cb_count++;
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
  return flags;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Create a callback processing thread.
 | 
						|
 * @notes   Packet callbacks are processed by individual threads.
 | 
						|
 * @notes   Thus packet callbacks are non-blocking to the decoder thread.
 | 
						|
 * @notes   After callback completes the thread it is scheduled for release.
 | 
						|
 * @notes   Release is initiated by posting the packet buffer to the queue.
 | 
						|
 *
 | 
						|
 * @post    Call back has been executed (for however long it takes).
 | 
						|
 * @post    Callback thread release is completed in the terminator thread.
 | 
						|
 *
 | 
						|
 * @param[in] pkt_data_object_t    pointer to a @p packet buffer object.
 | 
						|
 *
 | 
						|
 * @return  The callback thread.
 | 
						|
 *
 | 
						|
 * @api
 | 
						|
 */
 | 
						|
thread_t *pktCreateBufferCallback(pkt_data_object_t *pkt_buffer) {
 | 
						|
 | 
						|
  chDbgAssert(pkt_buffer != NULL, "invalid packet buffer");
 | 
						|
 | 
						|
  /* Create a callback thread name which is the address of the buffer. */
 | 
						|
  /* TODO: Create a more meaningful but still unique thread name. */
 | 
						|
  chsnprintf(pkt_buffer->cb_thd_name, sizeof(pkt_buffer->cb_thd_name),
 | 
						|
             "%x", pkt_buffer);
 | 
						|
 | 
						|
  /* Start a callback dispatcher thread. */
 | 
						|
  thread_t *cb_thd = chThdCreateFromHeap(NULL,
 | 
						|
              THD_WORKING_AREA_SIZE(PKT_CALLBACK_WA_SIZE),
 | 
						|
              pkt_buffer->cb_thd_name,
 | 
						|
              NORMALPRIO - 20,
 | 
						|
              pktCallback,
 | 
						|
              pkt_buffer);
 | 
						|
 | 
						|
  return cb_thd;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Run a callback processing thread.
 | 
						|
 * @notes   Packet callbacks are processed by individual threads.
 | 
						|
 * @notes   Thus packet callbacks are non-blocking to the decoder thread.
 | 
						|
 * @notes   After callback completes the thread it is scheduled for release.
 | 
						|
 * @notes   Release is initiated by posting the packet buffer to the queue.
 | 
						|
 *
 | 
						|
 * @post    Call back has been executed (for however long it takes).
 | 
						|
 * @post    Callback thread release is completed in the terminator thread.
 | 
						|
 *
 | 
						|
 * @param[in] arg pointer to a @p packet buffer object.
 | 
						|
 *
 | 
						|
 * @return  status (MSG_OK).
 | 
						|
 *
 | 
						|
 * @notapi
 | 
						|
 */
 | 
						|
THD_FUNCTION(pktCallback, arg) {
 | 
						|
 | 
						|
  chDbgAssert(arg != NULL, "invalid buffer reference");
 | 
						|
 | 
						|
  pkt_data_object_t *pkt_buffer = arg;
 | 
						|
 | 
						|
  chDbgAssert(pkt_buffer->cb_func != NULL, "no callback set");
 | 
						|
 | 
						|
  dyn_objects_fifo_t *pkt_factory = pkt_buffer->pkt_factory;
 | 
						|
  chDbgAssert(pkt_factory != NULL, "invalid packet factory reference");
 | 
						|
 | 
						|
  objects_fifo_t *pkt_fifo = chFactoryGetObjectsFIFO(pkt_factory);
 | 
						|
  chDbgAssert(pkt_fifo != NULL, "no packet FIFO");
 | 
						|
 | 
						|
  /* Save thread pointer for use later in terminator. */
 | 
						|
  pkt_buffer->cb_thread = chThdGetSelfX();
 | 
						|
 | 
						|
  /* Perform the callback. */
 | 
						|
  pkt_buffer->cb_func(pkt_buffer);
 | 
						|
 | 
						|
  /*
 | 
						|
   * Upon return buffer is queued for release.
 | 
						|
   * Thread is scheduled for destruction in pktReleaseDataBuffer(...).
 | 
						|
   * .i.e pktReleaseDataBuffer does not return for callback.
 | 
						|
   */
 | 
						|
  pktReleaseDataBuffer(pkt_buffer);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Process release of completed callbacks.
 | 
						|
 * @notes   Release is initiated by posting the packet buffer to the queue.
 | 
						|
 * @notes   The queue is used as a completion mechanism in callback mode.
 | 
						|
 * @notes   In poll mode the received packet is posted to the consumer
 | 
						|
 *
 | 
						|
 * @post    Call back thread has been released.
 | 
						|
 * @post    Packet buffer object is returned to free pool.
 | 
						|
 * @post    Packet object is released (for this instance).
 | 
						|
 * @post    If the FIFO is now unused it will be released.
 | 
						|
 *
 | 
						|
 * @param[in] arg radio unit ID.
 | 
						|
 *
 | 
						|
 * @return  status (MSG_OK) on exit.
 | 
						|
 *
 | 
						|
 * @notapi
 | 
						|
 */
 | 
						|
 | 
						|
/* TODO: Deprecate and use radio manager thread for callback release? */
 | 
						|
THD_FUNCTION(pktCompletion, arg) {
 | 
						|
  packet_svc_t *handler = arg;
 | 
						|
#define PKT_COMPLETION_THREAD_TIMER 100 /* 100 mS. */
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid handler reference");
 | 
						|
 | 
						|
  dyn_objects_fifo_t *pkt_factory = handler->the_packet_fifo;
 | 
						|
  objects_fifo_t *pkt_queue = chFactoryGetObjectsFIFO(pkt_factory);
 | 
						|
  chDbgAssert(pkt_queue != NULL, "no packet fifo list");
 | 
						|
 | 
						|
  /* TODO: Implement thread events to control start/stop. */
 | 
						|
  while(true) {
 | 
						|
 | 
						|
    /*
 | 
						|
     * Wait for a callback to be outstanding.
 | 
						|
     * If no callbacks outstanding check for termination request.
 | 
						|
     */
 | 
						|
    if(handler->cb_count == 0) {
 | 
						|
      if(chThdShouldTerminateX())
 | 
						|
        chThdExit(MSG_OK);
 | 
						|
      chThdSleep(TIME_MS2I(PKT_COMPLETION_THREAD_TIMER));
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    /* Wait for a buffer to be released. */
 | 
						|
    pkt_data_object_t *pkt_object;
 | 
						|
 | 
						|
    msg_t fmsg = chFifoReceiveObjectTimeout(pkt_queue,
 | 
						|
                         (void *)&pkt_object,
 | 
						|
                         TIME_MS2I(PKT_COMPLETION_THREAD_TIMER));
 | 
						|
    if(fmsg == MSG_TIMEOUT)
 | 
						|
      continue;
 | 
						|
 | 
						|
    /* Release the callback thread and recover heap. */
 | 
						|
    chThdRelease(pkt_object->cb_thread);
 | 
						|
 | 
						|
    /* Return packet buffer object to free list. */
 | 
						|
    chFifoReturnObject(pkt_queue, (pkt_data_object_t *)pkt_object);
 | 
						|
 | 
						|
    /*
 | 
						|
     * Decrease FIFO reference counter (increased by decoder).
 | 
						|
     * FIFO will be destroyed if all references now released.
 | 
						|
     */
 | 
						|
    chFactoryReleaseObjectsFIFO(pkt_factory);
 | 
						|
 | 
						|
    /* Decrease count of outstanding callbacks. */
 | 
						|
    --handler->cb_count;
 | 
						|
  }
 | 
						|
  chThdExit(MSG_OK);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief   Create receive callback thread terminator.
 | 
						|
 *
 | 
						|
 * @param[in] arg radio unit ID.
 | 
						|
 *
 | 
						|
 * @notapi
 | 
						|
 */
 | 
						|
void pktCallbackManagerOpen(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
 | 
						|
  /* Create the callback handler thread name. */
 | 
						|
  chsnprintf(handler->cbend_name, sizeof(handler->cbend_name),
 | 
						|
             "%s%02i", PKT_CALLBACK_TERMINATOR_PREFIX, radio);
 | 
						|
 | 
						|
  /* Start the callback thread terminator. */
 | 
						|
  thread_t *cbh = chThdCreateFromHeap(NULL,
 | 
						|
              THD_WORKING_AREA_SIZE(PKT_TERMINATOR_WA_SIZE),
 | 
						|
              handler->cbend_name,
 | 
						|
              NORMALPRIO - 30,
 | 
						|
              pktCompletion,
 | 
						|
              handler);
 | 
						|
 | 
						|
  chDbgAssert(cbh != NULL, "failed to create callback terminator thread");
 | 
						|
  handler->cb_terminator = cbh;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *
 | 
						|
 */
 | 
						|
dyn_objects_fifo_t *pktIncomingBufferPoolCreate(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
 | 
						|
  /* Create the packet buffer name for this radio. */
 | 
						|
  chsnprintf(handler->pbuff_name, sizeof(handler->pbuff_name),
 | 
						|
             "%s%02i", PKT_FRAME_QUEUE_PREFIX, radio);
 | 
						|
 | 
						|
  /* Check if the packet buffer factory is still in existence.
 | 
						|
   * If so we get a pointer to it.
 | 
						|
   */
 | 
						|
  dyn_objects_fifo_t *dyn_fifo =
 | 
						|
      chFactoryFindObjectsFIFO(handler->pbuff_name);
 | 
						|
 | 
						|
  if(dyn_fifo == NULL) {
 | 
						|
    /* Create the dynamic objects FIFO for the packet data queue. */
 | 
						|
    dyn_fifo = chFactoryCreateObjectsFIFO(handler->pbuff_name,
 | 
						|
        sizeof(pkt_data_object_t),
 | 
						|
        NUMBER_RX_PKT_BUFFERS, sizeof(msg_t));
 | 
						|
 | 
						|
    chDbgAssert(dyn_fifo != NULL, "failed to create receive PKT objects FIFO");
 | 
						|
 | 
						|
    if(dyn_fifo == NULL) {
 | 
						|
      /* TODO: Close decoder on fail. */
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Save the factory FIFO reference. */
 | 
						|
  handler->the_packet_fifo = dyn_fifo;
 | 
						|
 | 
						|
  /* Initialize packet buffer pointer. */
 | 
						|
  handler->active_packet_object = NULL;
 | 
						|
  return dyn_fifo;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Send shares a common pool of buffers.
 | 
						|
 */
 | 
						|
dyn_objects_fifo_t *pktCommonBufferPoolCreate(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
 | 
						|
  /* Check if the transmit packet buffer factory already exists.
 | 
						|
   * If so we get a pointer to it and just return that.
 | 
						|
   * Otherwise create the FIFO and return result.
 | 
						|
   */
 | 
						|
  dyn_objects_fifo_t *dyn_fifo =
 | 
						|
      chFactoryFindObjectsFIFO(PKT_SEND_BUFFER_NAME);
 | 
						|
 | 
						|
  if(dyn_fifo == NULL) {
 | 
						|
    /* Create the dynamic objects FIFO for the packet data queue. */
 | 
						|
    dyn_fifo = chFactoryCreateObjectsFIFO(PKT_SEND_BUFFER_NAME,
 | 
						|
        sizeof(packet_tx_t),
 | 
						|
        NUMBER_COMMON_PKT_BUFFERS, sizeof(msg_t));
 | 
						|
 | 
						|
    chDbgAssert(dyn_fifo != NULL, "failed to create send PKT objects FIFO");
 | 
						|
  }
 | 
						|
  /* Save the factory FIFO reference. */
 | 
						|
  handler->tx_packet_fifo = dyn_fifo;
 | 
						|
  return dyn_fifo;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Send shares a common pool of buffers.
 | 
						|
 */
 | 
						|
void pktCommonBufferPoolRelease(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
  chDbgAssert(handler->tx_packet_fifo != NULL, "no outgoing FIFO assigned");
 | 
						|
  /* Release FIFO. If this is the last radio using it the FIFO is released. */
 | 
						|
  chFactoryReleaseObjectsFIFO(handler->tx_packet_fifo);
 | 
						|
  handler->tx_packet_fifo = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Send shares a common pool of buffers.
 | 
						|
 */
 | 
						|
dyn_semaphore_t *pktCommonBufferSemaphoreCreate(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
 | 
						|
  /* Check if the transmit packet buffer semaphore already exists.
 | 
						|
   * If so we get a pointer to it and just return that.
 | 
						|
   * Otherwise create the semaphore and return result.
 | 
						|
   */
 | 
						|
  dyn_semaphore_t *dyn_sem =
 | 
						|
      chFactoryFindSemaphore(PKT_SEND_BUFFER_SEM_NAME);
 | 
						|
 | 
						|
  if(dyn_sem == NULL) {
 | 
						|
    /* Create the dynamic objects FIFO for the packet data queue. */
 | 
						|
    dyn_sem = chFactoryCreateSemaphore(PKT_SEND_BUFFER_SEM_NAME,
 | 
						|
                                       NUMBER_COMMON_PKT_BUFFERS);
 | 
						|
 | 
						|
    chDbgAssert(dyn_sem != NULL, "failed to create send PKT semaphore");
 | 
						|
  } else {
 | 
						|
    /* Increase buffer number by adjusting semaphore.
 | 
						|
     * TODO: Once bumped up the count can't be decreased if a radio is closed. */
 | 
						|
/*    chSysLock();
 | 
						|
    chSemAddCounterI(chFactoryGetSemaphore(dyn_sem), NUMBER_TX_PKT_BUFFERS);
 | 
						|
    chSchRescheduleS();
 | 
						|
    chSysUnlock();*/
 | 
						|
  }
 | 
						|
  handler->tx_packet_sem = dyn_sem;
 | 
						|
  return dyn_sem;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Send shares a common pool of buffers.
 | 
						|
 * @retval MSG_RESET    if the semaphore has been reset using @p chSemReset().
 | 
						|
 * @retval MSG_TIMEOUT  if the semaphore has not been signaled or reset within
 | 
						|
 *                      the specified timeout.
 | 
						|
 */
 | 
						|
msg_t pktGetPacketBuffer(packet_t *pp, sysinterval_t timeout) {
 | 
						|
 | 
						|
  /* Check if the transmit packet buffer semaphore already exists.
 | 
						|
   * If so we get a pointer to it and just return that.
 | 
						|
   * Otherwise create the semaphore and return result.
 | 
						|
   */
 | 
						|
  dyn_semaphore_t *dyn_sem =
 | 
						|
      chFactoryFindSemaphore(PKT_SEND_BUFFER_SEM_NAME);
 | 
						|
 | 
						|
  chDbgAssert(dyn_sem != NULL, "no send PKT semaphore");
 | 
						|
 | 
						|
  *pp = NULL;
 | 
						|
 | 
						|
  if(dyn_sem == NULL)
 | 
						|
    return MSG_TIMEOUT;
 | 
						|
 | 
						|
  /* Decrease ref count. */
 | 
						|
  chFactoryReleaseSemaphore(dyn_sem);
 | 
						|
 | 
						|
  /* Wait in queue for permission to allocate a buffer. */
 | 
						|
  msg_t msg = chSemWaitTimeout(chFactoryGetSemaphore(dyn_sem), timeout);
 | 
						|
  if(msg != MSG_OK)
 | 
						|
    /* This can be MSG_TIMEOUT or MSG_RESET. */
 | 
						|
    return msg;
 | 
						|
 | 
						|
  /* Allocate buffer.
 | 
						|
   * If this returns null then all heap is consumed.
 | 
						|
   */
 | 
						|
  *pp = ax25_new();
 | 
						|
  if(pp == NULL)
 | 
						|
   return MSG_TIMEOUT;
 | 
						|
  return MSG_OK;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * A common pool of AX25 buffers.
 | 
						|
 */
 | 
						|
void pktReleasePacketBuffer(packet_t pp) {
 | 
						|
 | 
						|
  /* Check if the packet buffer semaphore exists.
 | 
						|
   * If not this is a system error.
 | 
						|
   */
 | 
						|
  dyn_semaphore_t *dyn_sem =
 | 
						|
      chFactoryFindSemaphore(PKT_SEND_BUFFER_SEM_NAME);
 | 
						|
 | 
						|
  chDbgAssert(dyn_sem != NULL, "no general packet buffer semaphore");
 | 
						|
 | 
						|
  /* Free buffer memory. */
 | 
						|
  ax25_delete(pp);
 | 
						|
 | 
						|
  /* Relinquish the buffer creation permission. */
 | 
						|
  chSemSignal(chFactoryGetSemaphore(dyn_sem));
 | 
						|
 | 
						|
  /* Decrease factory ref count. */
 | 
						|
  chFactoryReleaseSemaphore(dyn_sem);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Send shares a common pool of buffers.
 | 
						|
 */
 | 
						|
void pktBufferSemaphoreRelease(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
  chDbgAssert(handler->tx_packet_fifo != NULL, "no outgoing FIFO assigned");
 | 
						|
 | 
						|
  /*
 | 
						|
   *  Release Semaphore.
 | 
						|
   *  If this is the last radio using the semaphore it is released.
 | 
						|
   */
 | 
						|
  chFactoryReleaseSemaphore(handler->tx_packet_sem);
 | 
						|
  handler->tx_packet_sem = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *
 | 
						|
 */
 | 
						|
thread_t *pktCallbackManagerCreate(radio_unit_t radio) {
 | 
						|
 | 
						|
  packet_svc_t *handler = pktGetServiceObject(radio);
 | 
						|
 | 
						|
  chDbgAssert(handler != NULL, "invalid radio ID");
 | 
						|
 | 
						|
  /* Create the callback termination thread name. */
 | 
						|
  chsnprintf(handler->cbend_name, sizeof(handler->cbend_name),
 | 
						|
             "%s%02i", PKT_CALLBACK_TERMINATOR_PREFIX, radio);
 | 
						|
 | 
						|
  /*
 | 
						|
   * Initialize the outstanding callback count.
 | 
						|
   */
 | 
						|
  handler->cb_count = 0;
 | 
						|
 | 
						|
  /* Start the callback thread terminator. */
 | 
						|
  thread_t *cbh = chThdCreateFromHeap(NULL,
 | 
						|
              THD_WORKING_AREA_SIZE(PKT_TERMINATOR_WA_SIZE),
 | 
						|
              handler->cbend_name,
 | 
						|
              NORMALPRIO - 30,
 | 
						|
              pktCompletion,
 | 
						|
              handler);
 | 
						|
 | 
						|
  chDbgAssert(cbh != NULL, "failed to create callback terminator thread");
 | 
						|
  handler->cb_terminator = cbh;
 | 
						|
  return cbh;
 | 
						|
}
 | 
						|
 | 
						|
void pktIncomingBufferPoolRelease(packet_svc_t *handler) {
 | 
						|
 | 
						|
  /* Release the dynamic objects FIFO for the incoming packet data queue. */
 | 
						|
  chFactoryReleaseObjectsFIFO(handler->the_packet_fifo);
 | 
						|
  handler->the_packet_fifo = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void pktCallbackManagerRelease(packet_svc_t *handler) {
 | 
						|
 | 
						|
  /* Tell the callback terminator it should exit. */
 | 
						|
  chThdTerminate(handler->cb_terminator);
 | 
						|
 | 
						|
  /* Wait for it to terminate and release. */
 | 
						|
  chThdWait(handler->cb_terminator);
 | 
						|
}
 | 
						|
 | 
						|
/*void pktScheduleThreadRelease(thread_t *thread) {
 | 
						|
  (void)thread;
 | 
						|
}*/
 | 
						|
 | 
						|
/** @} */
 |