AB&T / SOEM

Dependents:   EasyCAT_LAB_simple EasyCAT_LAB_very_simple EasyCAT_LAB

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ethercatmain.c Source File

ethercatmain.c

Go to the documentation of this file.
00001 /*
00002  * Licensed under the GNU General Public License version 2 with exceptions. See
00003  * LICENSE file in the project root for full license information
00004  */
00005 
00006 /**
00007  * \file
00008  * \brief
00009  * Main EtherCAT functions.
00010  *
00011  * Initialisation, state set and read, mailbox primitives, EEPROM primitives,
00012  * SII reading and processdata exchange.
00013  *
00014  * Defines ec_slave[]. All slave information is put in this structure.
00015  * Needed for most user interaction with slaves.
00016  */
00017 
00018 #include <stdio.h>
00019 #include <string.h>
00020 #include "osal.h"
00021 #include "oshw.h"
00022 #include "ethercat.h"
00023 
00024 
00025 /** delay in us for eeprom ready loop */
00026 #define EC_LOCALDELAY  200
00027 
00028 /** record for ethercat eeprom communications */
00029 PACKED_BEGIN
00030 typedef struct PACKED
00031 {
00032    uint16    comm;
00033    uint16    addr;
00034    uint16    d2;
00035 } ec_eepromt;
00036 PACKED_END
00037 
00038 /** mailbox error structure */
00039 PACKED_BEGIN
00040 typedef struct PACKED
00041 {
00042    ec_mbxheadert   MbxHeader;
00043    uint16          Type;
00044    uint16          Detail;
00045 } ec_mbxerrort;
00046 PACKED_END
00047 
00048 /** emergency request structure */
00049 PACKED_BEGIN
00050 typedef struct PACKED
00051 {
00052    ec_mbxheadert   MbxHeader;
00053    uint16          CANOpen;
00054    uint16          ErrorCode;
00055    uint8           ErrorReg;
00056    uint8           bData;
00057    uint16          w1,w2;
00058 } ec_emcyt;
00059 PACKED_END
00060 
00061 #ifdef EC_VER1
00062 /** Main slave data array.
00063  *  Each slave found on the network gets its own record.
00064  *  ec_slave[0] is reserved for the master. Structure gets filled
00065  *  in by the configuration function ec_config().
00066  */
00067 ec_slavet               ec_slave[EC_MAXSLAVE];
00068 /** number of slaves found on the network */
00069 int                     ec_slavecount;
00070 /** slave group structure */
00071 ec_groupt               ec_group[EC_MAXGROUP];
00072 
00073 /** cache for EEPROM read functions */
00074 static uint8            ec_esibuf[EC_MAXEEPBUF];
00075 /** bitmap for filled cache buffer bytes */
00076 static uint32           ec_esimap[EC_MAXEEPBITMAP];
00077 /** current slave for EEPROM cache buffer */
00078 static ec_eringt        ec_elist;
00079 static ec_idxstackT     ec_idxstack;
00080 
00081 /** SyncManager Communication Type struct to store data of one slave */
00082 static ec_SMcommtypet   ec_SMcommtype[EC_MAX_MAPT];
00083 /** PDO assign struct to store data of one slave */
00084 static ec_PDOassignt    ec_PDOassign[EC_MAX_MAPT];
00085 /** PDO description struct to store data of one slave */
00086 static ec_PDOdesct      ec_PDOdesc[EC_MAX_MAPT];
00087 
00088 /** buffer for EEPROM SM data */
00089 static ec_eepromSMt     ec_SM;
00090 /** buffer for EEPROM FMMU data */
00091 static ec_eepromFMMUt   ec_FMMU;
00092 /** Global variable TRUE if error available in error stack */
00093 boolean                 EcatError = FALSE;
00094 
00095 int64                   ec_DCtime;
00096 
00097 ecx_portt               ecx_port;
00098 ecx_redportt            ecx_redport;
00099 
00100 ecx_contextt  ecx_context = {
00101     &ecx_port,          // .port          =
00102     &ec_slave[0],       // .slavelist     =
00103     &ec_slavecount,     // .slavecount    =
00104     EC_MAXSLAVE,        // .maxslave      =
00105     &ec_group[0],       // .grouplist     =
00106     EC_MAXGROUP,        // .maxgroup      =
00107     &ec_esibuf[0],      // .esibuf        =
00108     &ec_esimap[0],      // .esimap        =
00109     0,                  // .esislave      =
00110     &ec_elist,          // .elist         =
00111     &ec_idxstack,       // .idxstack      =
00112     &EcatError,         // .ecaterror     =
00113     0,                  // .DCtO          =
00114     0,                  // .DCl           =
00115     &ec_DCtime,         // .DCtime        =
00116     &ec_SMcommtype[0],  // .SMcommtype    =
00117     &ec_PDOassign[0],   // .PDOassign     =
00118     &ec_PDOdesc[0],     // .PDOdesc       =
00119     &ec_SM,             // .eepSM         =
00120     &ec_FMMU,           // .eepFMMU       =
00121     NULL,               // .FOEhook()
00122     NULL                // .EOEhook()
00123 };
00124 #endif
00125 
00126 /** Create list over available network adapters.
00127  *
00128  * @return First element in list over available network adapters.
00129  */
00130 ec_adaptert * ec_find_adapters (void)
00131 {
00132    ec_adaptert * ret_adapter;
00133 
00134    ret_adapter = oshw_find_adapters ();
00135 
00136    return ret_adapter;
00137 }
00138 
00139 /** Free dynamically allocated list over available network adapters.
00140  *
00141  * @param[in] adapter = Struct holding adapter name, description and pointer to next.
00142  */
00143 void ec_free_adapters (ec_adaptert * adapter)
00144 {
00145    oshw_free_adapters (adapter);
00146 }
00147 
00148 /** Pushes an error on the error list.
00149  *
00150  * @param[in] context        = context struct
00151  * @param[in] Ec pointer describing the error.
00152  */
00153 void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec)
00154 {
00155    context->elist->Error[context->elist->head] = *Ec;
00156    context->elist->Error[context->elist->head].Signal = TRUE;
00157    context->elist->head++;
00158    if (context->elist->head > EC_MAXELIST)
00159    {
00160       context->elist->head = 0;
00161    }
00162    if (context->elist->head == context->elist->tail)
00163    {
00164       context->elist->tail++;
00165    }
00166    if (context->elist->tail > EC_MAXELIST)
00167    {
00168       context->elist->tail = 0;
00169    }
00170    *(context->ecaterror) = TRUE;
00171 }
00172 
00173 /** Pops an error from the list.
00174  *
00175  * @param[in] context        = context struct
00176  * @param[out] Ec = Struct describing the error.
00177  * @return TRUE if an error was popped.
00178  */
00179 boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec)
00180 {
00181    boolean notEmpty = (context->elist->head != context->elist->tail);
00182 
00183    *Ec = context->elist->Error[context->elist->tail];
00184    context->elist->Error[context->elist->tail].Signal = FALSE;
00185    if (notEmpty)
00186    {
00187       context->elist->tail++;
00188       if (context->elist->tail > EC_MAXELIST)
00189       {
00190          context->elist->tail = 0;
00191       }
00192    }
00193    else
00194    {
00195       *(context->ecaterror) = FALSE;
00196    }
00197    return notEmpty;
00198 }
00199 
00200 /** Check if error list has entries.
00201  *
00202  * @param[in] context        = context struct
00203  * @return TRUE if error list contains entries.
00204  */
00205 boolean ecx_iserror(ecx_contextt *context)
00206 {
00207    return (context->elist->head != context->elist->tail);
00208 }
00209 
00210 /** Report packet error
00211  *
00212  * @param[in]  context        = context struct
00213  * @param[in]  Slave      = Slave number
00214  * @param[in]  Index      = Index that generated error
00215  * @param[in]  SubIdx     = Subindex that generated error
00216  * @param[in]  ErrorCode  = Error code
00217  */
00218 void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode)
00219 {
00220    ec_errort Ec;
00221 
00222    memset(&Ec, 0, sizeof(Ec));
00223    Ec.Time = osal_current_time();
00224    Ec.Slave = Slave;
00225    Ec.Index = Index;
00226    Ec.SubIdx = SubIdx;
00227    *(context->ecaterror) = TRUE;
00228    Ec.Etype = EC_ERR_TYPE_PACKET_ERROR;
00229    Ec.ErrorCode = ErrorCode;
00230    ecx_pusherror(context, &Ec);
00231 }
00232 
00233 /** Report Mailbox Error
00234  *
00235  * @param[in]  context        = context struct
00236  * @param[in]  Slave        = Slave number
00237  * @param[in]  Detail       = Following EtherCAT specification
00238  */
00239 static void ecx_mbxerror(ecx_contextt *context, uint16 Slave,uint16 Detail)
00240 {
00241    ec_errort Ec;
00242 
00243    memset(&Ec, 0, sizeof(Ec));
00244    Ec.Time = osal_current_time();
00245    Ec.Slave = Slave;
00246    Ec.Index = 0;
00247    Ec.SubIdx = 0;
00248    Ec.Etype = EC_ERR_TYPE_MBX_ERROR;
00249    Ec.ErrorCode = Detail;
00250    ecx_pusherror(context, &Ec);
00251 }
00252 
00253 /** Report Mailbox Emergency Error
00254  *
00255  * @param[in]  context        = context struct
00256  * @param[in]  Slave      = Slave number
00257  * @param[in]  ErrorCode  = Following EtherCAT specification
00258  * @param[in]  ErrorReg
00259  * @param[in]  b1
00260  * @param[in]  w1
00261  * @param[in]  w2
00262  */
00263 static void ecx_mbxemergencyerror(ecx_contextt *context, uint16 Slave,uint16 ErrorCode,uint16 ErrorReg,
00264     uint8 b1, uint16 w1, uint16 w2)
00265 {
00266    ec_errort Ec;
00267 
00268    memset(&Ec, 0, sizeof(Ec));
00269    Ec.Time = osal_current_time();
00270    Ec.Slave = Slave;
00271    Ec.Index = 0;
00272    Ec.SubIdx = 0;
00273    Ec.Etype = EC_ERR_TYPE_EMERGENCY;
00274    Ec.ErrorCode = ErrorCode;
00275    Ec.ErrorReg = (uint8)ErrorReg;
00276    Ec.b1 = b1;
00277    Ec.w1 = w1;
00278    Ec.w2 = w2;
00279    ecx_pusherror(context, &Ec);
00280 }
00281 
00282 /** Initialise lib in single NIC mode
00283  * @param[in]  context = context struct
00284  * @param[in] ifname   = Dev name, f.e. "eth0"
00285  * @return >0 if OK
00286  */
00287 int ecx_init(ecx_contextt *context, const char * ifname)
00288 {
00289    return ecx_setupnic(context->port, ifname, FALSE);
00290 }
00291 
00292 /** Initialise lib in redundant NIC mode
00293  * @param[in]  context  = context struct
00294  * @param[in]  redport  = pointer to redport, redundant port data
00295  * @param[in]  ifname   = Primary Dev name, f.e. "eth0"
00296  * @param[in]  if2name  = Secondary Dev name, f.e. "eth1"
00297  * @return >0 if OK
00298  */
00299 int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name)
00300 {
00301    int rval, zbuf;
00302    ec_etherheadert *ehp;
00303 
00304    context->port->redport = redport;
00305    ecx_setupnic(context->port, ifname, FALSE);
00306    rval = ecx_setupnic(context->port, if2name, TRUE);
00307    /* prepare "dummy" BRD tx frame for redundant operation */
00308    ehp = (ec_etherheadert *)&(context->port->txbuf2);
00309    ehp->sa1 = oshw_htons(secMAC[0]);
00310    zbuf = 0;
00311    ecx_setupdatagram(context->port, &(context->port->txbuf2), EC_CMD_BRD, 0, 0x0000, 0x0000, 2, &zbuf);
00312    context->port->txbuflength2 = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + 2;
00313 
00314    return rval;
00315 }
00316 
00317 /** Close lib.
00318  * @param[in]  context        = context struct
00319  */
00320 void ecx_close(ecx_contextt *context)
00321 {
00322    ecx_closenic(context->port);
00323 };
00324 
00325 /** Read one byte from slave EEPROM via cache.
00326  *  If the cache location is empty then a read request is made to the slave.
00327  *  Depending on the slave capabilities the request is 4 or 8 bytes.
00328  *  @param[in] context = context struct
00329  *  @param[in] slave   = slave number
00330  *  @param[in] address = eeprom address in bytes (slave uses words)
00331  *  @return requested byte, if not available then 0xff
00332  */
00333 uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address)
00334 {
00335    uint16 configadr, eadr;
00336    uint64 edat64;
00337    uint32 edat32;
00338    uint16 mapw, mapb;
00339    int lp,cnt;
00340    uint8 retval;
00341 
00342    retval = 0xff;
00343    if (slave != context->esislave) /* not the same slave? */
00344    {
00345       memset(context->esimap, 0x00, EC_MAXEEPBITMAP * sizeof(uint32)); /* clear esibuf cache map */
00346       context->esislave = slave;
00347    }
00348    if (address < EC_MAXEEPBUF)
00349    {
00350       mapw = address >> 5;
00351       mapb = address - (mapw << 5);
00352       if (context->esimap[mapw] & (uint32)(1 << mapb))
00353       {
00354          /* byte is already in buffer */
00355          retval = context->esibuf[address];
00356       }
00357       else
00358       {
00359          /* byte is not in buffer, put it there */
00360          configadr = context->slavelist[slave].configadr;
00361          ecx_eeprom2master(context, slave); /* set eeprom control to master */
00362          eadr = address >> 1;
00363          edat64 = ecx_readeepromFP (context, configadr, eadr, EC_TIMEOUTEEP);
00364          /* 8 byte response */
00365          if (context->slavelist[slave].eep_8byte)
00366          {
00367             put_unaligned64(edat64, &(context->esibuf[eadr << 1]));
00368             cnt = 8;
00369          }
00370          /* 4 byte response */
00371          else
00372          {
00373             edat32 = (uint32)edat64;
00374             put_unaligned32(edat32, &(context->esibuf[eadr << 1]));
00375             cnt = 4;
00376          }
00377          /* find bitmap location */
00378          mapw = eadr >> 4;
00379          mapb = (eadr << 1) - (mapw << 5);
00380          for(lp = 0 ; lp < cnt ; lp++)
00381          {
00382             /* set bitmap for each byte that is read */
00383             context->esimap[mapw] |= (1 << mapb);
00384             mapb++;
00385             if (mapb > 31)
00386             {
00387                mapb = 0;
00388                mapw++;
00389             }
00390          }
00391          retval = context->esibuf[address];
00392       }
00393    }
00394 
00395    return retval;
00396 }
00397 
00398 /** Find SII section header in slave EEPROM.
00399  *  @param[in]  context        = context struct
00400  *  @param[in] slave   = slave number
00401  *  @param[in] cat     = section category
00402  *  @return byte address of section at section length entry, if not available then 0
00403  */
00404 int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat)
00405 {
00406    int16 a;
00407    uint16 p;
00408    uint8 eectl = context->slavelist[slave].eep_pdi;
00409 
00410    a = ECT_SII_START << 1;
00411    /* read first SII section category */
00412    p = ecx_siigetbyte(context, slave, a++);
00413    p += (ecx_siigetbyte(context, slave, a++) << 8);
00414    /* traverse SII while category is not found and not EOF */
00415    while ((p != cat) && (p != 0xffff))
00416    {
00417       /* read section length */
00418       p = ecx_siigetbyte(context, slave, a++);
00419       p += (ecx_siigetbyte(context, slave, a++) << 8);
00420       /* locate next section category */
00421       a += p << 1;
00422       /* read section category */
00423       p = ecx_siigetbyte(context, slave, a++);
00424       p += (ecx_siigetbyte(context, slave, a++) << 8);
00425    }
00426    if (p != cat)
00427    {
00428       a = 0;
00429    }
00430    if (eectl)
00431    {
00432       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
00433    }
00434 
00435    return a;
00436 }
00437 
00438 /** Get string from SII string section in slave EEPROM.
00439  *  @param[in]  context = context struct
00440  *  @param[out] str     = requested string, 0x00 if not found
00441  *  @param[in]  slave   = slave number
00442  *  @param[in]  Sn      = string number
00443  */
00444 void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn)
00445 {
00446    uint16 a,i,j,l,n,ba;
00447    char *ptr;
00448    uint8 eectl = context->slavelist[slave].eep_pdi;
00449 
00450    ptr = str;
00451    a = ecx_siifind (context, slave, ECT_SII_STRING); /* find string section */
00452    if (a > 0)
00453    {
00454       ba = a + 2; /* skip SII section header */
00455       n = ecx_siigetbyte(context, slave, ba++); /* read number of strings in section */
00456       if (Sn <= n) /* is req string available? */
00457       {
00458          for (i = 1; i <= Sn; i++) /* walk through strings */
00459          {
00460             l = ecx_siigetbyte(context, slave, ba++); /* length of this string */
00461             if (i < Sn)
00462             {
00463                ba += l;
00464             }
00465             else
00466             {
00467                ptr = str;
00468                for (j = 1; j <= l; j++) /* copy one string */
00469                {
00470                   if(j <= EC_MAXNAME)
00471                   {
00472                      *ptr = (char)ecx_siigetbyte(context, slave, ba++);
00473                      ptr++;
00474                   }
00475                   else
00476                   {
00477                      ba++;
00478                   }
00479                }
00480             }
00481          }
00482          *ptr = 0; /* add zero terminator */
00483       }
00484       else
00485       {
00486          ptr = str;
00487          *ptr = 0; /* empty string */
00488       }
00489    }
00490    if (eectl)
00491    {
00492       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
00493    }
00494 }
00495 
00496 /** Get FMMU data from SII FMMU section in slave EEPROM.
00497  *  @param[in]  context = context struct
00498  *  @param[in]  slave   = slave number
00499  *  @param[out] FMMU    = FMMU struct from SII, max. 4 FMMU's
00500  *  @return number of FMMU's defined in section
00501  */
00502 uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU)
00503 {
00504    uint16  a;
00505    uint8 eectl = context->slavelist[slave].eep_pdi;
00506 
00507    FMMU->nFMMU = 0;
00508    FMMU->FMMU0 = 0;
00509    FMMU->FMMU1 = 0;
00510    FMMU->FMMU2 = 0;
00511    FMMU->FMMU3 = 0;
00512    FMMU->Startpos = ecx_siifind(context, slave, ECT_SII_FMMU);
00513 
00514    if (FMMU->Startpos > 0)
00515    {
00516       a = FMMU->Startpos;
00517       FMMU->nFMMU = ecx_siigetbyte(context, slave, a++);
00518       FMMU->nFMMU += (ecx_siigetbyte(context, slave, a++) << 8);
00519       FMMU->nFMMU *= 2;
00520       FMMU->FMMU0 = ecx_siigetbyte(context, slave, a++);
00521       FMMU->FMMU1 = ecx_siigetbyte(context, slave, a++);
00522       if (FMMU->nFMMU > 2)
00523       {
00524          FMMU->FMMU2 = ecx_siigetbyte(context, slave, a++);
00525          FMMU->FMMU3 = ecx_siigetbyte(context, slave, a++);
00526       }
00527    }
00528    if (eectl)
00529    {
00530       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
00531    }
00532 
00533    return FMMU->nFMMU;
00534 }
00535 
00536 /** Get SM data from SII SM section in slave EEPROM.
00537  *  @param[in]  context = context struct
00538  *  @param[in]  slave   = slave number
00539  *  @param[out] SM      = first SM struct from SII
00540  *  @return number of SM's defined in section
00541  */
00542 uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM)
00543 {
00544    uint16 a,w;
00545    uint8 eectl = context->slavelist[slave].eep_pdi;
00546 
00547    SM->nSM = 0;
00548    SM->Startpos = ecx_siifind(context, slave, ECT_SII_SM);
00549    if (SM->Startpos > 0)
00550    {
00551       a = SM->Startpos;
00552       w = ecx_siigetbyte(context, slave, a++);
00553       w += (ecx_siigetbyte(context, slave, a++) << 8);
00554       SM->nSM = (w / 4);
00555       SM->PhStart = ecx_siigetbyte(context, slave, a++);
00556       SM->PhStart += (ecx_siigetbyte(context, slave, a++) << 8);
00557       SM->Plength = ecx_siigetbyte(context, slave, a++);
00558       SM->Plength += (ecx_siigetbyte(context, slave, a++) << 8);
00559       SM->Creg = ecx_siigetbyte(context, slave, a++);
00560       SM->Sreg = ecx_siigetbyte(context, slave, a++);
00561       SM->Activate = ecx_siigetbyte(context, slave, a++);
00562       SM->PDIctrl = ecx_siigetbyte(context, slave, a++);
00563    }
00564    if (eectl)
00565    {
00566       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
00567    }
00568 
00569    return SM->nSM;
00570 }
00571 
00572 /** Get next SM data from SII SM section in slave EEPROM.
00573  *  @param[in]  context = context struct
00574  *  @param[in]  slave   = slave number
00575  *  @param[out] SM      = first SM struct from SII
00576  *  @param[in]  n       = SM number
00577  *  @return >0 if OK
00578  */
00579 uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n)
00580 {
00581    uint16 a;
00582    uint16 retVal = 0;
00583    uint8 eectl = context->slavelist[slave].eep_pdi;
00584 
00585    if (n < SM->nSM)
00586    {
00587       a = SM->Startpos + 2 + (n * 8);
00588       SM->PhStart = ecx_siigetbyte(context, slave, a++);
00589       SM->PhStart += (ecx_siigetbyte(context, slave, a++) << 8);
00590       SM->Plength = ecx_siigetbyte(context, slave, a++);
00591       SM->Plength += (ecx_siigetbyte(context, slave, a++) << 8);
00592       SM->Creg = ecx_siigetbyte(context, slave, a++);
00593       SM->Sreg = ecx_siigetbyte(context, slave, a++);
00594       SM->Activate = ecx_siigetbyte(context, slave, a++);
00595       SM->PDIctrl = ecx_siigetbyte(context, slave, a++);
00596       retVal = 1;
00597    }
00598    if (eectl)
00599    {
00600       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
00601    }
00602 
00603    return retVal;
00604 }
00605 
00606 /** Get PDO data from SII PDO section in slave EEPROM.
00607  *  @param[in]  context = context struct
00608  *  @param[in]  slave   = slave number
00609  *  @param[out] PDO     = PDO struct from SII
00610  *  @param[in]  t       = 0=RXPDO 1=TXPDO
00611  *  @return mapping size in bits of PDO
00612  */
00613 int ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t)
00614 {
00615    uint16 a , w, c, e, er, Size;
00616    uint8 eectl = context->slavelist[slave].eep_pdi;
00617 
00618    Size = 0;
00619    PDO->nPDO = 0;
00620    PDO->Length = 0;
00621    PDO->Index[1] = 0;
00622    for (c = 0 ; c < EC_MAXSM ; c++) PDO->SMbitsize[c] = 0;
00623    if (t > 1)
00624       t = 1;
00625    PDO->Startpos = ecx_siifind(context, slave, ECT_SII_PDO + t);
00626    if (PDO->Startpos > 0)
00627    {
00628       a = PDO->Startpos;
00629       w = ecx_siigetbyte(context, slave, a++);
00630       w += (ecx_siigetbyte(context, slave, a++) << 8);
00631       PDO->Length = w;
00632       c = 1;
00633       /* traverse through all PDOs */
00634       do
00635       {
00636          PDO->nPDO++;
00637          PDO->Index[PDO->nPDO] = ecx_siigetbyte(context, slave, a++);
00638          PDO->Index[PDO->nPDO] += (ecx_siigetbyte(context, slave, a++) << 8);
00639          PDO->BitSize[PDO->nPDO] = 0;
00640          c++;
00641          e = ecx_siigetbyte(context, slave, a++);
00642          PDO->SyncM[PDO->nPDO] = ecx_siigetbyte(context, slave, a++);
00643          a += 4;
00644          c += 2;
00645          if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) /* active and in range SM? */
00646          {
00647             /* read all entries defined in PDO */
00648             for (er = 1; er <= e; er++)
00649             {
00650                c += 4;
00651                a += 5;
00652                PDO->BitSize[PDO->nPDO] += ecx_siigetbyte(context, slave, a++);
00653                a += 2;
00654             }
00655             PDO->SMbitsize[ PDO->SyncM[PDO->nPDO] ] += PDO->BitSize[PDO->nPDO];
00656             Size += PDO->BitSize[PDO->nPDO];
00657             c++;
00658          }
00659          else /* PDO deactivated because SM is 0xff or > EC_MAXSM */
00660          {
00661             c += 4 * e;
00662             a += 8 * e;
00663             c++;
00664          }
00665          if (PDO->nPDO >= (EC_MAXEEPDO - 1))
00666          {
00667             c = PDO->Length; /* limit number of PDO entries in buffer */
00668          }
00669       }
00670       while (c < PDO->Length);
00671    }
00672    if (eectl)
00673    {
00674       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
00675    }
00676 
00677    return (Size);
00678 }
00679 
00680 #define MAX_FPRD_MULTI 64
00681 
00682 int ecx_FPRD_multi(ecx_contextt *context, int n, uint16 *configlst, ec_alstatust *slstatlst, int timeout)
00683 {
00684    int wkc;
00685    uint8 idx;
00686    ecx_portt *port;
00687    int sldatapos[MAX_FPRD_MULTI];
00688    int slcnt;
00689 
00690    port = context->port;
00691    idx = ecx_getindex(port);
00692    slcnt = 0;
00693    ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx,
00694       *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
00695    sldatapos[slcnt] = EC_HEADERSIZE;
00696    while(++slcnt < (n - 1))
00697    {
00698       sldatapos[slcnt] = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, TRUE,
00699                             *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
00700    }
00701    if(slcnt < n)
00702    {
00703       sldatapos[slcnt] = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, FALSE,
00704                             *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
00705    }
00706    wkc = ecx_srconfirm(port, idx, timeout);
00707    if (wkc >= 0)
00708    {
00709       for(slcnt = 0 ; slcnt < n ; slcnt++)
00710       {
00711          memcpy(slstatlst + slcnt, &(port->rxbuf[idx][sldatapos[slcnt]]), sizeof(ec_alstatust));
00712       }
00713    }
00714    ecx_setbufstat(port, idx, EC_BUF_EMPTY);
00715    return wkc;
00716 }
00717 
00718 /** Read all slave states in ec_slave.
00719  * @param[in] context = context struct
00720  * @return lowest state found
00721  */
00722 int ecx_readstate(ecx_contextt *context)
00723 {
00724    uint16 slave, fslave, lslave, configadr, lowest, rval, bitwisestate;
00725    ec_alstatust sl[MAX_FPRD_MULTI];
00726    uint16 slca[MAX_FPRD_MULTI];
00727    boolean noerrorflag, allslavessamestate;
00728    boolean allslavespresent = FALSE;
00729    int wkc;
00730 
00731    /* Try to establish the state of all slaves sending only one broadcast datagram.
00732     * This way a number of datagrams equal to the number of slaves will be sent only if needed.*/
00733    rval = 0;
00734    wkc = ecx_BRD(context->port, 0, ECT_REG_ALSTAT, sizeof(rval), &rval, EC_TIMEOUTRET);
00735 
00736    if(wkc >= *(context->slavecount))
00737    {
00738       allslavespresent = TRUE;
00739    }
00740 
00741    rval = etohs(rval);
00742    bitwisestate = (rval & 0x0f);
00743 
00744    if ((rval & EC_STATE_ERROR) == 0)
00745    {
00746       noerrorflag = TRUE;
00747       context->slavelist[0].ALstatuscode = 0;
00748    }   
00749    else
00750    {
00751       noerrorflag = FALSE;
00752    }
00753 
00754    switch (bitwisestate)
00755    {
00756       case EC_STATE_INIT:
00757       case EC_STATE_PRE_OP:
00758       case EC_STATE_BOOT:
00759       case EC_STATE_SAFE_OP:
00760       case EC_STATE_OPERATIONAL:
00761          allslavessamestate = TRUE;
00762          context->slavelist[0].state = bitwisestate;
00763          break;
00764       default:
00765          allslavessamestate = FALSE;
00766          break;
00767    }
00768     
00769    if (noerrorflag && allslavessamestate && allslavespresent)
00770    {
00771       /* No slave has toggled the error flag so the alstatuscode
00772        * (even if different from 0) should be ignored and
00773        * the slaves have reached the same state so the internal state
00774        * can be updated without sending any datagram. */
00775       for (slave = 1; slave <= *(context->slavecount); slave++)
00776       {
00777          context->slavelist[slave].ALstatuscode = 0x0000;
00778          context->slavelist[slave].state = bitwisestate;
00779       }
00780       lowest = bitwisestate;
00781    }
00782    else
00783    {
00784       /* Not all slaves have the same state or at least one is in error so one datagram per slave
00785        * is needed. */
00786       context->slavelist[0].ALstatuscode = 0;
00787       lowest = 0xff;
00788       fslave = 1;
00789       do
00790       {
00791          lslave = *(context->slavecount);
00792          if ((lslave - fslave) >= MAX_FPRD_MULTI)
00793          {
00794             lslave = fslave + MAX_FPRD_MULTI - 1;
00795          }
00796          for (slave = fslave; slave <= lslave; slave++)
00797          {
00798             const ec_alstatust zero = { 0, 0, 0 };
00799 
00800             configadr = context->slavelist[slave].configadr;
00801             slca[slave - fslave] = configadr;
00802             sl[slave - fslave] = zero;
00803          }
00804          ecx_FPRD_multi(context, (lslave - fslave) + 1, &(slca[0]), &(sl[0]), EC_TIMEOUTRET3);
00805          for (slave = fslave; slave <= lslave; slave++)
00806          {
00807             configadr = context->slavelist[slave].configadr;
00808             rval = etohs(sl[slave - fslave].alstatus);
00809             context->slavelist[slave].ALstatuscode = etohs(sl[slave - fslave].alstatuscode);
00810             if ((rval & 0xf) < lowest)
00811             {
00812                lowest = (rval & 0xf);
00813             }
00814             context->slavelist[slave].state = rval;
00815             context->slavelist[0].ALstatuscode |= context->slavelist[slave].ALstatuscode;
00816          }
00817          fslave = lslave + 1;
00818       } while (lslave < *(context->slavecount));
00819       context->slavelist[0].state = lowest;
00820    }
00821   
00822    return lowest;
00823 }
00824 
00825 /** Write slave state, if slave = 0 then write to all slaves.
00826  * The function does not check if the actual state is changed.
00827  * @param[in]  context        = context struct
00828  * @param[in] slave    = Slave number, 0 = master
00829  * @return Workcounter or EC_NOFRAME
00830  */
00831 int ecx_writestate(ecx_contextt *context, uint16 slave)
00832 {
00833    int ret;
00834    uint16 configadr, slstate;
00835 
00836    if (slave == 0)
00837    {
00838       slstate = htoes(context->slavelist[slave].state);
00839       ret = ecx_BWR(context->port, 0, ECT_REG_ALCTL, sizeof(slstate),
00840                 &slstate, EC_TIMEOUTRET3);
00841    }
00842    else
00843    {
00844       configadr = context->slavelist[slave].configadr;
00845 
00846       ret = ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL,
00847             htoes(context->slavelist[slave].state), EC_TIMEOUTRET3);
00848    }
00849    return ret;
00850 }
00851 
00852 /** Check actual slave state.
00853  * This is a blocking function.
00854  * To refresh the state of all slaves ecx_readstate()should be called
00855  * @param[in] context     = context struct
00856  * @param[in] slave       = Slave number, 0 = all slaves (only the "slavelist[0].state" is refreshed)
00857  * @param[in] reqstate    = Requested state
00858  * @param[in] timeout     = Timeout value in us
00859  * @return Requested state, or found state after timeout.
00860  */
00861 uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout)
00862 {
00863    uint16 configadr, state, rval;
00864    ec_alstatust slstat;
00865    osal_timert timer;
00866 
00867    if ( slave > *(context->slavecount) )
00868    {
00869       return 0;
00870    }
00871    osal_timer_start(&timer, timeout);
00872    configadr = context->slavelist[slave].configadr;
00873    do
00874    {
00875       if (slave < 1)
00876       {
00877          rval = 0;
00878          ecx_BRD(context->port, 0, ECT_REG_ALSTAT, sizeof(rval), &rval , EC_TIMEOUTRET);
00879          rval = etohs(rval);
00880       }
00881       else
00882       {
00883          slstat.alstatus = 0;
00884          slstat.alstatuscode = 0;
00885          ecx_FPRD(context->port, configadr, ECT_REG_ALSTAT, sizeof(slstat), &slstat, EC_TIMEOUTRET);
00886          rval = etohs(slstat.alstatus);
00887          context->slavelist[slave].ALstatuscode = etohs(slstat.alstatuscode);
00888       }
00889       state = rval & 0x000f; /* read slave status */
00890       if (state != reqstate)
00891       {
00892          osal_usleep(1000);
00893       }
00894    }
00895    while ((state != reqstate) && (osal_timer_is_expired(&timer) == FALSE));
00896    context->slavelist[slave].state = rval;
00897 
00898    return state;
00899 }
00900 
00901 /** Get index of next mailbox counter value.
00902  * Used for Mailbox Link Layer.
00903  * @param[in] cnt     = Mailbox counter value [0..7]
00904  * @return next mailbox counter value
00905  */
00906 uint8 ec_nextmbxcnt(uint8 cnt)
00907 {
00908    cnt++;
00909    if (cnt > 7)
00910    {
00911       cnt = 1; /* wrap around to 1, not 0 */
00912    }
00913 
00914    return cnt;
00915 }
00916 
00917 /** Clear mailbox buffer.
00918  * @param[out] Mbx     = Mailbox buffer to clear
00919  */
00920 void ec_clearmbx(ec_mbxbuft *Mbx)
00921 {
00922     memset(Mbx, 0x00, EC_MAXMBX);
00923 }
00924 
00925 /** Check if IN mailbox of slave is empty.
00926  * @param[in] context  = context struct
00927  * @param[in] slave    = Slave number
00928  * @param[in] timeout  = Timeout in us
00929  * @return >0 is success
00930  */
00931 int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout)
00932 {
00933    uint16 configadr;
00934    uint8 SMstat;
00935    int wkc;
00936    osal_timert timer;
00937 
00938    osal_timer_start(&timer, timeout);
00939    configadr = context->slavelist[slave].configadr;
00940    do
00941    {
00942       SMstat = 0;
00943       wkc = ecx_FPRD(context->port, configadr, ECT_REG_SM0STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
00944       SMstat = etohs(SMstat);
00945       if (((SMstat & 0x08) != 0) && (timeout > EC_LOCALDELAY))
00946       {
00947          osal_usleep(EC_LOCALDELAY);
00948       }
00949    }
00950    while (((wkc <= 0) || ((SMstat & 0x08) != 0)) && (osal_timer_is_expired(&timer) == FALSE));
00951 
00952    if ((wkc > 0) && ((SMstat & 0x08) == 0))
00953    {
00954       return 1;
00955    }
00956 
00957    return 0;
00958 }
00959 
00960 /** Write IN mailbox to slave.
00961  * @param[in]  context    = context struct
00962  * @param[in]  slave      = Slave number
00963  * @param[out] mbx        = Mailbox data
00964  * @param[in]  timeout    = Timeout in us
00965  * @return Work counter (>0 is success)
00966  */
00967 int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout)
00968 {
00969    uint16 mbxwo,mbxl,configadr;
00970    int wkc;
00971 
00972    wkc = 0;
00973    configadr = context->slavelist[slave].configadr;
00974    mbxl = context->slavelist[slave].mbx_l;
00975    if ((mbxl > 0) && (mbxl <= EC_MAXMBX))
00976    {
00977       if (ecx_mbxempty(context, slave, timeout))
00978       {
00979          mbxwo = context->slavelist[slave].mbx_wo;
00980          /* write slave in mailbox */
00981          wkc = ecx_FPWR(context->port, configadr, mbxwo, mbxl, mbx, EC_TIMEOUTRET3);
00982       }
00983       else
00984       {
00985          wkc = 0;
00986       }
00987    }
00988 
00989    return wkc;
00990 }
00991 
00992 /** Read OUT mailbox from slave.
00993  * Supports Mailbox Link Layer with repeat requests.
00994  * @param[in]  context    = context struct
00995  * @param[in]  slave      = Slave number
00996  * @param[out] mbx        = Mailbox data
00997  * @param[in]  timeout    = Timeout in us
00998  * @return Work counter (>0 is success)
00999  */
01000 int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout)
01001 {
01002    uint16 mbxro,mbxl,configadr;
01003    int wkc=0;
01004    int wkc2;
01005    uint16 SMstat;
01006    uint8 SMcontr;
01007    ec_mbxheadert *mbxh;
01008    ec_emcyt *EMp;
01009    ec_mbxerrort *MBXEp;
01010 
01011    configadr = context->slavelist[slave].configadr;
01012    mbxl = context->slavelist[slave].mbx_rl;
01013    if ((mbxl > 0) && (mbxl <= EC_MAXMBX))
01014    {
01015       osal_timert timer;
01016 
01017       osal_timer_start(&timer, timeout);
01018       wkc = 0;
01019       do /* wait for read mailbox available */
01020       {
01021          SMstat = 0;
01022          wkc = ecx_FPRD(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
01023          SMstat = etohs(SMstat);
01024          if (((SMstat & 0x08) == 0) && (timeout > EC_LOCALDELAY))
01025          {
01026             osal_usleep(EC_LOCALDELAY);
01027          }
01028       }
01029       while (((wkc <= 0) || ((SMstat & 0x08) == 0)) && (osal_timer_is_expired(&timer) == FALSE));
01030 
01031       if ((wkc > 0) && ((SMstat & 0x08) > 0)) /* read mailbox available ? */
01032       {
01033          mbxro = context->slavelist[slave].mbx_ro;
01034          mbxh = (ec_mbxheadert *)mbx;
01035          do
01036          {
01037             wkc = ecx_FPRD(context->port, configadr, mbxro, mbxl, mbx, EC_TIMEOUTRET); /* get mailbox */
01038             if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == 0x00)) /* Mailbox error response? */
01039             {
01040                MBXEp = (ec_mbxerrort *)mbx;
01041                ecx_mbxerror(context, slave, etohs(MBXEp->Detail));
01042                wkc = 0; /* prevent emergency to cascade up, it is already handled. */
01043             }
01044             else if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == ECT_MBXT_COE)) /* CoE response? */
01045             {
01046                EMp = (ec_emcyt *)mbx;
01047                if ((etohs(EMp->CANOpen) >> 12) == 0x01) /* Emergency request? */
01048                {
01049                   ecx_mbxemergencyerror(context, slave, etohs(EMp->ErrorCode), EMp->ErrorReg,
01050                           EMp->bData, etohs(EMp->w1), etohs(EMp->w2));
01051                   wkc = 0; /* prevent emergency to cascade up, it is already handled. */
01052                }
01053             }
01054             else if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == ECT_MBXT_EOE)) /* EoE response? */
01055             {
01056                ec_EOEt * eoembx = (ec_EOEt *)mbx;
01057                uint16 frameinfo1 = etohs(eoembx->frameinfo1);
01058                /* All non fragment data frame types are expected to be handled by
01059                * slave send/receive API if the EoE hook is set
01060                */
01061                if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) == EOE_FRAG_DATA)
01062                {
01063                   if (context->EOEhook)
01064                   {
01065                      if (context->EOEhook(context, slave, eoembx) > 0)
01066                      {
01067                         /* Fragment handled by EoE hook */
01068                         wkc = 0;
01069                      }
01070                   }
01071                }
01072             }
01073             else
01074             {
01075                if (wkc <= 0) /* read mailbox lost */
01076                {
01077                   SMstat ^= 0x0200; /* toggle repeat request */
01078                   SMstat = htoes(SMstat);
01079                   wkc2 = ecx_FPWR(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
01080                   SMstat = etohs(SMstat);
01081                   do /* wait for toggle ack */
01082                   {
01083                      wkc2 = ecx_FPRD(context->port, configadr, ECT_REG_SM1CONTR, sizeof(SMcontr), &SMcontr, EC_TIMEOUTRET);
01084                    } while (((wkc2 <= 0) || ((SMcontr & 0x02) != (HI_BYTE(SMstat) & 0x02))) && (osal_timer_is_expired(&timer) == FALSE));
01085                   do /* wait for read mailbox available */
01086                   {
01087                      wkc2 = ecx_FPRD(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
01088                      SMstat = etohs(SMstat);
01089                      if (((SMstat & 0x08) == 0) && (timeout > EC_LOCALDELAY))
01090                      {
01091                         osal_usleep(EC_LOCALDELAY);
01092                      }
01093                   } while (((wkc2 <= 0) || ((SMstat & 0x08) == 0)) && (osal_timer_is_expired(&timer) == FALSE));
01094                }
01095             }
01096          } while ((wkc <= 0) && (osal_timer_is_expired(&timer) == FALSE)); /* if WKC<=0 repeat */
01097       }
01098       else /* no read mailbox available */
01099       {
01100           wkc = 0;
01101       }
01102    }
01103 
01104    return wkc;
01105 }
01106 
01107 /** Dump complete EEPROM data from slave in buffer.
01108  * @param[in]  context  = context struct
01109  * @param[in]  slave    = Slave number
01110  * @param[out] esibuf   = EEPROM data buffer, make sure it is big enough.
01111  */
01112 void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf)
01113 {
01114    int address, incr;
01115    uint16 configadr;
01116    uint64 *p64;
01117    uint16 *p16;
01118    uint64 edat;
01119    uint8 eectl = context->slavelist[slave].eep_pdi;
01120 
01121    ecx_eeprom2master(context, slave); /* set eeprom control to master */
01122    configadr = context->slavelist[slave].configadr;
01123    address = ECT_SII_START;
01124    p16=(uint16*)esibuf;
01125    if (context->slavelist[slave].eep_8byte)
01126    {
01127       incr = 4;
01128    }
01129    else
01130    {
01131       incr = 2;
01132    }
01133    do
01134    {
01135       edat = ecx_readeepromFP(context, configadr, address, EC_TIMEOUTEEP);
01136       p64 = (uint64*)p16;
01137       *p64 = edat;
01138       p16 += incr;
01139       address += incr;
01140    } while ((address <= (EC_MAXEEPBUF >> 1)) && ((uint32)edat != 0xffffffff));
01141 
01142    if (eectl)
01143    {
01144       ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
01145    }
01146 }
01147 
01148 /** Read EEPROM from slave bypassing cache.
01149  * @param[in] context   = context struct
01150  * @param[in] slave     = Slave number
01151  * @param[in] eeproma   = (WORD) Address in the EEPROM
01152  * @param[in] timeout   = Timeout in us.
01153  * @return EEPROM data 32bit
01154  */
01155 uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout)
01156 {
01157    uint16 configadr;
01158 
01159    ecx_eeprom2master(context, slave); /* set eeprom control to master */
01160    configadr = context->slavelist[slave].configadr;
01161 
01162    return ((uint32)ecx_readeepromFP(context, configadr, eeproma, timeout));
01163 }
01164 
01165 /** Write EEPROM to slave bypassing cache.
01166  * @param[in] context   = context struct
01167  * @param[in] slave     = Slave number
01168  * @param[in] eeproma   = (WORD) Address in the EEPROM
01169  * @param[in] data      = 16bit data
01170  * @param[in] timeout   = Timeout in us.
01171  * @return >0 if OK
01172  */
01173 int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout)
01174 {
01175    uint16 configadr;
01176 
01177    ecx_eeprom2master(context, slave); /* set eeprom control to master */
01178    configadr = context->slavelist[slave].configadr;
01179    return (ecx_writeeepromFP(context, configadr, eeproma, data, timeout));
01180 }
01181 
01182 /** Set eeprom control to master. Only if set to PDI.
01183  * @param[in] context   = context struct
01184  * @param[in] slave     = Slave number
01185  * @return >0 if OK
01186  */
01187 int ecx_eeprom2master(ecx_contextt *context, uint16 slave)
01188 {
01189    int wkc = 1, cnt = 0;
01190    uint16 configadr;
01191    uint8 eepctl;
01192 
01193    if ( context->slavelist[slave].eep_pdi )
01194    {
01195       configadr = context->slavelist[slave].configadr;
01196       eepctl = 2;
01197       do
01198       {
01199          wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* force Eeprom from PDI */
01200       }
01201       while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01202       eepctl = 0;
01203       cnt = 0;
01204       do
01205       {
01206          wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* set Eeprom to master */
01207       }
01208       while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01209       context->slavelist[slave].eep_pdi = 0;
01210    }
01211 
01212    return wkc;
01213 }
01214 
01215 /** Set eeprom control to PDI. Only if set to master.
01216  * @param[in]  context        = context struct
01217  * @param[in] slave     = Slave number
01218  * @return >0 if OK
01219  */
01220 int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave)
01221 {
01222    int wkc = 1, cnt = 0;
01223    uint16 configadr;
01224    uint8 eepctl;
01225 
01226    if ( !context->slavelist[slave].eep_pdi )
01227    {
01228       configadr = context->slavelist[slave].configadr;
01229       eepctl = 1;
01230       do
01231       {
01232          wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* set Eeprom to PDI */
01233       }
01234       while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01235       context->slavelist[slave].eep_pdi = 1;
01236    }
01237 
01238    return wkc;
01239 }
01240 
01241 uint16 ecx_eeprom_waitnotbusyAP(ecx_contextt *context, uint16 aiadr,uint16 *estat, int timeout)
01242 {
01243    int wkc, cnt = 0, retval = 0;
01244    osal_timert timer;
01245 
01246    osal_timer_start(&timer, timeout);
01247    do
01248    {
01249       if (cnt++)
01250       {
01251          osal_usleep(EC_LOCALDELAY);
01252       }
01253       *estat = 0;
01254       wkc=ecx_APRD(context->port, aiadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
01255       *estat = etohs(*estat);
01256    }
01257    while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
01258    if ((*estat & EC_ESTAT_BUSY) == 0)
01259    {
01260       retval = 1;
01261    }
01262 
01263    return retval;
01264 }
01265 
01266 /** Read EEPROM from slave bypassing cache. APRD method.
01267  * @param[in] context     = context struct
01268  * @param[in] aiadr       = auto increment address of slave
01269  * @param[in] eeproma     = (WORD) Address in the EEPROM
01270  * @param[in] timeout     = Timeout in us.
01271  * @return EEPROM data 64bit or 32bit
01272  */
01273 uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout)
01274 {
01275    uint16 estat;
01276    uint32 edat32;
01277    uint64 edat64;
01278    ec_eepromt ed;
01279    int wkc, cnt, nackcnt = 0;
01280 
01281    edat64 = 0;
01282    edat32 = 0;
01283    if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
01284    {
01285       if (estat & EC_ESTAT_EMASK) /* error bits are set */
01286       {
01287          estat = htoes(EC_ECMD_NOP); /* clear error bits */
01288          wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
01289       }
01290 
01291       do
01292       {
01293          ed.comm = htoes(EC_ECMD_READ);
01294          ed.addr = htoes(eeproma);
01295          ed.d2   = 0x0000;
01296          cnt = 0;
01297          do
01298          {
01299             wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
01300          }
01301          while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01302          if (wkc)
01303          {
01304             osal_usleep(EC_LOCALDELAY);
01305             estat = 0x0000;
01306             if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
01307             {
01308                if (estat & EC_ESTAT_NACK)
01309                {
01310                   nackcnt++;
01311                   osal_usleep(EC_LOCALDELAY * 5);
01312                }
01313                else
01314                {
01315                   nackcnt = 0;
01316                   if (estat & EC_ESTAT_R64)
01317                   {
01318                      cnt = 0;
01319                      do
01320                      {
01321                         wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
01322                      }
01323                      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01324                   }
01325                   else
01326                   {
01327                      cnt = 0;
01328                      do
01329                      {
01330                         wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
01331                      }
01332                      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01333                      edat64=(uint64)edat32;
01334                   }
01335                }
01336             }
01337          }
01338       }
01339       while ((nackcnt > 0) && (nackcnt < 3));
01340    }
01341 
01342    return edat64;
01343 }
01344 
01345 /** Write EEPROM to slave bypassing cache. APWR method.
01346  * @param[in] context   = context struct
01347  * @param[in] aiadr     = configured address of slave
01348  * @param[in] eeproma   = (WORD) Address in the EEPROM
01349  * @param[in] data      = 16bit data
01350  * @param[in] timeout   = Timeout in us.
01351  * @return >0 if OK
01352  */
01353 int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout)
01354 {
01355    uint16 estat;
01356    ec_eepromt ed;
01357    int wkc, rval = 0, cnt = 0, nackcnt = 0;
01358 
01359    if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
01360    {
01361       if (estat & EC_ESTAT_EMASK) /* error bits are set */
01362       {
01363          estat = htoes(EC_ECMD_NOP); /* clear error bits */
01364          wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
01365       }
01366       do
01367       {
01368          cnt = 0;
01369          do
01370          {
01371             wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
01372          }
01373          while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01374 
01375          ed.comm = EC_ECMD_WRITE;
01376          ed.addr = eeproma;
01377          ed.d2   = 0x0000;
01378          cnt = 0;
01379          do
01380          {
01381             wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
01382          }
01383          while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01384          if (wkc)
01385          {
01386             osal_usleep(EC_LOCALDELAY * 2);
01387             estat = 0x0000;
01388             if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
01389             {
01390                if (estat & EC_ESTAT_NACK)
01391                {
01392                   nackcnt++;
01393                   osal_usleep(EC_LOCALDELAY * 5);
01394                }
01395                else
01396                {
01397                   nackcnt = 0;
01398                   rval = 1;
01399                }
01400             }
01401          }
01402 
01403       }
01404       while ((nackcnt > 0) && (nackcnt < 3));
01405    }
01406 
01407    return rval;
01408 }
01409 
01410 uint16 ecx_eeprom_waitnotbusyFP(ecx_contextt *context, uint16 configadr,uint16 *estat, int timeout)
01411 {
01412    int wkc, cnt = 0, retval = 0;
01413    osal_timert timer;
01414 
01415    osal_timer_start(&timer, timeout);
01416    do
01417    {
01418       if (cnt++)
01419       {
01420          osal_usleep(EC_LOCALDELAY);
01421       }
01422       *estat = 0;
01423       wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
01424       *estat = etohs(*estat);
01425    }
01426    while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
01427    if ((*estat & EC_ESTAT_BUSY) == 0)
01428    {
01429       retval = 1;
01430    }
01431 
01432    return retval;
01433 }
01434 
01435 /** Read EEPROM from slave bypassing cache. FPRD method.
01436  * @param[in] context     = context struct
01437  * @param[in] configadr   = configured address of slave
01438  * @param[in] eeproma     = (WORD) Address in the EEPROM
01439  * @param[in] timeout     = Timeout in us.
01440  * @return EEPROM data 64bit or 32bit
01441  */
01442 uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout)
01443 {
01444    uint16 estat;
01445    uint32 edat32;
01446    uint64 edat64;
01447    ec_eepromt ed;
01448    int wkc, cnt, nackcnt = 0;
01449 
01450    edat64 = 0;
01451    edat32 = 0;
01452    if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
01453    {
01454       if (estat & EC_ESTAT_EMASK) /* error bits are set */
01455       {
01456          estat = htoes(EC_ECMD_NOP); /* clear error bits */
01457          wkc=ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
01458       }
01459 
01460       do
01461       {
01462          ed.comm = htoes(EC_ECMD_READ);
01463          ed.addr = htoes(eeproma);
01464          ed.d2   = 0x0000;
01465          cnt = 0;
01466          do
01467          {
01468             wkc=ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
01469          }
01470          while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01471          if (wkc)
01472          {
01473             osal_usleep(EC_LOCALDELAY);
01474             estat = 0x0000;
01475             if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
01476             {
01477                if (estat & EC_ESTAT_NACK)
01478                {
01479                   nackcnt++;
01480                   osal_usleep(EC_LOCALDELAY * 5);
01481                }
01482                else
01483                {
01484                   nackcnt = 0;
01485                   if (estat & EC_ESTAT_R64)
01486                   {
01487                      cnt = 0;
01488                      do
01489                      {
01490                         wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
01491                      }
01492                      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01493                   }
01494                   else
01495                   {
01496                      cnt = 0;
01497                      do
01498                      {
01499                         wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
01500                      }
01501                      while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01502                      edat64=(uint64)edat32;
01503                   }
01504                }
01505             }
01506          }
01507       }
01508       while ((nackcnt > 0) && (nackcnt < 3));
01509    }
01510 
01511    return edat64;
01512 }
01513 
01514 /** Write EEPROM to slave bypassing cache. FPWR method.
01515  * @param[in]  context        = context struct
01516  * @param[in] configadr   = configured address of slave
01517  * @param[in] eeproma     = (WORD) Address in the EEPROM
01518  * @param[in] data        = 16bit data
01519  * @param[in] timeout     = Timeout in us.
01520  * @return >0 if OK
01521  */
01522 int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout)
01523 {
01524    uint16 estat;
01525    ec_eepromt ed;
01526    int wkc, rval = 0, cnt = 0, nackcnt = 0;
01527 
01528    if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
01529    {
01530       if (estat & EC_ESTAT_EMASK) /* error bits are set */
01531       {
01532          estat = htoes(EC_ECMD_NOP); /* clear error bits */
01533          wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
01534       }
01535       do
01536       {
01537          cnt = 0;
01538          do
01539          {
01540             wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
01541          }
01542          while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01543          ed.comm = EC_ECMD_WRITE;
01544          ed.addr = eeproma;
01545          ed.d2   = 0x0000;
01546          cnt = 0;
01547          do
01548          {
01549             wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
01550          }
01551          while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01552          if (wkc)
01553          {
01554             osal_usleep(EC_LOCALDELAY * 2);
01555             estat = 0x0000;
01556             if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
01557             {
01558                if (estat & EC_ESTAT_NACK)
01559                {
01560                   nackcnt++;
01561                   osal_usleep(EC_LOCALDELAY * 5);
01562                }
01563                else
01564                {
01565                   nackcnt = 0;
01566                   rval = 1;
01567                }
01568             }
01569          }
01570       }
01571       while ((nackcnt > 0) && (nackcnt < 3));
01572    }
01573 
01574    return rval;
01575 }
01576 
01577 /** Read EEPROM from slave bypassing cache.
01578  * Parallel read step 1, make request to slave.
01579  * @param[in] context     = context struct
01580  * @param[in] slave       = Slave number
01581  * @param[in] eeproma     = (WORD) Address in the EEPROM
01582  */
01583 void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma)
01584 {
01585    uint16 configadr, estat;
01586    ec_eepromt ed;
01587    int wkc, cnt = 0;
01588 
01589    ecx_eeprom2master(context, slave); /* set eeprom control to master */
01590    configadr = context->slavelist[slave].configadr;
01591    if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, EC_TIMEOUTEEP))
01592    {
01593       if (estat & EC_ESTAT_EMASK) /* error bits are set */
01594       {
01595          estat = htoes(EC_ECMD_NOP); /* clear error bits */
01596          wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
01597       }
01598       ed.comm = htoes(EC_ECMD_READ);
01599       ed.addr = htoes(eeproma);
01600       ed.d2   = 0x0000;
01601       do
01602       {
01603          wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
01604       }
01605       while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01606    }
01607 }
01608 
01609 /** Read EEPROM from slave bypassing cache.
01610  * Parallel read step 2, actual read from slave.
01611  * @param[in]  context        = context struct
01612  * @param[in] slave       = Slave number
01613  * @param[in] timeout     = Timeout in us.
01614  * @return EEPROM data 32bit
01615  */
01616 uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout)
01617 {
01618    uint16 estat, configadr;
01619    uint32 edat;
01620    int wkc, cnt = 0;
01621 
01622    configadr = context->slavelist[slave].configadr;
01623    edat = 0;
01624    estat = 0x0000;
01625    if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
01626    {
01627       do
01628       {
01629           wkc = ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat), &edat, EC_TIMEOUTRET);
01630       }
01631       while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
01632    }
01633 
01634    return edat;
01635 }
01636 
01637 /** Push index of segmented LRD/LWR/LRW combination.
01638  * @param[in]  context        = context struct
01639  * @param[in] idx         = Used datagram index.
01640  * @param[in] data        = Pointer to process data segment.
01641  * @param[in] length      = Length of data segment in bytes.
01642  */
01643 static void ecx_pushindex(ecx_contextt *context, uint8 idx, void *data, uint16 length)
01644 {
01645    if(context->idxstack->pushed < EC_MAXBUF)
01646    {
01647       context->idxstack->idx[context->idxstack->pushed] = idx;
01648       context->idxstack->data[context->idxstack->pushed] = data;
01649       context->idxstack->length[context->idxstack->pushed] = length;
01650       context->idxstack->pushed++;
01651    }
01652 }
01653 
01654 /** Pull index of segmented LRD/LWR/LRW combination.
01655  * @param[in]  context        = context struct
01656  * @return Stack location, -1 if stack is empty.
01657  */
01658 static int ecx_pullindex(ecx_contextt *context)
01659 {
01660    int rval = -1;
01661    if(context->idxstack->pulled < context->idxstack->pushed)
01662    {
01663       rval = context->idxstack->pulled;
01664       context->idxstack->pulled++;
01665    }
01666 
01667    return rval;
01668 }
01669 
01670 /** 
01671  * Clear the idx stack.
01672  * 
01673  * @param context           = context struct
01674  */
01675 static void ecx_clearindex(ecx_contextt *context)  {
01676 
01677    context->idxstack->pushed = 0;
01678    context->idxstack->pulled = 0;
01679 
01680 }
01681 
01682 /** Transmit processdata to slaves.
01683  * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
01684  * Both the input and output processdata are transmitted.
01685  * The outputs with the actual data, the inputs have a placeholder.
01686  * The inputs are gathered with the receive processdata function.
01687  * In contrast to the base LRW function this function is non-blocking.
01688  * If the processdata does not fit in one datagram, multiple are used.
01689  * In order to recombine the slave response, a stack is used.
01690  * @param[in]  context        = context struct
01691  * @param[in]  group          = group number
01692  * @return >0 if processdata is transmitted.
01693  */
01694 static int ecx_main_send_processdata(ecx_contextt *context, uint8 group, boolean use_overlap_io)
01695 {
01696    uint32 LogAdr;
01697    uint16 w1, w2;
01698    int length, sublength;
01699    uint8 idx;
01700    int wkc;
01701    uint8* data;
01702    boolean first=FALSE;
01703    uint16 currentsegment = 0;
01704    uint32 iomapinputoffset;
01705 
01706    wkc = 0;
01707    if(context->grouplist[group].hasdc)
01708    {
01709       first = TRUE;
01710    }
01711 
01712    /* For overlapping IO map use the biggest */
01713    if(use_overlap_io == TRUE)
01714    {
01715       /* For overlap IOmap make the frame EQ big to biggest part */
01716       length = (context->grouplist[group].Obytes > context->grouplist[group].Ibytes) ?
01717          context->grouplist[group].Obytes : context->grouplist[group].Ibytes;
01718       /* Save the offset used to compensate where to save inputs when frame returns */
01719       iomapinputoffset = context->grouplist[group].Obytes;
01720    }
01721    else
01722    {
01723       length = context->grouplist[group].Obytes + context->grouplist[group].Ibytes;
01724       iomapinputoffset = 0;
01725    }
01726    
01727    LogAdr = context->grouplist[group].logstartaddr;
01728    if(length)
01729    {
01730 
01731       wkc = 1;
01732       /* LRW blocked by one or more slaves ? */
01733       if(context->grouplist[group].blockLRW)
01734       {
01735          /* if inputs available generate LRD */
01736          if(context->grouplist[group].Ibytes)
01737          {
01738             currentsegment = context->grouplist[group].Isegment;
01739             data = context->grouplist[group].inputs;
01740             length = context->grouplist[group].Ibytes;
01741             LogAdr += context->grouplist[group].Obytes;
01742             /* segment transfer if needed */
01743             do
01744             {
01745                if(currentsegment == context->grouplist[group].Isegment)
01746                {
01747                   sublength = context->grouplist[group].IOsegment[currentsegment++] - context->grouplist[group].Ioffset;
01748                }
01749                else
01750                {
01751                   sublength = context->grouplist[group].IOsegment[currentsegment++];
01752                }
01753                /* get new index */
01754                idx = ecx_getindex(context->port);
01755                w1 = LO_WORD(LogAdr);
01756                w2 = HI_WORD(LogAdr);
01757                ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LRD, idx, w1, w2, sublength, data);
01758                if(first)
01759                {
01760                   context->DCl = sublength;
01761                   /* FPRMW in second datagram */
01762                   context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
01763                                            context->slavelist[context->grouplist[group].DCnext].configadr,
01764                                            ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
01765                   first = FALSE;
01766                }
01767                /* send frame */
01768                ecx_outframe_red(context->port, idx);
01769                /* push index and data pointer on stack */
01770                ecx_pushindex(context, idx, data, sublength);
01771                length -= sublength;
01772                LogAdr += sublength;
01773                data += sublength;
01774             } while (length && (currentsegment < context->grouplist[group].nsegments));
01775          }
01776          /* if outputs available generate LWR */
01777          if(context->grouplist[group].Obytes)
01778          {
01779             data = context->grouplist[group].outputs;
01780             length = context->grouplist[group].Obytes;
01781             LogAdr = context->grouplist[group].logstartaddr;
01782             currentsegment = 0;
01783             /* segment transfer if needed */
01784             do
01785             {
01786                sublength = context->grouplist[group].IOsegment[currentsegment++];
01787                if((length - sublength) < 0)
01788                {
01789                   sublength = length;
01790                }
01791                /* get new index */
01792                idx = ecx_getindex(context->port);
01793                w1 = LO_WORD(LogAdr);
01794                w2 = HI_WORD(LogAdr);
01795                ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LWR, idx, w1, w2, sublength, data);
01796                if(first)
01797                {
01798                   context->DCl = sublength;
01799                   /* FPRMW in second datagram */
01800                   context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
01801                                            context->slavelist[context->grouplist[group].DCnext].configadr,
01802                                            ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
01803                   first = FALSE;
01804                }
01805                /* send frame */
01806                ecx_outframe_red(context->port, idx);
01807                /* push index and data pointer on stack */
01808                ecx_pushindex(context, idx, data, sublength);
01809                length -= sublength;
01810                LogAdr += sublength;
01811                data += sublength;
01812             } while (length && (currentsegment < context->grouplist[group].nsegments));
01813          }
01814       }
01815       /* LRW can be used */
01816       else
01817       {
01818          if (context->grouplist[group].Obytes)
01819          {
01820             data = context->grouplist[group].outputs;
01821          }
01822          else
01823          {
01824             data = context->grouplist[group].inputs;
01825             /* Clear offset, don't compensate for overlapping IOmap if we only got inputs */
01826             iomapinputoffset = 0;
01827          }
01828          /* segment transfer if needed */
01829          do
01830          {
01831             sublength = context->grouplist[group].IOsegment[currentsegment++];
01832             /* get new index */
01833             idx = ecx_getindex(context->port);
01834             w1 = LO_WORD(LogAdr);
01835             w2 = HI_WORD(LogAdr);
01836             ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LRW, idx, w1, w2, sublength, data);
01837             if(first)
01838             {
01839                context->DCl = sublength;
01840                /* FPRMW in second datagram */
01841                context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
01842                                         context->slavelist[context->grouplist[group].DCnext].configadr,
01843                                         ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
01844                first = FALSE;
01845             }
01846             /* send frame */
01847             ecx_outframe_red(context->port, idx);
01848             /* push index and data pointer on stack.
01849              * the iomapinputoffset compensate for where the inputs are stored 
01850              * in the IOmap if we use an overlapping IOmap. If a regular IOmap
01851              * is used it should always be 0.
01852              */
01853             ecx_pushindex(context, idx, (data + iomapinputoffset), sublength);      
01854             length -= sublength;
01855             LogAdr += sublength;
01856             data += sublength;
01857          } while (length && (currentsegment < context->grouplist[group].nsegments));
01858       }
01859    }
01860 
01861    return wkc;
01862 }
01863 
01864 /** Transmit processdata to slaves.
01865 * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
01866 * Both the input and output processdata are transmitted in the overlapped IOmap.
01867 * The outputs with the actual data, the inputs replace the output data in the
01868 * returning frame. The inputs are gathered with the receive processdata function.
01869 * In contrast to the base LRW function this function is non-blocking.
01870 * If the processdata does not fit in one datagram, multiple are used.
01871 * In order to recombine the slave response, a stack is used.
01872 * @param[in]  context        = context struct
01873 * @param[in]  group          = group number
01874 * @return >0 if processdata is transmitted.
01875 */
01876 int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group)
01877 {
01878    return ecx_main_send_processdata(context, group, TRUE);
01879 }
01880 
01881 /** Transmit processdata to slaves.
01882 * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
01883 * Both the input and output processdata are transmitted.
01884 * The outputs with the actual data, the inputs have a placeholder.
01885 * The inputs are gathered with the receive processdata function.
01886 * In contrast to the base LRW function this function is non-blocking.
01887 * If the processdata does not fit in one datagram, multiple are used.
01888 * In order to recombine the slave response, a stack is used.
01889 * @param[in]  context        = context struct
01890 * @param[in]  group          = group number
01891 * @return >0 if processdata is transmitted.
01892 */
01893 int ecx_send_processdata_group(ecx_contextt *context, uint8 group)
01894 {
01895    return ecx_main_send_processdata(context, group, FALSE);
01896 }
01897 
01898 /** Receive processdata from slaves.
01899  * Second part from ec_send_processdata().
01900  * Received datagrams are recombined with the processdata with help from the stack.
01901  * If a datagram contains input processdata it copies it to the processdata structure.
01902  * @param[in]  context        = context struct
01903  * @param[in]  group          = group number
01904  * @param[in]  timeout        = Timeout in us.
01905  * @return Work counter.
01906  */
01907 int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout)
01908 {
01909    int pos, idx;
01910    int wkc = 0, wkc2;
01911    uint16 le_wkc = 0;
01912    int valid_wkc = 0;
01913    int64 le_DCtime;
01914    boolean first = FALSE;
01915 
01916    if(context->grouplist[group].hasdc)
01917    {
01918       first = TRUE;
01919    }
01920    /* get first index */
01921    pos = ecx_pullindex(context);
01922    /* read the same number of frames as send */
01923    while (pos >= 0)
01924    {
01925       idx = context->idxstack->idx[pos];
01926       wkc2 = ecx_waitinframe(context->port, context->idxstack->idx[pos], timeout);
01927       /* check if there is input data in frame */
01928       if (wkc2 > EC_NOFRAME)
01929       {
01930          if((context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD) || (context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRW))
01931          {
01932             if(first)
01933             {
01934                memcpy(context->idxstack->data[pos], &(context->port->rxbuf[idx][EC_HEADERSIZE]), context->DCl);
01935                memcpy(&le_wkc, &(context->port->rxbuf[idx][EC_HEADERSIZE + context->DCl]), EC_WKCSIZE);
01936                wkc = etohs(le_wkc);
01937                memcpy(&le_DCtime, &(context->port->rxbuf[idx][context->DCtO]), sizeof(le_DCtime));
01938                *(context->DCtime) = etohll(le_DCtime);
01939                first = FALSE;
01940             }
01941             else
01942             {
01943                /* copy input data back to process data buffer */
01944                memcpy(context->idxstack->data[pos], &(context->port->rxbuf[idx][EC_HEADERSIZE]), context->idxstack->length[pos]);
01945                wkc += wkc2;
01946             }
01947             valid_wkc = 1;
01948          }
01949          else if(context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LWR)
01950          {
01951             if(first)
01952             {
01953                memcpy(&le_wkc, &(context->port->rxbuf[idx][EC_HEADERSIZE + context->DCl]), EC_WKCSIZE);
01954                /* output WKC counts 2 times when using LRW, emulate the same for LWR */
01955                wkc = etohs(le_wkc) * 2;
01956                memcpy(&le_DCtime, &(context->port->rxbuf[idx][context->DCtO]), sizeof(le_DCtime));
01957                *(context->DCtime) = etohll(le_DCtime);
01958                first = FALSE;
01959             }
01960             else
01961             {
01962                /* output WKC counts 2 times when using LRW, emulate the same for LWR */
01963                wkc += wkc2 * 2;
01964             }
01965             valid_wkc = 1;
01966          }
01967       }
01968       /* release buffer */
01969       ecx_setbufstat(context->port, idx, EC_BUF_EMPTY);
01970       /* get next index */
01971       pos = ecx_pullindex(context);
01972    }
01973 
01974    ecx_clearindex(context);
01975 
01976    /* if no frames has arrived */
01977    if (valid_wkc == 0)
01978    {
01979       return EC_NOFRAME;
01980    }
01981    return wkc;
01982 }
01983 
01984 
01985 int ecx_send_processdata(ecx_contextt *context)
01986 {
01987    return ecx_send_processdata_group(context, 0);
01988 }
01989 
01990 int ecx_send_overlap_processdata(ecx_contextt *context)
01991 {
01992    return ecx_send_overlap_processdata_group(context, 0);
01993 }
01994 
01995 int ecx_receive_processdata(ecx_contextt *context, int timeout)
01996 {
01997    return ecx_receive_processdata_group(context, 0, timeout);
01998 }
01999 
02000 #ifdef EC_VER1
02001 void ec_pusherror(const ec_errort *Ec)
02002 {
02003    ecx_pusherror(&ecx_context, Ec);
02004 }
02005 
02006 boolean ec_poperror(ec_errort *Ec)
02007 {
02008    return ecx_poperror(&ecx_context, Ec);
02009 }
02010 
02011 boolean ec_iserror(void)
02012 {
02013    return ecx_iserror(&ecx_context);
02014 }
02015 
02016 void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode)
02017 {
02018    ecx_packeterror(&ecx_context, Slave, Index, SubIdx, ErrorCode);
02019 }
02020 
02021 /** Initialise lib in single NIC mode
02022  * @param[in] ifname   = Dev name, f.e. "eth0"
02023  * @return >0 if OK
02024  * @see ecx_init
02025  */
02026 int ec_init(const char * ifname)
02027 {
02028    return ecx_init(&ecx_context, ifname);
02029 }
02030 
02031 /** Initialise lib in redundant NIC mode
02032  * @param[in]  ifname   = Primary Dev name, f.e. "eth0"
02033  * @param[in]  if2name  = Secondary Dev name, f.e. "eth1"
02034  * @return >0 if OK
02035  * @see ecx_init_redundant
02036  */
02037 int ec_init_redundant(const char *ifname, char *if2name)
02038 {
02039    return ecx_init_redundant (&ecx_context, &ecx_redport, ifname, if2name);
02040 }
02041 
02042 /** Close lib.
02043  * @see ecx_close
02044  */
02045 void ec_close(void)
02046 {
02047    ecx_close(&ecx_context);
02048 };
02049 
02050 /** Read one byte from slave EEPROM via cache.
02051  *  If the cache location is empty then a read request is made to the slave.
02052  *  Depending on the slave capabillities the request is 4 or 8 bytes.
02053  *  @param[in] slave   = slave number
02054  *  @param[in] address = eeprom address in bytes (slave uses words)
02055  *  @return requested byte, if not available then 0xff
02056  * @see ecx_siigetbyte
02057  */
02058 uint8 ec_siigetbyte(uint16 slave, uint16 address)
02059 {
02060    return ecx_siigetbyte (&ecx_context, slave, address);
02061 }
02062 
02063 /** Find SII section header in slave EEPROM.
02064  *  @param[in] slave   = slave number
02065  *  @param[in] cat     = section category
02066  *  @return byte address of section at section length entry, if not available then 0
02067  *  @see ecx_siifind
02068  */
02069 int16 ec_siifind(uint16 slave, uint16 cat)
02070 {
02071    return ecx_siifind (&ecx_context, slave, cat);
02072 }
02073 
02074 /** Get string from SII string section in slave EEPROM.
02075  *  @param[out] str    = requested string, 0x00 if not found
02076  *  @param[in]  slave  = slave number
02077  *  @param[in]  Sn     = string number
02078  *  @see ecx_siistring
02079  */
02080 void ec_siistring(char *str, uint16 slave, uint16 Sn)
02081 {
02082    ecx_siistring(&ecx_context, str, slave, Sn);
02083 }
02084 
02085 /** Get FMMU data from SII FMMU section in slave EEPROM.
02086  *  @param[in]  slave  = slave number
02087  *  @param[out] FMMU   = FMMU struct from SII, max. 4 FMMU's
02088  *  @return number of FMMU's defined in section
02089  *  @see ecx_siiFMMU
02090  */
02091 uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU)
02092 {
02093    return ecx_siiFMMU (&ecx_context, slave, FMMU);
02094 }
02095 
02096 /** Get SM data from SII SM section in slave EEPROM.
02097  *  @param[in]  slave   = slave number
02098  *  @param[out] SM      = first SM struct from SII
02099  *  @return number of SM's defined in section
02100  *  @see ecx_siiSM
02101  */
02102 uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM)
02103 {
02104    return ecx_siiSM (&ecx_context, slave, SM);
02105 }
02106 
02107 /** Get next SM data from SII SM section in slave EEPROM.
02108  *  @param[in]  slave  = slave number
02109  *  @param[out] SM     = first SM struct from SII
02110  *  @param[in]  n      = SM number
02111  *  @return >0 if OK
02112  *  @see ecx_siiSMnext
02113  */
02114 uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n)
02115 {
02116    return ecx_siiSMnext (&ecx_context, slave, SM, n);
02117 }
02118 
02119 /** Get PDO data from SII PDO section in slave EEPROM.
02120  *  @param[in]  slave  = slave number
02121  *  @param[out] PDO    = PDO struct from SII
02122  *  @param[in]  t      = 0=RXPDO 1=TXPDO
02123  *  @return mapping size in bits of PDO
02124  *  @see ecx_siiPDO
02125  */
02126 int ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t)
02127 {
02128    return ecx_siiPDO (&ecx_context, slave, PDO, t);
02129 }
02130 
02131 /** Read all slave states in ec_slave.
02132  * @return lowest state found
02133  * @see ecx_readstate
02134  */
02135 int ec_readstate(void)
02136 {
02137    return ecx_readstate (&ecx_context);
02138 }
02139 
02140 /** Write slave state, if slave = 0 then write to all slaves.
02141  * The function does not check if the actual state is changed.
02142  * @param[in] slave = Slave number, 0 = master
02143  * @return 0
02144  * @see ecx_writestate
02145  */
02146 int ec_writestate(uint16 slave)
02147 {
02148    return ecx_writestate(&ecx_context, slave);
02149 }
02150 
02151 /** Check actual slave state.
02152  * This is a blocking function.
02153  * @param[in] slave       = Slave number, 0 = all slaves
02154  * @param[in] reqstate    = Requested state
02155  * @param[in] timeout     = Timeout value in us
02156  * @return Requested state, or found state after timeout.
02157  * @see ecx_statecheck
02158  */
02159 uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout)
02160 {
02161    return ecx_statecheck (&ecx_context, slave, reqstate, timeout);
02162 }
02163 
02164 /** Check if IN mailbox of slave is empty.
02165  * @param[in] slave    = Slave number
02166  * @param[in] timeout  = Timeout in us
02167  * @return >0 is success
02168  * @see ecx_mbxempty
02169  */
02170 int ec_mbxempty(uint16 slave, int timeout)
02171 {
02172    return ecx_mbxempty (&ecx_context, slave, timeout);
02173 }
02174 
02175 /** Write IN mailbox to slave.
02176  * @param[in]  slave      = Slave number
02177  * @param[out] mbx        = Mailbox data
02178  * @param[in]  timeout    = Timeout in us
02179  * @return Work counter (>0 is success)
02180  * @see ecx_mbxsend
02181  */
02182 int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout)
02183 {
02184    return ecx_mbxsend (&ecx_context, slave, mbx, timeout);
02185 }
02186 
02187 /** Read OUT mailbox from slave.
02188  * Supports Mailbox Link Layer with repeat requests.
02189  * @param[in]  slave      = Slave number
02190  * @param[out] mbx        = Mailbox data
02191  * @param[in]  timeout    = Timeout in us
02192  * @return Work counter (>0 is success)
02193  * @see ecx_mbxreceive
02194  */
02195 int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout)
02196 {
02197    return ecx_mbxreceive (&ecx_context, slave, mbx, timeout);
02198 }
02199 
02200 /** Dump complete EEPROM data from slave in buffer.
02201  * @param[in]  slave    = Slave number
02202  * @param[out] esibuf   = EEPROM data buffer, make sure it is big enough.
02203  * @see ecx_esidump
02204  */
02205 void ec_esidump(uint16 slave, uint8 *esibuf)
02206 {
02207    ecx_esidump (&ecx_context, slave, esibuf);
02208 }
02209 
02210 /** Read EEPROM from slave bypassing cache.
02211  * @param[in] slave     = Slave number
02212  * @param[in] eeproma   = (WORD) Address in the EEPROM
02213  * @param[in] timeout   = Timeout in us.
02214  * @return EEPROM data 32bit
02215  * @see ecx_readeeprom
02216  */
02217 uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout)
02218 {
02219    return ecx_readeeprom (&ecx_context, slave, eeproma, timeout);
02220 }
02221 
02222 /** Write EEPROM to slave bypassing cache.
02223  * @param[in] slave     = Slave number
02224  * @param[in] eeproma   = (WORD) Address in the EEPROM
02225  * @param[in] data      = 16bit data
02226  * @param[in] timeout   = Timeout in us.
02227  * @return >0 if OK
02228  * @see ecx_writeeeprom
02229  */
02230 int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout)
02231 {
02232    return ecx_writeeeprom (&ecx_context, slave, eeproma, data, timeout);
02233 }
02234 
02235 /** Set eeprom control to master. Only if set to PDI.
02236  * @param[in] slave = Slave number
02237  * @return >0 if OK
02238  * @see ecx_eeprom2master
02239  */
02240 int ec_eeprom2master(uint16 slave)
02241 {
02242    return ecx_eeprom2master(&ecx_context, slave);
02243 }
02244 
02245 int ec_eeprom2pdi(uint16 slave)
02246 {
02247    return ecx_eeprom2pdi(&ecx_context, slave);
02248 }
02249 
02250 uint16 ec_eeprom_waitnotbusyAP(uint16 aiadr,uint16 *estat, int timeout)
02251 {
02252    return ecx_eeprom_waitnotbusyAP (&ecx_context, aiadr, estat, timeout);
02253 }
02254 
02255 /** Read EEPROM from slave bypassing cache. APRD method.
02256  * @param[in] aiadr       = auto increment address of slave
02257  * @param[in] eeproma     = (WORD) Address in the EEPROM
02258  * @param[in] timeout     = Timeout in us.
02259  * @return EEPROM data 64bit or 32bit
02260  */
02261 uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout)
02262 {
02263    return ecx_readeepromAP (&ecx_context, aiadr, eeproma, timeout);
02264 }
02265 
02266 /** Write EEPROM to slave bypassing cache. APWR method.
02267  * @param[in] aiadr     = configured address of slave
02268  * @param[in] eeproma   = (WORD) Address in the EEPROM
02269  * @param[in] data      = 16bit data
02270  * @param[in] timeout   = Timeout in us.
02271  * @return >0 if OK
02272  * @see ecx_writeeepromAP
02273  */
02274 int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout)
02275 {
02276    return ecx_writeeepromAP (&ecx_context, aiadr, eeproma, data, timeout);
02277 }
02278 
02279 uint16 ec_eeprom_waitnotbusyFP(uint16 configadr,uint16 *estat, int timeout)
02280 {
02281    return ecx_eeprom_waitnotbusyFP (&ecx_context, configadr, estat, timeout);
02282 }
02283 
02284 /** Read EEPROM from slave bypassing cache. FPRD method.
02285  * @param[in] configadr   = configured address of slave
02286  * @param[in] eeproma     = (WORD) Address in the EEPROM
02287  * @param[in] timeout     = Timeout in us.
02288  * @return EEPROM data 64bit or 32bit
02289  * @see ecx_readeepromFP
02290  */
02291 uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout)
02292 {
02293    return ecx_readeepromFP (&ecx_context, configadr, eeproma, timeout);
02294 }
02295 
02296 /** Write EEPROM to slave bypassing cache. FPWR method.
02297  * @param[in] configadr   = configured address of slave
02298  * @param[in] eeproma     = (WORD) Address in the EEPROM
02299  * @param[in] data        = 16bit data
02300  * @param[in] timeout     = Timeout in us.
02301  * @return >0 if OK
02302  * @see ecx_writeeepromFP
02303  */
02304 int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout)
02305 {
02306    return ecx_writeeepromFP (&ecx_context, configadr, eeproma, data, timeout);
02307 }
02308 
02309 /** Read EEPROM from slave bypassing cache.
02310  * Parallel read step 1, make request to slave.
02311  * @param[in] slave       = Slave number
02312  * @param[in] eeproma     = (WORD) Address in the EEPROM
02313  * @see ecx_readeeprom1
02314  */
02315 void ec_readeeprom1(uint16 slave, uint16 eeproma)
02316 {
02317    ecx_readeeprom1 (&ecx_context, slave, eeproma);
02318 }
02319 
02320 /** Read EEPROM from slave bypassing cache.
02321  * Parallel read step 2, actual read from slave.
02322  * @param[in] slave       = Slave number
02323  * @param[in] timeout     = Timeout in us.
02324  * @return EEPROM data 32bit
02325  * @see ecx_readeeprom2
02326  */
02327 uint32 ec_readeeprom2(uint16 slave, int timeout)
02328 {
02329    return ecx_readeeprom2 (&ecx_context, slave, timeout);
02330 }
02331 
02332 /** Transmit processdata to slaves.
02333  * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
02334  * Both the input and output processdata are transmitted.
02335  * The outputs with the actual data, the inputs have a placeholder.
02336  * The inputs are gathered with the receive processdata function.
02337  * In contrast to the base LRW function this function is non-blocking.
02338  * If the processdata does not fit in one datagram, multiple are used.
02339  * In order to recombine the slave response, a stack is used.
02340  * @param[in]  group          = group number
02341  * @return >0 if processdata is transmitted.
02342  * @see ecx_send_processdata_group
02343  */
02344 int ec_send_processdata_group(uint8 group)
02345 {
02346    return ecx_send_processdata_group (&ecx_context, group);
02347 }
02348 
02349 /** Transmit processdata to slaves.
02350 * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
02351 * Both the input and output processdata are transmitted in the overlapped IOmap.
02352 * The outputs with the actual data, the inputs replace the output data in the
02353 * returning frame. The inputs are gathered with the receive processdata function.
02354 * In contrast to the base LRW function this function is non-blocking.
02355 * If the processdata does not fit in one datagram, multiple are used.
02356 * In order to recombine the slave response, a stack is used.
02357 * @param[in]  context        = context struct
02358 * @param[in]  group          = group number
02359 * @return >0 if processdata is transmitted.
02360 * @see ecx_send_overlap_processdata_group
02361 */
02362 int ec_send_overlap_processdata_group(uint8 group)
02363 {
02364    return ecx_send_overlap_processdata_group(&ecx_context, group);
02365 }
02366 
02367 /** Receive processdata from slaves.
02368  * Second part from ec_send_processdata().
02369  * Received datagrams are recombined with the processdata with help from the stack.
02370  * If a datagram contains input processdata it copies it to the processdata structure.
02371  * @param[in]  group          = group number
02372  * @param[in]  timeout        = Timeout in us.
02373  * @return Work counter.
02374  * @see ecx_receive_processdata_group
02375  */
02376 int ec_receive_processdata_group(uint8 group, int timeout)
02377 {
02378    return ecx_receive_processdata_group (&ecx_context, group, timeout);
02379 }
02380 
02381 int ec_send_processdata(void)
02382 {
02383    return ec_send_processdata_group(0);
02384 }
02385 
02386 int ec_send_overlap_processdata(void)
02387 {
02388    return ec_send_overlap_processdata_group(0);
02389 }
02390 
02391 int ec_receive_processdata(int timeout)
02392 {
02393    return ec_receive_processdata_group(0, timeout);
02394 }
02395 #endif