/*
 * 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);
}
Exemple #5
0
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;
    }
}
Exemple #7
0
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;
}