/* qav_qbv.c - Wind River 802.1Qav and 802.1Qbv library */ /* * Copyright (c) 2016~2017 Wind River Systems, Inc. * * The right to copy, distribute, modify, or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. */ /* modification history -------------------- 05sep16,xxy Init version. */ /** * @file qav_qbv.c * @date Mon Sep 05 10:10:02 2010 * * @brief Functions to interact with the network mux and NIC driver. * * */ /* includes */ #include #include #include #include #include /* defines */ #define CYCLE_NUMBER_MAX 8 #define CYCLE_RUN_OK 1 /* typedefs */ #ifndef BILLION #define BILLION 1000000000L #endif typedef struct tsnQbvCycle { int cycleId; void * ethCookie; UINT64 cycleStartCount; size_t cycleLen; QBV_TIME_SLOT * QBVTsHead; int runningFlag; } TSN_QBV_CYCLE; typedef struct tsnQavQbvInfo { int cycleNum; TSN_QBV_CYCLE cycleOpt[CYCLE_NUMBER_MAX]; } TSN_QAVQBV_INFO; /* globals */ /* locals */ LOCAL int qavQbvInited = 0; LOCAL TSN_QAVQBV_INFO qbvCycleInfo; /* forward declarations */ /****************************************************************************** * * qbvTsCreateHead - create the QBV queue head * * This function creates a QBV queue head bound to a specific timer cycle. It is * called by tsnQbvTimerCycleCreate(). * * RETURNS: HEAD or NULL * * ERRNO: N/A */ LOCAL QBV_TIME_SLOT *qbvTsCreateHead (void) { QBV_TIME_SLOT *head; head = (QBV_TIME_SLOT *)malloc(sizeof (QBV_TIME_SLOT) ); if (head == NULL) { return NULL; } head->next = NULL; return head; } /****************************************************************************** * * qbvTsInsertNod - insert a node in the QBV queue * * This function inserts a node in the QBV queue. It is called by * tsnQbvTimeSlotAdd(). * * RETURNS: OK or EINVAL * * ERRNO: N/A */ LOCAL void qbvTsInsertNod ( QBV_TIME_SLOT *head, QBV_TIME_SLOT *s ) { QBV_TIME_SLOT *p = head; while ((p->next != NULL) && (s->slotStart > p->next->slotStart)) { p = p->next; } if (p->next == NULL) { s->next = NULL; p->next = s; } else { s->next = p->next; p->next = s; } return ; } /****************************************************************************** * * qbvTsSearchNod - search for a QBV node * * This function searchs for a QBV node. It is called by tsnQbvTimeSlotAdd(). * * RETURNS: node or NULL * * ERRNO: N/A */ LOCAL QBV_TIME_SLOT *qbvTsSearchNod ( QBV_TIME_SLOT *head, int tsId ) { QBV_TIME_SLOT *p = head->next; while ( (p != NULL) && (p->slotId != tsId)) { p = p->next; } if (p == NULL) { return NULL; } return p; } /****************************************************************************** * * qbvTsDeleteNod - delete a node from the QBV queue list * * This function deletes a node from the QBV queue list. It is called by * tsnQbvTimeSlotDelete(). * * RETURNS: OK or ERROR * * ERRNO: N/A */ LOCAL STATUS qbvTsDeleteNod ( QBV_TIME_SLOT *head, int tsId ) { QBV_TIME_SLOT *p1 = head->next , *p2 = head ; while ( (p1 != NULL) && (p1->slotId != tsId)) { p2 = p1; p1 = p1->next; } if (p1 == NULL) { printf("node not founded!!!\n"); return ERROR; } if (p1->slotId == tsId) { p2->next = p1->next; free(p1); printf("delete sucess!\n"); } return OK; } /******************************************************************************* * * tsnQbvTimerCycleCreate - create a QBV timer cycle * * This function creates a QBV timer cycle bound to a specific Ethernet physical * device. * * * Parameters: * \is * \i * A pointer to the Ethernet device name. * \i * Ethernet device index. * \i * A pointer to the absolute time according to the 1588 clock when the QBV cycle * comes into effect. The 1588 clock time can be obtained by calling * muxIoctl(ethCookie, EIOCG1588SYSTIM, (void *)&ts). If this parameter is * zero, or less than the current time, the QBV cycle comes into effect * immediately. If this parameter is a future time, tsnQbvMuxSend() will not send * the packet and return ERROR. * \i * Length of time for the cycle. The units are us. * \ie * * RETURNS: \is * \i 'cycle ID' * if successful. * \i 'ERROR' * if unsuccessful. * \ie * * ERRNO: * * * SYNOPSIS * int tsnQbvTimerCycleCreate * ( * char *pName, * int index, * struct timespec *cycleStart, * size_t cycleLen * ) * */ int tsnQbvTimerCycleCreate ( char *pName, /*eth device name */ int index,/*eth device index */ struct timespec *cycleStart,/*start time of cycle*/ size_t cycleLen /*length of time cycle,unit us*/ ) { void *ethCookie; size_t cyclelen = cycleLen; TSN_QBV_CYCLE *pCycle; if (qavQbvInited == 0) { memset(&qbvCycleInfo, 0, sizeof(TSN_QAVQBV_INFO)); qavQbvInited = 1; } if (qbvCycleInfo.cycleNum >= CYCLE_NUMBER_MAX ) { (void)printf("Achieve max cycle number.\n"); return ERROR; } ethCookie = (void *)endFindByName(pName, index); if (ethCookie == NULL) { (void)printf("QBV Interface %s%d does not exist.\n", pName, index); return ERROR; } pCycle = &qbvCycleInfo.cycleOpt[qbvCycleInfo.cycleNum]; if (pCycle->runningFlag != CYCLE_RUN_OK) { pCycle->cycleId = qbvCycleInfo.cycleNum; pCycle->ethCookie = ethCookie; pCycle->cycleStartCount = (UINT64)((((UINT64)cycleStart->tv_sec) * BILLION) + (UINT64)cycleStart->tv_nsec); pCycle->cycleLen = cyclelen * 1000; pCycle->QBVTsHead = qbvTsCreateHead(); if (pCycle->QBVTsHead == NULL) { (void)printf("create TS head failed!\n"); return ERROR; } pCycle->runningFlag = CYCLE_RUN_OK; qbvCycleInfo.cycleNum++; return (pCycle->cycleId); } return ERROR; } /******************************************************************************* * * tsnQbvTimeSlotAdd - add a time slot to the timer cycle of the QBV * * This function adds a time slot to the timer cycle of the QBV. cycleId is the * return value of the tsnQbvTimerCycleCreate( ) function. slotId is a unique, * user-assigned value. * * Parameters: * \is * \i * ID for the cycle. This value is returned by tsnQbvTimerCycleCreate(). * \i * Time slot ID. This value is a unique, user-assigned value. * \i * Offset to the start boundary for each cycle. The unit for this offset is us. * \i * Length of the time slot. The unit for this length is us. * \ie * * RETURNS: \is * \i 'OK' * if successful. * \i 'ERROR' * if unsuccessful. * \ie * * ERRNO: * * * SYNOPSIS * STATUS tsnQbvTimeSlotAdd * ( * int cycleId, * int slotId, * int slotStart, * size_t slotLen * ) */ STATUS tsnQbvTimeSlotAdd ( int cycleId, /* ID of cycle */ int slotId, /* ID of time slot */ int slotStart, /* Offset of cycle start,unit us */ size_t slotLen /* length of time slot,unit us */ ) { QBV_TIME_SLOT *pnod; QBV_TIME_SLOT *tsHead; TSN_QBV_CYCLE *pCycle; if (cycleId >= qbvCycleInfo.cycleNum ) { (void)printf("cycle ID %d not created.\n", cycleId); return ERROR; } pCycle = &qbvCycleInfo.cycleOpt[cycleId]; tsHead = pCycle->QBVTsHead; pnod = qbvTsSearchNod(tsHead, slotId); if (pnod != NULL) { printf("slot %d alloced error!\n", slotId); return ERROR; } pnod = (QBV_TIME_SLOT *)malloc(sizeof (QBV_TIME_SLOT) ); if (pnod == NULL) { printf("create nod failed!\n"); return ERROR; } pnod->slotId = slotId; pnod->slotStart = slotStart * 1000; pnod->slotLen = slotLen * 1000; qbvTsInsertNod(tsHead, pnod ); return OK; } /******************************************************************************* * * tsnQbvTimeSlotDelete - delete a time slot from the timer cycle of the QBV * * This function deletes a time slot from the timer cycle of the QBV. * * Parameters: * \is * \i * ID for the cycle. This value is returned by tsnQbvTimerCycleCreate(). * \i * Time slot ID. This value is the unique, user-assigned value that was passed * to tsnQbvTimeSlotAdd. * \ie * * RETURNS: \is * \i 'OK' * if successful. * \i 'ERROR' * if unsuccessful. * \ie * * ERRNO: * * SYNOPSIS * STATUS tsnQbvTimeSlotDelete * ( * int cycleId, * int slotId * ) * */ STATUS tsnQbvTimeSlotDelete ( int cycleId, /*ID of cycle*/ int slotId /*ID of time slot*/ ) { QBV_TIME_SLOT *tsHead; TSN_QBV_CYCLE *pCycle; if (cycleId >= qbvCycleInfo.cycleNum ) { (void)printf("cycle ID %d not created.\n", cycleId); return ERROR; } pCycle = &qbvCycleInfo.cycleOpt[cycleId]; tsHead = pCycle->QBVTsHead; return (qbvTsDeleteNod(tsHead, slotId)); } /****************************************************************************** * * tsnQbvTimeSlotShow - display time slot information for a cycle * * This function displays the time slot information for a cycle. * * RETURNS: OK or ERROR * * ERRNO: N/A */ void tsnQbvTimeSlotShow ( int cycleId /*ID of cycle*/ ) { QBV_TIME_SLOT *tsHead; QBV_TIME_SLOT *p; TSN_QBV_CYCLE *pCycle; if (cycleId >= qbvCycleInfo.cycleNum ) { (void)printf("cycle ID %d not created.\n", cycleId); return; } pCycle = &qbvCycleInfo.cycleOpt[cycleId]; tsHead = pCycle->QBVTsHead; p = tsHead->next; while (p != NULL) { printf("ID:%d,cycle:%lu, start:%d ,len:%lu\n", p->slotId, pCycle->cycleLen, p->slotStart, p->slotLen); p = p->next; } return; } /******************************************************************************* * * tsnQbvMuxSend - send an Ethernet packet by TSN QBV mux * * This function sends an Ethernet packet by TSN QBV mux. The cycleId is the * return value of the tsnQbvTimerCycleCreate( ) function. The cycleOffset is * the number of additional cycles to be added to the current running cycles. * If the cycleOffset is zero, the TSN-scheduled send is launched in the next * cycle adjacent to the current running cycle. The cycleOffset is used to * ensure that the TSN-scheduled send is launched in a future time. Because * it is limited by the NIC, the value of cycleOffset should not be a large * value. For example, the Intel I210 chip can only hold launch time for a * maximum of 0.5 seconds. Therefore, the value of cycleOffset is calculated * as follows: (cycleOffset * cycleLen) < 500 ms. slotDelay is the time added * to the start time of the slot. slotDelay is used to keep the order for * multiple packets in the same slot. slotId is the parameter passed to * tsnQbvTimeSlotAdd(). When calling tsnQbvMuxSend(), slotId must reuse the * value that was previously passed to tsnQbvTimeSlotAdd(). * * * Parameters: * \is * \i * ID for the cycle. This value is returned by tsnQbvTimerCycleCreate(). * \i * The number of additional cycles to be added to the current running cycles. * \i * Time slot ID. This value is the unique, user-assigned value that was passed * to tsnQbvTimeSlotAdd. * \i * The time added to the start time of the slot. slotDelay is used to keep the * order for multiple packets in the same slot. The units are ns. * \i * trafficType : 0 Time-Aware(TSN-scheduled) * 1 Credit-based A(none TSN-scheduled) * 2 Credit-based B(none TSN-scheduled) * 3 Prioritized(none TSN-scheduled) * 4 Best-Effort(none TSN-scheduled) * \i * A pointer to the buffer to be sent. * \i * Length of the buffer. The units are bytes. * \ie * * RETURNS: \is * \i 'OK' * if successful. * \i 'ERROR' * if unsuccessful. * \ie * * ERRNO: * * SYNOPSIS * STATUS tsnQbvMuxSend * ( * int cycleId, * int cycleOffset, * int slotId, * int slotDelay, * int trafficType, * const void *buf, * size_t len * ) * */ STATUS tsnQbvMuxSend ( int cycleId, int cycleOffset, int slotId, int slotDelay, int trafficType, const void *buf, size_t len ) { TSN_QBV_CYCLE *pCycle; END_OBJ *pEnd; M_BLK_ID pMblk; QBV_TIME_SLOT *tsHead; QBV_TIME_SLOT *pnod; STATUS status; UINT32 numOfBytes = 0; struct timespec tsCycle; UINT64 runningCounts; if (cycleId >= qbvCycleInfo.cycleNum ) { (void)printf("cycle ID %d not created.\n", cycleId); return ERROR; } pCycle = &qbvCycleInfo.cycleOpt[cycleId]; pEnd = (END_OBJ *)pCycle->ethCookie; if ((pMblk = endPoolTupleGet (pEnd->pNetPool)) == NULL) { return ERROR; } numOfBytes = (UINT32)netMblkFromBufCopy(pMblk, (char *)buf, 0, (UINT32)len, M_WAIT, NULL); pMblk->mBlkHdr.mLen = numOfBytes; pMblk->mBlkHdr.mFlags = M_EXT | M_PKTHDR; pMblk->mBlkHdr.mType = MT_DATA; pMblk->mBlkPktHdr.len = numOfBytes; pMblk->mBlkPktHdr.launch_time = 0; pMblk->mBlkPktHdr.time_flags = 0; switch (trafficType) { case 0: case 1: pMblk->mBlkPktHdr.queue_priority = PKT_PRIORITY_FLAGS_SR; break; case 2: pMblk->mBlkPktHdr.queue_priority = PKT_PRIORITY_FLAGS_SR2; break; case 3: pMblk->mBlkPktHdr.queue_priority = PKT_PRIORITY_FLAGS_SP; break; default: pMblk->mBlkPktHdr.queue_priority = PKT_PRIORITY_FLAGS_BE; break; } if (trafficType == 0) { tsHead = pCycle->QBVTsHead; pnod = qbvTsSearchNod(tsHead, slotId); if (pnod == NULL) { endPoolTupleFree(pMblk); return ERROR; } status = muxIoctl(pEnd, EIOCG1588SYSTIM, (void *)&tsCycle); if (status != OK) { endPoolTupleFree(pMblk); return ERROR; } /*Get the current running time */ runningCounts = (UINT64)((((UINT64)tsCycle.tv_sec) * BILLION) + (UINT64)tsCycle.tv_nsec); /*Calculate the launch time*/ if (pCycle->cycleStartCount > 0) { /* Check that cycle start time is reached */ if (runningCounts < pCycle->cycleStartCount) { endPoolTupleFree (pMblk); return ERROR; } } /* Calculate the number of cycles passed since cycle start */ runningCounts = (runningCounts - pCycle->cycleStartCount)/pCycle->cycleLen; /* Send packet in the next cycle plus offset cycles */ runningCounts += cycleOffset + 1; /* Time interval of send cycle relative to the cycle start in nanoseconds */ runningCounts *= pCycle->cycleLen; /* Add cycles start time, timeslot offset and slot delay */ runningCounts += pCycle->cycleStartCount + pnod->slotStart + slotDelay; /* Launchtime is the nanoseconds part of the absolute launchtime */ pMblk->mBlkPktHdr.launch_time = (UINT32)(runningCounts%BILLION); pMblk->mBlkPktHdr.time_flags = PKT_TSN_TIME_LAUNCH_EN; } status = muxSend(pEnd, pMblk); return status; }