kopia lustrzana https://github.com/DL7AD/pecanpico10
				
				
				
			
		
			
				
	
	
		
			685 wiersze
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			685 wiersze
		
	
	
		
			20 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.                                                */
 | |
| /*===========================================================================*/
 | |
| 
 | |
| packet_svc_t RPKTD1;
 | |
| 
 | |
| /*===========================================================================*/
 | |
| /* 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.
 | |
|  *
 | |
|  *
 | |
|  * @api
 | |
|  */
 | |
| void pktServiceCreate() {
 | |
| 
 | |
|   /* TODO: This should create the top level object for each radio (RPKTDx). */
 | |
|   packet_svc_t *handler = &RPKTD1;
 | |
| 
 | |
|   /*
 | |
|    * Initialize the packet common event object.
 | |
|    */
 | |
|   chEvtObjectInit(pktGetEventSource(handler));
 | |
| 
 | |
|   memset(&handler->radio_config, 0, sizeof(radio_task_object_t));
 | |
| 
 | |
|   /* Set parameters and send request. */
 | |
|   handler->radio_config.radio_id = PKT_RADIO_1;
 | |
| 
 | |
|   /* Set service semaphore to idle state. */
 | |
|   chBSemObjectInit(&handler->close_sem, false);
 | |
| 
 | |
|   pktRadioManagerCreate(handler);
 | |
|   handler->state = PACKET_IDLE;
 | |
| }
 | |
| 
 | |
| void pktServiceRelease() {
 | |
| 
 | |
|   /* TODO: This should release top level resources for each radio (RPKTDx). */
 | |
|   packet_svc_t *handler = &RPKTD1;
 | |
| 
 | |
|   pktRadioManagerRelease(handler);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief   Opens a packet service.
 | |
|  * @post    A reference to the packet handler is returned.
 | |
|  * @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              the reference to the packet handler object.
 | |
|  * @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 pktOpenRadioService(radio_unit_t radio,
 | |
|                            encoding_type_t encoding,
 | |
|                            radio_freq_t frequency,
 | |
|                            channel_hz_t ch_step,
 | |
|                            packet_svc_t **ph) {
 | |
| 
 | |
|   /* TODO: implement mapping from radio config to packet handler object.
 | |
|    * TODO: implement channel step size in Hz in radio driver.
 | |
|    */
 | |
|   (void)radio;
 | |
|   packet_svc_t *handler = &RPKTD1;
 | |
| 
 | |
|   chDbgCheck(handler->state == PACKET_IDLE);
 | |
| 
 | |
|   if(handler->state != PACKET_IDLE)
 | |
|     return MSG_RESET;
 | |
| 
 | |
|   /* Wait for any prior session to complete closing. */
 | |
|   chBSemWait(&handler->close_sem);
 | |
| 
 | |
|   /* Save radio configuration. */
 | |
|   handler->radio_config.type = encoding;
 | |
|   handler->radio_config.base_frequency = frequency;
 | |
|   handler->radio_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_config;
 | |
| 
 | |
|   /* Set parameters for radio command. */
 | |
|   rt.command = PKT_RADIO_OPEN;
 | |
| 
 | |
|   /*
 | |
|    * Open (init) the radio (via submit radio task).
 | |
|    */
 | |
|   msg_t msg = pktSendRadioCommand(handler, &rt);
 | |
| 
 | |
|   if(msg != MSG_OK)
 | |
|     return msg;
 | |
| 
 | |
|   handler->state = PACKET_OPEN;
 | |
|   pktAddEventFlags(handler, EVT_PKT_CHANNEL_OPEN);
 | |
| 
 | |
|   /* Set the pointer. */
 | |
|   *ph = handler;
 | |
|   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]   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(packet_svc_t *handler,
 | |
|                             radio_ch_t channel,
 | |
|                             radio_squelch_t sq,
 | |
|                             pkt_buffer_cb_t cb) {
 | |
| 
 | |
|   chDbgAssert(handler != NULL, "invalid handler reference");
 | |
| 
 | |
|   if(!(handler->state == PACKET_OPEN || handler->state == PACKET_STOP))
 | |
|     return MSG_RESET;
 | |
| 
 | |
|   handler->usr_callback = cb;
 | |
| 
 | |
|   handler->radio_config.channel = channel;
 | |
|   handler->radio_config.squelch = sq;
 | |
| 
 | |
|   radio_task_object_t rt = handler->radio_config;
 | |
| 
 | |
|   rt.command = PKT_RADIO_RX;
 | |
| 
 | |
|   msg_t msg = pktSendRadioCommand(handler, &rt);
 | |
|   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]   handler pointer to a @p packet handler object.
 | |
|  *
 | |
|  * @api
 | |
|  */
 | |
| void pktStartDecoder(packet_svc_t *handler) {
 | |
| 
 | |
|   //chDbgAssert(handler->state == PACKET_RUN, "invalid handler state");
 | |
| 
 | |
|   event_listener_t el;
 | |
|   event_source_t *esp;
 | |
| 
 | |
|   switch(handler->radio_config.type) {
 | |
|     case DECODE_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 DECODE_FSK: {
 | |
|       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] handler       pointer to a @p packet handler object.
 | |
|  *
 | |
|  * @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(packet_svc_t *handler) {
 | |
|   if(handler->state != PACKET_RUN)
 | |
|     return MSG_RESET;
 | |
| 
 | |
|   /* Stop the radio processing. */
 | |
| 
 | |
|   radio_task_object_t rt = handler->radio_config;
 | |
| 
 | |
|   rt.command = PKT_RADIO_RX_STOP;
 | |
| 
 | |
|   msg_t msg = pktSendRadioCommand(handler, &rt);
 | |
|   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]   handler pointer to a @p packet handler object.
 | |
|  *
 | |
|  * @api
 | |
|  */
 | |
| void pktStopDecoder(packet_svc_t *handler) {
 | |
| 
 | |
|   //chDbgAssert(handler->state == PACKET_STOP, "invalid handler state");
 | |
| 
 | |
|   event_listener_t el;
 | |
|   event_source_t *esp;
 | |
| 
 | |
|   switch(handler->radio_config.type) {
 | |
|     case DECODE_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 DECODE_FSK: {
 | |
|       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 service.
 | |
|  * @pre     The packet service must have been stopped.
 | |
|  * @post    The packet service is closed.
 | |
|  * @post    Memory used by the decoder thread is released.
 | |
|  *
 | |
|  * @param[in] handler       pointer to a @p packet handler object.
 | |
|  *
 | |
|  * @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 pktCloseRadioService(packet_svc_t *handler) {
 | |
| 
 | |
|   if(handler->state != PACKET_STOP)
 | |
|     return MSG_RESET;
 | |
| 
 | |
|   handler->state = PACKET_CLOSE;
 | |
| 
 | |
|   /* Set parameters for radio. */;
 | |
| 
 | |
|   radio_task_object_t rt = handler->radio_config;
 | |
| 
 | |
|   rt.command = PKT_RADIO_CLOSE;
 | |
| 
 | |
|   /* Submit command. A timeout can occur waiting for a command queue object. */
 | |
|   msg_t msg = pktSendRadioCommand(handler, &rt);
 | |
|   if(msg != MSG_OK)
 | |
|     return msg;
 | |
| 
 | |
|   handler->state = PACKET_IDLE;
 | |
|   pktAddEventFlags(handler, EVT_PKT_CHANNEL_CLOSE);
 | |
|   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_AFSK_INVALID_FRAME;
 | |
|   }
 | |
| 
 | |
|   /* Update status in packet buffer object. */
 | |
|   pkt_buffer->status |= flags;
 | |
| 
 | |
|   if(pkt_buffer->cb_func == NULL) {
 | |
|     objects_fifo_t *pkt_fifo = chFactoryGetObjectsFIFO(pkt_buffer->pkt_factory);
 | |
| 
 | |
|     chDbgAssert(pkt_fifo != NULL, "no packet FIFO");
 | |
| 
 | |
|     /* 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");
 | |
| 
 | |
|     /* 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. */
 | |
|   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 releasae of completed callbacks.
 | |
|  * @notes   Release is initiated by posting the packet buffer to the queue.
 | |
|  *
 | |
|  * @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 pointer to a @p packet service object.
 | |
|  *
 | |
|  * @return  status (MSG_OK).
 | |
|  *
 | |
|  * @notapi
 | |
|  */
 | |
| THD_FUNCTION(pktCompletion, arg) {
 | |
|   packet_svc_t *handler = arg;
 | |
| #define PKT_COMPLETION_THREAD_TIMER 100 /* 100 mS. */
 | |
|   chDbgAssert(arg != 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);
 | |
| }
 | |
| 
 | |
| void pktCallbackManagerOpen(packet_svc_t *handler) {
 | |
| 
 | |
|   radio_unit_t rid = handler->radio_config.radio_id;
 | |
| 
 | |
|   /* Create the callback handler thread name. */
 | |
|   chsnprintf(handler->cbend_name, sizeof(handler->cbend_name),
 | |
|              "%s%02i", PKT_CALLBACK_TERMINATOR_PREFIX, rid);
 | |
| 
 | |
|   /* 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 *pktBufferManagerCreate(packet_svc_t *handler) {
 | |
|   /* The radio associated with this AFSK driver. */
 | |
|   radio_unit_t rid = handler->radio_config.radio_id;
 | |
| 
 | |
|   /* Create the packet buffer name for this radio. */
 | |
|   chsnprintf(handler->pbuff_name, sizeof(handler->pbuff_name),
 | |
|              "%s%02i", PKT_FRAME_QUEUE_PREFIX, rid);
 | |
| 
 | |
|   /* 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_PKT_FIFOS, sizeof(msg_t));
 | |
| 
 | |
|     chDbgAssert(dyn_fifo != NULL, "failed to create PKT objects FIFO");
 | |
| 
 | |
|     if(dyn_fifo == NULL) {
 | |
|       /* TODO: Close decoder on fail. */
 | |
|       return NULL;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Save the factory FIFO. */
 | |
|   handler->the_packet_fifo = dyn_fifo;
 | |
| 
 | |
|   /* Initialize packet buffer pointer. */
 | |
|   handler->active_packet_object = NULL;
 | |
|   return dyn_fifo;
 | |
| }
 | |
| 
 | |
| 
 | |
| void pktCallbackManagerCreate(packet_svc_t *handler) {
 | |
|   radio_unit_t rid = handler->radio_config.radio_id;
 | |
| 
 | |
|   /* Create the callback termination thread name. */
 | |
|   chsnprintf(handler->cbend_name, sizeof(handler->cbend_name),
 | |
|              "%s%02i", PKT_CALLBACK_TERMINATOR_PREFIX, rid);
 | |
| 
 | |
|   /*
 | |
|    * 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;
 | |
| }
 | |
| 
 | |
| void pktBufferManagerRelease(packet_svc_t *handler) {
 | |
| 
 | |
|   /* Release the dynamic objects FIFO for the 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);
 | |
| }
 | |
| 
 | |
| /** @} */
 |