/* * M U L T I C H A N * * This module manages communications on a multi-channel controller. * * A multi-channel controller is one where multiple channels or units are on a * single controller and share a single command channel. * * Douglas Clowes, February 2007 * */ #include #include #include #include "network.h" #include "multichan.h" #include "nwatch.h" typedef struct __MultiChanController MultiChanController, *pMultiChanController; typedef struct __mcc_command MC_Cmd, *pMC_Cmd; struct __mcc_command { pMC_Cmd next; void* cntx; MCC_Transmit tx; MCC_Receive rx; pMultiChan unit; }; struct __MultiChan { pMultiChan next; pMultiChanController mcc; MCC_Notify notify_func; void* notify_cntx; int timeout; }; struct __MultiChanController { int unit_count; /* number of units connected */ pMultiChan units; /* head of unit chain */ prs232 controller; /* associated RS232 controller object */ pMC_Cmd command_head; /* first/next command in queue */ pMC_Cmd command_tail; /* last command in queue */ pNWContext nw_ctx; /* NetWait context handle */ pNWTimer nw_tmr; /* NetWait timer handle */ }; static pMultiChanController mcc_array[FD_SETSIZE]; static mcc_index = 0; static int MC_Notify(pMultiChanController self, int event) { pMultiChan unit; for (unit = self->units; unit; unit = unit->next) if (unit->notify_func != NULL) unit->notify_func(unit, event); } static int MC_Reconnect(pMultiChanController self) { int iRet; int sock; int flag = 1; iRet = NETReconnect(self->controller->pSock); if (iRet <= 0) { MC_Notify(self, MCC_DISCONNECT); return iRet; } sock = self->controller->pSock->sockid; iRet = setsockopt(sock, /* socket */ IPPROTO_TCP, /* set option at TCP level */ TCP_NODELAY, /* name of option */ (char *) &flag, /* the cast is historical cruft */ sizeof(int)); /* length of option value */ MC_Notify(self, MCC_RECONNECT); return 1; } static int CommandTimeout(void* cntx, int mode); static int StartCommand(pMultiChanController self) { pMC_Cmd myCmd = self->command_head; if (self->nw_tmr) NetWatchRemoveTimer(self->nw_tmr); if (myCmd->unit->timeout > 0) NetWatchRegisterTimer(&self->nw_tmr, myCmd->unit->timeout * 1000, CommandTimeout, self); else NetWatchRegisterTimer(&self->nw_tmr, 1000000, CommandTimeout, self); return myCmd->tx(myCmd->cntx); } static int CommandTimeout(void* cntx, int mode) { pMultiChanController self = (pMultiChanController) cntx; self->nw_tmr = 0; StartCommand(self); } static int QueCommand(pMultiChanController self, pMC_Cmd cmd) { /* * If the command queue is empty, start transmission */ if (self->command_head == NULL) { self->command_head = self->command_tail = cmd; StartCommand(self); } else { self->command_tail->next = cmd; self->command_tail = cmd; } } static int PopCommand(pMultiChanController self) { pMC_Cmd myCmd = self->command_head; if (self->nw_tmr) NetWatchRemoveTimer(self->nw_tmr); self->nw_tmr = 0; /* * If this is not the last in queue, start transmission */ if (myCmd->next) { pMC_Cmd pNew = myCmd->next; self->command_head = pNew; StartCommand(self); } else self->command_head = self->command_tail = NULL; free(myCmd); } static int MyCallback(void* context, int mode) { pMultiChanController self = (pMultiChanController) context; if (mode & nwatch_read) { int iRet; char reply[1]; iRet = NETRead(self->controller->pSock, reply, 1, 0); if (iRet < 0) { /* TODO: EOF */ iRet = MC_Reconnect(self); if (iRet <= 0) return iRet; /* restart the command */ StartCommand(self); return 1; } if (iRet == 0) { /* TODO: timeout or error */ return 0; } else { pMC_Cmd myCmd = self->command_head; if (myCmd) { iRet = myCmd->rx(myCmd->cntx, reply[0]); if (iRet < 0) /* TODO: error */ ; else if (iRet == 0) /* end of command */ PopCommand(self); } } } return 1; } int MultiChanCreate(const char* name, pMultiChan* handle) { int i; prs232 ctlr = NULL; pMultiChanController self = NULL; pMultiChan unit = NULL; *handle = NULL; ctlr = (prs232)FindCommandData(pServ->pSics, name, "RS232 Controller"); if(ctlr == NULL || ctlr->pSock == NULL) { return 0; } for (i = 0; i < mcc_index; ++i) if (mcc_array[i]->controller == ctlr) { self = mcc_array[i]; break; } if (self == NULL) { self = (pMultiChanController) malloc(sizeof(MultiChanController)); /* TODO check malloc */ if (self == NULL) return 0; memset(self, 0, sizeof(MultiChanController)); mcc_array[mcc_index++] = self; self->controller = ctlr; NetWatchRegisterCallback(&self->nw_ctx, self->controller->pSock->sockid, MyCallback, self); } ++self->unit_count; unit = (pMultiChan) malloc(sizeof(MultiChan)); /* TODO: check malloc failure */ memset(unit, 0, sizeof(MultiChan)); unit->mcc = self; unit->next = self->units; self->units = unit; *handle = unit; return 1; } int MultiChanDestroy(pMultiChan chan) { assert(chan); assert(chan->mcc); pMultiChanController self = chan->mcc; pMultiChan* pNxt = &self->units; while (*pNxt) { if (*pNxt == chan) { *pNxt = (*pNxt)->next; break; } pNxt = &(*pNxt)->next; } --self->unit_count; if (self->unit_count <= 0) { int i; for (i = 0; i < mcc_index; ++i) if (mcc_array[i] == self) { --mcc_index; if (mcc_index > 0) mcc_array[i] = mcc_array[mcc_index]; if (self->nw_ctx) NetWatchRemoveCallback(self->nw_ctx); if (self->nw_tmr) NetWatchRemoveTimer(self->nw_tmr); free(self); } } free(chan); return 1; } int MultiChanReconnect(pMultiChan unit) { int iRet; iRet = MC_Reconnect(unit->mcc); /* TODO: handle in-progress */ return iRet; } mkChannel* MultiChanGetSocket(pMultiChan unit) { assert(unit); assert(unit->mcc); return unit->mcc->controller->pSock; } int MultiChanEnque(pMultiChan unit, void* context, MCC_Transmit tx, MCC_Receive rx) { pMC_Cmd myCmd = NULL; myCmd = (pMC_Cmd) malloc(sizeof(MC_Cmd)); /* TODO: check malloc */ memset(myCmd, 0, sizeof(MC_Cmd)); myCmd->cntx = context; myCmd->tx = tx; myCmd->rx = rx; myCmd->unit = unit; return QueCommand(unit->mcc, myCmd); }