#include #include #include #include #include #include #include #include #include #include "output.h" #include "error.h" #include "modbmst.h" // Table of CRC values for high-order byte static const uint8_t g_crcHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 }; // Table of CRC values for low-order byte static const uint8_t g_crcLo[] = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 }; ///////////////////////////////////////////////////////////////////////////// #define _PING_SIGNATURE 0xA55A ///////////////////////////////////////////////////////////////////////////// typedef struct _GFA_MB_MST { HGFAMINEMST hMiNeMst; HMINETDEV hMiNeDev; int nVerbosity; FILE *pDumpCtx; uint32_t nBaudrate; uint16_t nModbusCtrlReg; struct timespec tsStart; uint8_t parity; }GFA_MB_MST, *LPGFA_MB_MST; typedef const GFA_MB_MST *LPCGFA_MB_MST; ///////////////////////////////////////////////////////////////////////////// const struct timespec* Ns2Timespec(uint64_t nsTime, struct timespec *pts); int64_t TimespecDiff(const struct timespec *pts1, const struct timespec *pts2); ///////////////////////////////////////////////////////////////////////////// static uint16_t _Crc16(const void *pData, size_t nCbData) { const uint8_t *pBuffer = (const uint8_t*)pData; uint8_t crcHi = 0xFF; // high CRC byte initialized uint8_t crcLo = 0xFF; // low CRC byte initialized unsigned int i; // will index into CRC lookup while(nCbData--) { i = crcHi ^ *pBuffer++; crcHi = crcLo ^ g_crcHi[i]; crcLo = g_crcLo[i]; } return (((uint16_t)crcHi << 8) | (uint16_t)crcLo); } ///////////////////////////////////////////////////////////////////////////// static void _CpyUnalignedUint16(void *pTo, const void *pFrom, size_t nCntWords) { size_t i, j; uint8_t *p1 = (uint8_t*)pTo; const uint8_t *p2 = (const uint8_t*)pFrom; for(i = 0; i < nCntWords; i++) { p2 += sizeof(uint16_t); for(j = 0; j < sizeof(uint16_t); j++) { *p1++ = *--p2; } p2 += sizeof(uint16_t); } } ///////////////////////////////////////////////////////////////////////////// HGFAMBMST GfaMbMstOpen(HGFAMINEMST hMiNeMst, uint32_t nBaudrate, uint8_t parity, uint16_t nModbusCtrlReg) { if(hMiNeMst) { LPGFA_MB_MST pMst = (LPGFA_MB_MST)malloc(sizeof(GFA_MB_MST)); memset(pMst, 0, sizeof(GFA_MB_MST)); pMst->hMiNeMst = hMiNeMst; pMst->hMiNeDev = GfaMininetMasterGetDeviceHandle(hMiNeMst); pMst->nBaudrate = nBaudrate; pMst->parity = parity; pMst->nModbusCtrlReg = nModbusCtrlReg; pMst->nVerbosity = 2; pMst->pDumpCtx = stdout; GfaMininetMasterGetStartClock(hMiNeMst, &pMst->tsStart); return (HGFAMBMST)pMst; } errno = EINVAL; return NULL; } ///////////////////////////////////////////////////////////////////////////// void GfaMbMstClose(HGFAMBMST hMst) { if(hMst) { LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; free(pMst); } } ///////////////////////////////////////////////////////////////////////////// int GfaMbMstSetVerbosity(HGFAMBMST hMst, int nVerbosity) { if(hMst) { LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; if(nVerbosity < 0) nVerbosity = 0; else if(nVerbosity > 4) nVerbosity = 4; pMst->nVerbosity = nVerbosity; return 0; } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// ssize_t GfaMbMstCreateADU(uint8_t slvID, uint8_t func, const void *pData, size_t nCbData, LPMODBUS_RTU_ADU pAdu) { if( pAdu && MODBUS_IS_VALID_SLAVE_ID(slvID)) { uint16_t nCRC16; size_t nCbAdu = 2; pAdu->slvID = slvID; pAdu->func = func; if(pData && nCbData) { if((nCbData > MODBUS_MAX_DATA_PAYLOAD)) { errno = ENOMEM; return -1; } memcpy(pAdu->b, pData, nCbData); nCbAdu += nCbData; } else { nCbData = 0; } nCRC16 = _Crc16(pAdu, nCbAdu); _CpyUnalignedUint16(&pAdu->b[nCbData], &nCRC16, 1); nCbAdu += sizeof(nCRC16); return nCbAdu; } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// bool GfaMbMstValidateADU(uint8_t slvID, uint8_t func, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu, int *pnMbErr) { bool bRet; uint16_t nCRC16Received, nCRC16Expected; if(pnMbErr) *pnMbErr = 0; if(pAdu->slvID != slvID) { errno = EPROTO; return false; } else if(pAdu->func != func) { if((pAdu->func != (func | MB_FUNC_ERROR_FLAG)) || (nCbAdu != 5)) { errno = EPROTO; return false; } else if(pnMbErr) *pnMbErr = pAdu->b[0]; } _CpyUnalignedUint16(&nCRC16Received, ((uint8_t*)pAdu) + nCbAdu - sizeof(uint16_t), sizeof(uint16_t)); nCRC16Expected = _Crc16(pAdu, nCbAdu - sizeof(uint16_t)); if(!(bRet = (nCRC16Received == nCRC16Expected))) errno = EPROTO; return bRet; } ///////////////////////////////////////////////////////////////////////////// void GfaMbMstWaitFrameDelay(HGFAMBMST hMst) { if(hMst) { uint32_t nDelay, nBaudrate; LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; if( !GfaMininetDeviceGetBaudrate(pMst->hMiNeDev, &nBaudrate) && nBaudrate <= 19200) nDelay = (350000000 / nBaudrate + 5) / 10; else nDelay = 1750; usleep(nDelay); } } ///////////////////////////////////////////////////////////////////////////// ssize_t GfaMbMstSendADU(HGFAMBMST hMst, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu) { if(hMst && pAdu && nCbAdu >= 4) { LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; GfaMbMstDumpADU(hMst, pAdu, nCbAdu, true, NULL); return GfaMininetDeviceTransmit(pMst->hMiNeDev, pAdu, nCbAdu); } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// ssize_t GfaMbMstRecvADU(HGFAMBMST hMst, LPMODBUS_RTU_ADU pAdu, size_t nCbAdu) { if(hMst && pAdu && nCbAdu >= 4) { ssize_t nRet; LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; if((nRet = GfaMininetDeviceReceive(pMst->hMiNeDev, pAdu, nCbAdu)) > 0) GfaMbMstDumpADU(hMst, pAdu, nCbAdu, false, NULL); return nRet; } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// void GfaMbMstDumpADU(HGFAMBMST hMst, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu, bool bTX, const char *pszAnnotation) { if(hMst && pAdu && nCbAdu) { LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; FILE *pf = pMst->pDumpCtx; if(pf && (pMst->nVerbosity >= 4)) { size_t i, nCbData = nCbAdu - 4; uint16_t nCRC16; struct timespec tsCur, tsIntv; clock_gettime(CLOCK_MONOTONIC, &tsCur); uint64_t nInterval = TimespecDiff(&tsCur, &pMst->tsStart); Ns2Timespec(nInterval, &tsIntv); if(nCbAdu >= 4) { fprintf(pf, "\n///////////////////////////////////////\n"); fprintf(pf, "// Modbus - %ld.%03ld\n", tsIntv.tv_sec, tsIntv.tv_nsec / 1000000); fprintf(pf, "// %s:\n", bTX ? "TX" : "RX"); fprintf(pf, "SlvID: %02hhX (%hhu)\n", pAdu->slvID, pAdu->slvID); fprintf(pf, "Func: %02hhX (%hhu)\n", pAdu->func, pAdu->func); fprintf(pf, "Data: "); for(i = 0; i < nCbData; i++) { fprintf(pf, "[%02hhX]", (char)pAdu->b[i]); } _CpyUnalignedUint16(&nCRC16, &pAdu->b[i], 1); fprintf(pf, "\nCRC16: %02hX (%hu)\n", nCRC16, nCRC16); if(pszAnnotation) fprintf(pf, "Annot.: %s\n", pszAnnotation); } else { fprintf(pf, "\n///////////////////////////////////////\n"); fprintf(pf, "// Modbus - %ld:%03ld\n", tsIntv.tv_sec, tsIntv.tv_nsec / 1000000); fprintf(pf, "// %s:\n", bTX ? "TX" : "RX"); fprintf(pf, "Invalid Modbus-Frame!\n"); } fprintf(pf, "\n"); fflush(pf); } } } ///////////////////////////////////////////////////////////////////////////// ssize_t GfaMbMstReadHoldingRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t nRegStart, uint16_t nRegCount, void *pRegs) { if(hMst && pRegs && nRegCount > 0) { int nMBErr; ssize_t nRet; uint8_t func = MB_FUNC_READ_HOLDING_REGISTERS; MODBUS_RTU_ADU adu; size_t nCbExpt = 5 + (nRegCount * sizeof(uint16_t)); uint8_t ri[4]; _CpyUnalignedUint16(&ri[0], &nRegStart, 1); _CpyUnalignedUint16(&ri[2], &nRegCount, 1); if((nRet = GfaMbMstCreateADU(slvID, func, &ri, sizeof(ri), &adu)) < 0) return nRet; if((nRet = GfaMbMstSendADU(hMst, &adu, nRet)) < 0) return nRet; if((nRet = GfaMbMstRecvADU(hMst, &adu, nCbExpt)) <= 0) return -1; if(!GfaMbMstValidateADU(slvID, func, &adu, nRet, &nMBErr)) return -1; if(nMBErr) { errno = MAKE_GFA_MODBUS_ERROR(nMBErr); return -1; } else if(nRet != (int)(5 + nRegCount * sizeof(uint16_t))) { errno = EPROTO; return -1; } else if(adu.b[0] != (nRegCount * sizeof(uint16_t))) { errno = EPROTO; return -1; } _CpyUnalignedUint16(pRegs, &adu.b[1], nRegCount); return 0; } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// ssize_t GfaMbMstWriteMultipleRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t nRegStart, uint16_t nRegCount, const void *pRegs) { if(hMst && pRegs && nRegCount > 0) { int nMBErr; ssize_t nRet; MODBUS_RTU_ADU adu; uint8_t func = MB_FUNC_WRITE_MULTIPLE_REGISTERS; uint8_t data[MODBUS_MAX_DATA_PAYLOAD]; _CpyUnalignedUint16(&data[0], &nRegStart, 1); _CpyUnalignedUint16(&data[2], &nRegCount, 1); data[4] = (uint8_t)(nRegCount * sizeof(uint16_t)); _CpyUnalignedUint16(&data[5], pRegs, nRegCount); if((nRet = GfaMbMstCreateADU(slvID, func, &data, 5 + nRegCount * sizeof(uint16_t), &adu)) < 0) return -1; if((nRet = GfaMbMstSendADU(hMst, &adu, nRet)) < 0) return -1; if((nRet = GfaMbMstRecvADU(hMst, &adu, 8)) <= 0) return -1; if(!GfaMbMstValidateADU(slvID, func, &adu, nRet, &nMBErr)) return -1; if(nMBErr) { errno = MAKE_GFA_MODBUS_ERROR(nMBErr); return -1; } else if(nRet != 8) { errno = EPROTO; return -1; } else if((bswap_16(adu.w[0]) != nRegStart) || (bswap_16(adu.w[1]) != nRegCount)) { errno = EPROTO; return -1; } return 0; } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// int GfaMbMstPing(HGFAMBMST hMst, uint8_t slvID) { if(hMst) { static uint8_t b = 0; int nMBErr; ssize_t nRet; MODBUS_RTU_ADU adu; uint8_t func = MB_FUNC_DIAGNOSTIC; uint16_t nEcho = ++b; nEcho |= (~b << 8); uint16_t data[2] = {bswap_16(MB_SUBFUNC_RETURN_QUERY_DATA), nEcho}; if((nRet = GfaMbMstCreateADU(slvID, func, &data, sizeof(data), &adu)) < 0) return -1; if((nRet = GfaMbMstSendADU(hMst, &adu, nRet)) < 0) return -1; adu.w[0] = ~MB_SUBFUNC_RETURN_QUERY_DATA; adu.w[1] = ~adu.w[1]; adu.w[2] = 0; if((nRet = GfaMbMstRecvADU(hMst, &adu, 8)) <= 0) return -1; if(!GfaMbMstValidateADU(slvID, func, &adu, nRet, &nMBErr)) return -1; if(nMBErr) { errno = MAKE_GFA_MODBUS_ERROR(nMBErr); return -1; } else if(nRet != 8) { errno = EPROTO; return -1; } else if((adu.w[0] != data[0]) || (adu.w[1] != data[1])) { errno = EPROTO; return -1; } return 0; } errno = EINVAL; return -1; } ///////////////////////////////////////////////////////////////////////////// int GfaMbMstBootloaderExecute(HGFAMBMST hMst, uint8_t slvID) { if(hMst) { int nRet, nErr; GFA_SER_CFG_PARAMS scp, scpSave; struct timeval tvRX = {0, 500000}; uint16_t regs[2]; LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst; if(GfaMininetDeviceGetConfigParams(pMst->hMiNeDev, &scp, sizeof(scp)) != sizeof(scp)) return -1; memcpy(&scpSave, &scp, sizeof(scpSave)); scp.parity = pMst->parity; scp.baud = pMst->nBaudrate; if(GfaMininetDeviceSetConfigParams(pMst->hMiNeDev, &scp, sizeof(scp)) != 0) return -1; GfaMininetMasterSaveTimeouts(pMst->hMiNeMst); GfaMininetMasterSetTimeouts(pMst->hMiNeMst, &tvRX, NULL); TRACE4("Reading modbus registers %hu and %hu.\n", pMst->nModbusCtrlReg, pMst->nModbusCtrlReg + 1); if((nRet = GfaMbMstReadHoldingRegisters(hMst, slvID, pMst->nModbusCtrlReg, 2, regs)) == 0) { GfaMbMstWaitFrameDelay(hMst); TRACE4("Writing modbus registers %hu and %hu - values: 0x%04hX and 0x%04hX\n", pMst->nModbusCtrlReg + 2, pMst->nModbusCtrlReg + 3, regs[0], regs[1]); if((nRet = GfaMbMstWriteMultipleRegisters(hMst, slvID, pMst->nModbusCtrlReg + 2, 2, regs)) == 0) usleep(GFA_BOOTLOADER_EXEC_WAIT_TIME * 1000); } nErr = errno; GfaMininetMasterRestoreTimeouts(pMst->hMiNeMst); if(GfaMininetDeviceSetConfigParams(pMst->hMiNeDev, &scpSave, sizeof(scpSave)) != 0) return -1; errno = nErr; return nRet; } errno = EINVAL; return -1; }