summaryrefslogtreecommitdiff
path: root/io/hpmud/mlc.c
diff options
context:
space:
mode:
Diffstat (limited to 'io/hpmud/mlc.c')
-rw-r--r--io/hpmud/mlc.c772
1 files changed, 772 insertions, 0 deletions
diff --git a/io/hpmud/mlc.c b/io/hpmud/mlc.c
new file mode 100644
index 0000000..e8b9784
--- /dev/null
+++ b/io/hpmud/mlc.c
@@ -0,0 +1,772 @@
+/*****************************************************************************\
+
+ mlc.c - MLC support for multi-point tranport driver
+
+ (c) 2004-2007 Copyright Hewlett-Packard Development Company, LP
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+\*****************************************************************************/
+
+#include "hpmud.h"
+#include "hpmudi.h"
+
+int __attribute__ ((visibility ("hidden"))) cut_buf(mud_channel *pc, char *buf, int size)
+{
+ int len;
+
+ if (pc->rcnt > size)
+ {
+ /* Return part of rbuf. */
+ len = size;
+ memcpy(buf, &pc->rbuf[pc->rindex], len);
+ pc->rindex += len;
+ pc->rcnt -= len;
+ }
+ else
+ {
+ /* Return all of rbuf. */
+ len = pc->rcnt;
+ memcpy(buf, &pc->rbuf[pc->rindex], len);
+ pc->rindex = pc->rcnt = 0;
+ }
+
+ return len;
+}
+
+/* Write command reply back to peripheral. */
+static int MlcForwardReply(mud_channel *pc, int fd, unsigned char *buf, int size)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ int len=0;
+
+ if ((len = (pd->vf.write)(fd, buf, size, HPMUD_EXCEPTION_TIMEOUT)) != size)
+ {
+ BUG("unable to MlcForwarReply: %m\n");
+ }
+ return len;
+}
+
+/* Execute command from peripheral. */
+static int MlcExecReverseCmd(mud_channel *pc, int fd, unsigned char *buf)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ mud_channel *out_of_bound_channel;
+ MLCCmd *pCmd;
+ MLCReply *pReply;
+ MLCCredit *pCredit;
+ MLCCreditReply *pCreditReply;
+ MLCCreditRequest *pCreditReq;
+ MLCCreditRequestReply *pCreditReqReply;
+ MLCError *pError;
+ int len, size;
+ static int cnt;
+
+ pCmd = (MLCCmd *)buf;
+
+ /* See if this packet is a command packet. */
+ if (!(pCmd->h.hsid == 0 && pCmd->h.psid == 0))
+ {
+ if (pCmd->h.hsid == pCmd->h.psid)
+ {
+ /* Got a valid data packet handle it. This can happen when channel_read timeouts and p2hcredit=1. */
+ out_of_bound_channel = &pd->channel[pCmd->h.hsid];
+
+ if (out_of_bound_channel->ta.p2hcredit <= 0)
+ {
+ BUG("invalid data packet credit=%d\n", out_of_bound_channel->ta.p2hcredit);
+ return 0;
+ }
+
+ size = ntohs(pCmd->h.length) - sizeof(MLCHeader);
+ if (size > (HPMUD_BUFFER_SIZE - out_of_bound_channel->rcnt))
+ {
+ BUG("invalid data packet size=%d\n", size);
+ return 0;
+ }
+ memcpy(&out_of_bound_channel->rbuf[out_of_bound_channel->rcnt], buf+sizeof(MLCHeader), size);
+ out_of_bound_channel->rcnt += size;
+ if (pCmd->h.credit)
+ out_of_bound_channel->ta.h2pcredit += pCmd->h.credit; /* note, piggy back credit is 1 byte wide */
+ out_of_bound_channel->ta.p2hcredit--; /* one data packet was read, decrement credit count */
+ }
+ else
+ {
+ len = ntohs(pCmd->h.length);
+ BUG("unsolicited data packet: hsid=%x, psid=%x, length=%d, credit=%d, status=%x\n", pCmd->h.hsid,
+ pCmd->h.psid, len, pCmd->h.credit, pCmd->h.status);
+ DBG_DUMP(buf, len);
+ }
+ return 0;
+ }
+
+ /* Process any command. */
+ switch (pCmd->cmd)
+ {
+ case MLC_CREDIT:
+ pCredit = (MLCCredit *)buf;
+ out_of_bound_channel = &pd->channel[pCredit->hsocket];
+ out_of_bound_channel->ta.h2pcredit += ntohs(pCredit->credit);
+ pCreditReply = (MLCCreditReply *)buf;
+ pCreditReply->h.length = htons(sizeof(MLCCreditReply));
+ pCreditReply->cmd |= 0x80;
+ pCreditReply->result = 0;
+ MlcForwardReply(pc, fd, (unsigned char *)pCreditReply, sizeof(MLCCreditReply));
+ break;
+ case MLC_CREDIT_REQUEST:
+ pCreditReq = (MLCCreditRequest *)buf;
+ if (cnt++ < 5)
+ BUG("unexpected MLCCreditRequest: cmd=%x, hid=%x, pid=%x, credit=%d\n", pCreditReq->cmd,
+ pCreditReq->hsocket, pCreditReq->psocket, ntohs(pCreditReq->credit));
+ pCreditReqReply = (MLCCreditRequestReply *)buf;
+ pCreditReqReply->h.length = htons(sizeof(MLCCreditRequestReply));
+ pCreditReqReply->cmd |= 0x80;
+ pCreditReqReply->result = 0;
+ pCreditReqReply->credit = 0;
+ MlcForwardReply(pc, fd, (unsigned char *)pCreditReqReply, sizeof(MLCCreditRequestReply));
+ break;
+ case MLC_ERROR:
+ pError = (MLCError *)buf;
+ BUG("unexpected MLCError: cmd=%x, result=%x\n", pError->cmd, pError->result);
+ return 1;
+ default:
+ pReply = (MLCReply *)buf;
+ BUG("unexpected command: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ pReply->h.length = htons(sizeof(MLCReply));
+ pReply->cmd |= 0x80;
+ pReply->result = 1;
+ MlcForwardReply(pc, fd, (unsigned char *)pReply, sizeof(MLCReply));
+ break;
+ }
+ return 0;
+}
+
+/* Get command from peripheral and processes the reverse command. */
+int __attribute__ ((visibility ("hidden"))) MlcReverseCmd(mud_channel *pc, int fd)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, size;
+ unsigned int pklen;
+ unsigned char *pBuf;
+ MLCReply *pPk;
+
+ pPk = (MLCReply *)buf;
+
+ pBuf = buf;
+
+ /* Read packet header. */
+ size = sizeof(MLCHeader);
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
+ {
+ BUG("unable to read MlcReverseCmd header: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+ size-=len;
+ pBuf+=len;
+ }
+
+ /* Determine packet size. */
+ if ((pklen = ntohs(pPk->h.length)) > sizeof(buf))
+ {
+ BUG("invalid MlcReverseCmd packet size: size=%d\n", pklen);
+ stat = 1;
+ goto bugout;
+ }
+
+ /* Read packet data field. */
+ size = pklen - sizeof(MLCHeader);
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
+ {
+ BUG("unable to read MlcReverseCmd data: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+ size-=len;
+ pBuf+=len;
+ }
+
+ stat = MlcExecReverseCmd(pc, fd, buf);
+
+bugout:
+ return stat;
+}
+
+/*
+ * Get command reply from peripheral. Waits for reply then returns. Processes any reverse commands
+ * while waiting for a reply.
+ */
+static int MlcReverseReply(mud_channel *pc, int fd, unsigned char *buf, int bufsize)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ int stat=0, len, size, pklen;
+ unsigned char *pBuf;
+ MLCReply *pPk;
+
+ pPk = (MLCReply *)buf;
+
+ while (1)
+ {
+ pBuf = buf;
+
+ /* Read packet header. */
+ size = sizeof(MLCHeader);
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, pBuf, size, 4000000)) < 0) /* wait 4 seconds, same as dot4 */
+ {
+ BUG("unable to read MlcReverseReply header: %m bytesRead=%zd\n", sizeof(MLCHeader)-size);
+ stat = 2; /* short timeout */
+ goto bugout;
+ }
+ size-=len;
+ pBuf+=len;
+ }
+
+ /* Determine packet size. */
+ pklen = ntohs(pPk->h.length);
+ if (pklen < 0 || pklen > bufsize)
+ {
+ BUG("invalid MlcReverseReply packet size: size=%d, buf=%d\n", pklen, bufsize);
+ stat = 1;
+ goto bugout;
+ }
+
+ if (pklen == 0)
+ {
+ /* Got invalid MLC header from peripheral, try this "off-by-one" firmware hack (ie: OJ600). */
+ BUG("trying MlcReverseReply firmware hack\n");
+ memcpy(buf, &buf[1], sizeof(MLCHeader)-1);
+ pklen = ntohs(pPk->h.length);
+ if (pklen <= 0 || pklen > bufsize)
+ {
+ BUG("invalid MlcReverseReply packet size: size=%d, buf=%d\n", pklen, bufsize);
+ stat = 1;
+ goto bugout;
+ }
+ if ((len = (pd->vf.read)(fd, --pBuf, 1, 1000000)) < 0) /* wait 1 second */
+ {
+ BUG("unable to read MlcReverseReply header: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+ pBuf++;
+ DBG_DUMP(buf, sizeof(MLCHeader));
+ }
+
+ /* Read packet data field. */
+ size = pklen - sizeof(MLCHeader);
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
+ {
+ BUG("unable to read MlcReverseReply data: %m exp=%zd act=%zd\n", pklen-sizeof(MLCHeader), pklen-sizeof(MLCHeader)-size);
+ stat = 1;
+ goto bugout;
+ }
+ size-=len;
+ pBuf+=len;
+ }
+
+ /* Check for reply. */
+ if (pPk->cmd & 0x80)
+ break;
+
+ stat = MlcExecReverseCmd(pc, fd, buf);
+
+ if (stat != 0)
+ break;
+
+ } /* while (1) */
+
+bugout:
+ return stat;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcInit(mud_channel *pc, int fd)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n, cnt;
+ MLCInit *pCmd;
+ MLCInitReply *pReply;
+
+ memset(buf, 0, sizeof(MLCInit));
+ pCmd = (MLCInit *)buf;
+ n = sizeof(MLCInit);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_INIT;
+ pCmd->rev = 3;
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MLCInit: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ cnt=0;
+ while(1)
+ {
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCInitReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_INIT)) || (pReply->result != 0))
+ {
+ if (errno == EIO && cnt<1)
+ {
+ /* hack for usblp.c 2.6.5 */
+ BUG("invalid MLCInitReply retrying...\n");
+ sleep(1);
+ cnt++;
+ continue;
+ }
+ if (stat == 2 && cnt<1)
+ {
+ /* hack for Tahoe */
+ BUG("invalid MLCInitReply retrying command...\n");
+ memset(buf, 0, sizeof(MLCInit));
+ n = sizeof(MLCInit);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_INIT;
+ pCmd->rev = 3;
+ (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT);
+ cnt++;
+ continue;
+ }
+ BUG("invalid MLCInitReply: cmd=%x, result=%x\n, revision=%x\n", pReply->cmd, pReply->result, pReply->rev);
+ stat = 1;
+ goto bugout;
+ }
+ break;
+ }
+
+bugout:
+ return stat;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcExit(mud_channel *pc, int fd)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n;
+ MLCExit *pCmd;
+ MLCExitReply *pReply;
+
+ memset(buf, 0, sizeof(MLCExit));
+ pCmd = (MLCExit *)buf;
+ n = sizeof(MLCExit);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_EXIT;
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MLCExit: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCExitReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_EXIT)) || (pReply->result != 0))
+ {
+ BUG("invalid MLCExitReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ stat = 1;
+ goto bugout;
+ }
+
+bugout:
+ return stat;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcConfigSocket(mud_channel *pc, int fd)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n;
+ MLCConfigSocket *pCmd;
+ MLCConfigSocketReply *pReply;
+
+ if (pc->ta.h2psize > 0)
+ return stat; /* already got host/peripheral packet sizes */
+
+ memset(buf, 0, sizeof(MLCConfigSocket));
+ pCmd = (MLCConfigSocket *)buf;
+ n = sizeof(MLCConfigSocket);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_CONFIG_SOCKET;
+ pCmd->socket = pc->sockid;
+ pCmd->h2psize = htons(HPMUD_BUFFER_SIZE);
+ pCmd->p2hsize = htons(HPMUD_BUFFER_SIZE);
+ pCmd->status = 0; /* status level?? */
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MLCConfigSocket: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCConfigSocketReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_CONFIG_SOCKET)) || (pReply->result != 0))
+ {
+ BUG("invalid MLCConfigSocketReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ stat = 1;
+ goto bugout;
+ }
+
+ pc->ta.h2psize = ntohs(pReply->h2psize);
+ pc->ta.p2hsize = ntohs(pReply->p2hsize);
+
+bugout:
+ return stat;
+}
+
+/* Write data to peripheral. */
+int __attribute__ ((visibility ("hidden"))) MlcForwardData(mud_channel *pc, int fd, const void *buf, int size, int usec_timeout)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ int stat=0, len, n;
+ MLCHeader h;
+
+ memset(&h, 0, sizeof(h));
+ n = sizeof(MLCHeader) + size;
+ h.length = htons(n);
+ h.hsid = pc->sockid;
+ h.psid = pc->sockid;
+
+ if ((len = (pd->vf.write)(fd, &h, sizeof(MLCHeader), usec_timeout)) != sizeof(MLCHeader))
+ {
+ BUG("unable to write MlcForwardData header: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ if ((len = (pd->vf.write)(fd, buf, size, usec_timeout)) != size)
+ {
+ BUG("unable to write MlcForwardData: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+bugout:
+ return stat;
+}
+
+/* Read data from peripheral. */
+int __attribute__ ((visibility ("hidden"))) MlcReverseData(mud_channel *pc, int fd, void *buf, int length, int usec_timeout)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ mud_channel *out_of_bound_channel;
+ int len, size, total;
+ MLCHeader *pPk;
+
+ pPk = (MLCHeader *)buf;
+
+ while (1)
+ {
+ total = 0;
+
+ /* Read packet header. */
+ size = sizeof(MLCHeader);
+ while (size > 0)
+ {
+ /* Use requested client timeout until we start reading. */
+ if (total == 0)
+ len = (pd->vf.read)(fd, buf+total, size, usec_timeout);
+ else
+ len = (pd->vf.read)(fd, buf+total, size, HPMUD_EXCEPTION_TIMEOUT);
+
+ if (len < 0)
+ {
+ /* Got a timeout, if exception timeout or timeout occured after read started thats an error. */
+ if (usec_timeout >= HPMUD_EXCEPTION_TIMEOUT || total > 0)
+ BUG("unable to read MlcReverseData header: %m %s\n", pd->uri);
+ goto bugout;
+ }
+ size-=len;
+ total+=len;
+ }
+
+ /* Determine data size. */
+ size = ntohs(pPk->length) - sizeof(MLCHeader);
+
+ if (size > length)
+ {
+ BUG("invalid MlcReverseData size: size=%d, buf=%d\n", size, length);
+ goto bugout;
+ }
+
+ /* Make sure data packet is for this channel. */
+ if (pPk->hsid != pc->sockid && pPk->psid != pc->sockid)
+ {
+ if (pPk->hsid == 0 && pPk->psid == 0)
+ {
+ /* Ok, got a command channel packet instead of a data packet, handle it... */
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, buf+total, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
+ {
+ BUG("unable to read MlcReverseData command: %m\n");
+ goto bugout;
+ }
+ size-=len;
+ total=len;
+ }
+ MlcExecReverseCmd(pc, fd, buf);
+ continue; /* try again for data packet */
+ }
+ else if (pPk->hsid == pPk->psid)
+ {
+ /* Got a valid data packet for another channel handle it. This can happen when ReadData timeouts and p2hcredit=1. */
+ out_of_bound_channel = &pd->channel[pPk->hsid];
+ unsigned char *pBuf;
+
+ if (out_of_bound_channel->ta.p2hcredit <= 0)
+ {
+ BUG("invalid data packet credit=%d\n", out_of_bound_channel->ta.p2hcredit);
+ goto bugout;
+ }
+
+ if (size > (HPMUD_BUFFER_SIZE - out_of_bound_channel->rcnt))
+ {
+ BUG("invalid data packet size=%d\n", size);
+ goto bugout;
+ }
+
+ total = 0;
+ pBuf = &out_of_bound_channel->rbuf[out_of_bound_channel->rcnt];
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, pBuf+total, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
+ {
+ BUG("unable to read MlcReverseData: %m\n");
+ goto bugout;
+ }
+ size-=len;
+ total+=len;
+ }
+
+ out_of_bound_channel->rcnt += total;
+ if (pPk->credit)
+ out_of_bound_channel->ta.h2pcredit += pPk->credit; /* note, piggy back credit is 1 byte wide */
+ out_of_bound_channel->ta.p2hcredit--; /* one data packet was read, decrement credit count */
+ continue; /* try again for data packet */
+ }
+ else
+ {
+ MLCCmd *pCmd = (MLCCmd *)buf;
+ BUG("invalid MlcReverseData state: exp hsid=%x, act hsid=%x, psid=%x, length=%d, credit=%d, status=%x, cmd=%x\n", pc->sockid,
+ pPk->hsid, pPk->psid, ntohs(pPk->length), pPk->credit, pPk->status, pCmd->cmd);
+ goto bugout;
+ }
+ }
+
+ if (pPk->credit)
+ {
+ pc->ta.h2pcredit += pPk->credit; /* note, piggy back credit is 1 byte wide */
+ }
+
+ total = 0; /* eat packet header */
+
+ /* Read packet data field with exception_timeout. */
+ while (size > 0)
+ {
+ if ((len = (pd->vf.read)(fd, buf+total, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
+ {
+ BUG("unable to read MlcReverseData: %m\n");
+ goto bugout;
+ }
+ size-=len;
+ total+=len;
+ }
+ break; /* done reading data packet */
+ } /* while (1) */
+
+bugout:
+ return total;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcOpenChannel(mud_channel *pc, int fd)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n;
+ MLCOpenChannel *pCmd;
+ MLCOpenChannelReply *pReply;
+
+ memset(buf, 0, sizeof(MLCOpenChannel));
+ pCmd = (MLCOpenChannel *)buf;
+ n = sizeof(MLCOpenChannel);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_OPEN_CHANNEL;
+ pCmd->hsocket = pc->sockid; /* assume static socket ids */
+ pCmd->psocket = pc->sockid;
+ pCmd->credit = htons(0); /* credit sender will accept from receiver (set by MlcDevice::ReadData) */
+ // SetH2PCredit(0); /* initialize sender to receiver credit */
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MlcOpenChannel: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCOpenChannelReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_OPEN_CHANNEL)) || (pReply->result != 0))
+ {
+ BUG("invalid MlcOpenChannelReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ stat = 1;
+ goto bugout;
+ }
+
+ pc->ta.h2pcredit = ntohs(pReply->credit);
+
+bugout:
+ return stat;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcCloseChannel(mud_channel *pc, int fd)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n;
+ MLCCloseChannel *pCmd;
+ MLCCloseChannelReply *pReply;
+
+ memset(buf, 0, sizeof(MLCCloseChannel));
+ pCmd = (MLCCloseChannel *)buf;
+ n = sizeof(MLCCloseChannel);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_CLOSE_CHANNEL;
+ pCmd->hsocket = pc->sockid; /* assume static socket ids */
+ pCmd->psocket = pc->sockid;
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MlcCloseChannel: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCCloseChannelReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_CLOSE_CHANNEL)) || (pReply->result != 0))
+ {
+ BUG("invalid MlcCloseChannelReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ stat = 1;
+ goto bugout;
+ }
+
+bugout:
+ return stat;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcCredit(mud_channel *pc, int fd, unsigned short credit)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n;
+ MLCCredit *pCmd;
+ MLCCreditReply *pReply;
+
+ memset(buf, 0, sizeof(MLCCredit));
+ pCmd = (MLCCredit *)buf;
+ n = sizeof(MLCCredit);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_CREDIT;
+ pCmd->hsocket = pc->sockid; /* assume static socket ids */
+ pCmd->psocket = pc->sockid;
+ pCmd->credit = htons(credit); /* set peripheral to host credit */
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MlcCredit: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCCreditReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_CREDIT)) || (pReply->result != 0))
+ {
+ BUG("invalid MlcCreditReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ stat = 1;
+ goto bugout;
+ }
+
+ pc->ta.p2hcredit += credit;
+
+bugout:
+ return stat;
+}
+
+int __attribute__ ((visibility ("hidden"))) MlcCreditRequest(mud_channel *pc, int fd, unsigned short credit)
+{
+ mud_device *pd = &msp->device[pc->dindex];
+ unsigned char buf[HPMUD_BUFFER_SIZE];
+ int stat=0, len, n;
+ MLCCreditRequest *pCmd;
+ MLCCreditRequestReply *pReply;
+
+ memset(buf, 0, sizeof(MLCCreditRequest));
+ pCmd = (MLCCreditRequest *)buf;
+ n = sizeof(MLCCreditRequest);
+ pCmd->h.length = htons(n);
+ pCmd->cmd = MLC_CREDIT_REQUEST;
+ pCmd->hsocket = pc->sockid; /* assume static socket ids */
+ pCmd->psocket = pc->sockid;
+ pCmd->credit = htons(credit); /* request host to peripheral credit */
+
+ if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
+ {
+ BUG("unable to write MlcCreditRequest: %m\n");
+ stat = 1;
+ goto bugout;
+ }
+
+ stat = MlcReverseReply(pc, fd, buf, sizeof(buf));
+ pReply = (MLCCreditRequestReply *)buf;
+
+ if ((stat != 0) || (pReply->cmd != (0x80 | MLC_CREDIT_REQUEST)) || (pReply->result != 0))
+ {
+ BUG("invalid MlcCreditRequestReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
+ stat = 1;
+ goto bugout;
+ }
+
+ pc->ta.h2pcredit += ntohs(pReply->credit);
+
+bugout:
+ return stat;
+}
+
+
+