Files
sics/multichan.c
Douglas Clowes 9298401846 Add MultiChannelController a controller with multiple channels or units on a single network connection.
r1487 | dcl | 2007-02-15 07:53:09 +1100 (Thu, 15 Feb 2007) | 2 lines
2012-11-15 12:58:59 +11:00

276 lines
6.5 KiB
C

/*
* 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 <netinet/tcp.h>
#include <sics.h>
#include <rs232controller.h>
#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);
}