#include "vxi11_server.h" #include "vxi11.h" #include "globals.h" #include "gpib.h" #include "parser.h" #include #include #include #include #include #include #define DEBUG #ifdef DEBUG #include #include #endif #define SIZEOFARRAY(a) (sizeof(a) / sizeof(a[0])) #define MAXRECV 1024 // change this to change the max recv size #define ERR_SYNTAXERROR 1 #define ERR_DEVICENOTACCESSIBLE 3 #define ERR_INVALIDLINKINDENTIFIER 4 #define ERR_PARAMETERERROR 5 #define ERR_CHANNELNOTESTABLISHED 6 #define ERR_OPERATIONNOTSUPPORTED 8 #define ERR_OUTOFRESOURCES 9 #define ERR_DEVICELOCKEDBYANOTHERLINK 11 #define ERR_NOLOCKHELDBYTHISLINK 12 #define ERR_IOTIMEOUT 15 #define ERR_IOERROR 17 #define ERR_INVALIDADDRESS 21 #define ERR_ABORT 23 #define ERR_CHANNELALREADYESTABLISHED 29 // these are to handle the RPC client that is needed to // connect back to the client for handling interrupts // the "interrupt channel destroy" method doesn't give // us anything to tell which channel to destroy so // it seems like there can only be one. static CLIENT* intclient = NULL; static bool intenabled = false; static gchar* inthandler = NULL; #define LINKLONGSINCEDEADTIMEOUT ((10 * 60) * 1000000) // ten minutes typedef struct { int lid; int sock; GTimeVal lastactivity; bool aborted; } ActiveLink; static ActiveLink* links[MAX_SESSIONS] = { NULL }; static void freelink(ActiveLink* link); static void touchlink(int lid) { g_get_current_time(&(links[lid]->lastactivity)); } static bool isValidLink(int linkid) { if (linkid > MAX_SESSIONS - 1) { #ifdef DEBUG printf("Crazy link id %d\n", linkid); #endif return false; } return links[linkid] != NULL ; } // test if a link has expired, this is to avoid dead locks static bool linkexpired(ActiveLink* link) { GTimeVal cutoff; g_get_current_time(&cutoff); g_time_val_add(&cutoff, -LINKLONGSINCEDEADTIMEOUT); if (link->lastactivity.tv_sec < cutoff.tv_sec) { #ifdef DEBUG printf("Link %d is apparently dead, removing\n", link->lid); #endif freelink(link); return true; } else return false; } // locking wrappers static void lock(int lid) { globals.VxiLocks.locked_network_server = lid; } static void unlock() { globals.VxiLocks.locked_network_server = NO_SERVER_LOCKED; } static bool haveLock(int lid) { return globals.VxiLocks.locked_network_server == lid; } static bool isLocked() { if (globals.VxiLocks.locked_network_server == NO_SERVER_LOCKED) return false; else { ActiveLink* link = links[globals.VxiLocks.locked_network_server]; return !(linkexpired(link)); } } #define FLAG_WAITLOCK 1 // todo static bool waitForLock(Device_Flags flags, long timeout) { if (!(flags & FLAG_WAITLOCK)) return false; return false; } // static void freelink(ActiveLink* link) { links[link->lid] = NULL; globals.Remote.vxi_connections--; if (globals.Remote.vxi_connections == 0) { // cancel any remaining panel locks globals.Remote.vxi_panel_lock = 0; } #ifdef DEBUG printf("link %d destroyed, %d active links\n", link->lid, globals.Remote.vxi_connections); #endif if (haveLock(link->lid)) { #ifdef DEBUG printf("destroyed link was holding a lock, freeing it\n"); #endif unlock(); } g_free(link); } void vxi11_deadsocketcallback(int socket) { int linkid; for (linkid = 0; linkid < SIZEOFARRAY(links); linkid++) { if (links[linkid] != NULL ) { if (links[linkid]->sock == socket) { #ifdef DEBUG printf("Freeing link %d, socket died\n", linkid); #endif freelink(links[linkid]); } } } } static bool waitforio(long timeout) { do { if (!globals.VxiLocks.command_in_progress) return true; usleep(250); timeout -= 250; } while (timeout < 0); #ifdef DEBUG printf("timeout waiting for command to finish\n"); #endif return false; } Device_Error * device_abort_1_svc(Device_Link *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_abort_1_svc()\n"); #endif if (!isValidLink(*argp)) result.error = ERR_INVALIDLINKINDENTIFIER; else { links[*argp]->aborted = true; result.error = 0; } return &result; } Create_LinkResp * create_link_1_svc(Create_LinkParms *argp, struct svc_req *rqstp) { static Create_LinkResp result; memset(&result, 0, sizeof(result)); #ifdef DEBUG printf("create_link_1_svc()\n"); #endif if (globals.Remote.vxi_connections == MAX_SESSIONS) { result.error = ERR_OUTOFRESOURCES; } else if (argp->lockDevice && isLocked() && !waitForLock(FLAG_WAITLOCK, argp->lock_timeout)) { result.error = ERR_DEVICELOCKEDBYANOTHERLINK; } else { ActiveLink* link = g_malloc(sizeof(ActiveLink)); if (link == NULL ) { result.error = ERR_OUTOFRESOURCES; } else { int linkid; for (linkid = 0; linkid < SIZEOFARRAY(links); linkid++) { if (links[linkid] != NULL ) // clean up any dead links linkexpired(links[linkid]); if (links[linkid] == NULL ) { links[linkid] = link; break; } } g_assert(linkid != SIZEOFARRAY(links)); link->lid = linkid; link->sock = rqstp->rq_xprt->xp_sock; touchlink(linkid); globals.Remote.vxi_connections++; #ifdef DEBUG printf("created link %d(socket %d), %d active links\n", linkid, link->sock, globals.Remote.vxi_connections); #endif struct sockaddr_in sin; socklen_t len = sizeof(sin); getsockname(rqstp->rq_xprt->xp_sock, (struct sockaddr *) &sin, &len); result.error = 0; result.abortPort = ntohs(sin.sin_port); result.maxRecvSize = MAXRECV; result.lid = linkid; if (argp->lockDevice) { lock(link->lid); } } } return &result; } Device_WriteResp * device_write_1_svc(Device_WriteParms *argp, struct svc_req *rqstp) { static Device_WriteResp result; memset(&result, 0, sizeof(result)); #ifdef DEBUG printf("device_write_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else if (!waitforio(argp->io_timeout)) result.error = ERR_IOTIMEOUT; else { touchlink(argp->lid); links[argp->lid]->aborted = false; gchar* str = g_strndup(argp->data.data_val, argp->data.data_len); #ifdef DEBUG int n; for (n = 0; str[n] != '\0'; n++) printf("%02x ", (unsigned char) str[n]); printf("\n"); printf("got <<%s>> (reported length %d)on link %d.\n", str, (int) argp->data.data_len, (int) argp->lid); #endif Parser_main(str, 0, GPIB_and_VXI_start_query_response, NULL ); g_free(str); result.size = argp->data.data_len; } return &result; } Device_ReadResp * device_read_1_svc(Device_ReadParms *argp, struct svc_req *rqstp) { static Device_ReadResp result; #ifdef DEBUG printf("device_read_1_svc()\n"); #endif // this free's the data from the last call if (result.data.data_val != NULL ) g_free(result.data.data_val); memset(&result, 0, sizeof(result)); if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else if (!waitforio(argp->io_timeout)) { result.error = ERR_IOTIMEOUT; } else { touchlink(argp->lid); if (globals.Registers.pending_output_message != NULL ) { result.data.data_len = strlen(globals.Registers.pending_output_message); result.data.data_val = globals.Registers.pending_output_message; globals.Registers.pending_output_message = NULL; #ifdef DEBUG printf("sending ---%s---\n", result.data.data_val); #endif } else result.data.data_val = NULL; if (links[argp->lid]->aborted) { result.error = ERR_ABORT; links[argp->lid]->aborted = false; } result.reason = 0x4; } return &result; } Device_ReadStbResp * device_readstb_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) { static Device_ReadStbResp result; memset(&result, 0, sizeof(result)); #ifdef DEBUG printf("device_readstb_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { touchlink(argp->lid); result.stb = GPIB_and_VXI_get_STB(); } return &result; } Device_Error * device_trigger_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_trigger_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { touchlink(argp->lid); result.error = ERR_OPERATIONNOTSUPPORTED; } return &result; } Device_Error * device_clear_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_clear_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { touchlink(argp->lid); result.error = 0; GPIB_and_VXI_device_clear(); } return &result; } Device_Error * device_remote_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_remote_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { if (isLocked() && !haveLock(argp->lid)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { touchlink(argp->lid); result.error = 0; globals.Remote.vxi_panel_lock = 1; } } return &result; } Device_Error * device_local_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_local_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { if (isLocked() && !haveLock(argp->lid)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { touchlink(argp->lid); result.error = 0; globals.Remote.vxi_panel_lock = 0; } } return &result; } Device_Error * device_lock_1_svc(Device_LockParms *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_lock_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else { if (isLocked(argp->lid)) { result.error = ERR_DEVICELOCKEDBYANOTHERLINK; if (haveLock(argp->lid)) { // maybe do something here to warn about a device trying to lock multiple times? } } else { touchlink(argp->lid); lock(argp->lid); result.error = 0; } } return &result; } Device_Error * device_unlock_1_svc(Device_Link *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_unlock_1_svc()\n"); #endif if (!isValidLink(*argp)) result.error = ERR_INVALIDLINKINDENTIFIER; else { if (!isLocked(*argp) || !haveLock(*argp)) result.error = ERR_NOLOCKHELDBYTHISLINK; else { touchlink(*argp); result.error = 0; unlock(); } } return &result; } void vxi11_fireinterrupt() { #ifdef DEBUG printf("vxi11_fireinterrupt()\n"); #endif if (intclient != NULL ) { Device_SrqParms params; params.handle.handle_val = inthandler; params.handle.handle_len = strlen(inthandler); device_intr_srq_1(¶ms, intclient); } } Device_Error * device_enable_srq_1_svc(Device_EnableSrqParms *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("device_enable_srq_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else { touchlink(argp->lid); if (inthandler != NULL ) { g_free(inthandler); inthandler = NULL; } if (argp->enable) { if (argp->handle.handle_val != NULL ) { inthandler = g_strdup(argp->handle.handle_val); #ifdef DEBUG printf("Interrupt handle set to %s\n", inthandler); #endif } vxi11_fireinterrupt(); } #ifdef DEBUG else { printf("Interrupts disabled\n"); } #endif result.error = 0; intenabled = argp->enable; } return &result; } Device_DocmdResp * device_docmd_1_svc(Device_DocmdParms *argp, struct svc_req *rqstp) { static Device_DocmdResp result; memset(&result, 0, sizeof(result)); #ifdef DEBUG printf("device_docmd_1_svc()\n"); #endif if (!isValidLink(argp->lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else if (isLocked() && !haveLock(argp->lid) && !waitForLock(argp->flags, argp->lock_timeout)) result.error = ERR_DEVICELOCKEDBYANOTHERLINK; else { touchlink(argp->lid); result.error = ERR_OPERATIONNOTSUPPORTED; } return &result; } Device_Error * destroy_link_1_svc(Device_Link *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("destroy_link_1_svc()\n"); #endif int lid = *argp; if (!isValidLink(lid)) result.error = ERR_INVALIDLINKINDENTIFIER; else { freelink(links[lid]); result.error = 0; } return &result; } Device_Error * create_intr_chan_1_svc(Device_RemoteFunc *argp, struct svc_req *rqstp) { static Device_Error result; in_port_t port = htons(argp->hostPort); uint32_t addr = htonl(argp->hostAddr); #ifdef DEBUG printf("create_intr_chan_1_svc()\n"); char clientaddressstring[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &addr, clientaddressstring, sizeof(clientaddressstring)); printf("Client %s is asking for an interrupt connection on port %u\n", clientaddressstring, (unsigned int) port); #endif // if a client had an interrupt channel and died without closing it // we need to clean it up to avoid a deadlock static struct rpc_err err; if (intclient != NULL ) { vxi11_fireinterrupt(); clnt_geterr(intclient, &err); if (err.re_errno != 0) { #ifdef DEBUG printf("killing stale client\n"); #endif clnt_destroy(intclient); intclient = NULL; } } if (argp->progFamily != DEVICE_TCP || argp->progNum != DEVICE_INTR || argp->progVers != DEVICE_INTR_VERSION) result.error = ERR_OPERATIONNOTSUPPORTED; else if (intclient != NULL ) { #ifdef DEBUG printf("Not creating interrupt channel, already exists\n"); #endif result.error = ERR_CHANNELALREADYESTABLISHED; } else { struct sockaddr_in clientaddr; clientaddr.sin_family = AF_INET; clientaddr.sin_port = port; clientaddr.sin_addr.s_addr = addr; int sock = RPC_ANYSOCK; CLIENT* clnt = clnttcp_create(&clientaddr, DEVICE_INTR, DEVICE_INTR_VERSION, &sock, 0, 0); if (clnt == NULL ) { #ifdef DEBUG printf("Couldn't create interrupt channel client\n"); #endif result.error = ERR_CHANNELNOTESTABLISHED; } else { intclient = clnt; result.error = 0; #ifdef DEBUG printf("Client created\n"); #endif } } return &result; } Device_Error * destroy_intr_chan_1_svc(void *argp, struct svc_req *rqstp) { static Device_Error result; #ifdef DEBUG printf("destroy_intr_chan_1_svc()\n"); #endif if (intclient == NULL ) { result.error = ERR_CHANNELNOTESTABLISHED; } else { clnt_destroy(intclient); intclient = NULL; result.error = 0; } return &result; }