/* System */ #include #include #include /* EPICS Base */ #include #include #include /* This driver */ #include "drvNetdev.h" static int verbosity = 10; /* Driver task that tries to connect to the network device, * then sends requests and parses the response. * * This code is EPICS-specific because we use "epicsSocketCreate" etc. * This allows the code to run on any OS supported by EPICS base: * Linux, Solaris, Win32, vxWorks, RTEMS, .... * * Replacing epicsSocketCreate() with socket(), * epicsMutexCreate() with e.g. pthread_mutex_init() and so on * would turn this into code that knows nothing about EPICS * and is specific to Linux. */ static void driver_task(drvNetdev *drv) { SOCKET s; char buf[100]; fd_set fds; struct timeval timeout; int total_len, len; int waiting_for_response; while (1) { s = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); sockAddrToDottedIP((const struct sockaddr *)&drv->ip, buf, sizeof(buf)); if (verbosity > 1) printf("Connecting to %s\n", buf); if (connect(s, (const struct sockaddr *)&drv->ip, sizeof(drv->ip)) != 0) { fprintf(stderr, "driver_task cannot connect to %s\n", buf); epicsThreadSleep(5.0); continue; /* retry after 5 seconds */ } if (verbosity > 1) printf("Connected\n"); while (1) { if (verbosity > 1) printf("Sending request\n"); if (write(s, "Value?\n", 7) != 7) break; /* Read response, ending in '\n' */ waiting_for_response = 1; total_len = 0; while (waiting_for_response) { /* try to read some characters */ FD_ZERO(&fds); FD_SET(s, &fds); timeout.tv_sec = 5; /* 5 second read timeout */ timeout.tv_usec = 0; if (select(s+1, &fds, 0, 0, &timeout) > 0 && (len = read(s, buf+total_len, sizeof(buf)-total_len)) > 0) { /* Anything? Add to buffer. */ total_len += len; buf[total_len] = '\0'; if (verbosity > 1) printf("Got: '%s'\n", buf); if (strchr(buf, '\n')) { /* Is it a full response ? */ epicsMutexLock(drv->mutex); drv->value = atoi(buf); drv->is_valid = 1; epicsMutexUnlock(drv->mutex); waiting_for_response = 0; } } else { /* Nothing. Time out, start over. */ if (verbosity > 1) printf("Timeout / no data\n"); epicsMutexLock(drv->mutex); drv->is_valid = 0; epicsMutexUnlock(drv->mutex); waiting_for_response = 0; } } } epicsMutexLock(drv->mutex); drv->is_valid = 0; epicsMutexUnlock(drv->mutex); if (verbosity > 1) printf("Disconnecting\n"); epicsSocketDestroy(s); } } drvNetdev *drvNetdev_init(const char *address) { if (verbosity > 1) printf("drvNetdev_init(%s)\n", address); drvNetdev *drv = calloc(1, sizeof(drvNetdev)); if (drv) { aToIPAddr(address, 7543, &drv->ip); drv->mutex = epicsMutexCreate(); epicsThreadCreate("drvNetdev", epicsThreadPriorityLow, epicsThreadStackMedium, (EPICSTHREADFUNC)driver_task, drv); } return drv; } /* EPICS Driver support entry table: Don't need one. */ /* IOC Shell Registration Stuff. * None of this is required to use the driver * from an EPICS device support module. * Registration allows interactive stand-alone testing * of the driver from the EPICS IOC shell, * which can be useful when debugging the driver. * Yes, it's ugly but there really isn't much to it. */ static const iocshArg verbArg0 = {"value", iocshArgInt}; static const iocshArg *const verbArgs[1] = {&verbArg0}; static const iocshFuncDef verbosityDef = {"drvNetdev_verbosity", 1, verbArgs}; static void verbosityCall(const iocshArgBuf * args) { verbosity = args[0].ival; } static const iocshArg initArg0 = {"address", iocshArgString}; static const iocshArg *const initArgs[1] = {&initArg0}; static const iocshFuncDef initDef = {"drvNetdev_init", 1, initArgs}; static void initCall(const iocshArgBuf * args) { drvNetdev_init(args[0].sval); } static void drvNetdevRegistrar(void) { static int firstTime = 1; if (firstTime) { firstTime = 0; iocshRegister(&verbosityDef, verbosityCall); iocshRegister(&initDef, initCall); } }; /* Refer to this in DBD: registrar(drvNetdevRegistrar) */ epicsExportRegistrar(drvNetdevRegistrar);