/* * This function is called from the receive side with the sequence number of * the last packet received. */ void AJ_SerialTx_ReceivedSeq(uint8_t seq) { /* * If we think we have already acked this sequence number we don't adjust * the ack count. */ if (!SEQ_GT(currentTxAck, seq)) { currentTxAck = (seq + 1) & 0x7; } #ifdef ALWAYS_ACK AJ_SerialTX_EnqueueCtrl(NULL, 0, AJ_SERIAL_ACK); #else ++pendingAcks; /* * If there are no packets to send we are allowed to accumulate a * backlog of pending ACKs up to a maximum equal to the window size. * In any case we are required to send an ack within a timeout * period so if this is the first pending ack we need to prime a timer. */ if (pendingAcks == 1) { AJ_InitTimer(&ackTime); AJ_TimeAddOffset(&ackTime, AJ_SerialLinkParams.txAckTimeout); return; } /* * If we have hit our pending ACK limit send a explicit ACK packet immediately. */ if (pendingAcks == AJ_SerialLinkParams.windowSize) { AJ_SerialTX_EnqueueCtrl(NULL, 0, AJ_SERIAL_ACK); } #endif }
/** * This function resets the transmit side of the transport. */ AJ_Status AJ_SerialTX_Reset(void) { TxPkt volatile* pkt; /* * Hold the timeouts. */ AJ_InitTimer(&resendTime); AJ_TimeAddOffset(&resendTime, AJ_TIMER_FOREVER); AJ_InitTimer(&ackTime); AJ_TimeAddOffset(&ackTime, AJ_TIMER_FOREVER); /* * Put ACL packets back on the free list. */ while (txSent != NULL) { pkt = txSent; txSent = txSent->next; if (pkt->type == AJ_SERIAL_DATA) { pkt->next = txFreeList; txFreeList = pkt; } } while (txQueue != NULL) { pkt = txQueue; txQueue = txQueue->next; if (pkt->type == AJ_SERIAL_DATA) { pkt->next = txFreeList; txFreeList = pkt; } } /* * Re-initialize global state. */ txSeqNum = 0; pendingAcks = 0; currentTxAck = 0; txSeqNum = 0; resendPrimed = FALSE; return AJ_OK; }
/** * This function is called by the receive layer when a data packet or an explicit ACK * has been received. The ACK value is one greater (modulo 8) than the seq number of the * last packet successfully received. */ void AJ_SerialTx_ReceivedAck(uint8_t ack) { TxPkt volatile* ackedPkt = NULL; if (txSent == NULL) { return; } /* * Remove acknowledged packets from sent queue. */ while ((txSent != NULL) && SEQ_GT(ack, txSent->seq)) { ackedPkt = txSent; txSent = txSent->next; //AJ_AlwaysPrintf("Releasing seq=%d (acked by %d)\n", ackedPkt->seq, ack); AJ_ASSERT(ackedPkt->type == AJ_SERIAL_DATA); /* * Return pkt to ACL free list. */ ackedPkt->next = txFreeList; txFreeList = ackedPkt; /* * If all packet have been ack'd, halt the resend timer and return. */ if (txSent == NULL) { AJ_InitTimer(&resendTime); AJ_TimeAddOffset(&resendTime, AJ_TIMER_FOREVER); resendPrimed = FALSE; return; } } /* * Reset the resend timer if one or more packets were ack'd. */ if (ackedPkt != NULL) { AJ_InitTimer(&resendTime); AJ_TimeAddOffset(&resendTime, AJ_SerialLinkParams.txResendTimeout); resendPrimed = TRUE; } }
/* * Send a explicit ACK (acknowledgement). */ void SendAck() { if (pendingAcks) { pendingAcks = 0; AJ_SerialTX_EnqueueCtrl(NULL, 0, AJ_SERIAL_ACK); } /* * Disable explicit ack. */ AJ_InitTimer(&ackTime); AJ_TimeAddOffset(&ackTime, AJ_TIMER_FOREVER); }
AJ_Timer* AJ_TimerInit(uint32_t timeout, AJ_TimerCallback timerCallback, void* context, uint32_t timerId) { AJ_Timer* timer; timer = (AJ_Timer*) AJ_Malloc(sizeof(AJ_Timer)); timer->id = timerId; timer->next = NULL; timer->callback = timerCallback; timer->context = context; AJ_InitTimer(&(timer->timeNextRaised)); AJ_TimeAddOffset(&(timer->timeNextRaised), timeout); return timer; }
/** * This function is called if an acknowledgement is not received within the required * timeout period. */ void ResendPackets() { TxPkt volatile* last; /* * Re-register the send timeout callback, it will not be primed until it * is needed. */ resendPrimed = FALSE; AJ_InitTimer(&resendTime); AJ_TimeAddOffset(&resendTime, AJ_TIMER_FOREVER); /* * No resends if the link is not up. */ if (AJ_SerialLinkParams.linkState != AJ_LINK_ACTIVE) { return; } /* * To preserve packet order, all unacknowleged packets must be resent. This * simply means moving packets on txSent to the head of txQueue. */ if (txSent != NULL) { last = txSent; while (last->next != NULL) { last = last->next; } /* * Put resend packets after the unreliable packet. */ if (txQueue == txUnreliable) { last->next = txQueue->next; txQueue->next = last; } else { last->next = txQueue; txQueue = txSent; } txSent = NULL; } }
AJ_Status AJ_TimerRefresh(uint32_t timerId, uint32_t timeout) { AJ_DebugTimerPrintf("AJ_TimerRefresh id 0x%lx timeout %ld\n", timerId, timeout); // BUGBUG take a lock AJ_Timer* iter = NULL; iter = AJ_TimerRemoveFromList(&TimerList, timerId); if (!iter) { iter = AJ_TimerRemoveFromList(&InactiveTimerList, timerId); } if (iter) { // set the trigger time to now + timeout. AJ_InitTimer(&(iter->timeNextRaised)); AJ_TimeAddOffset(&(iter->timeNextRaised), timeout); // move a timer from the active to the inactive list. AJ_TimerInsertInList(&TimerList, iter); } if (!iter) { // look on the inactive list and insert it into the active list AJ_Printf("ERROR! refreshing a non existant timer %u!\n", timerId); } if (TimerList) { // restart the global timer, in case we were stopped. AJ_GlobalTimerStart(); } // BUGBUG release a lock AJ_DebugCheckTimerList(TimerList); return AJ_OK; }
void AJ_FillTxBufferList() { AJ_SlippedBuffer volatile* currentSlippedBuffer; if (!txQueue) { return; } AJ_PauseTX(); while (bufferTxFreeList && txQueue) { // Pull the head off the queue. TxPkt volatile* txCurrent; currentSlippedBuffer = bufferTxFreeList; bufferTxFreeList = bufferTxFreeList->next; currentSlippedBuffer->next = NULL; if (pendingSendBuffer != NULL) { AJ_ResumeTX(); } txCurrent = txQueue; txQueue = txQueue->next; txCurrent->next = NULL; ConvertPacketToBytes(currentSlippedBuffer, txCurrent); if (txCurrent->type == AJ_SERIAL_DATA) { //put it onto txSent if (txSent == NULL) { txSent = txCurrent; } else { TxPkt volatile* last = txSent; while (last->next != NULL) { last = last->next; } last->next = txCurrent; } if (!resendPrimed) { AJ_InitTimer(&resendTime); AJ_TimeAddOffset(&resendTime, AJ_SerialLinkParams.txResendTimeout); resendPrimed = FALSE; } } AJ_PauseTX(); //put the buffer on the pending list if (pendingSendBuffer == NULL) { //Free list was previously NULL, so re-enable reading //Save a pointer to the recv buffer, so we can keep track when the AJ_RecieveCallback occurs. pendingSendBuffer = currentSlippedBuffer; //pendingSendBuffer->next = NULL; AJ_TX(pendingSendBuffer->buffer, pendingSendBuffer->actualLen); } else { if (bufferTxPending != NULL) { volatile AJ_SlippedBuffer* buf = bufferTxPending; while (buf->next != NULL) { buf = buf->next; } buf->next = currentSlippedBuffer; } else { bufferTxPending = currentSlippedBuffer; } } } if (!bufferTxFreeList) { dataSent = FALSE; } if (pendingSendBuffer != NULL) { AJ_ResumeTX(); } }
void ConvertPacketToBytes(AJ_SlippedBuffer volatile* slip, TxPkt volatile* txCurrent) { uint8_t header[4]; uint16_t crc = AJ_SERIAL_CRC_INIT; uint8_t crcBytes[2]; //AJ_DumpBytes("Raw buffer",txCurrent->payload,txCurrent->len); slip->actualLen = 0; /* * Apply SLIP encoding to the header. */ slip->buffer[slip->actualLen++] = BOUNDARY_BYTE; /* * Compose flags */ // byte 1 is the message type header[1] = txCurrent->type; if (txCurrent->type == AJ_SERIAL_DATA) { /* * If we have maxed the windows we need to go idle */ if (txSentPending() == AJ_SerialLinkParams.windowSize) { AJ_AlwaysPrintf(("TxSend - reached window size: %u\n", txSentPending())); AJ_ASSERT(FALSE); } header[0] = (txCurrent->seq << 4); // AJ_AlwaysPrintf("Tx seq %d, ack %d\n", txCurrent->seq, currentTxAck); } else { header[0] = 0; // AJ_AlwaysPrintf("Tx %s seq %d\n", !txCurrent->type ? "ack" : "unreliable", txCurrent->seq); } /* * All packets except link control packets carry ACK information. */ if (txCurrent->type != AJ_SERIAL_CTRL) { // Acknowledge the last packet received. header[0] |= (currentTxAck & 0x0F); /* * If there was an ACK backlog, we halt the explicit ACK timeout. */ if (pendingAcks) { pendingAcks = 0; pendingAcks = 0; AJ_InitTimer(&ackTime); AJ_TimeAddOffset(&ackTime, AJ_TIMER_FOREVER); } } // bytes 2 and 3 are the payload length header[2] = txCurrent->len >> 8; header[3] = txCurrent->len & 0x00FF; AJ_CRC16_Compute(header, ArraySize(header), &crc); AJ_CRC16_Compute(txCurrent->payload, txCurrent->len, &crc); SlipBytes(slip, header, 4); SlipBytes(slip, txCurrent->payload, txCurrent->len); AJ_CRC16_Complete(crc, crcBytes); SlipBytes(slip, crcBytes, 2); slip->buffer[slip->actualLen++] = BOUNDARY_BYTE; }
AJ_Status AJ_SerialTX_Init() { int i; TxPkt volatile* prev; if (AJ_SerialLinkParams.packetSize == 0) { return AJ_ERR_DRIVER; } /* * Initialize local static data */ txQueue = NULL; txSent = NULL; txFreeList = NULL; txUnreliable = NULL; txSeqNum = 0; resendPrimed = FALSE; pendingAcks = 0; currentTxAck = 0; dataSent = 1; /* * Data packets: To maximize throughput we need as many packets as the * window size. */ for (i = 0; i < AJ_SerialLinkParams.maxWindowSize; ++i) { void* payload; prev = txFreeList; txFreeList = AJ_Malloc(sizeof(TxPkt)); payload = AJ_Malloc(AJ_SerialLinkParams.packetSize); if (!txFreeList || !payload) { return AJ_ERR_RESOURCES; } txFreeList->payload = payload; txFreeList->next = prev; } AJ_SlippedBuffer volatile* prevBuf = NULL; bufferTxFreeList = NULL; for (i = 0; i < AJ_SerialLinkParams.maxWindowSize; i++) { void* buf; prevBuf = bufferTxFreeList; bufferTxFreeList = AJ_Malloc(sizeof(AJ_SlippedBuffer)); buf = AJ_Malloc(SLIPPED_LEN(AJ_SerialLinkParams.packetSize)); //TODO: calculate slipped length based on packet size if (!bufferTxFreeList || !buf) { return AJ_ERR_RESOURCES; } bufferTxFreeList->buffer = buf; bufferTxFreeList->actualLen = 0; bufferTxFreeList->allocatedLen = SLIPPED_LEN(AJ_SerialLinkParams.packetSize); bufferTxFreeList->next = prevBuf; } prevBuf = bufferTxFreeList; /* * Buffer for unreliable packets */ txUnreliable = (TxPkt*)AJ_Malloc(sizeof(TxPkt)); memset((void*)txUnreliable, 0, sizeof(TxPkt)); txUnreliable->payload = (uint8_t*)AJ_Malloc(AJ_LINK_PACKET_PAYLOAD); AJ_InitTimer(&resendTime); AJ_TimeAddOffset(&resendTime, AJ_TIMER_FOREVER); resendPrimed = FALSE; AJ_InitTimer(&ackTime); AJ_TimeAddOffset(&ackTime, AJ_TIMER_FOREVER); AJ_SetTxSerialTransmit(&__AJ_TX); AJ_SetTxCB(&AJ_TransmitCallback); return AJ_OK; }