diff options
author | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-13 01:42:35 +0900 |
---|---|---|
committer | Jinkun Jang <jinkun.jang@samsung.com> | 2013-03-13 01:42:35 +0900 |
commit | 72835b3d805ac6c7cdaac7d3aff107567e938314 (patch) | |
tree | 0f2a04dc3d0672c0960a62804c6e7758673e393c /io | |
parent | eb5e5ee9adb02776056d1b4494f66150a2fc45f1 (diff) | |
download | hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.tar.gz hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.tar.bz2 hplip-72835b3d805ac6c7cdaac7d3aff107567e938314.zip |
Tizen 2.1 base
Diffstat (limited to 'io')
-rw-r--r-- | io/hpmud/dot4.c | 750 | ||||
-rw-r--r-- | io/hpmud/dot4.h | 187 | ||||
-rw-r--r-- | io/hpmud/hp-mkuri.c | 543 | ||||
-rw-r--r-- | io/hpmud/hpmud.c | 699 | ||||
-rw-r--r-- | io/hpmud/hpmud.h | 581 | ||||
-rw-r--r-- | io/hpmud/hpmudi.h | 212 | ||||
-rw-r--r-- | io/hpmud/jd.c | 906 | ||||
-rw-r--r-- | io/hpmud/jd.h | 56 | ||||
-rw-r--r-- | io/hpmud/list.h | 131 | ||||
-rw-r--r-- | io/hpmud/mlc.c | 772 | ||||
-rw-r--r-- | io/hpmud/mlc.h | 150 | ||||
-rw-r--r-- | io/hpmud/model.c | 633 | ||||
-rw-r--r-- | io/hpmud/musb.c | 2197 | ||||
-rw-r--r-- | io/hpmud/musb.h | 121 | ||||
-rw-r--r-- | io/hpmud/pml.c | 520 | ||||
-rw-r--r-- | io/hpmud/pml.h | 77 | ||||
-rw-r--r-- | io/hpmud/pp.c | 1310 | ||||
-rw-r--r-- | io/hpmud/pp.h | 100 | ||||
-rw-r--r-- | io/mudext/hpmudext.c | 495 |
19 files changed, 10440 insertions, 0 deletions
diff --git a/io/hpmud/dot4.c b/io/hpmud/dot4.c new file mode 100644 index 0000000..ba56e53 --- /dev/null +++ b/io/hpmud/dot4.c @@ -0,0 +1,750 @@ +/*****************************************************************************\ + + dot4.c - 1284.4 support multi-point tranport driver + + (c) 2005-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" + +/* + * This 1284.4 implementation does not support "Multiple Outstanding Transactions" which is optional. + */ + +/* Write command reply back to peripheral. */ +static int Dot4ForwardReply(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 Dot4ForwarReply: %m\n"); + } + return len; +} + +/* Execute command from peripheral. */ +static int Dot4ExecReverseCmd(mud_channel *pc, int fd, unsigned char *buf) +{ + mud_device *pd = &msp->device[pc->dindex]; + mud_channel *out_of_bound_channel; + DOT4Cmd *pCmd; + DOT4Reply *pReply; + DOT4Credit *pCredit; + DOT4CreditReply *pCreditReply; + DOT4CreditRequest *pCreditReq; + DOT4CreditRequestReply *pCreditReqReply; + DOT4Error *pError; + int len, size; + unsigned char socket; + static int cnt; + + pCmd = (DOT4Cmd *)buf; + + /* See if this packet is a command packet. */ + if (!(pCmd->h.psid == 0 && pCmd->h.ssid == 0)) + { + if (pCmd->h.psid == pCmd->h.ssid) + { + /* 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.psid]; + + 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(DOT4Header); + 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: psid=%x, ssid=%x, length=%d, credit=%d, status=%x\n", pCmd->h.psid, + pCmd->h.ssid, len, pCmd->h.credit, pCmd->h.control); + DBG_DUMP(buf, len); + } + return 0; + } + + /* Process any command. */ + switch (pCmd->cmd) + { + case DOT4_CREDIT: + pCredit = (DOT4Credit *)buf; + out_of_bound_channel = &pd->channel[pCredit->psocket]; + out_of_bound_channel->ta.h2pcredit += ntohs(pCredit->credit); + pCreditReply = (DOT4CreditReply *)buf; + pCreditReply->h.length = htons(sizeof(DOT4CreditReply)); + pCreditReply->h.credit = 1; /* transaction credit for next command */ + pCreditReply->h.control = 0; + pCreditReply->cmd |= 0x80; + pCreditReply->result = 0; + pCreditReply->psocket = out_of_bound_channel->sockid; + pCreditReply->ssocket = out_of_bound_channel->sockid; + Dot4ForwardReply(pc, fd, (unsigned char *)pCreditReply, sizeof(DOT4CreditReply)); + break; + case DOT4_CREDIT_REQUEST: + pCreditReq = (DOT4CreditRequest *)buf; + if (cnt++ < 5) + BUG("unexpected DOT4CreditRequest: cmd=%x, hid=%x, pid=%x, maxcredit=%d\n", pCreditReq->cmd, + pCreditReq->psocket, pCreditReq->ssocket, ntohs(pCreditReq->maxcredit)); + socket = pCreditReq->ssocket; + pCreditReqReply = (DOT4CreditRequestReply *)buf; + pCreditReqReply->h.length = htons(sizeof(DOT4CreditRequestReply)); + pCreditReqReply->h.credit = 1; /* transaction credit for next command */ + pCreditReqReply->h.control = 0; + pCreditReqReply->cmd |= 0x80; + pCreditReqReply->result = 0; + pCreditReqReply->psocket = socket; + pCreditReqReply->ssocket = socket; + pCreditReqReply->credit = 0; + Dot4ForwardReply(pc, fd, (unsigned char *)pCreditReqReply, sizeof(DOT4CreditRequestReply)); + break; + case DOT4_ERROR: + pError = (DOT4Error *)buf; + BUG("unexpected DOT4Error: cmd=%x, psocket=%d, ssocket=%d, error=%x\n", pError->cmd, pError->psocket, pError->ssocket, pError->error); + return 1; + default: + pReply = (DOT4Reply *)buf; + BUG("unexpected command: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + pReply->h.length = htons(sizeof(DOT4Reply)); + pReply->h.credit = 1; /* transaction credit for next command */ + pReply->h.control = 0; + pReply->cmd |= 0x80; + pReply->result = 1; + Dot4ForwardReply(pc, fd, (unsigned char *)pReply, sizeof(DOT4Reply)); + break; + } + return 0; +} + +/* Get command from peripheral and processes the reverse command. */ +int __attribute__ ((visibility ("hidden"))) Dot4ReverseCmd(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; + DOT4Reply *pPk; + + pPk = (DOT4Reply *)buf; + + pBuf = buf; + + /* Read packet header. */ + size = sizeof(DOT4Header); + while (size > 0) + { + if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0) + { + BUG("unable to read Dot4ReverseCmd header: %m\n"); + stat = 1; + goto bugout; + } + size-=len; + pBuf+=len; + } + + /* Determine packet size. */ + if ((pklen = ntohs(pPk->h.length)) > sizeof(buf)) + { + BUG("invalid Dot4ReverseCmd packet size: size=%d\n", pklen); + stat = 1; + goto bugout; + } + + /* Read packet data field. */ + size = pklen - sizeof(DOT4Header); + while (size > 0) + { + if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0) + { + BUG("unable to read Dot4ReverseCmd data: %m exp=%zd act=%zd\n", pklen-sizeof(DOT4Header), pklen-sizeof(DOT4Header)-size); + stat = 1; + goto bugout; + } + size-=len; + pBuf+=len; + } + + stat = Dot4ExecReverseCmd(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 Dot4ReverseReply(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; + DOT4Reply *pPk; + + pPk = (DOT4Reply *)buf; + + while (1) + { + pBuf = buf; + + /* Read packet header. */ + size = sizeof(DOT4Header); + while (size > 0) + { + if ((len = (pd->vf.read)(fd, pBuf, size, 4000000)) < 0) /* wait 4 seconds, 2 fails on PS2575 1200dpi uncompressed scanning */ + { + BUG("unable to read Dot4ReverseReply header: %m bytesRead=%zd\n", sizeof(DOT4Header)-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 Dot4ReverseReply packet size: size=%d, buf=%d\n", pklen, bufsize); + stat = 1; + goto bugout; + } + + /* Read packet data field. */ + size = pklen - sizeof(DOT4Header); + while (size > 0) + { + if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0) + { + BUG("unable to read Dot4ReverseReply data: %m exp=%zd act=%zd\n", pklen-sizeof(DOT4Header), pklen-sizeof(DOT4Header)-size); + stat = 1; + goto bugout; + } + size-=len; + pBuf+=len; + } + + /* Check for reply. */ + if (pPk->cmd & 0x80) + break; + + stat = Dot4ExecReverseCmd(pc, fd, buf); + + if (stat != 0) + break; + + } /* while (1) */ + +bugout: + return stat; +} + +int __attribute__ ((visibility ("hidden"))) Dot4Init(mud_channel *pc, int fd) +{ + mud_device *pd = &msp->device[pc->dindex]; + unsigned char buf[HPMUD_BUFFER_SIZE]; + int stat=0, len, n, cnt; + DOT4Init *pCmd; + DOT4InitReply *pReply; + + memset(buf, 0, sizeof(DOT4Init)); + pCmd = (DOT4Init *)buf; + n = sizeof(DOT4Init); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; /* transaction credit for reply */ + pCmd->cmd = DOT4_INIT; + pCmd->rev = 0x20; + + if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n) + { + BUG("unable to write DOT4Init: %m\n"); + stat = 1; + goto bugout; + } + + cnt=0; + while(1) + { + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4InitReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_INIT)) || (pReply->result != 0)) + { + if (errno == EIO && cnt<1) + { + /* hack for usblp.c 2.6.5 */ + BUG("invalid DOT4InitReply retrying...\n"); + sleep(1); + cnt++; + continue; + } + if (stat == 2 && cnt<1) + { + /* hack for Fullhouse, Swami and Northstar */ + BUG("invalid DOT4InitReply retrying command...\n"); + memset(buf, 0, sizeof(DOT4Init)); + n = sizeof(DOT4Init); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; /* transaction credit for reply */ + pCmd->cmd = DOT4_INIT; + pCmd->rev = 0x20; + (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT); + cnt++; + continue; + } + BUG("invalid DOT4InitReply: 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"))) Dot4Exit(mud_channel *pc, int fd) +{ + mud_device *pd = &msp->device[pc->dindex]; + unsigned char buf[HPMUD_BUFFER_SIZE]; + int stat=0, len, n; + DOT4Exit *pCmd; + DOT4ExitReply *pReply; + + memset(buf, 0, sizeof(DOT4Exit)); + pCmd = (DOT4Exit *)buf; + n = sizeof(DOT4Exit); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; + pCmd->cmd = DOT4_EXIT; + + if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n) + { + BUG("unable to write DOT4Exit: %m\n"); + stat = 1; + goto bugout; + } + + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4ExitReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_EXIT)) || (pReply->result != 0)) + { + BUG("invalid DOT4ExitReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + stat = 1; + goto bugout; + } + +bugout: + return stat; +} + +int __attribute__ ((visibility ("hidden"))) Dot4GetSocket(mud_channel *pc, int fd) +{ + mud_device *pd = &msp->device[pc->dindex]; + unsigned char buf[HPMUD_BUFFER_SIZE]; + int stat=0, len, n; + DOT4GetSocket *pCmd; + DOT4GetSocketReply *pReply; + + memset(buf, 0, sizeof(DOT4GetSocket)); + pCmd = (DOT4GetSocket *)buf; + n = sizeof(DOT4GetSocket); + len = strlen(pc->sn); + memcpy(buf+sizeof(DOT4GetSocket), pc->sn, len); + n += len; + pCmd->h.length = htons(n); + pCmd->h.credit = 1; + pCmd->cmd = DOT4_GET_SOCKET; + + if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n) + { + BUG("unable to write DOT4GetSocket: %m\n"); + stat = 1; + goto bugout; + } + + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4GetSocketReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_GET_SOCKET)) || (pReply->result != 0)) + { + BUG("invalid DOT4GetSocketReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + stat = 1; + goto bugout; + } + + pc->sockid = pReply->socket; + + if (pc->sockid != pc->index) + BUG("invalid sockid match sockid=%d index=%d\n", pc->sockid, pc->index); + +bugout: + return stat; +} + +/* Write data to peripheral. */ +int __attribute__ ((visibility ("hidden"))) Dot4ForwardData(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; + DOT4Header h; + + memset(&h, 0, sizeof(h)); + n = sizeof(DOT4Header) + size; + h.length = htons(n); + h.psid = pc->sockid; + h.ssid = pc->sockid; + + if ((len = (pd->vf.write)(fd, &h, sizeof(DOT4Header), usec_timeout)) != sizeof(DOT4Header)) + { + BUG("unable to write Dot4ForwardData header: %m\n"); + stat = 1; + goto bugout; + } + + if ((len = (pd->vf.write)(fd, buf, size, usec_timeout)) != size) + { + BUG("unable to write Dot4ForwardData: %m\n"); + stat = 1; + goto bugout; + } + +bugout: + return stat; +} + +/* Read data from peripheral. */ +int __attribute__ ((visibility ("hidden"))) Dot4ReverseData(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; + DOT4Header *pPk; + + pPk = (DOT4Header *)buf; + + while (1) + { + total = 0; + + /* Read packet header. */ + size = sizeof(DOT4Header); + 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 Dot4ReverseData header: %m %s\n", pd->uri); + goto bugout; + } + size-=len; + total+=len; + } + + /* Determine data size. */ + size = ntohs(pPk->length) - sizeof(DOT4Header); + + if (size > length) + { + BUG("invalid Dot4ReverseData size: size=%d, buf=%d\n", size, length); + goto bugout; + } + + /* Make sure data packet is for this channel. */ + if (pPk->psid != pc->sockid && pPk->ssid != pc->sockid) + { + if (pPk->psid == 0 && pPk->ssid == 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 Dot4ReverseData command: %m\n"); + goto bugout; + } + size-=len; + total=len; + } + Dot4ExecReverseCmd(pc, fd, buf); + continue; /* try again for data packet */ + } + else if (pPk->psid == pPk->ssid) + { + /* 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->psid]; + 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 + { + DOT4Cmd *pCmd = (DOT4Cmd *)buf; + BUG("invalid Dot4ReverseData state: unexpected packet psid=%x, ssid=%x, cmd=%x\n", pPk->psid, pPk->ssid, 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 Dot4ReverseData: %m\n"); + goto bugout; + } + size-=len; + total+=len; + } + break; /* done reading data packet */ + } /* while (1) */ + +bugout: + return total; +} + +int __attribute__ ((visibility ("hidden"))) Dot4OpenChannel(mud_channel *pc, int fd) +{ + mud_device *pd = &msp->device[pc->dindex]; + unsigned char buf[HPMUD_BUFFER_SIZE]; + int stat=0, len, n; + DOT4OpenChannel *pCmd; + DOT4OpenChannelReply *pReply; + + memset(buf, 0, sizeof(DOT4OpenChannel)); + pCmd = (DOT4OpenChannel *)buf; + n = sizeof(DOT4OpenChannel); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; + pCmd->cmd = DOT4_OPEN_CHANNEL; + pCmd->psocket = pc->sockid; + pCmd->ssocket = pc->sockid; + pCmd->maxp2s = htons(HPMUD_BUFFER_SIZE); /* max primary to secondary packet size in bytes */ + pCmd->maxs2p = htons(HPMUD_BUFFER_SIZE); /* max secondary to primary packet size in bytes */ + pCmd->maxcredit = htons(0xffff); /* "unlimited credit" mode, give primary (sender) as much credit as possible */ + + if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n) + { + BUG("unable to write Dot4OpenChannel: %m\n"); + stat = 1; + goto bugout; + } + + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4OpenChannelReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_OPEN_CHANNEL)) || (pReply->result != 0)) + { + BUG("invalid Dot4OpenChannelReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + stat = 1; + goto bugout; + } + + pc->ta.h2psize = ntohs(pReply->maxp2s); + pc->ta.p2hsize = ntohs(pReply->maxs2p); + pc->ta.h2pcredit = ntohs(pReply->credit); + +bugout: + return stat; +} + +int __attribute__ ((visibility ("hidden"))) Dot4CloseChannel(mud_channel *pc, int fd) +{ + mud_device *pd = &msp->device[pc->dindex]; + unsigned char buf[HPMUD_BUFFER_SIZE]; + int stat=0, len, n; + DOT4CloseChannel *pCmd; + DOT4CloseChannelReply *pReply; + + memset(buf, 0, sizeof(DOT4CloseChannel)); + pCmd = (DOT4CloseChannel *)buf; + n = sizeof(DOT4CloseChannel); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; + pCmd->cmd = DOT4_CLOSE_CHANNEL; + pCmd->psocket = pc->sockid; + pCmd->ssocket = pc->sockid; + + if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n) + { + BUG("unable to write Dot4CloseChannel: %m\n"); + stat = 1; + goto bugout; + } + + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4CloseChannelReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_CLOSE_CHANNEL)) || (pReply->result != 0)) + { + BUG("invalid Dot4CloseChannelReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + stat = 1; + goto bugout; + } + +bugout: + return stat; +} + +int __attribute__ ((visibility ("hidden"))) Dot4Credit(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; + DOT4Credit *pCmd; + DOT4CreditReply *pReply; + + memset(buf, 0, sizeof(DOT4Credit)); + pCmd = (DOT4Credit *)buf; + n = sizeof(DOT4Credit); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; + pCmd->cmd = DOT4_CREDIT; + pCmd->psocket = pc->sockid; + pCmd->ssocket = 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 Dot4Credit: %m\n"); + stat = 1; + goto bugout; + } + + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4CreditReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_CREDIT)) || (pReply->result != 0)) + { + BUG("invalid Dot4CreditReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + stat = 1; + goto bugout; + } + + pc->ta.p2hcredit += credit; + +bugout: + return stat; +} + +int __attribute__ ((visibility ("hidden"))) Dot4CreditRequest(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; + DOT4CreditRequest *pCmd; + DOT4CreditRequestReply *pReply; + + memset(buf, 0, sizeof(DOT4CreditRequest)); + pCmd = (DOT4CreditRequest *)buf; + n = sizeof(DOT4CreditRequest); + pCmd->h.length = htons(n); + pCmd->h.credit = 1; + pCmd->cmd = DOT4_CREDIT_REQUEST; + pCmd->psocket = pc->sockid; + pCmd->ssocket = pc->sockid; + // pCmd->maxcredit = htons(credit); /* request host to peripheral credit */ + pCmd->maxcredit = htons(0xffff); /* request host to peripheral credit */ + + if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n) + { + BUG("unable to write Dot4CreditRequest: %m\n"); + stat = 1; + goto bugout; + } + + stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf)); + pReply = (DOT4CreditRequestReply *)buf; + + if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_CREDIT_REQUEST)) || (pReply->result != 0)) + { + BUG("invalid Dot4CreditRequestReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result); + stat = 1; + goto bugout; + } + + pc->ta.h2pcredit += ntohs(pReply->credit); + +bugout: + return stat; +} + diff --git a/io/hpmud/dot4.h b/io/hpmud/dot4.h new file mode 100644 index 0000000..2623194 --- /dev/null +++ b/io/hpmud/dot4.h @@ -0,0 +1,187 @@ +/*****************************************************************************\ + + dot4.h - 1284.4 support for multi-point transport driver + + (c) 2005-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. + +\*****************************************************************************/ + +#ifndef _DOT4_H +#define _DOT4_H + +enum DOT4_COMMAND +{ + DOT4_INIT = MLC_INIT, + DOT4_OPEN_CHANNEL = MLC_OPEN_CHANNEL, + DOT4_CLOSE_CHANNEL = MLC_CLOSE_CHANNEL, + DOT4_CREDIT = MLC_CREDIT, + DOT4_CREDIT_REQUEST = MLC_CREDIT_REQUEST, + DOT4_GET_SOCKET = 0x9, + DOT4_GET_SERVICE = 0xa, + DOT4_EXIT = MLC_EXIT, + DOT4_ERROR = MLC_ERROR +}; + +/* + * Note, following structures must be packed. The "pragma pack" statement is not recognized by all gcc compilers (ie: ARM based), + * so we use __attribute__((packed)) instead. + */ + +typedef struct +{ + unsigned char psid; /* primary socket id (ie: host) */ + unsigned char ssid; /* secondary socket id (ie: peripheral) */ + unsigned short length; /* packet length (includes header) */ + unsigned char credit; /* data packet credit, reserved if command */ + unsigned char control; /* bit field: 0=normal */ +} __attribute__((packed)) DOT4Header; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char rev; +} __attribute__((packed)) DOT4Init; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char result; + unsigned char rev; +} __attribute__((packed)) DOT4InitReply; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; +} __attribute__((packed)) DOT4Exit; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char result; +} __attribute__((packed)) DOT4ExitReply; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char psocket; /* primary socket id */ + unsigned char ssocket; /* secondary socket id */ + unsigned short maxp2s; /* max primary to secondary packet size in bytes */ + unsigned short maxs2p; /* max secondary to primary packet size in bytes */ + unsigned short maxcredit; /* max outstanding credit */ +} __attribute__((packed)) DOT4OpenChannel; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char result; + unsigned char psocket; + unsigned char ssocket; + unsigned short maxp2s; /* max primary to secondary packet size in bytes */ + unsigned short maxs2p; /* max secondary to primary packet size in bytes */ + unsigned short maxcredit; /* max outstanding credit */ + unsigned short credit; +} __attribute__((packed)) DOT4OpenChannelReply; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char psocket; /* primary socket id */ + unsigned char ssocket; /* secondary socket id */ +} __attribute__((packed)) DOT4CloseChannel; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char result; + unsigned char psocket; /* primary socket id */ + unsigned char ssocket; /* secondary socket id */ +} __attribute__((packed)) DOT4CloseChannelReply; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char result; + unsigned char socket; +} __attribute__((packed)) DOT4GetSocketReply; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char psocket; + unsigned char ssocket; + unsigned short credit; /* credit for sender */ +} __attribute__((packed)) DOT4Credit; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char psocket; + unsigned char ssocket; + unsigned short maxcredit; /* maximum outstanding credit */ +} __attribute__((packed)) DOT4CreditRequest; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char result; + unsigned char psocket; + unsigned char ssocket; + unsigned short credit; /* credit for sender */ +} __attribute__((packed)) DOT4CreditRequestReply; + +typedef struct +{ + DOT4Header h; + unsigned char cmd; + unsigned char psocket; /* primary socket id which contains the error */ + unsigned char ssocket; /* secondary socket id which contains the error */ + unsigned char error; +} __attribute__((packed)) DOT4Error; + +typedef DOT4ExitReply DOT4Reply; +typedef DOT4Exit DOT4Cmd; +typedef DOT4CloseChannelReply DOT4CreditReply; +typedef DOT4Exit DOT4GetSocket; + +int __attribute__ ((visibility ("hidden"))) Dot4ReverseCmd(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) Dot4Init(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) Dot4Exit(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) Dot4GetSocket(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) Dot4ForwardData(struct _mud_channel *pc, int fd, const void *buf, int size, int usec_timeout); +int __attribute__ ((visibility ("hidden"))) Dot4ReverseData(struct _mud_channel *pc, int fd, void *buf, int length, int usec_timeout); +int __attribute__ ((visibility ("hidden"))) Dot4OpenChannel(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) Dot4CloseChannel(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) Dot4Credit(struct _mud_channel *pc, int fd, unsigned short credit); +int __attribute__ ((visibility ("hidden"))) Dot4CreditRequest(struct _mud_channel *pc, int fd, unsigned short credit); + +#endif // _DOT4_H + diff --git a/io/hpmud/hp-mkuri.c b/io/hpmud/hp-mkuri.c new file mode 100644 index 0000000..7c32ff7 --- /dev/null +++ b/io/hpmud/hp-mkuri.c @@ -0,0 +1,543 @@ +/*****************************************************************************\ + + hp-mkuri.c - make uri with multi-point transport driver (HPMUD) + + (c) 2008-2009 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <signal.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <syslog.h> +#include <dlfcn.h> +#include "hpmud.h" + +#define _STRINGIZE(x) #x +#define STRINGIZE(x) _STRINGIZE(x) +//#define BUG(args...) fprintf(stderr, __FILE__ " " STRINGIZE(__LINE__) ": " args) +#define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args) + +static int verbose; +static char homedir[255] = ""; + +static void usage() +{ + fprintf(stdout, "HPLIP Make URI %s\n", VERSION); + fprintf(stdout, "(c) 2008 Copyright Hewlett-Packard Development Company, LP\n"); + fprintf(stdout, "usage: hp-mkuri -i ip [-p port]\n"); + fprintf(stdout, "usage: hp-mkuri -z hostname\n"); + fprintf(stdout, "usage: hp-mkuri -b busnum -d devnum\n"); + fprintf(stdout, "usage: hp-mkuri -s serialnum\n"); + fprintf(stdout, "usage: hp-mkuri -l /dev/parportx\n"); + fprintf(stdout, "usage: hp-mkuri -m hostname [-p port]\n"); + fprintf(stdout, "usage: hp-mkuri -o (probe)\n"); + fprintf(stdout, "usage: hp-mkuri -c [-n (no notifier)] (check support)\n"); + fprintf(stdout, "\nSupport matrix:\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| return value | printer | fax | plugin_required | plugin_optional |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 0 | yes | | | |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 1 | * | * | * | * |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 2 | yes | | yes | |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 3 | yes | | | yes |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 4 | yes | yes | | |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 5 | yes | yes | yes | |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, "| 6 | yes | yes | | yes |\n"); + fprintf(stdout, "+--------------+---------+-----+-----------------+-----------------+\n"); + fprintf(stdout, " * no support or error\n"); +} /* usage */ + +static int GetPair(char *buf, int buf_len, char *key, char *value, char **tail) +{ + int i=0, j; + + key[0] = 0; + value[0] = 0; + + if (buf[i] == '#') + { + for (; buf[i] != '\n' && i < buf_len; i++); /* eat comment line */ + if (buf[i] == '\n') + i++; /* bump past '\n' */ + } + + j = 0; + while ((buf[i] != '=') && (i < buf_len) && (j < HPMUD_LINE_SIZE)) + key[j++] = buf[i++]; + for (j--; key[j] == ' ' && j > 0; j--); /* eat white space before = */ + key[++j] = 0; + + if (buf[i] == '=') + for (i++; buf[i] == ' ' && i < buf_len; i++); /* eat white space after = */ + + j = 0; + while ((buf[i] != '\n') && (i < buf_len) && (j < HPMUD_LINE_SIZE)) + value[j++] = buf[i++]; + for (j--; value[j] == ' ' && j > 0; j--); /* eat white space before \n */ + value[++j] = 0; + + if (buf[i] == '\n') + i++; /* bump past '\n' */ + + if (tail != NULL) + *tail = buf + i; /* tail points to next line */ + + return i; +} + +static int ReadConfig() +{ + char key[HPMUD_LINE_SIZE]; + char value[HPMUD_LINE_SIZE]; + char rcbuf[255]; + char section[32]; + char *tail; + FILE *inFile = NULL; + int stat=1; + + homedir[0] = 0; + + if((inFile = fopen(CONFDIR "/hplip.conf", "r")) == NULL) + { + BUG("unable to open %s: %m\n", CONFDIR "/hplip.conf"); + goto bugout; + } + + section[0] = 0; + + /* Read the config file */ + while ((fgets(rcbuf, sizeof(rcbuf), inFile) != NULL)) + { + if (rcbuf[0] == '[') + { + strncpy(section, rcbuf, sizeof(section)); /* found new section */ + continue; + } + + GetPair(rcbuf, strlen(rcbuf), key, value, &tail); + + if ((strncasecmp(section, "[dirs]", 6) == 0) && (strcasecmp(key, "home") == 0)) + { + strncpy(homedir, value, sizeof(homedir)); + break; /* done */ + } + } + + stat = 0; + +bugout: + if (inFile != NULL) + fclose(inFile); + + return stat; +} + +static int generalize_model(const char *sz, char *buf, int bufSize) +{ + const char *pMd=sz; + int i, j, dd=0; + + for (i=0; pMd[i] == ' ' && i < bufSize; i++); /* eat leading white space */ + + for (j=0; (pMd[i] != 0) && (pMd[i] != ';') && (j < bufSize); i++) + { + if (pMd[i]==' ' || pMd[i]=='/') + { + /* Remove double spaces. */ + if (!dd) + { + buf[j++] = '_'; /* convert space to "_" */ + dd=1; + } + } + else + { + buf[j++] = tolower(pMd[i]); + dd=0; + } + } + + for (j--; buf[j] == '_' && j > 0; j--); /* eat trailing white space */ + + buf[++j] = 0; + + return j; /* length does not include zero termination */ +} + +static int set_x_environment(void) +{ + DIR *dir=NULL; + FILE *file=NULL; + struct dirent *entry; + char path[32], line[256], cookie[128], *p; + int i, c, stat=1; + + if ((dir = opendir("/proc"))==NULL) + { + BUG("unable to open /proc: %m\n"); + goto bugout; + } + + while ((entry = readdir(dir)) != NULL) + { + if (!isdigit(*entry->d_name)) + continue; + + /* Get command line for this PID. */ + snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name); + if ((file = fopen(path, "r")) == NULL) + continue; + for (i=0; ((c = getc(file)) != EOF) && (i < (sizeof(line)-1)); i++) + { + if (c == 0) + c = ' '; + line[i] = c; + } + line[i]=0; + fclose(file); + if ((p = strstr(line, "-auth "))) + { + /* Found X server. */ + for (p+=6; (*p == ' ') && (*p != 0); p++); /* eat any white space before cookie */ + for (i=0; (*(p+i) != ' ') && (*(p+i) != 0) && i < (sizeof(cookie)-1); i++) + cookie[i] = *(p+i); + cookie[i]=0; + setenv("XAUTHORITY", cookie, 1); + setenv("DISPLAY", ":0.0", 1); + break; + } + } /* while ((entry = readdir(dir)) != NULL) */ + + stat = 0; + +bugout: + if (dir) + closedir(dir); + return(stat); +} /* set_x_environment */ + +static int notify(const char *summary, const char *message, int ms_timeout) +{ + void *handle=NULL, *n; + int stat=1; + + typedef void (*notify_init_t)(char *); + typedef void *(*notify_notification_new_t)(const char *, const char *, const char *, void *); + typedef void (*notify_notification_set_timeout_t)(void *, int); + typedef void (*notify_notification_show_t)(void *, char *); + + notify_init_t n_init; + notify_notification_new_t n_new; + notify_notification_set_timeout_t n_timeout; + notify_notification_show_t n_show; + + set_x_environment(); + + /* Bypass glib build dependencies by loading libnotify manually. */ + + if ((handle = dlopen("libnotify.so.1", RTLD_LAZY)) == NULL) + { + BUG("failed to open libnotify: %m\n"); + goto bugout; + } + + if ((n_init = (notify_init_t)dlsym(handle, "notify_init")) == NULL) + { + BUG("failed to find notify_init: %m\n"); + goto bugout; + } + n_init("Basics"); + + if ((n_new = (notify_notification_new_t)dlsym(handle, "notify_notification_new")) == NULL) + { + BUG("failed to find notify_notification_new: %m\n"); + goto bugout; + } + n = n_new(summary, message, NULL, NULL); + + if ((n_timeout = (notify_notification_set_timeout_t)dlsym(handle, "notify_notification_set_timeout")) == NULL) + { + BUG("failed to find notify_notification_set_timeout: %m\n"); + goto bugout; + } + n_timeout(n, ms_timeout); + + if ((n_show = (notify_notification_show_t)dlsym(handle, "notify_notification_show")) == NULL) + { + BUG("failed to find notify_notification_show: %m\n"); + goto bugout; + } + n_show(n, NULL); + + stat=0; + +bugout: + if (handle) + dlclose(handle); + + return stat; +} /* notify */ + +static int check_support(int send_notify) +{ + struct stat sb; + char model[256]; + int ret=1, plugin_installed=1; + const char *pm; + char m[256]; + char datfile[256]; + char value[32]; + int support, plugin, fax; + + /* Get hp model from environment variables. */ + if ((pm = getenv("hp_model"))) + { + strncpy(model, pm, sizeof(model)); + } + else + { + fprintf(stderr, "error no hp_model environment variable set\n"); + BUG("error no hp_model environment variable set\n"); + goto bugout; + } + + if (model[0]==0) + { + BUG("invalid parameter(s)\n"); + usage(); + goto bugout; + } + + generalize_model(model, m, sizeof(m)); + snprintf(model, sizeof(model), "[%s]", m); + + if (ReadConfig()) + goto bugout; + + snprintf(datfile, sizeof(datfile), "%s/data/models/models.dat", homedir); + + if (hpmud_get_key_value(datfile, model, "support-type", value, sizeof(value)) != HPMUD_R_OK) + goto bugout; + support = strtol(value, NULL, 10); + if (hpmud_get_key_value(datfile, model, "plugin", value, sizeof(value)) != HPMUD_R_OK) + goto bugout; + plugin = strtol(value, NULL, 10); + if (hpmud_get_key_value(datfile, model, "fax-type", value, sizeof(value)) != HPMUD_R_OK) + goto bugout; + fax = strtol(value, NULL, 10); + + /* See if device is supported by hplip. */ + if (support == HPMUD_SUPPORT_TYPE_NONE) + { + BUG("%s is not supported by HPLIP %s\n", pm, VERSION); + goto bugout; + } + + if (stat("/etc/udev/rules.d/86-hpmud-hp_laserjet_1018.rules", &sb) == -1) + plugin_installed=0; + + if (send_notify && !plugin_installed) + { + /* See if device requires a Plugin. */ + switch (plugin) + { + case HPMUD_PLUGIN_TYPE_REQUIRED: + BUG("%s requires a proprietary plugin\n", pm); + notify(pm, "requires a proprietary plugin, run hp-setup", 30000); + break; + case HPMUD_PLUGIN_TYPE_OPTIONAL: + BUG("%s has a optional proprietary plugin\n", pm); + notify(pm, "has a optional proprietary plugin, run hp-setup", 30000); + break; + default: + break; + } + } + + ret = 0; + if (plugin == HPMUD_PLUGIN_TYPE_REQUIRED) + ret = 2; + else if (plugin == HPMUD_PLUGIN_TYPE_OPTIONAL) + ret = 3; + if (fax > 0) + { + ret = 4; + if (plugin == HPMUD_PLUGIN_TYPE_REQUIRED) + ret = 5; + else if (plugin == HPMUD_PLUGIN_TYPE_OPTIONAL) + ret = 6; + } + +bugout: + return ret; +} /* check_support */ + +int main(int argc, char *argv[]) +{ + char ip[HPMUD_LINE_SIZE]; /* internet address */ + char bn[HPMUD_LINE_SIZE]; /* usb bus number */ + char dn[HPMUD_LINE_SIZE]; /* usb device number */ + char sn[HPMUD_LINE_SIZE]; /* usb serial number */ + char pp[HPMUD_LINE_SIZE]; /* parallel port device */ + char uri[HPMUD_LINE_SIZE]; + char host[HPMUD_LINE_SIZE]; + int i, port=1, ret=1, probe=0, support=0, send_notify=1; + enum HPMUD_RESULT stat; + char buf[HPMUD_LINE_SIZE*64]; + int cnt, bytes_read; + + ip[0] = bn[0] = dn[0] = pp[0] = uri[0] = sn[0] = host[0] = 0; + while ((i = getopt(argc, argv, "vhocni:p:b:d:l:s:z:")) != -1) + { + switch (i) + { + case 'i': + strncpy(ip, optarg, sizeof(ip)); + break; + case 'z': + strncpy(host, optarg, sizeof(host)); + break; + case 'p': + port = strtol(optarg, NULL, 10); + break; + case 'b': + strncpy(bn, optarg, sizeof(bn)); + break; + case 'd': + strncpy(dn, optarg, sizeof(dn)); + break; + case 'l': + strncpy(pp, optarg, sizeof(pp)); + break; + case 's': + strncpy(sn, optarg, sizeof(sn)); + break; + case 'o': + probe++; + break; + case 'c': + support++; + break; + case 'v': + verbose++; + break; + case 'n': + send_notify=0; + break; + case 'h': + usage(); + exit(0); + case '?': + usage(); + fprintf(stderr, "unknown argument: %s\n", argv[1]); + exit(-1); + default: + break; + } + } + + if (ip[0]==0 && (!(bn[0] && dn[0])) && pp[0]==0 && probe==0 && sn[0]==0 && support==0 && host[0]==0) + { + fprintf(stderr, "invalid command parameter(s)\n"); + usage(); + goto bugout; + } + + if (probe) + { + hpmud_probe_devices(HPMUD_BUS_ALL, buf, sizeof(buf), &cnt, &bytes_read); + if (bytes_read) + fprintf(stdout, "%s", buf); + } + +#ifdef HAVE_LIBNETSNMP + if (ip[0]) + { + stat = hpmud_make_net_uri(ip, port, uri, sizeof(uri), &bytes_read); + if (stat == HPMUD_R_OK) + { + fprintf(stdout, "%s\n", uri); + fprintf(stdout, "hpaio%s\n", &uri[2]); + } + } + if (host[0]) + { + stat = hpmud_make_mdns_uri(host, port, uri, sizeof(uri), &bytes_read); + if (stat == HPMUD_R_OK) + { + fprintf(stdout, "%s\n", uri); + fprintf(stdout, "hpaio%s\n", &uri[2]); + } + } +#endif + + if (bn[0] && dn[0]) + { + stat = hpmud_make_usb_uri(bn, dn, uri, sizeof(uri), &bytes_read); + if (stat == HPMUD_R_OK) + { + fprintf(stdout, "%s\n", uri); + fprintf(stdout, "hpaio%s\n", &uri[2]); + } + } + + if (sn[0]) + { + stat = hpmud_make_usb_serial_uri(sn, uri, sizeof(uri), &bytes_read); + if (stat == HPMUD_R_OK) + { + fprintf(stdout, "%s\n", uri); + fprintf(stdout, "hpaio%s\n", &uri[2]); + } + } + +#ifdef HAVE_PPORT + if (pp[0]) + { + stat = hpmud_make_par_uri(pp, uri, sizeof(uri), &bytes_read); + if (stat == HPMUD_R_OK) + { + fprintf(stdout, "%s\n", uri); + fprintf(stdout, "hpaio%s\n", &uri[2]); + } + } +#endif + + ret = 0; + + if (support) + ret = check_support(send_notify); + +bugout: + exit(ret); +} /* main */ diff --git a/io/hpmud/hpmud.c b/io/hpmud/hpmud.c new file mode 100644 index 0000000..d24ebd2 --- /dev/null +++ b/io/hpmud/hpmud.c @@ -0,0 +1,699 @@ +/*****************************************************************************\ + + hpmud.cpp - multi-point transport 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. + + Author: Naga Samrat Chowdary Narla, + Contributor: Sarbeswar Meher +\*****************************************************************************/ + +#include "hpmud.h" +#include "hpmudi.h" + +/* Client data. */ +mud_session ms __attribute__ ((visibility ("hidden"))); /* mud session, one per client */ +mud_session *msp __attribute__ ((visibility ("hidden"))) = &ms; + +/* + * sysdump() originally came from http://sws.dett.de/mini/hexdump-c , steffen@dett.de . + */ +void __attribute__ ((visibility ("hidden"))) sysdump(const void *data, int size) +{ + /* Dump size bytes of *data. Output looks like: + * [0000] 75 6E 6B 6E 6F 77 6E 20 30 FF 00 00 00 00 39 00 unknown 0.....9. + */ + + unsigned char *p = (unsigned char *)data; + unsigned char c; + int n; + char bytestr[4] = {0}; + char addrstr[10] = {0}; + char hexstr[16*3 + 5] = {0}; + char charstr[16*1 + 5] = {0}; + for(n=1;n<=size;n++) { + if (n%16 == 1) { + /* store address for this line */ + snprintf(addrstr, sizeof(addrstr), "%.4d", (int)((p-(unsigned char *)data) & 0xffff)); + } + + c = *p; + if (isprint(c) == 0) { + c = '.'; + } + + /* store hex str (for left side) */ + snprintf(bytestr, sizeof(bytestr), "%02X ", *p); + strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1); + + /* store char str (for right side) */ + snprintf(bytestr, sizeof(bytestr), "%c", c); + strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1); + + if(n%16 == 0) { + /* line completed */ + DBG_SZ("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); + hexstr[0] = 0; + charstr[0] = 0; + } + p++; /* next byte */ + } + + if (strlen(hexstr) > 0) { + /* print rest of buffer if not empty */ + DBG_SZ("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); + } +} + +/* Given the IEEE 1284 device id string, determine if this is a HP product. */ +int __attribute__ ((visibility ("hidden"))) is_hp(const char *id) +{ + char *pMf; + + if ((pMf = strstr(id, "MFG:")) != NULL) + pMf+=4; + else if ((pMf = strstr(id, "MANUFACTURER:")) != NULL) + pMf+=13; + else + return 0; + + if ((strncasecmp(pMf, "HEWLETT-PACKARD", 15) == 0) || + (strncasecmp(pMf, "APOLLO", 6) == 0) || (strncasecmp(pMf, "HP", 2) == 0)) + { + return 1; /* found HP product */ + } + return 0; +} + +int __attribute__ ((visibility ("hidden"))) generalize_model(const char *sz, char *buf, int bufSize) +{ + const char *pMd=sz; + int i, j, dd=0; + + for (i=0; pMd[i] == ' ' && i < bufSize; i++); /* eat leading white space */ + + for (j=0; (pMd[i] != 0) && (pMd[i] != ';') && (j < bufSize); i++) + { + if (pMd[i]==' ' || pMd[i]=='/') + { + /* Remove double spaces. */ + if (!dd) + { + buf[j++] = '_'; /* convert space to "_" */ + dd=1; + } + } + else + { + buf[j++] = pMd[i]; + dd=0; + } + } + + for (j--; buf[j] == '_' && j > 0; j--); /* eat trailing white space */ + + buf[++j] = 0; + + return j; /* length does not include zero termination */ +} + +int __attribute__ ((visibility ("hidden"))) generalize_serial(const char *sz, char *buf, int bufSize) +{ + const char *pMd=sz; + int i, j; + + for (i=0; pMd[i] == ' ' && i < bufSize; i++); /* eat leading white space */ + + for (j=0; (pMd[i] != 0) && (i < bufSize); i++) + { + buf[j++] = pMd[i]; + } + + for (i--; buf[i] == ' ' && i > 0; i--); /* eat trailing white space */ + + buf[++i] = 0; + + return i; /* length does not include zero termination */ +} + +/* Parse serial number from uri string. */ +int __attribute__ ((visibility ("hidden"))) get_uri_serial(const char *uri, char *buf, int bufSize) +{ + char *p; + int i; + + buf[0] = 0; + + if ((p = strcasestr(uri, "serial=")) != NULL) + p+=7; + else + return 0; + + for (i=0; (p[i] != 0) && (p[i] != '+') && (i < bufSize); i++) + buf[i] = p[i]; + + buf[i] = 0; + + return i; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) service_to_channel(mud_device *pd, const char *sn, HPMUD_CHANNEL *index) +{ + enum HPMUD_RESULT stat; + + *index=-1; + + /* Check for valid service requests. */ + if (strncasecmp(sn, "print", 5) == 0) + { + *index = HPMUD_PRINT_CHANNEL; + } + else if (strncasecmp(sn, "hp-ews-ledm", 11) == 0) + { + *index = HPMUD_EWS_LEDM_CHANNEL; + } + else if (strncasecmp(sn, "hp-ews", 6) == 0) + { + *index = HPMUD_EWS_CHANNEL; + } + else if (strncasecmp(sn, "hp-soap-scan", 12) == 0) + { + *index = HPMUD_SOAPSCAN_CHANNEL; + } + else if (strncasecmp(sn, "hp-soap-fax", 11) == 0) + { + *index = HPMUD_SOAPFAX_CHANNEL; + } + else if (strncasecmp(sn, "hp-marvell-scan", 15) == 0) + { + *index = HPMUD_MARVELL_SCAN_CHANNEL; + } + else if (strncasecmp(sn, "hp-marvell-fax", 14) == 0) + { + *index = HPMUD_MARVELL_FAX_CHANNEL; + } + else if (strncasecmp(sn, "hp-ledm-scan", 12) == 0) + { + *index = HPMUD_LEDM_SCAN_CHANNEL; + } + /* All the following services require MLC/1284.4. */ + else if (pd->io_mode == HPMUD_RAW_MODE || pd->io_mode == HPMUD_UNI_MODE) + { + BUG("invalid channel_open state, current io_mode=raw/uni service=%s %s\n", sn, pd->uri); + stat = HPMUD_R_INVALID_STATE; + goto bugout; + } + else if (strncasecmp(sn, "hp-message", 10) == 0) + { + *index = HPMUD_PML_CHANNEL; + } + else if (strncasecmp(sn, "hp-scan", 7) == 0) + { + *index = HPMUD_SCAN_CHANNEL; + } + else if (strncasecmp(sn, "hp-fax-send", 11) == 0) + { + *index = HPMUD_FAX_SEND_CHANNEL; + } + else if (strncasecmp(sn, "hp-card-access", 14) == 0) + { + *index = HPMUD_MEMORY_CARD_CHANNEL; + } + else if (strncasecmp(sn, "hp-configuration-upload", 23) == 0) + { + *index = HPMUD_CONFIG_UPLOAD_CHANNEL; + } + else if (strncasecmp(sn, "hp-configuration-download", 25) == 0) + { + *index = HPMUD_CONFIG_DOWNLOAD_CHANNEL; + } + else if (strncasecmp(sn, "hp-devmgmt", 10) == 0) + { + *index = HPMUD_DEVMGMT_CHANNEL; + } + else if (strncasecmp(sn, "hp-wificonfig", 13) == 0) + { + *index = HPMUD_WIFI_CHANNEL; + } + else + { + BUG("invalid service=%s %s\n", sn, pd->uri); + stat = HPMUD_R_INVALID_SN; + goto bugout; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +static int new_device(const char *uri, enum HPMUD_IO_MODE mode, int *result) +{ + int index=0; /* device[0] is unused */ + int i=1; + + if (uri[0] == 0) + return 0; + + pthread_mutex_lock(&msp->mutex); + + if (msp->device[i].index) + { + BUG("invalid device_open state\n"); /* device is already open for this client, one device per session */ + *result = HPMUD_R_INVALID_STATE; + goto bugout; + } + + index = i; /* currently only support one device per client or process */ + + /* Based on uri, set local session attributes. */ + if (strcasestr(uri, ":/usb") != NULL) + { + msp->device[i].vf = musb_mud_device_vf; + } +#ifdef HAVE_LIBNETSNMP + else if (strcasestr(uri, ":/net") != NULL) + { + msp->device[i].vf = jd_mud_device_vf; + } +#endif +#ifdef HAVE_PPORT + else if (strcasestr(uri, ":/par") != NULL) + { + msp->device[i].vf = pp_mud_device_vf; + } +#endif + else + { + BUG("invalid uri %s\n", uri); + *result = HPMUD_R_INVALID_URI; + index = 0; + goto bugout; + } + msp->device[i].io_mode = mode; + msp->device[i].index = index; + msp->device[i].channel_cnt = 0; + msp->device[i].open_fd = -1; + strcpy(msp->device[i].uri, uri); + +bugout: + pthread_mutex_unlock(&msp->mutex); + + return index; /* return device index */ +} + +static int del_device(HPMUD_DEVICE index) +{ + pthread_mutex_lock(&msp->mutex); + + msp->device[index].index = 0; + + pthread_mutex_unlock(&msp->mutex); + + return 0; +} + +/* Make sure client closed down the device. */ +int device_cleanup(mud_session *ps) +{ + int i, dd=1; + + if(!ps->device[dd].index) + return 0; /* nothing to do */ + + BUG("device_cleanup: device uri=%s\n", ps->device[dd].uri); + + for (i=0; i<HPMUD_CHANNEL_MAX; i++) + { + if (ps->device[dd].channel[i].client_cnt) + { + BUG("device_cleanup: close channel %d...\n", i); + hpmud_close_channel(dd, ps->device[dd].channel[i].index); + BUG("device_cleanup: done closing channel %d\n", i); + } + } + + BUG("device_cleanup: close device dd=%d...\n", dd); + hpmud_close_device(dd); + BUG("device_cleanup: done closing device dd=%d\n", dd); + + return 0; +} + +static void __attribute__ ((constructor)) mud_init(void) +{ + DBG("[%d] hpmud_init()\n", getpid()); +} + +static void __attribute__ ((destructor)) mud_exit(void) +{ + DBG("[%d] hpmud_exit()\n", getpid()); + device_cleanup(msp); +} + +/******************************************************************************************************************************* + * Helper functions. + */ + +/* Parse the model from the IEEE 1284 device id string and generalize the model name */ +int hpmud_get_model(const char *id, char *buf, int buf_size) +{ + char *pMd; + + buf[0] = 0; + + if ((pMd = strstr(id, "MDL:")) != NULL) + pMd+=4; + else if ((pMd = strstr(id, "MODEL:")) != NULL) + pMd+=6; + else + return 0; + + return generalize_model(pMd, buf, buf_size); +} + +/* Parse the model from the IEEE 1284 device id string. */ +int hpmud_get_raw_model(char *id, char *raw, int rawSize) +{ + char *pMd; + int i; + + raw[0] = 0; + + if ((pMd = strstr(id, "MDL:")) != NULL) + pMd+=4; + else if ((pMd = strstr(id, "MODEL:")) != NULL) + pMd+=6; + else + return 0; + + for (i=0; (pMd[i] != ';') && (i < rawSize); i++) + raw[i] = pMd[i]; + raw[i] = 0; + + return i; +} + +/* Parse device model from uri string. */ +int hpmud_get_uri_model(const char *uri, char *buf, int buf_size) +{ + char *p; + int i; + + buf[0] = 0; + + if ((p = strstr(uri, "/")) == NULL) + return 0; + if ((p = strstr(p+1, "/")) == NULL) + return 0; + p++; + + for (i=0; (p[i] != '?') && (i < buf_size); i++) + buf[i] = p[i]; + + buf[i] = 0; + + return i; +} + +/* Parse the data link from a uri string. */ +int hpmud_get_uri_datalink(const char *uri, char *buf, int buf_size) +{ + char *p; + int i; + int zc=0; +#ifdef HAVE_LIBNETSNMP + char ip[HPMUD_LINE_SIZE]; +#endif + + buf[0] = 0; + + if ((p = strcasestr(uri, "device=")) != NULL) + p+=7; + else if ((p = strcasestr(uri, "ip=")) != NULL) + p+=3; + else if ((p = strcasestr(uri, "zc=")) != NULL) + { + p+=3; + zc=1; + } + else + return 0; + + if (zc) + { +#ifdef HAVE_LIBNETSNMP + if (hpmud_mdns_lookup(p, HPMUD_MDNS_TIMEOUT, ip) != HPMUD_R_OK) + return 0; + for (i=0; (ip[i] != 0) && (i < buf_size); i++) + buf[i] = ip[i]; +#else + return 0; +#endif + } + else { + for (i=0; (p[i] != 0) && (p[i] != '&') && (i < buf_size); i++) + buf[i] = p[i]; + } + + buf[i] = 0; + + return i; +} + +/*************************************************************************************************** + * Core functions. + */ + +enum HPMUD_RESULT hpmud_open_device(const char *uri, enum HPMUD_IO_MODE iomode, HPMUD_DEVICE *dd) +{ + HPMUD_DEVICE index=0; + enum HPMUD_RESULT stat = HPMUD_R_INVALID_URI; + int result; + + DBG("[%d,%d,%d,%d,%d,%d] hpmud_device_open() uri=%s iomode=%d\n", getpid(), getppid(), getuid(), geteuid(), getgid(), getegid(), uri, iomode); + + if ((index = new_device(uri, iomode, &result)) == 0) + { + stat = result; + goto bugout; + } + else + { + if ((stat = (msp->device[index].vf.open)(&msp->device[index])) != HPMUD_R_OK) + { + (msp->device[index].vf.close)(&msp->device[index]); /* Open failed perform device cleanup. */ + del_device(index); + goto bugout; + } + } + + *dd = index; + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_close_device(HPMUD_DEVICE dd) +{ + enum HPMUD_RESULT stat; + + DBG("[%d] hpmud_device_close() dd=%d\n", getpid(), dd); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd) + { + BUG("invalid device_close state\n"); + stat = HPMUD_R_INVALID_STATE; + } + else + { + stat = (msp->device[dd].vf.close)(&msp->device[dd]); + del_device(dd); + } + return stat; +} + +enum HPMUD_RESULT hpmud_get_device_id(HPMUD_DEVICE dd, char *buf, int size, int *bytes_read) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + + DBG("[%d] hpmud_get_device_id() dd=%d\n", getpid(), dd); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd) + { + BUG("invalid get_device_id state\n"); + goto bugout; + } + + stat = (msp->device[dd].vf.get_device_id)(&msp->device[dd], buf, size, bytes_read); + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_get_device_status(HPMUD_DEVICE dd, unsigned int *status) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + + DBG("[%d] hpmud_get_device_status() dd=%d\n", getpid(), dd); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd) + { + BUG("invalid get_device_status state\n"); + goto bugout; + } + + stat = (msp->device[dd].vf.get_device_status)(&msp->device[dd], status); + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_probe_devices(enum HPMUD_BUS_ID bus, char *buf, int buf_size, int *cnt, int *bytes_read) +{ + int len=0; + + DBG("[%d] hpmud_probe_devices() bus=%d\n", getpid(), bus); + + buf[0] = 0; + *cnt = 0; + + if (bus == HPMUD_BUS_USB) + { + len = musb_probe_devices(buf, buf_size, cnt); + } +#ifdef HAVE_PPORT + else if (bus == HPMUD_BUS_PARALLEL) + { + len = pp_probe_devices(buf, buf_size, cnt); + } +#endif + else if (bus == HPMUD_BUS_ALL) + { + len = musb_probe_devices(buf, buf_size, cnt); +#ifdef HAVE_PPORT + len += pp_probe_devices(buf+len, buf_size-len, cnt); +#endif + } + + *bytes_read = len; + + return HPMUD_R_OK; +} + +enum HPMUD_RESULT hpmud_open_channel(HPMUD_DEVICE dd, const char *channel_name, HPMUD_CHANNEL *cd) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + + DBG("[%d] hpmud_channel_open() dd=%d name=%s\n", getpid(), dd, channel_name); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd) + { + BUG("invalid channel_open state\n"); + goto bugout; + } + + stat = (msp->device[dd].vf.channel_open)(&msp->device[dd], channel_name, cd); + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_close_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + + DBG("[%d] hpmud_channel_close() dd=%d cd=%d\n", getpid(), dd, cd); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd || + cd <=0 || cd > HPMUD_CHANNEL_MAX || msp->device[dd].channel[cd].client_cnt == 0) + { + BUG("invalid channel_close state\n"); + goto bugout; + } + + stat = (msp->device[dd].vf.channel_close)(&msp->device[dd], &msp->device[dd].channel[cd]); + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_write_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, const void *buf, int size, int sec_timeout, int *bytes_wrote) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + + DBG("[%d] hpmud_channel_write() dd=%d cd=%d buf=%p size=%d sectime=%d\n", getpid(), dd, cd, buf, size, sec_timeout); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd || + cd <=0 || cd > HPMUD_CHANNEL_MAX || msp->device[dd].channel[cd].client_cnt == 0) + { + BUG("invalid channel_write state\n"); + goto bugout; + } + + stat = (msp->device[dd].vf.channel_write)(&msp->device[dd], &msp->device[dd].channel[cd], buf, size, sec_timeout, bytes_wrote); + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_read_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, void *buf, int size, int sec_timeout, int *bytes_read) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + DBG("[%d] hpmud_channel_read() dd=%d cd=%d buf=%p size=%d sectime=%d\n", getpid(), dd, cd, buf, size, sec_timeout); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd || + cd <=0 || cd > HPMUD_CHANNEL_MAX || msp->device[dd].channel[cd].client_cnt == 0) + { + BUG("invalid channel_read state\n"); + goto bugout; + } + + stat = (msp->device[dd].vf.channel_read)(&msp->device[dd], &msp->device[dd].channel[cd], buf, size, sec_timeout, bytes_read); + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_get_dstat(HPMUD_DEVICE dd, struct hpmud_dstat *ds) +{ + enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE; + + DBG("[%d] hpmud_dstat() dd=%d ds=%p\n", getpid(), dd, ds); + + if (dd <= 0 || dd > HPMUD_DEVICE_MAX) + { + BUG("invalid dstat state\n"); + goto bugout; + } + + strncpy(ds->uri, msp->device[dd].uri, sizeof(ds->uri)); + ds->io_mode = msp->device[dd].io_mode; + ds->channel_cnt = msp->device[dd].channel_cnt; + ds->mlc_up = msp->device[dd].mlc_up; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + diff --git a/io/hpmud/hpmud.h b/io/hpmud/hpmud.h new file mode 100644 index 0000000..3970d28 --- /dev/null +++ b/io/hpmud/hpmud.h @@ -0,0 +1,581 @@ +/*****************************************************************************\ + + hpmud.h - public definitions for multi-point transport driver + + (c) 2004-2015 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. + + Author: Naga Samrat Chowdary Narla, Yashwant Sahu, Sarbeswar Meher +\*****************************************************************************/ + +#ifndef _HPMUD_H +#define _HPMUD_H + +enum HPMUD_RESULT +{ + HPMUD_R_OK = 0, + HPMUD_R_INVALID_DEVICE = 2, + HPMUD_R_INVALID_DESCRIPTOR = 3, + HPMUD_R_INVALID_URI = 4, + HPMUD_R_INVALID_LENGTH = 8, + HPMUD_R_IO_ERROR = 12, + HPMUD_R_DEVICE_BUSY = 21, + HPMUD_R_INVALID_SN = 28, + HPMUD_R_INVALID_CHANNEL_ID = 30, + HPMUD_R_INVALID_STATE = 31, + HPMUD_R_INVALID_DEVICE_OPEN = 37, + HPMUD_R_INVALID_DEVICE_NODE = 38, + HPMUD_R_INVALID_IP = 45, + HPMUD_R_INVALID_IP_PORT = 46, + HPMUD_R_INVALID_TIMEOUT = 47, + HPMUD_R_DATFILE_ERROR = 48, + HPMUD_R_IO_TIMEOUT = 49, + HPMUD_R_INVALID_MDNS = 50, +}; + +enum HPMUD_IO_MODE +{ + HPMUD_UNI_MODE=0, /* uni-di */ + HPMUD_RAW_MODE=1, /* bi-di */ + HPMUD_DOT4_MODE=3, + HPMUD_DOT4_PHOENIX_MODE=4, /* (ie: clj2550, clj2840, lj3050, lj3055, clj4730mfp) */ + HPMUD_DOT4_BRIDGE_MODE=5, /* (ie: clj2500) not USB compatable, use HPMUD_RAW_MODE, tested on F10 12/10/08 DES */ + HPMUD_MLC_GUSHER_MODE=6, /* most new devices */ + HPMUD_MLC_MISER_MODE=7, /* old stuff */ +}; + +enum HPMUD_BUS_ID +{ + HPMUD_BUS_NA=0, + HPMUD_BUS_USB=1, + HPMUD_BUS_PARALLEL, + HPMUD_BUS_ALL +}; + +enum HPMUD_SCANTYPE +{ + HPMUD_SCANTYPE_NA = 0, + HPMUD_SCANTYPE_SCL = 1, + HPMUD_SCANTYPE_PML = 2, + HPMUD_SCANTYPE_SOAP = 3, /* Wookie (ie:ljcm1017) */ + HPMUD_SCANTYPE_MARVELL = 4, /* (ie: ljm1005) */ + HPMUD_SCANTYPE_SOAPHT = 5, /* HorseThief (ie: ljm1522) */ + HPMUD_SCANTYPE_SCL_DUPLEX = 6, + HPMUD_SCANTYPE_LEDM = 7, + HPMUD_SCANTYPE_MARVELL2 = 8, /* (Tsunami lj 1212 and series) */ +}; + +enum HPMUD_SCANSRC +{ + HPMUD_SCANSRC_NA = 0, + HPMUD_SCANSRC_FLATBED = 0x1, + HPMUD_SCANSRC_ADF= 0x2, + HPMUD_SCANSRC_CAMERA = 0x4, +}; + +enum HPMUD_STATUSTYPE +{ + HPMUD_STATUSTYPE_NA = 0, + HPMUD_STATUSTYPE_VSTATUS = 1, /* device-id vstatus */ + HPMUD_STATUSTYPE_SFIELD = 2, /* device-id s-field */ + HPMUD_STATUSTYPE_PML = 3, /* laserjet pml */ + HPMUD_STATUSTYPE_EWS = 6, /* laserjet hp ews */ + HPMUD_STATUSTYPE_PJL = 8, /* laserjet pjl */ + HPMUD_STATUSTYPE_PJLPML = 9, /* laserjet pjl and pml */ +}; + +enum HPMUD_SUPPORT_TYPE +{ + HPMUD_SUPPORT_TYPE_NONE = 0, /* not supported */ + HPMUD_SUPPORT_TYPE_HPIJS = 1, /* supported by hpijs only */ + HPMUD_SUPPORT_TYPE_HPLIP = 2, /* supported by hpijs and "hp" backend */ +}; + +enum HPMUD_PLUGIN_TYPE +{ + HPMUD_PLUGIN_TYPE_NONE = 0, + HPMUD_PLUGIN_TYPE_REQUIRED = 1, + HPMUD_PLUGIN_TYPE_OPTIONAL = 2, +}; + +#define HPMUD_S_PRINT_CHANNEL "PRINT" +#define HPMUD_S_PML_CHANNEL "HP-MESSAGE" +#define HPMUD_S_SCAN_CHANNEL "HP-SCAN" +#define HPMUD_S_FAX_SEND_CHANNEL "HP-FAX-SEND" +#define HPMUD_S_CONFIG_UPLOAD_CHANNEL "HP-CONFIGURATION-UPLOAD" +#define HPMUD_S_CONFIG_DOWNLOAD_CHANNEL "HP-CONFIGURATION-DOWNLOAD" +#define HPMUD_S_MEMORY_CARD_CHANNEL "HP-CARD-ACCESS" +#define HPMUD_S_EWS_CHANNEL "HP-EWS" +#define HPMUD_S_EWS_LEDM_CHANNEL "HP-EWS-LEDM" +#define HPMUD_S_SOAP_SCAN "HP-SOAP-SCAN" +#define HPMUD_S_SOAP_FAX "HP-SOAP-FAX" +#define HPMUD_S_DEVMGMT_CHANNEL "HP-DEVMGMT" +#define HPMUD_S_MARVELL_SCAN_CHANNEL "HP-MARVELL-SCAN" +#define HPMUD_S_MARVELL_FAX_CHANNEL "HP-MARVELL-FAX" +#define HPMUD_S_LEDM_SCAN "HP-LEDM-SCAN" +#define HPMUD_S_WIFI_CHANNEL "HP-WIFICONFIG" + +typedef int HPMUD_DEVICE; /* usb, parallel or jetdirect */ +#define HPMUD_DEVICE_MAX 2 /* zero is not used */ + +typedef int HPMUD_CHANNEL; +#define HPMUD_CHANNEL_MAX HPMUD_MAX_CHANNEL_ID + +#define HPMUD_LINE_SIZE 256 /* Length of a line. */ +#define HPMUD_BUFFER_SIZE 16384 /* General Read/Write buffer. */ + +struct hpmud_dstat +{ + char uri[HPMUD_LINE_SIZE]; + int client_cnt; /* number of clients that have this device opend */ + enum HPMUD_IO_MODE io_mode; + int channel_cnt; /* number of open channels */ + int mlc_up; /* 0 = MLC/1284.4 transport up, 1 = MLD/1284.4 transport down */ +}; + +struct hpmud_model_attributes +{ + enum HPMUD_IO_MODE prt_mode; /* print only (io_mode) */ + enum HPMUD_IO_MODE mfp_mode; /* pml | scan | fax (io_mode) */ + enum HPMUD_SCANTYPE scantype; /* scan protocol i.e. SCL, PML, SOAP, MARVELL, LEDM */ + enum HPMUD_STATUSTYPE statustype; + enum HPMUD_SUPPORT_TYPE support; + enum HPMUD_PLUGIN_TYPE plugin; + enum HPMUD_SUPPORT_TYPE reserved[5]; + enum HPMUD_SCANSRC scansrc; /*Flatbed, ADF, Camera or combination of these*/ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * hpmud_device_open - open specified device, call normally does not block + * + * inputs: + * uri - specifies device to open + * io_mode - see enum definition + * + * outputs: + * dd - device descriptor + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_open_device(const char *uri, enum HPMUD_IO_MODE io_mode, HPMUD_DEVICE *dd); + +/* + * hpmud_device_close - close specified device, call does not block + * + * inputs: + * dd - device descriptor + * + * outputs: + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_close_device(HPMUD_DEVICE dd); + +/* + * hpmud_get_device_id - read IEEE 1284 device ID string, call normally does not block + * + * If the device is busy, a cached copy may be returned. + * + * inputs: + * dd - device descriptor + * buf_size - maximum size of buf + * + * outputs: + * buf - zero terminated device ID string + * bytes_read - size of device ID string, does not include zero termination + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_device_id(HPMUD_DEVICE dd, char *buf, int buf_size, int *bytes_read); + +/* + * hpmud_get_device_status - read 8-bit device status, call normally does not block + * + * inputs: + * dd - device descriptor + * + * outputs: + * status - 3-bit status, supported by inkjets only + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_device_status(HPMUD_DEVICE dd, unsigned int *status); + +/* + * hpmud_probe_devices - probe local buses for HP supported devices, call normally does not block + * + * inputs: + * bus - see enum definiton + * buf_size - size of read buffer + * + * outputs: + * buf - zero terminated CUPS backend formatted data + * cnt - number of HP devices found + * bytes_read - number of bytes actually read + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_probe_devices(enum HPMUD_BUS_ID bus, char *buf, int buf_size, int *cnt, int *bytes_read); + +/* + * hpmud_channel_open - open specified channel, call will block + * + * Only EWS channel can be opened by more than one process. + * + * inputs: + * dd - device descriptor + * channel_name - requested service name + * + * outputs: + * cd - channel descriptor + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_open_channel(HPMUD_DEVICE dd, const char *channel_name, HPMUD_CHANNEL *cd); + +/* + * hpmud_channel_close - close specified channel, call will block + * + * inputs: + * dd - device descriptor + * cd - channel descriptor + * + * outputs: + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_close_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd); + +/* + * hpmud_channel_write - write data to specified channel, call will block + * + * May return with partial bytes written (ie: bytes_wrote < size) with or with-out a timeout. + * + * inputs: + * dd - device descriptor + * cd - channel descriptor + * buf - data to write + * size - number of bytes to write + * timeout - in seconds + * + * outputs: + * bytes_wrote - number of bytes actually wrote + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_write_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, const void *buf, int size, int timeout, int *bytes_written); + +/* + * hpmud_channel_read - read data from specified channel, call will block + * + * May return with partial bytes read (ie: bytes_read < size) or zero if timeout occured. + * + * inputs: + * dd - device descriptor + * cd - channel descriptor + * size - number of bytes to read + * timeout - in seconds + * + * outputs: + * buf - read data buffer + * bytes_read - number of bytes actually read + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_read_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, void *buf, int size, int timeout, int *bytes_read); + +/* + * hpmud_dstat - get device information + * + * inputs: + * dd - device descriptor + * + * outputs: + * ds - see dstat definition + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_dstat(HPMUD_DEVICE dd, struct hpmud_dstat *ds); + +/* + * hpmud_set_pml - set pml object + * + * Set_pml is a high level interface to hpmud. This command calls the hpmud core interface. + * This command can be used with local or jetdirect connections. Jetdirect connection will + * use snmp. + * + * inputs: + * dd - device descriptor + * cc - channel descriptor + * snmp_oid - snmp encoded pml oid + * type - oid data type + * data - data payload + * data_size - number of bytes to write + * + * outputs: + * pml_result + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_set_pml(HPMUD_DEVICE device, HPMUD_CHANNEL channel, const char *snmp_oid, int type, void *data, int data_size, int *pml_result); + +/* + * hpmud_get_pml - get pml object + * + * Get_pml is a high level interface to hpmud. This command calls the hpmud core interface. + * This command can be used with local or jetdirect connections. Jetdirect connection will + * use snmp. + * + * inputs: + * dd - device descriptor + * cc - channel descriptor + * snmp_oid - snmp encoded pml oid + * data_size - data buffer size in bytes + * + * outputs: + * data - data payload + * type - pml data type + * pml_result + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_pml(HPMUD_DEVICE device, HPMUD_CHANNEL channel, const char *snmp_oid, void *buf, int buf_size, int *bytes_read, int *type, int *pml_result); + +/* + * hpmud_get_model - parse device model from the IEEE 1284 device id string. + * + * This function is a stateless hpmud helper function. + * + * inputs: + * id - IEEE 1284 device id string + * buf_size - size of buf in bytes + * + * outputs: + * buf - device model string (generalized) + * return value - length of string in bytes, does not include zero termination + */ +int hpmud_get_model(const char *id, char *buf, int buf_size); + +/* + * hpmud_get_raw_model - parse device model from the IEEE 1284 device id string. + * + * This function is a stateless hpmud helper function. + * + * inputs: + * id - IEEE 1284 device id string + * buf_size - size of buf in bytes + * + * outputs: + * buf - device model string (raw) + * return value - length of string in bytes, does not include zero termination + */ +int hpmud_get_raw_model(char *id, char *raw, int rawSize); + +/* + * hpmud_get_uri_model - parse device model from uri + * + * This function is a stateless hpmud helper function. + * + * inputs: + * uri + * buf_size - size of buf in bytes + * + * outputs: + * buf - device model string + * return value - length of string in bytes, does not include zero termination + */ +int hpmud_get_uri_model(const char *uri, char *buf, int buf_size); + +/* + * hpmud_get_uri_datalink - parse the data link from uri + * + * This function is a stateless hpmud helper function. + * + * inputs: + * uri + * buf_size - size of buf in bytes + * + * outputs: + * buf - device model string + * return value - length of string in bytes, does not include zero termination + */ +int hpmud_get_uri_datalink(const char *uri, char *buf, int buf_size); + +/* + * hpmud_get_model_attributes - get all model attributes for specified device + * + * Reads device model attributes from models.dat file. This function is a + * stateless hpmud helper function. + * + * inputs: + * uri - specifies device + * buf_size - size of buf in bytes + * + * outputs: + * buf - buffer for all model attributes, key/value pair, one per line + * bytes_read - number of bytes actually read + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_model_attributes(char *uri, char *attr, int attrSize, int *bytes_read); + +/* + * hpmud_model_query - get model attributes structure for specified device + * + * Reads device model attributes from models.dat file. This function is a + * stateless hpmud helper function. + * + * inputs: + * uri - specifies device + * + * outputs: + * ma - see structure definition + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_query_model(char *uri, struct hpmud_model_attributes *ma); + +/* + * hpmud_make_usb_uri - make a usb uri from bus:dev pair + * + * This function is a stateless hpmud helper function. The lsusb command can be used + * determine the bus:dev pair. + * + * inputs: + * busnum - specifies usbfs bus number + * devnum - specifies usbfs device number + * uri_size - size of uri buffer in bytes + * + * outputs: + * uri - zero terminated string + * bytes_read - size of uri + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_make_usb_uri(const char *busnum, const char *devnum, char *uri, int uri_size, int *bytes_read); + +/* + * hpmud_make_usb_serial_uri - make a usb uri from product serial number + * + * This function is a stateless hpmud helper function. The lsusb command can be used + * determine the product serial number. + * + * inputs: + * sn - specifies product serial number + * uri_size - size of uri buffer in bytes + * + * outputs: + * uri - zero terminated string + * bytes_read - size of uri + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_make_usb_serial_uri(const char *sn, char *uri, int uri_size, int *bytes_read); + +/* + * hpmud_make_net_uri - make a net uri from IP + * + * This function is a stateless hpmud helper function. + * + * inputs: + * ip - internet address + * port - 1-4 + * uri_size - size of uri buffer in bytes + * + * outputs: + * uri - zero terminated string + * bytes_read - size of uri + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_make_net_uri(const char *ip, int port, char *uri, int uri_size, int *bytes_read); + +/* + * hpmud_make_par_uri - make a par uri from parallel port + * + * This function is a stateless hpmud helper function. + * + * inputs: + * dnode - device node + * uri_size - size of uri buffer in bytes + * + * outputs: + * uri - zero terminated string + * bytes_read - size of uri + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_make_par_uri(const char *dnode, char *uri, int uri_size, int *bytes_read); + +/* + * hpmud_get_conf - get key value from hplip.conf + * + * This function is a stateless hpmud helper function. + * + * inputs: + * section - zero terminated string (ie: "[dirs]") + * key - zero terminated string (ie: "home") + * value_size - size of value buffer in bytes + * + * outputs: + * value - zero terminated string + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_conf(const char *section, const char *key, char *value, int value_size); + +/* + * hpmud_get_key_value - get key value from specified file + * + * This function is a stateless hpmud helper function. + * + * inputs: + * file - zero terminated file path + * section - zero terminated string (ie: "[dirs]") + * key - zero terminated string (ie: "home") + * value_size - size of value buffer in bytes + * + * outputs: + * value - zero terminated string + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_get_key_value(const char *file, const char *section, const char *key, char *value, int value_size); + +/* + * hpmud_mdns_lookup - lookup IP for MDNS host name + * + * This function is a stateless hpmud helper function. + * + * inputs: + * host_name - zero terminated string (ie: "npi7c8a3e") + * sec_timeout - in seconds + * + * outputs: + * ip - zero terminated string + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_mdns_lookup(const char *host_name, int sec_timeout, char *ip); + +/* + * hpmud_make_mdns_uri - make a network uri from host name + * + * This function is a stateless hpmud helper function. Requires UDP port 5353 to be open. + * + * inputs: + * host - zero terminated string (ie: "npi7c8a3e") + * uri_size - size of uri buffer in bytes + * + * outputs: + * uri - zero terminated string + * bytes_read - size of uri + * return value - see enum definition + */ +enum HPMUD_RESULT hpmud_make_mdns_uri(const char *host, int port, char *uri, int uri_size, int *bytes_read); + +#ifdef __cplusplus +} +#endif + +#endif // _HPMUD_H + diff --git a/io/hpmud/hpmudi.h b/io/hpmud/hpmudi.h new file mode 100644 index 0000000..269cb06 --- /dev/null +++ b/io/hpmud/hpmudi.h @@ -0,0 +1,212 @@ +/*****************************************************************************\ + + hpmudi.h - internal definitions for multi-point transport 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. + + Author: Naga Samrat Chowdary Narla, +\*****************************************************************************/ + +#ifndef _HPMUDI_H +#define _HPMUDI_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <syslog.h> +#include <time.h> +#include <ctype.h> +#include <pthread.h> +#include <arpa/inet.h> +#include "hpmud.h" +#include "musb.h" +#include "mlc.h" +#include "dot4.h" +#include "pml.h" +#ifdef HAVE_LIBNETSNMP +#include "jd.h" +#endif +#ifdef HAVE_PPORT +#include "pp.h" +#endif + +// Don DO NOT commit with HPMUD_DEBUG enabled :( +//#define HPMUD_DEBUG + +#define _STRINGIZE(x) #x +#define STRINGIZE(x) _STRINGIZE(x) + +#define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args) +//#define BUG(args...) fprintf(stderr, __FILE__ " " STRINGIZE(__LINE__) ": " args) + +#ifdef HPMUD_DEBUG + #define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args) +// #define DBG(args...) fprintf(stderr, __FILE__ " " STRINGIZE(__LINE__) ": " args) + #define DBG_DUMP(data, size) sysdump((data), (size)) + #define DBG_SZ(args...) syslog(LOG_INFO, args) +#else + #define DBG(args...) + #define DBG_DUMP(data, size) + #define DBG_SZ(args...) +#endif + +#define HEX2INT(x, i) if (x >= '0' && x <= '9') i |= x - '0'; \ + else if (x >= 'A' && x <= 'F') i |= 0xA + x - 'A'; \ + else if (x >= 'a' && x <= 'f') i |= 0xA + x - 'a' + +/* offset_of returns the number of bytes that the fieldname MEMBER is offset from the beginning of the structure TYPE */ +#define offset_of(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define HPMUD_EXCEPTION_TIMEOUT 45000000 /* microseconds */ +#define HPMUD_EXCEPTION_SEC_TIMEOUT 45 /* seconds */ +#define HPMUD_MDNS_TIMEOUT 10 /* seconds */ + +#define NFAULT_BIT 0x08 +#define PERROR_BIT 0x20 + +enum HPMUD_CHANNEL_ID +{ + HPMUD_PML_CHANNEL = 1, + HPMUD_PRINT_CHANNEL = 2, + HPMUD_SCAN_CHANNEL = 4, + HPMUD_FAX_SEND_CHANNEL = 7, + HPMUD_CONFIG_UPLOAD_CHANNEL = 0xe, + HPMUD_CONFIG_DOWNLOAD_CHANNEL = 0xf, + HPMUD_MEMORY_CARD_CHANNEL = 0x11, + HPMUD_EWS_CHANNEL = 0x12, /* Embeded Web Server interface ff/1/1, any unused socket id */ + HPMUD_SOAPSCAN_CHANNEL = 0x13, /* Soap Scan interface ff/2/1, any unused socket id */ + HPMUD_SOAPFAX_CHANNEL = 0x14, /* Soap Fax interface ff/3/1, any unused socket id */ + HPMUD_MARVELL_SCAN_CHANNEL = 0x15, /* Marvell scan interface ff/ff/ff, any unused socket id */ + HPMUD_MARVELL_FAX_CHANNEL = 0x16, /* Marvell fax interface ff/ff/ff, any unused socket id */ + HPMUD_EWS_LEDM_CHANNEL = 0x17, /* Embeded Web Server interface ff/4/1, any unused socket id */ + HPMUD_LEDM_SCAN_CHANNEL = 0x18, /* LEDM scan interface ff/cc/0, any unused socket id */ + HPMUD_WIFI_CHANNEL = 0x2b, /* WIFI config */ + HPMUD_DEVMGMT_CHANNEL = 0x2c, /* decimal 44 */ + HPMUD_MAX_CHANNEL_ID +}; + +#define HPMUD_DEVICE_MAX 2 /* zero is not used */ +#define HPMUD_CHANNEL_MAX HPMUD_MAX_CHANNEL_ID + +/* MLC/1284.4 attributes. Note for MLC, attributes must remain persistant while transport is up. */ +typedef struct +{ + unsigned short h2pcredit; /* host to peripheral credit (dot4: primary socket id credit for sending) */ + unsigned short p2hcredit; /* peripheral to host credit (dot4: secondary socket id credit for sending) */ + unsigned short h2psize; /* host to peripheral packet size in bytes (dot4: primary max packet size for sending) */ + unsigned short p2hsize; /* peripheral to host packet size in bytes (dot4: secondary max packet size for sending) */ +} transport_attributes; + +typedef struct _mud_channel_vf +{ + enum HPMUD_RESULT (*open)(struct _mud_channel *pc); /* transport specific open */ + enum HPMUD_RESULT (*close)(struct _mud_channel *pc); /* transport specific close */ + enum HPMUD_RESULT (*channel_write)(struct _mud_channel *pc, const void *buf, int size, int timeout, int *bytes_wrote); /* tranport specific write */ + enum HPMUD_RESULT (*channel_read)(struct _mud_channel *pc, void *buf, int size, int timeout, int *bytes_read); /* transport specific read */ +} mud_channel_vf; + +typedef struct _mud_device_vf +{ + int (*write)(int fd, const void *buf, int size, int usec_timeout); /* low level device write */ + int (*read)(int fd, void *buf, int size, int usec_timout); /* low level device read */ + enum HPMUD_RESULT (*open)(struct _mud_device *pd); /* device specific open */ + enum HPMUD_RESULT (*close)(struct _mud_device *pd); /* device specific close */ + enum HPMUD_RESULT (*get_device_id)(struct _mud_device *pd, char *id, int size, int *bytes_read); /* IEEE 1284 device id string */ + enum HPMUD_RESULT (*get_device_status)(struct _mud_device *pd, unsigned int *status); /* device 8-bit status */ + enum HPMUD_RESULT (*channel_open)(struct _mud_device *pd, const char *channel_name, HPMUD_CHANNEL *cd); /* channel specific open */ + enum HPMUD_RESULT (*channel_close)(struct _mud_device *pd, struct _mud_channel *pc); /* channel specific close */ + enum HPMUD_RESULT (*channel_write)(struct _mud_device *pd, struct _mud_channel *pc, const void *buf, int size, int sec_timeout, int *bytes_wrote); + enum HPMUD_RESULT (*channel_read)(struct _mud_device *pd, struct _mud_channel *pc, void *buf, int size, int sec_timeout, int *bytes_read); +} mud_device_vf; + +typedef struct _mud_channel +{ + char sn[HPMUD_LINE_SIZE]; /* service name */ + unsigned char sockid; /* socket id */ + int client_cnt; /* number of clients using this channel */ + int index; /* channel[index] of this object */ + int fd; /* file descriptor for this channel */ + pid_t pid; /* process owner */ + int dindex; /* device[dindex] parent device */ + + /* MLC/1284.4 specific variables. */ + transport_attributes ta; + unsigned char rbuf[HPMUD_BUFFER_SIZE]; /* read packet buffer */ + int rindex; + int rcnt; + + /* JetDirect specific data. */ + int socket; + + mud_channel_vf vf; +} mud_channel; + +typedef struct _mud_device +{ + char uri[HPMUD_LINE_SIZE]; + char id[1024]; /* device id */ + int index; /* device[index] of this object */ + enum HPMUD_IO_MODE io_mode; + mud_channel channel[HPMUD_CHANNEL_MAX]; + int channel_cnt; /* number of open channels */ + int open_fd; /* file descriptor used by device_open */ + + /* MLC/1284.4 specific variables. */ + int mlc_up; /* 0=transport down, 1=transport up */ + int mlc_fd; /* file descriptor used by 1284.4/MLC transport */ + + /* JetDirect specific data. */ + char ip[HPMUD_LINE_SIZE]; /* internet address */ + int port; + + mud_device_vf vf; /* virtual function table */ + pthread_mutex_t mutex; +} mud_device; + +typedef struct +{ + mud_device device[HPMUD_DEVICE_MAX]; + pthread_mutex_t mutex; +} mud_session; + +extern mud_session *msp __attribute__ ((visibility ("hidden"))); + +void __attribute__ ((visibility ("hidden"))) sysdump(const void *data, int size); +int __attribute__ ((visibility ("hidden"))) mm_device_lock(int fd, HPMUD_DEVICE index); +int __attribute__ ((visibility ("hidden"))) mm_device_unlock(int fd, HPMUD_DEVICE index); +int __attribute__ ((visibility ("hidden"))) mm_device_trylock(int fd, HPMUD_DEVICE index); +int __attribute__ ((visibility ("hidden"))) is_hp(const char *id); +int __attribute__ ((visibility ("hidden"))) generalize_model(const char *sz, char *buf, int bufSize); +int __attribute__ ((visibility ("hidden"))) generalize_serial(const char *sz, char *buf, int bufSize); +int __attribute__ ((visibility ("hidden"))) get_uri_model(const char *uri, char *buf, int bufSize); +int __attribute__ ((visibility ("hidden"))) get_uri_serial(const char *uri, char *buf, int bufSize); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) service_to_channel(mud_device *pd, const char *sn, HPMUD_CHANNEL *index); + +#endif // _HPMUDI_H + diff --git a/io/hpmud/jd.c b/io/hpmud/jd.c new file mode 100644 index 0000000..1b7a382 --- /dev/null +++ b/io/hpmud/jd.c @@ -0,0 +1,906 @@ +/*****************************************************************************\ + + jd.c - JetDirect support for multi-point transport 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. + Client/Server generic message format (see messaging-protocol.doc): + + Author: Naga Samrat Chowdary Narla, Sarbeswar Meher +\*****************************************************************************/ + +#ifdef HAVE_LIBNETSNMP + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <signal.h> +#include "hpmud.h" +#include "hpmudi.h" + +mud_device_vf __attribute__ ((visibility ("hidden"))) jd_mud_device_vf = +{ + .open = jd_open, + .close = jd_close, + .get_device_id = jd_get_device_id, + .get_device_status = jd_get_device_status, + .channel_open = jd_channel_open, + .channel_close = jd_channel_close, + .channel_write = jd_channel_write, + .channel_read = jd_channel_read +}; + +static mud_channel_vf jd_channel_vf = +{ + .open = jd_s_channel_open, + .close = jd_s_channel_close, + .channel_write = jd_s_channel_write, + .channel_read = jd_s_channel_read +}; + +static const int PrintPort[] = { 0, 9100, 9101, 9102 }; +static const int ScanPort0[] = { 0, 9290, 9291, 9292 }; +static const int GenericPort[] = { 0, 9220, 9221, 9222 }; +static const int ScanPort1[] = { 0, 8290, 0, 0 }; /* hack for CLJ28xx */ +static const int GenericPort1[] = { 0, 8292, 0, 0 }; /* hack for CLJ28xx (fax) */ + +const char __attribute__ ((visibility ("hidden"))) *kStatusOID = "1.3.6.1.4.1.11.2.3.9.1.1.7.0"; /* device id snmp oid */ + +static int ReadReply(mud_channel *pc) +{ + char buf[HPMUD_LINE_SIZE]; + int len=0, num=0; + char *tail; + enum HPMUD_RESULT stat; + + stat = jd_s_channel_read(pc, buf, sizeof(buf), 2, &len); + buf[len] = 0; + + if (stat == HPMUD_R_OK) + num = strtol((char *)buf, &tail, 10); + + return num; +} + +static int device_id(const char *ip, int port, char *buffer, int size) +{ + int len=0, maxSize, result, dt, status; + + maxSize = (size > 1024) ? 1024 : size; /* RH8 has a size limit for device id */ + + if ((len = GetSnmp(ip, port, (char *)kStatusOID, (unsigned char *)buffer, maxSize, &dt, &status, &result)) == 0) + BUG("unable to read device-id\n"); + + return len; /* length does not include zero termination */ +} + +/* Create channel object given the requested socket id and service name. */ +static int new_channel(mud_device *pd, int index, const char *sn) +{ + int stat=1; + + /* Check for existing name service already open. */ + if (pd->channel[index].client_cnt) + { +#if 0 + if (index == HPMUD_EWS_CHANNEL) + { + pd->channel[index].client_cnt++; /* allow multiple clients for separate USB interfaces only */ + stat = 0; + DBG("reused %s channel=%d clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].client_cnt, pd->channel_cnt); + } + else +#endif + BUG("%s channel=%d is busy, used by [%d], clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].pid, pd->channel[index].client_cnt, pd->channel_cnt); + goto bugout; + } + + pd->channel[index].vf = jd_channel_vf; + pd->channel[index].index = index; + pd->channel[index].client_cnt = 1; + pd->channel[index].sockid = index; + pd->channel[index].pid = getpid(); + pd->channel[index].dindex = pd->index; + pd->channel[index].fd = 0; + pd->channel[index].socket = -1; + strcpy(pd->channel[index].sn, sn); + pd->channel_cnt++; + + stat = 0; + DBG("new %s channel=%d clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].client_cnt, pd->channel_cnt); + +bugout: + return stat; +} + +/* Remove channel object given the channel decriptor. */ +static int del_channel(mud_device *pd, mud_channel *pc) +{ + pc->client_cnt--; + + if (pc->client_cnt <= 0) + { + pd->channel_cnt--; + } + DBG("removed %s channel=%d clientCnt=%d channelCnt=%d\n", pc->sn, pc->index, pc->client_cnt, pd->channel_cnt); + return 0; +} + +/********************************************************************************************************************************* + * JetDirect mud_device functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_open(mud_device *pd) +{ + char uri_model[128]; + char model[128]; + char *p, *tail; + int len=0; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + pthread_mutex_lock(&pd->mutex); + + if (pd->id[0] == 0) + { + /* First client. */ + hpmud_get_uri_datalink(pd->uri, pd->ip, sizeof(pd->ip)); + + if ((p = strcasestr(pd->uri, "port=")) != NULL) + pd->port = strtol(p+5, &tail, 10); + else + pd->port = 1; + if (pd->port > 3) + { + stat = HPMUD_R_INVALID_IP_PORT; + BUG("invalid ip port=%d\n", pd->port); + goto blackout; + } + + len = device_id(pd->ip, pd->port, pd->id, sizeof(pd->id)); /* get new copy and cache it */ + if (len == 0) + { + stat = HPMUD_R_IO_ERROR; + goto blackout; + } + } + + /* Make sure uri model matches device id model. */ + hpmud_get_uri_model(pd->uri, uri_model, sizeof(uri_model)); + hpmud_get_model(pd->id, model, sizeof(model)); + if (strcmp(uri_model, model) != 0) + { + stat = HPMUD_R_INVALID_URI; /* different device plugged in */ + BUG("invalid uri model %s != %s\n", uri_model, model); + goto blackout; + } + + stat = HPMUD_R_OK; + +blackout: + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_close(mud_device *pd) +{ + enum HPMUD_RESULT stat = HPMUD_R_OK; + + pthread_mutex_lock(&pd->mutex); + pd->id[0] = 0; + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_get_device_id(mud_device *pd, char *buf, int size, int *len) +{ + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *len=0; + + pthread_mutex_lock(&pd->mutex); + + *len = device_id(pd->ip, pd->port, pd->id, sizeof(pd->id)); /* get new copy and cache it */ + + if (*len) + { + memcpy(buf, pd->id, *len > size ? size : *len); + stat = HPMUD_R_OK; + } + + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_get_device_status(mud_device *pd, unsigned int *status) +{ + *status = NFAULT_BIT; /* there is no 8-bit status, so fake it */ + return HPMUD_R_OK; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_write(mud_device *pd, mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote) +{ + enum HPMUD_RESULT stat; + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.channel_write)(pc, buf, length, sec_timeout, bytes_wrote); + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_read(mud_device *pd, mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read) +{ + enum HPMUD_RESULT stat; + + if (pd->io_mode == HPMUD_UNI_MODE) + { + stat = HPMUD_R_INVALID_STATE; + BUG("invalid channel_read io_mode=%d\n", pd->io_mode); + } + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.channel_read)(pc, buf, length, sec_timeout, bytes_read); + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_open(mud_device *pd, const char *sn, HPMUD_CHANNEL *cd) +{ + int index; + enum HPMUD_RESULT stat; + + /* Check for valid service requests. */ + if ((stat = service_to_channel(pd, sn, &index)) != HPMUD_R_OK) + goto bugout; + + pthread_mutex_lock(&pd->mutex); + + if (new_channel(pd, index, sn)) + { + stat = HPMUD_R_DEVICE_BUSY; + } + else + { + if ((stat = (pd->channel[index].vf.open)(&pd->channel[index])) != HPMUD_R_OK) /* call transport specific open */ + del_channel(pd, &pd->channel[index]); /* open failed, cleanup */ + else + *cd = index; + } + + pthread_mutex_unlock(&pd->mutex); + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_close(mud_device *pd, mud_channel *pc) +{ + enum HPMUD_RESULT stat = HPMUD_R_OK; + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.close)(pc); /* call trasport specific close */ + del_channel(pd, pc); + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +/******************************************************************************************************************************* + * JetDirect channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_open(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + struct sockaddr_in pin; + char buf[HPMUD_LINE_SIZE]; + int r, len, port; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + bzero(&pin, sizeof(pin)); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = inet_addr(pd->ip); + + switch (pc->index) + { + case HPMUD_PRINT_CHANNEL: + port = PrintPort[pd->port]; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open print port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to print port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_SCAN_CHANNEL: + if (pd->io_mode == HPMUD_DOT4_PHOENIX_MODE) + port = ScanPort1[pd->port]; + else + port = ScanPort0[pd->port]; + pin.sin_port = htons(port); + + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to scan err=%d port %d: %m %s\n", errno, port, pd->uri); + goto bugout; + } + if (pd->io_mode != HPMUD_DOT4_PHOENIX_MODE) + { + r = ReadReply(pc); + if (r != 0) + { + BUG("invalid scan response %d port %d %s\n", r, port, pd->uri); + goto bugout; + } + } + break; + case HPMUD_MEMORY_CARD_CHANNEL: + case HPMUD_FAX_SEND_CHANNEL: + case HPMUD_CONFIG_UPLOAD_CHANNEL: + case HPMUD_CONFIG_DOWNLOAD_CHANNEL: + if (pd->io_mode == HPMUD_DOT4_PHOENIX_MODE) + port = GenericPort1[pd->port]; + else + port = GenericPort[pd->port]; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to port %d: %m %s\n", port, pd->uri); + goto bugout; + } + + if (pd->io_mode != HPMUD_DOT4_PHOENIX_MODE) + { + r = ReadReply(pc); + if (r != 220) + { + BUG("invalid response %d port %d %s\n", r, port, pd->uri); + goto bugout; + } + len = sprintf(buf, "open %d\n", pc->index); + send(pc->socket, buf, len, 0); + r = ReadReply(pc); + if (r != 200) + { + BUG("invalid response %d port %d %s\n", r, port, pd->uri); + goto bugout; + } + len = sprintf(buf, "data\n"); + send(pc->socket, "data\n", len, 0); + r = ReadReply(pc); + if (r != 200) + { + BUG("invalid response %d port %d %s\n", r, port, pd->uri); + goto bugout; + } + } + + break; + case HPMUD_EWS_CHANNEL: + port = 80; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open ews port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to ews port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_SOAPSCAN_CHANNEL: + port = 8289; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open soap-scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to soap-scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_SOAPFAX_CHANNEL: + port = 8295; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open soap-fax port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to soap-fax port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_MARVELL_SCAN_CHANNEL: + port = 8290; /* same as ScanPort1[1] */ + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open marvell-scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to marvell-scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_LEDM_SCAN_CHANNEL: + case HPMUD_EWS_LEDM_CHANNEL: + port = 8080; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open ledm-scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to ledm-scan port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_MARVELL_FAX_CHANNEL: + port = 8285; + pin.sin_port = htons(port); + if ((pc->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + BUG("unable to open marvell-fax port %d: %m %s\n", port, pd->uri); + goto bugout; + } + if (connect(pc->socket, (struct sockaddr *)&pin, sizeof(pin)) == -1) + { + BUG("unable to connect to marvell-fax port %d: %m %s\n", port, pd->uri); + goto bugout; + } + break; + case HPMUD_PML_CHANNEL: + /* Do nothing here, use GetPml/SetPml instead of ReadData/WriteData. */ + break; + default: + BUG("unsupported service %d %s\n", pc->index, pd->uri); + stat = HPMUD_R_INVALID_SN; + goto bugout; + break; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_close(mud_channel *pc) +{ + if (pc->socket >= 0) + { + close(pc->socket); + + /* Delay for back-to-back scanning using scanimage. Otherwise next channel_open(HPMUD_SCAN_CHANNEL) can fail. */ + sleep(1); + } + + pc->socket = -1; + + return HPMUD_R_OK; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_write(mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote) +{ + mud_device *pd = &msp->device[pc->dindex]; + int len, size, total=0; + struct timeval tmo; + fd_set master; + fd_set writefd; + int maxfd, ret; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_wrote=0; + size = length; + + if (pc->socket<0) + { + stat = HPMUD_R_INVALID_STATE; + BUG("invalid data link socket=%d %s\n", pc->socket, pd->uri); + goto bugout; + } + + FD_ZERO(&master); + FD_SET(pc->socket, &master); + maxfd = pc->socket; + size = length; + + while (size > 0) + { + tmo.tv_sec = HPMUD_EXCEPTION_SEC_TIMEOUT; /* note linux select will modify tmo */ + tmo.tv_usec = 0; + writefd = master; + if ((ret = select(maxfd+1, NULL, &writefd, NULL, &tmo)) == 0) + { + stat = HPMUD_R_IO_TIMEOUT; + BUG("timeout write_channel %s\n", pd->uri); + goto bugout; /* timeout */ + } + len = send(pc->socket, buf+total, size, 0); + if (len < 0) + { + BUG("unable to write_channel: %m %s\n", pd->uri); + goto bugout; + } + size-=len; + total+=len; + *bytes_wrote+=len; + } + + DBG("write socket=%d len=%d size=%d\n", pc->socket, len, length); + DBG_DUMP(buf, len < 32 ? len : 32); + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/* + * Channel_read() tries to read "length" bytes from the peripheral. The returned read count may be zero + * (timeout, no data available), less than "length" or equal "length". + */ +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_read(mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read) +{ + mud_device *pd = &msp->device[pc->dindex]; + int len=0; + struct timeval tmo; + fd_set master; + fd_set readfd; + int maxfd, ret; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_read = 0; + + if (pc->socket<0) + { + stat = HPMUD_R_INVALID_STATE; + BUG("invalid data link socket=%d %s\n", pc->socket, pd->uri); + goto bugout; + } + + FD_ZERO(&master); + FD_SET(pc->socket, &master); + maxfd = pc->socket; + tmo.tv_sec = sec_timeout; + tmo.tv_usec = 0; + + readfd = master; + ret = select(maxfd+1, &readfd, NULL, NULL, &tmo); + if (ret < 0) + { + BUG("unable to read_channel: %m %s\n", pd->uri); + goto bugout; + } + if (ret == 0) + { + stat = HPMUD_R_IO_TIMEOUT; +// if (sec_timeout >= HPMUD_EXCEPTION_SEC_TIMEOUT) + BUG("timeout read_channel sec=%d %s\n", sec_timeout, pd->uri); + goto bugout; + } + else + { + if ((len = recv(pc->socket, buf, length, 0)) < 0) + { + BUG("unable to read_channel: %m %s\n", pd->uri); + goto bugout; + } + } + + DBG("read socket=%d len=%d size=%d\n", pc->socket, len, length); + DBG_DUMP(buf, len < 32 ? len : 32); + + *bytes_read = len; + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/* Convert "www.google.com" to "3www6google3com". */ +static int convert_name_to_dns(const char *name, int name_size, unsigned char *dns_name) +{ + int i, x=0; + unsigned char *p=dns_name; + + for (i=0; i<name_size; i++) + { + if (name[i]=='.') + { + *p++ = i-x; /* length */ + for (; x<i; x++) + *p++ = name[x]; + x++; + } + } + + if (i) + { + i--; + *p++ = i-x; /* length */ + for (; x<i; x++) + *p++ = name[x]; + x++; + } + + dns_name[x++]=0; + + return x; /* return length DOES include null termination */ +} + +/* + * Lookup IP for MDNS host name. + * MDNS host name example: "npi7c8a3e" (LaserJet p2055dn) + */ +enum HPMUD_RESULT hpmud_mdns_lookup(const char *host_name, int sec_timeout, char *ip) +{ + struct sockaddr_in send_addr; + struct sockaddr_in recv_addr; + struct sockaddr_in addr; + socklen_t addrlen; + struct timeval tmo; + fd_set master; + fd_set readfd; + int i, len, n, host_len, yes=1; + int maxfd, ret; + int udp_socket; + char recvbuffer[256], host[256]; + unsigned char dnsquery[256]={0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + unsigned char tail[] = {0x0, 0x1, 0x0, 0x1}; + unsigned char loop=0, ttl=255; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + DBG("mdns lookup '%s'\n", host_name); + + if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + BUG("unable to create udp socket: %m\n"); + goto bugout; + } + + /* Get rid of "address already in use" error message. */ + if (setsockopt(udp_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) + { + BUG("unable to setsockopt: %m\n"); + goto bugout; + } + + /* Bind the socket to port and IP equal to INADDR_ANY. */ + bzero(&recv_addr, sizeof(recv_addr)); + recv_addr.sin_family = AF_INET; + recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + recv_addr.sin_port = htons(5353); + if (bind(udp_socket, (struct sockaddr *)&recv_addr, sizeof(recv_addr)) == -1) + { + BUG("unable to bind udp socket: %m\n"); + goto bugout; + } + + /* Set multicast loopback off. */ + if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) == -1) + { + BUG("unable to setsockopt: %m\n"); + goto bugout; + } + + /* Set ttl to 255. Required by mdns. */ + if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) + { + BUG("unable to setsockopt: %m\n"); + goto bugout; + } + + /* Convert host name to mdns host name. */ + host_len = snprintf(host, sizeof(host), "%s.local", host_name) + 1; + + /* Create dns message. (header + question) */ + n = convert_name_to_dns(host, host_len, dnsquery+12); + memcpy(dnsquery+12+n, tail, sizeof(tail)); + n = 12+n+sizeof(tail); + + i=0; + while (1) + { + + DBG("send socket=%d len=%d\n", udp_socket, n); + DBG_DUMP(dnsquery, n); + + bzero(&send_addr, sizeof(send_addr)); + send_addr.sin_family = AF_INET; + send_addr.sin_addr.s_addr = inet_addr("224.0.0.251"); + send_addr.sin_port = htons(5353); + sendto(udp_socket, dnsquery, n, 0, (struct sockaddr *)&send_addr, sizeof(send_addr)); + + FD_ZERO(&master); + FD_SET(udp_socket, &master); + maxfd = udp_socket; + tmo.tv_sec = 0; + tmo.tv_usec = 500000; + + readfd = master; + ret = select(maxfd+1, &readfd, NULL, NULL, &tmo); + if (ret < 0) + { + BUG("error mdns lookup %s: %m\n", host); + goto bugout; + } + if (ret == 0) + { + goto retry; + } + else + { + bzero(&addr, sizeof(addr)); + addrlen = sizeof(addr); + if ((len = recvfrom(udp_socket, recvbuffer, sizeof(recvbuffer), 0, (struct sockaddr *)&addr, &addrlen)) < 0) + { + BUG("error mdns lookup %s: %m\n", host); + goto bugout; + } + + /* Make sure reply is from specified host. */ + if (strncasecmp((const char *)dnsquery+12, (const char *)recvbuffer+12, n)==0) + break; + BUG("error mdns lookup %s: bad hostname in reply from ip=%s port=%d\n", host, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + } + +retry: + if (i++ >= 2 * sec_timeout) + { + BUG("error timeout mdns lookup %s\n", host); + goto bugout; + } + + BUG("mdns lookup %s retry %d...\n", host, i); + } + + strcpy(ip, inet_ntoa(addr.sin_addr)); + + DBG("recv socket=%d len=%d port=%d ip=%s\n", udp_socket, len, ntohs(addr.sin_port), ip); + DBG_DUMP(recvbuffer, len); + + stat = HPMUD_R_OK; + +bugout: + + if (udp_socket >= 0) + close(udp_socket); + + return stat; +} + +enum HPMUD_RESULT hpmud_make_net_uri(const char *ip, int port, char *uri, int uri_size, int *bytes_read) +{ + char id[1024]; + char model[128]; + enum HPMUD_RESULT stat; + + DBG("[%d] hpmud_make_net_uri() ip=%s port=%d\n", getpid(), ip, port); + + *bytes_read=0; + + uri[0]=0; + + if (ip[0]==0) + { + BUG("invalid ip %s\n", ip); + stat = HPMUD_R_INVALID_IP; + goto bugout; + } + + if (device_id(ip, port, id, sizeof(id)) > 0 && is_hp(id)) + { + hpmud_get_model(id, model, sizeof(model)); + if (port == 1) + *bytes_read = snprintf(uri, uri_size, "hp:/net/%s?ip=%s", model, ip); + else + *bytes_read = snprintf(uri, uri_size, "hp:/net/%s?ip=%s&port=%d", model, ip, port); + } + else + { + BUG("invalid ip %s\n", ip); + stat = HPMUD_R_INVALID_IP; + goto bugout; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT hpmud_make_mdns_uri(const char *host, int port, char *uri, int uri_size, int *bytes_read) +{ + char id[1024]; + char model[128]; + char ip[HPMUD_LINE_SIZE]; /* internet address */ + enum HPMUD_RESULT stat; + + DBG("[%d] hpmud_make_mdns_uri() host=%s port=%d\n", getpid(), host, port); + + *bytes_read=0; + + uri[0]=0; + + if (host[0]==0) + { + BUG("invalid host %s\n", host); + stat = HPMUD_R_INVALID_MDNS; + goto bugout; + } + + if (hpmud_mdns_lookup(host, HPMUD_MDNS_TIMEOUT, ip) != HPMUD_R_OK) + { + BUG("invalid host %s, check firewall UDP/5353 or try using IP\n", host); + stat = HPMUD_R_INVALID_MDNS; + goto bugout; + } + + if (device_id(ip, port, id, sizeof(id)) > 0 && is_hp(id)) + { + hpmud_get_model(id, model, sizeof(model)); + if (port == 1) + *bytes_read = snprintf(uri, uri_size, "hp:/net/%s?zc=%s", model, host); + else + *bytes_read = snprintf(uri, uri_size, "hp:/net/%s?zc=%s&port=%d", model, host, port); + } + else + { + BUG("invalid host %s, or try using IP\n", host); + stat = HPMUD_R_INVALID_MDNS; + goto bugout; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +#endif /* HAVE_LIBNETSNMP */ diff --git a/io/hpmud/jd.h b/io/hpmud/jd.h new file mode 100644 index 0000000..f252fd8 --- /dev/null +++ b/io/hpmud/jd.h @@ -0,0 +1,56 @@ +/*****************************************************************************\ + + jd.h - JetDirect support for multi-point transport 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. + +\*****************************************************************************/ + +#ifndef _JD_H +#define _JD_H + +#include <sys/socket.h> +#include <netinet/in.h> +#include "hpmud.h" +#include "hpmudi.h" + +struct _mud_device; +struct _mud_channel; + +extern const char __attribute__ ((visibility ("hidden"))) *kStatusOID; /* device id snmp oid */ + +extern struct _mud_device_vf __attribute__ ((visibility ("hidden"))) jd_mud_device_vf; + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_open(struct _mud_device *pd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_close(struct _mud_device *pd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_get_device_id(struct _mud_device *pd, char *buf, int size, int *len); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_get_device_status(struct _mud_device *pd, unsigned int *status); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_open(struct _mud_device *pd, const char *sn, HPMUD_CHANNEL *cd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_close(struct _mud_device *pd, struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_write(struct _mud_device *pd, struct _mud_channel *pc, const void *buf, int length, int timeout, int *bytes_wrote); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_channel_read(struct _mud_device *pd, struct _mud_channel *pc, void *buf, int length, int timeout, int *bytes_read); + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_close(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_write(struct _mud_channel *pc, const void *buf, int length, int timeout, int *bytes_wrote); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) jd_s_channel_read(struct _mud_channel *pc, void *buf, int length, int timeout, int *bytes_wrote); + +#endif // _JD_H + diff --git a/io/hpmud/list.h b/io/hpmud/list.h new file mode 100644 index 0000000..007e704 --- /dev/null +++ b/io/hpmud/list.h @@ -0,0 +1,131 @@ +#ifndef _LIST_H +#define _LIST_H + +/* + * This linked list implementation is the same as linux kernel implementation. + * Refer to the linux kernel documentation for usage. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head * new_entry, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new_entry; + new_entry->next = next; + new_entry->prev = prev; + prev->next = new_entry; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new_entry, struct list_head *head) +{ + __list_add(new_entry, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new_entry, struct list_head *head) +{ + __list_add(new_entry, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = entry->prev = 0; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#endif /* _LIST_H */ 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; +} + + + diff --git a/io/hpmud/mlc.h b/io/hpmud/mlc.h new file mode 100644 index 0000000..afc6b22 --- /dev/null +++ b/io/hpmud/mlc.h @@ -0,0 +1,150 @@ +/*****************************************************************************\ + + mlc.h - MLC support for multi-point transport 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. + +\*****************************************************************************/ + +#ifndef _MLC_H +#define _MLC_H + +enum MLC_COMMAND +{ + MLC_INIT = 0, + MLC_OPEN_CHANNEL = 1, + MLC_CLOSE_CHANNEL = 2, + MLC_CREDIT = 3, + MLC_CREDIT_REQUEST = 4, + MLC_DEBIT = 5, + MLC_DEBIT_REQUEST = 6, + MLC_CONFIG_SOCKET = 7, + MLC_EXIT = 8, + MLC_ERROR = 0x7f +}; + +typedef struct +{ + unsigned char hsid; /* host socket id */ + unsigned char psid; /* peripheral socket id */ + unsigned short length; /* packet length (includes header) */ + unsigned char credit; /* data packet credit, reserved if command */ + unsigned char status; /* upper layer status */ +} __attribute__((packed)) MLCHeader; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char rev; +} __attribute__((packed)) MLCInit; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char result; + unsigned char rev; +} __attribute__((packed)) MLCInitReply; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; +} __attribute__((packed)) MLCExit; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char result; +} __attribute__((packed)) MLCExitReply; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char socket; /* socket id */ + unsigned short h2psize; /* host-to-peripheral packet size */ + unsigned short p2hsize; /* peripheral-to-host packet size */ + unsigned char status; /* status level */ +} __attribute__((packed)) MLCConfigSocket; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char result; + unsigned short h2psize; /* host-to-peripheral packet size */ + unsigned short p2hsize; /* peripheral-to-host packet size */ + unsigned char status; /* status level */ +} __attribute__((packed)) MLCConfigSocketReply; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char hsocket; /* host socket id */ + unsigned char psocket; /* peripheral socket id */ + unsigned short credit; +} __attribute__((packed)) MLCOpenChannel; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char result; + unsigned short credit; +} __attribute__((packed)) MLCOpenChannelReply; + +typedef struct +{ + MLCHeader h; + unsigned char cmd; + unsigned char hsocket; /* host socket id */ + unsigned char psocket; /* peripheral socket id */ +} __attribute__((packed)) MLCCloseChannel; + +typedef MLCExitReply MLCCloseChannelReply; +typedef MLCExitReply MLCReply; +typedef MLCExit MLCCmd; +typedef MLCOpenChannel MLCCredit; +typedef MLCExitReply MLCCreditReply; +typedef MLCOpenChannel MLCCreditRequest; +typedef MLCOpenChannelReply MLCCreditRequestReply; +typedef MLCExitReply MLCError; + +struct _mud_device; +struct _mud_channel; + +int __attribute__ ((visibility ("hidden"))) cut_buf(struct _mud_channel *pc, char *buf, int size); +int __attribute__ ((visibility ("hidden"))) MlcInit(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) MlcExit(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) MlcConfigSocket(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) MlcForwardData(struct _mud_channel *pc, int fd, const void *buf, int size, int usec_timeout); +int __attribute__ ((visibility ("hidden"))) MlcReverseData(struct _mud_channel *pc, int fd, void *buf, int length, int usec_timeout); +int __attribute__ ((visibility ("hidden"))) MlcOpenChannel(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) MlcCloseChannel(struct _mud_channel *pc, int fd); +int __attribute__ ((visibility ("hidden"))) MlcCredit(struct _mud_channel *pc, int fd, unsigned short credit); +int __attribute__ ((visibility ("hidden"))) MlcCreditRequest(struct _mud_channel *pc, int fd, unsigned short credit); +int __attribute__ ((visibility ("hidden"))) MlcReverseCmd(struct _mud_channel *pc, int fd); + +#endif // _MLC_H + diff --git a/io/hpmud/model.c b/io/hpmud/model.c new file mode 100644 index 0000000..3678af5 --- /dev/null +++ b/io/hpmud/model.c @@ -0,0 +1,633 @@ +/*****************************************************************************\ + + model.c - model parser for hplip devices + + (c) 2006-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. + +\*****************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include "list.h" +#include "hpmud.h" +#include "hpmudi.h" + +#define SECTION_SIZE 4096 /* Rough estimate of key/value section size in bytes. */ + +typedef struct +{ + char *name; + char *incFile; + int valueSize; /* size of list in bytes */ + char *value; /* list of key/value pairs */ + struct list_head list; +} LabelRecord; + +static LabelRecord head; /* list of labels from include files */ +static char homedir[255] = ""; + +static int GetPair(char *buf, int buf_len, char *key, char *value, char **tail) +{ + int i=0, j; + + key[0] = 0; + value[0] = 0; + + if (buf[i] == '#') + { + for (; buf[i] != '\n' && i < buf_len; i++); /* eat comment line */ + if (buf[i] == '\n') + i++; /* bump past '\n' */ + } + + j = 0; + while ((buf[i] != '=') && (i < buf_len) && (j < HPMUD_LINE_SIZE)) + key[j++] = buf[i++]; + for (j--; key[j] == ' ' && j > 0; j--); /* eat white space before = */ + key[++j] = 0; + + if (buf[i] == '=') + for (i++; buf[i] == ' ' && i < buf_len; i++); /* eat white space after = */ + + j = 0; + while ((buf[i] != '\n') && (i < buf_len) && (j < HPMUD_LINE_SIZE)) + value[j++] = buf[i++]; + for (j--; value[j] == ' ' && j > 0; j--); /* eat white space before \n */ + value[++j] = 0; + + if (buf[i] == '\n') + i++; /* bump past '\n' */ + + if (tail != NULL) + *tail = buf + i; /* tail points to next line */ + + return i; +} + +static int ReadConfig() +{ + char key[HPMUD_LINE_SIZE]; + char value[HPMUD_LINE_SIZE]; + char rcbuf[255]; + char section[32]; + char *tail; + FILE *inFile = NULL; + int stat=1; + + homedir[0] = 0; + + if((inFile = fopen(CONFDIR "/hplip.conf", "r")) == NULL) + { + BUG("unable to open %s: %m\n", CONFDIR "/hplip.conf"); + goto bugout; + } + + section[0] = 0; + + /* Read the config file */ + while ((fgets(rcbuf, sizeof(rcbuf), inFile) != NULL)) + { + if (rcbuf[0] == '[') + { + strncpy(section, rcbuf, sizeof(section)); /* found new section */ + continue; + } + + GetPair(rcbuf, strlen(rcbuf), key, value, &tail); + + if ((strncasecmp(section, "[dirs]", 6) == 0) && (strcasecmp(key, "home") == 0)) + { + strncpy(homedir, value, sizeof(homedir)); + break; /* done */ + } + } + + stat = 0; + +bugout: + if (inFile != NULL) + fclose(inFile); + + return stat; +} + +/* Find last occurance of y in x. */ +static char *strrstr(const char *x, const char *y) +{ + char *prev=NULL, *next; + + if (*y == '\0') + return strchr(x, '\0'); + + while ((next = strstr(x, y)) != NULL) + { + prev = next; + x = next + 1; + } + return prev; +} + +static int CopyLabel(char *label, char *buf, int bufSize) +{ + struct list_head *p; + LabelRecord *pl; + int i=0, found=0; + + /* Look for label. */ + list_for_each(p, &head.list) + { + pl = list_entry(p, LabelRecord, list); + if (strcasecmp(pl->name, label) == 0) + { + found = 1; /* found label */ + break; + } + } + + if (!found) + { + BUG("error undefined label %s\n", label); + goto bugout; + } + + if (pl->valueSize > bufSize) + { + BUG("error label %s size=%d buf=%d\n", label, pl->valueSize, bufSize); + goto bugout; + } + + memcpy(buf, pl->value, pl->valueSize); + i=pl->valueSize; + +bugout: + return i; +} + +static int ResolveAttributes(FILE *fp, char *attr, int attrSize) +{ + char label[128]; + int i=0, j, ch; + + /* Process each key/value line. */ + ch = fgetc(fp); + while (ch != EOF) + { + if (ch == '[') + { + ungetc(ch, fp); /* found new section, done with current section */ + break; + } + + if (ch == '#' || ch == ' ') + { + while ((ch = fgetc(fp)) != '\n' && ch != EOF); /* skip line */ + } + else if (ch == '\n') + { + /* skip blank line */ + } + else if (ch == '%') + { + j=0; + while ((ch = fgetc(fp)) != '\n' && ch != EOF) /* get label */ + { + if (j < sizeof(label)-1) + label[j++] = ch; + } + label[j-1] = 0; + i += CopyLabel(label, attr+i, attrSize-i); + } + else + { + if (i < attrSize-1) + attr[i++] = ch; + while ((ch = fgetc(fp)) != '\n' && ch != EOF) /* get key/value line */ + { + if (i < attrSize-1) + attr[i++] = ch; + } + if (i < attrSize-1) + attr[i++] = '\n'; + } + + if (ch == '\n') + ch = fgetc(fp); /* bump to next line */ + continue; + } + + attr[i] = 0; /* terminate string */ + + return i; +} +static int RegisterLabel(FILE *fp, char *incFile, char *label) +{ + struct list_head *p; + LabelRecord *pl; + char buf[SECTION_SIZE]; + int i=0, stat=1, ch; + + /* Look for duplicate label. */ + list_for_each(p, &head.list) + { + pl = list_entry(p, LabelRecord, list); + if (strcasecmp(pl->name, label) == 0) + { + BUG("error duplicate label %s\n", label); + goto bugout; + } + } + + if ((pl = (LabelRecord *)malloc(sizeof(LabelRecord))) == NULL) + { + BUG("unable to creat label record: %m\n"); + goto bugout; + } + + pl->incFile = strdup(incFile); + pl->name = strdup(label); + + /* Process each key/value line. */ + ch = fgetc(fp); + while (ch != EOF) + { + if (ch == '[') + { + ungetc(ch, fp); /* found new section, done with label */ + break; + } + + if (ch == '#' || ch == ' ') + { + while ((ch = fgetc(fp)) != '\n' && ch != EOF); /* skip line */ + } + else if (ch == '\n') + { + /* skip blank line */ + } + else + { + if (i < SECTION_SIZE-1) + buf[i++] = ch; + while ((ch = fgetc(fp)) != '\n' && ch != EOF) /* get key/value line */ + { + if (i < SECTION_SIZE-1) + buf[i++] = ch; + } + if (i < SECTION_SIZE-1) + buf[i++] = '\n'; + } + + if (ch == '\n') + ch = fgetc(fp); /* bump to next line */ + continue; + } + + buf[i] = 0; /* terminate string */ + + pl->value = strdup(buf); + pl->valueSize = i; /* size does not include zero termination */ + + list_add(&(pl->list), &(head.list)); + stat = 0; + +bugout: + + return stat; +} + +static int UnRegisterLabel(LabelRecord *pl) +{ + if (pl->incFile) + free(pl->incFile); + if (pl->name) + free(pl->name); + if (pl->value) + free(pl->value); + list_del(&(pl->list)); + free(pl); + return 0; +} + +static int DelList() +{ + struct list_head *p, *n; + LabelRecord *pl; + + /* Remove each label. */ + list_for_each_safe(p, n, &head.list) + { + pl = list_entry(p, LabelRecord, list); + UnRegisterLabel(pl); + } + return 0; +} + +/* Parse *.inc file. */ +static int ParseInc(char *incFile) +{ + FILE *fp=NULL; + struct list_head *p; + LabelRecord *pl; + char rcbuf[255]; + char section[128]; + int stat=1, n; + + /* Look for duplicate include file. */ + list_for_each(p, &head.list) + { + pl = list_entry(p, LabelRecord, list); + if (strcmp(pl->incFile, incFile) == 0) + { + BUG("error duplicate include file %s\n", incFile); + goto bugout; + } + } + + if ((fp = fopen(incFile, "r")) == NULL) + { + BUG("open %s failed: %m\n", incFile); + goto bugout; + } + + section[0] = 0; + + /* Read the *.inc file, check each line for new label. */ + while ((fgets(rcbuf, sizeof(rcbuf), fp) != NULL)) + { + if (rcbuf[0] == '[') + { + strncpy(section, rcbuf+1, sizeof(section)); /* found new section */ + n = strlen(section); + section[n-2]=0; /* remove ']' and CR */ + RegisterLabel(fp, incFile, section); + } + } + + stat = 0; + +bugout: + if (fp) + fclose(fp); + return stat; +} + +/* Parse *.dat file. */ +static int ParseFile(char *datFile, char *model, char *attr, int attrSize, int *bytes_read) +{ + FILE *fp; + char rcbuf[255]; + char section[128]; + char file[128]; + int found=0, n; + + if ((fp = fopen(datFile, "r")) == NULL) + goto bugout; + + section[0] = 0; + + /* Read the *.dat file, check each line for model match. */ + while ((fgets(rcbuf, sizeof(rcbuf), fp) != NULL)) + { + if (rcbuf[0] == '[') + { + strncpy(section, rcbuf+1, sizeof(section)); /* found new section */ + n = strlen(section); + section[n-2]=0; /* remove ']' and CR */ + if (strcasecmp(model, section) == 0) + { + /* Found model match. */ + *bytes_read = ResolveAttributes(fp, attr, attrSize); + found = 1; + break; + } + } + else if (strncmp(rcbuf, "%include", 8) == 0) + { + strncpy(file, datFile, sizeof(file)); /* get dirname from *.dat file */ + n = strrstr(file, "/") - file + 1; + strncpy(file+n, rcbuf+9, sizeof(file)-n); /* concatenate include filename to dirname */ + n = strlen(file); + file[n-1]=0; /* remove CR */ + ParseInc(file); + } + } + +bugout: + if (fp) + fclose(fp); + + return found; +} + +/* Parse and convert all known key value pairs in buffer. Do sanity check on values. */ +static int parse_key_value_pair(char *buf, int len, struct hpmud_model_attributes *ma) +{ + char key[HPMUD_LINE_SIZE]; + char value[HPMUD_LINE_SIZE]; + char *tail, *tail2; + int i=0, ret=HPMUD_R_OK; + + ma->prt_mode = HPMUD_RAW_MODE; + ma->mfp_mode = HPMUD_DOT4_MODE; + ma->scantype = 0; + ma->statustype = HPMUD_STATUSTYPE_SFIELD; + ma->support = HPMUD_SUPPORT_TYPE_NONE; + + if (buf == NULL) + return HPMUD_R_OK; /* initialize ma */ + + tail = buf; + + while (i < len) + { + i += GetPair(tail, len-i, key, value, &tail); + + if (strcasecmp(key, "io-mode") == 0) + { + ma->prt_mode = strtol(value, &tail2, 10); /* uni | raw | mlc */ + } + else if (strcasecmp(key, "io-mfp-mode") == 0) + { + ma->mfp_mode = strtol(value, &tail2, 10); /* mfc | dot4 */ + } + else if(strcasecmp(key, "scan-type") == 0) + { + ma->scantype = strtol(value, &tail2, 10); /*SCL, PML, SOAP, MARVELL, LEDM*/ + } + else if(strcasecmp(key, "scan-src") == 0) + { + ma->scansrc = strtol(value, &tail2, 10); /*Flatbed, ADF, Camera or combination of these*/ + } + else if(strcasecmp(key, "status-type") == 0) + { + ma->statustype = strtol(value, &tail2, 10); + } + else if(strcasecmp(key, "support-type") == 0) + { + ma->support = strtol(value, &tail2, 10); + } + else if(strcasecmp(key, "plugin") == 0) + { + ma->plugin = strtol(value, &tail2, 10); + } + else + { + /* Unknown keys are ignored (R_AOK). */ + } + } // end while (i < len) + + return ret; +} + +/* Request device model attributes for URI. Return all attributes. */ +enum HPMUD_RESULT hpmud_get_model_attributes(char *uri, char *attr, int attrSize, int *bytes_read) +{ + char sz[256]; + char model[256]; + int found; + enum HPMUD_RESULT stat = HPMUD_R_DATFILE_ERROR; + + memset(attr, 0, attrSize); + + INIT_LIST_HEAD(&head.list); + + if (homedir[0] == 0) + ReadConfig(); + + hpmud_get_uri_model(uri, model, sizeof(model)); + + /* Search /data/models.dat file for specified model. */ + snprintf(sz, sizeof(sz), "%s/data/models/models.dat", homedir); + found = ParseFile(sz, model, attr, attrSize, bytes_read); /* save any labels in *.inc files */ + + if (!found) + { + BUG("no %s attributes found in %s\n", model, sz); + + DelList(); /* Unregister all labels. */ + + /* Search /data/models/unreleased/unreleased.dat file for specified model. */ + snprintf(sz, sizeof(sz), "%s/data/models/unreleased/unreleased.dat", homedir); + found = ParseFile(sz, model, attr, attrSize, bytes_read); /* save any *.inc files */ + } + + if (!found) + { + BUG("no %s attributes found in %s\n", model, sz); + goto bugout; + } + + stat = HPMUD_R_OK; + +bugout: + DelList(); /* Unregister all labels. */ + return stat; +} + +/* Request device model attributes for URI. Return filled in hpmud_model_attributes structure. */ +enum HPMUD_RESULT hpmud_query_model(char *uri, struct hpmud_model_attributes *ma) +{ + char buf[SECTION_SIZE]; + int len; + enum HPMUD_RESULT stat = HPMUD_R_DATFILE_ERROR; + + parse_key_value_pair(NULL, 0, ma); /* set ma defaults */ + + if (hpmud_get_model_attributes(uri, buf, sizeof(buf), &len) != 0) + goto bugout; /* model not found, return ma defaults */ + + parse_key_value_pair(buf, len, ma); + + stat=HPMUD_R_OK; + +bugout: + + return stat; +} + +/* Get value for specified section and key from hplip.conf. */ +enum HPMUD_RESULT hpmud_get_conf(const char *section, const char *key, char *value, int value_size) +{ + return hpmud_get_key_value(CONFDIR "/hplip.conf", section, key, value, value_size); +} + +/* Get value for specified section and key from specified file. */ +enum HPMUD_RESULT hpmud_get_key_value(const char *file, const char *section, const char *key, char *value, int value_size) +{ + char new_key[HPMUD_LINE_SIZE]; + char new_value[HPMUD_LINE_SIZE]; + char rcbuf[255]; + char new_section[32]; + char *tail; + FILE *inFile; + enum HPMUD_RESULT stat = HPMUD_R_DATFILE_ERROR; + int i,j; + + if((inFile = fopen(file, "r")) == NULL) + { + BUG("unable to open %s: %m\n", file); + goto bugout; + } + + new_section[0] = 0; + + /* Read the config file */ + while ((fgets(rcbuf, sizeof(rcbuf), inFile) != NULL)) + { + if (rcbuf[0] == '[') + { + i = j = 0; + while ((rcbuf[i] != ']') && (j < (sizeof(new_section)-2))) + new_section[j++] = rcbuf[i++]; + new_section[j++] = rcbuf[i++]; /* ']' */ + new_section[j] = 0; /* zero terminate */ + continue; + } + + GetPair(rcbuf, strlen(rcbuf), new_key, new_value, &tail); + + if ((strcasecmp(new_section, section) == 0) && (strcasecmp(new_key, key) == 0)) + { + strncpy(value, new_value, value_size); + stat = HPMUD_R_OK; + break; /* done */ + } + } + + if (stat != HPMUD_R_OK) + BUG("unable to find %s %s in %s\n", section, key, file); + +bugout: + if (inFile != NULL) + fclose(inFile); + + return stat; +} + diff --git a/io/hpmud/musb.c b/io/hpmud/musb.c new file mode 100644 index 0000000..1649297 --- /dev/null +++ b/io/hpmud/musb.c @@ -0,0 +1,2197 @@ +/*****************************************************************************\ + + musb.c - USB support for multi-point transport driver + + (c) 2010 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. + Client/Server generic message format (see messaging-protocol.doc): + + Author: Naga Samrat Chowdary Narla, Sarbeswar Meher +\*****************************************************************************/ + +#include "hpmud.h" +#include "hpmudi.h" + +mud_device_vf __attribute__ ((visibility ("hidden"))) musb_mud_device_vf = +{ + .read = musb_read, + .write = musb_write, + .open = musb_open, + .close = musb_close, + .get_device_id = musb_get_device_id, + .get_device_status = musb_get_device_status, + .channel_open = musb_channel_open, + .channel_close = musb_channel_close, + .channel_write = musb_channel_write, + .channel_read = musb_channel_read +}; + +static mud_channel_vf musb_raw_channel_vf = +{ + .open = musb_raw_channel_open, + .close = musb_raw_channel_close, + .channel_write = musb_raw_channel_write, + .channel_read = musb_raw_channel_read +}; + +static mud_channel_vf musb_comp_channel_vf = +{ + .open = musb_comp_channel_open, + .close = musb_raw_channel_close, + .channel_write = musb_raw_channel_write, + .channel_read = musb_raw_channel_read +}; + +static mud_channel_vf musb_mlc_channel_vf = +{ + .open = musb_mlc_channel_open, + .close = musb_mlc_channel_close, + .channel_write = musb_mlc_channel_write, + .channel_read = musb_mlc_channel_read +}; + +static mud_channel_vf musb_dot4_channel_vf = +{ + .open = musb_dot4_channel_open, + .close = musb_dot4_channel_close, + .channel_write = musb_dot4_channel_write, + .channel_read = musb_dot4_channel_read +}; + +/* + * The folloing fd arrays must match "enum FD_ID" definition. + */ + +static char *fd_name[MAX_FD] = +{ + "na", + "7/1/2", + "7/1/3", + "ff/1/1", + "ff/2/1", + "ff/3/1", + "ff/ff/ff", + "ff/d4/0", + "ff/4/1", + "ff/1/0", + "ff/cc/0", + "ff/2/10", +}; + +static int fd_class[MAX_FD] = +{ + 0,0x7,0x7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +}; + +static int fd_subclass[MAX_FD] = +{ + 0,0x1,0x1,0x1,0x2,0x3,0xff,0xd4,0x4,0x1,0xcc,0x2, +}; + +static int fd_protocol[MAX_FD] = +{ + 0,0x2,0x3,0x1,0x1,0x1,0xff,0,0x1,0,0,0x10, +}; + +static const unsigned char venice_power_on[] = {0x1b, '%','P','u','i','f','p','.','p','o','w','e','r',' ','1',';', + 'u','d','w','.','q','u','i','t',';',0x1b,'%','-','1','2','3','4','5','X' }; + +static struct usb_device *libusb_device; /* libusb device referenced by URI */ +//static int open_fd; /* 7/1/2 file descriptor, used by deviceid and status */ +static file_descriptor fd_table[MAX_FD]; /* usb file descriptors */ + +/* This function is similar to usb_get_string_simple, but it handles zero returns. */ +static int get_string_descriptor(usb_dev_handle *dev, int index, char *buf, size_t buflen) +{ + char tbuf[255]; /* Some devices choke on size > 255 */ + int ret, si, di, cnt=5; + + while (cnt--) + { + ret = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, + 0x409, tbuf, sizeof(tbuf), LIBUSB_CONTROL_REQ_TIMEOUT); + if (ret==0) + { + /* This retry is necessary for lj1000 and lj1005. des 12/12/07 */ + BUG("get_string_descriptor zero result, retrying..."); + continue; + } + break; + } + + if (ret < 0) + { + BUG("unable get_string_descriptor %d: %m\n", ret); + return ret; + } + + if (tbuf[1] != USB_DT_STRING) + { + BUG("invalid get_string_descriptor tag act=%d exp=%d\n", tbuf[1], USB_DT_STRING); + return -EIO; + } + + if (tbuf[0] > ret) + { + BUG("invalid get_string_descriptor size act=%d exp=%d\n", tbuf[0], ret); + return -EFBIG; + } + + for (di = 0, si = 2; si < tbuf[0]; si += 2) + { + if (di >= (buflen - 1)) + break; + + if (tbuf[si + 1]) /* high byte */ + buf[di++] = '0'; + else + buf[di++] = tbuf[si]; + } + + buf[di] = 0; + + return di; +} + +/* Check for USB interface descriptor with specified class. */ +static int is_interface(struct usb_device *dev, int dclass) +{ + struct usb_interface_descriptor *pi; + int i, j, k; + + for (i=0; i<dev->descriptor.bNumConfigurations; i++) + { + for (j=0; j<dev->config[i].bNumInterfaces; j++) + { + for (k=0; k<dev->config[i].interface[j].num_altsetting; k++) + { + pi = &dev->config[i].interface[j].altsetting[k]; + if (pi->bInterfaceClass == dclass) + { + return 1; /* found interface */ + } + } + } + } + return 0; /* no interface found */ +} + +/* Write HP vendor-specific ECP channel message. */ +static int write_ecp_channel(file_descriptor *pfd, int value) +{ + usb_dev_handle *hd; + int interface = pfd->interface; + int len, stat=1; + char byte; + + if (pfd->hd == NULL) + { + BUG("invalid write_ecp_channel state\n"); + goto bugout; + } + + hd = pfd->hd; + + len = usb_control_msg(hd, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, /* bmRequestType */ + USB_REQ_GET_STATUS, /* bRequest */ + value, /* wValue */ + interface, /* wIndex */ + &byte, 1, LIBUSB_CONTROL_REQ_TIMEOUT); + + if (len != 1) + { + BUG("invalid write_ecp_channel: %m\n"); + goto bugout; + } + + stat = 0; + +bugout: + return stat; +} + +/* Set Cypress USS-725 Bridge Chip to 1284.4 mode. */ +static int bridge_chip_up(file_descriptor *pfd) +{ + usb_dev_handle *hd; + int len, stat=1; + char buf[9]; + char nullByte=0; + + if (pfd->hd == NULL) + { + BUG("invalid bridge_chip_up state\n"); + goto bugout; + } + + hd = pfd->hd; + + memset(buf, 0, sizeof(buf)); + + /* Read register values. */ + len = usb_control_msg(hd, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + USB_REQ_SET_FEATURE, /* bRequest */ + 0, /* wValue */ + 0, /* wIndex */ + buf, sizeof(buf), LIBUSB_CONTROL_REQ_TIMEOUT); + if (len < 0) + { + BUG("invalid write_bridge_up: %m\n"); + goto bugout; + } + + /* Check for auto ECP mode. */ + if (buf[ECRR] != 0x43) + { + /* Place 725 chip in register mode. */ + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x0758, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + /* Turn off RLE in auto ECP mode. */ + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x0a1d, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + /* Place 725 chip in auto ECP mode. */ + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x0759, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + /* Force negotiation. */ + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x0817, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + /* Read register values. */ + len = usb_control_msg(hd, + USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + USB_REQ_SET_FEATURE, /* bRequest */ + 0, /* wValue */ + 0, /* wIndex */ + buf, sizeof(buf), LIBUSB_CONTROL_REQ_TIMEOUT); + if (buf[ECRR] != 0x43) + { + BUG("invalid auto ecp mode mode=%d\n", buf[ECRR]); + } + } + + /* Reset to ECP channel 0. */ + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x05ce, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + musb_write(pfd->fd, &nullByte, 1, HPMUD_EXCEPTION_TIMEOUT); + + /* Switch to ECP channel 77. */ + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x05cd, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + + stat = 0; + +bugout: + return stat; +} + +/* Set Cypress USS-725 Bridge Chip to compatibility mode. */ +static int bridge_chip_down(file_descriptor *pfd) +{ + usb_dev_handle *hd; + int len, stat=1; + + if (pfd->hd == NULL) + { + BUG("invalid bridge_chip_down state\n"); + goto bugout; + } + + hd = pfd->hd; + + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, /* bmRequestType */ + 0x04, /* bRequest */ + 0x080f, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + if (len < 0) + { + BUG("invalid write_bridge_up: %m\n"); + goto bugout; + } + + stat = 0; + +bugout: + return stat; +} + +/* Write HP vendor-specific Setup command. */ +static int write_phoenix_setup(file_descriptor *pfd) +{ + usb_dev_handle *hd; + int len, stat=1; + + if (pfd->hd == NULL) + { + BUG("invalid write_phoenix_setup state\n"); + goto bugout; + } + + hd = pfd->hd; + + len = usb_control_msg(hd, + USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER, /* bmRequestType */ + 0x02, /* bRequest */ + 0, /* wValue */ + 0, /* wIndex */ + NULL, 0, LIBUSB_CONTROL_REQ_TIMEOUT); + + if (len < 0) + { + BUG("invalid write_phoenix_setup: %m\n"); + goto bugout; + } + + stat = 0; + +bugout: + return stat; +} + +/* Detach any kernel module that may have claimed specified inteface. */ +static int detach(usb_dev_handle *hd, int interface) +{ + char driver[32]; + + driver[0] = 0; + +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP + /* If any kernel module (ie:usblp) has claimed this interface, detach it. */ + usb_get_driver_np(hd, interface, driver, sizeof(driver)); + if ((driver[0] != 0) && (strcasecmp(driver, "usbfs") != 0)) + { + DBG("removing %s driver interface=%d\n", driver, interface); + if (usb_detach_kernel_driver_np(hd, interface) < 0) + BUG("could not remove %s driver interface=%d: %m\n", driver, interface); + } +#endif + + return 0; +} + +/* Get interface descriptor for specified xx/xx/xx protocol. */ +static int get_interface(struct usb_device *dev, enum FD_ID index, file_descriptor *pfd) +{ + struct usb_interface_descriptor *pi; + int i, j, k; + + for (i=0; i<dev->descriptor.bNumConfigurations; i++) + { + if (dev->config == NULL) + goto bugout; + + for (j=0; j<dev->config[i].bNumInterfaces; j++) + { + if (dev->config[i].interface == NULL) + goto bugout; + + for (k=0; k<dev->config[i].interface[j].num_altsetting; k++) + { + if (dev->config[i].interface[j].altsetting == NULL) + goto bugout; + + pi = &dev->config[i].interface[j].altsetting[k]; + if (pi->bInterfaceClass == fd_class[index] && pi->bInterfaceSubClass == fd_subclass[index] && pi->bInterfaceProtocol == fd_protocol[index]) + { + pfd->config=i; /* found interface */ + pfd->interface=j; + pfd->alt_setting=k; + pfd->fd=index; + return 0; + } + } + } + } + +bugout: + return 1; /* no interface found */ +} + +/* Get out endpoint for specified interface descriptor. */ +static int get_out_ep(struct usb_device *dev, int config, int interface, int altset, int type) +{ + struct usb_interface_descriptor *pi; + int i; + + if (dev->config == NULL || dev->config[config].interface == NULL || dev->config[config].interface[interface].altsetting == NULL) + goto bugout; + + pi = &dev->config[config].interface[interface].altsetting[altset]; + for (i=0; i<pi->bNumEndpoints; i++) + { + if (pi->endpoint == NULL) + goto bugout; + if (pi->endpoint[i].bmAttributes == type && !(pi->endpoint[i].bEndpointAddress & USB_ENDPOINT_DIR_MASK)) { + DBG("get_out_ep(type=%d): out=%d\n", type, pi->endpoint[i].bEndpointAddress); + return pi->endpoint[i].bEndpointAddress; + } + } + +bugout: + DBG("get_out_ep: ERROR! returning -1\n"); + return -1; /* no endpoint found */ +} + +/* Get in endpoint for specified interface descriptor. */ +static int get_in_ep(struct usb_device *dev, int config, int interface, int altset, int type) +{ + struct usb_interface_descriptor *pi; + int i; + + if (dev->config == NULL || dev->config[config].interface == NULL || dev->config[config].interface[interface].altsetting == NULL) + goto bugout; + + pi = &dev->config[config].interface[interface].altsetting[altset]; + for (i=0; i<pi->bNumEndpoints; i++) + { + if (pi->endpoint == NULL) + goto bugout; + if (pi->endpoint[i].bmAttributes == type && (pi->endpoint[i].bEndpointAddress & USB_ENDPOINT_DIR_MASK)) { + DBG("get_in_ep(type=%d): out=%d\n", type, pi->endpoint[i].bEndpointAddress); + return pi->endpoint[i].bEndpointAddress; + } + } + +bugout: + DBG("get_in_ep: ERROR! returning -1\n"); + return -1; /* no endpoint found */ +} + +static int claim_interface(struct usb_device *dev, file_descriptor *pfd) +{ + int stat=1; + + if (pfd->hd != NULL) + return 0; /* interface is already claimed */ + + if ((pfd->hd = usb_open(dev)) == NULL) + { + BUG("invalid usb_open: %m\n"); + goto bugout; + } + + detach(pfd->hd, pfd->interface); + +#if 0 /* hp devices only have one configuration, so far ... */ + if (usb_set_configuration(FD[fd].pHD, dev->config[config].bConfigurationValue)) + goto bugout; +#endif + + if (usb_claim_interface(pfd->hd, pfd->interface)) + { + usb_close(pfd->hd); + pfd->hd = NULL; + DBG("invalid claim_interface %s: %m\n", fd_name[pfd->fd]); + goto bugout; + } + + if (usb_set_altinterface(pfd->hd, pfd->alt_setting)) + { + usb_release_interface(pfd->hd, pfd->interface); + usb_close(pfd->hd); + pfd->hd = NULL; + BUG("invalid set_altinterface %s altset=%d: %m\n", fd_name[pfd->fd], pfd->alt_setting); + goto bugout; + } + + pfd->write_active=0; + pthread_mutex_init(&pfd->mutex, NULL); + pthread_cond_init(&pfd->write_done_cond, NULL); + + DBG("claimed %s interface\n", fd_name[pfd->fd]); + + stat=0; + +bugout: + return stat; +} + +static int release_interface(file_descriptor *pfd) +{ + if (pfd->hd == NULL) + return 0; + + if (pfd->write_active) + { + BUG("aborting outstanding %s write\n", fd_name[pfd->fd]); + pthread_cancel(pfd->tid); /* kill outstanding write */ + pfd->write_active = 0; + } + + usb_release_interface(pfd->hd, pfd->interface); + usb_close(pfd->hd); + pfd->hd = NULL; + pthread_mutex_destroy(&pfd->mutex); + pthread_cond_destroy(&pfd->write_done_cond); + + DBG("released %s interface\n", fd_name[pfd->fd]); + + return 0; +} + +/* Claim any open interface which is valid for device_id and device status. */ +static int claim_id_interface(struct usb_device *dev) +{ + enum FD_ID i; + + for (i=FD_7_1_2; i!=MAX_FD; i++) + { + if (get_interface(dev, i, &fd_table[i]) == 0) + { + if (claim_interface(libusb_device, &fd_table[i])) + continue; /* interface is busy, try next interface */ + break; /* done */ + } + } + + return i; +} + +/* See if this usb device and URI match. */ +static int is_uri(struct usb_device *dev, const char *uri) +{ + usb_dev_handle *hd=NULL; + char sz[128]; + char uriModel[128]; + char uriSerial[128]; + char gen[128]; + int r, stat=0; + + if ((hd = usb_open(dev)) == NULL) + { + BUG("invalid usb_open: %m\n"); + goto bugout; + } + + if (dev->descriptor.idVendor != 0x3f0) + goto bugout; + + if ((r=get_string_descriptor(hd, dev->descriptor.iProduct, sz, sizeof(sz))) < 0) + { + BUG("invalid product id string ret=%d\n", r); + goto bugout; + } + + generalize_model(sz, gen, sizeof(gen)); + + hpmud_get_uri_model(uri, uriModel, sizeof(uriModel)); + if (strcasecmp(uriModel, gen) != 0) + goto bugout; + + if ((r=get_string_descriptor(hd, dev->descriptor.iSerialNumber, sz, sizeof(sz))) < 0) + { + BUG("invalid serial id string ret=%d\n", r); + goto bugout; + } + + if (sz[0]) + generalize_serial(sz, gen, sizeof(gen)); + else + strcpy(gen, "0"); + + get_uri_serial(uri, uriSerial, sizeof(uriSerial)); + if (strcmp(uriSerial, gen) != 0) + goto bugout; + + stat = 1; /* found usb device that matches uri */ + +bugout: + if (hd != NULL) + usb_close(hd); + + return stat; +} + +/* See if this usb device and serial number match. Return model if match. */ +static int is_serial(struct usb_device *dev, const char *sn, char *model, int model_size) +{ + usb_dev_handle *hd=NULL; + char sz[128]; + char gen[128]; + int r, stat=0; + + if ((hd = usb_open(dev)) == NULL) + { + BUG("invalid usb_open: %m\n"); + goto bugout; + } + + if (dev->descriptor.idVendor != 0x3f0) + goto bugout; /* not a HP product */ + + if ((r=get_string_descriptor(hd, dev->descriptor.iSerialNumber, sz, sizeof(sz))) < 0) + { + BUG("invalid serial id string ret=%d\n", r); + goto bugout; + } + if (sz[0]) + generalize_serial(sz, gen, sizeof(gen)); + else + strcpy(gen, "0"); + + if (strncmp(sn, gen, sizeof(gen)) != 0) + goto bugout; /* match failed */ + + if ((r=get_string_descriptor(hd, dev->descriptor.iProduct, sz, sizeof(sz))) < 0) + { + BUG("invalid product id string ret=%d\n", r); + goto bugout; + } + generalize_model(sz, model, model_size); + + stat = 1; /* found usb device that matches sn */ + +bugout: + if (hd != NULL) + usb_close(hd); + + return stat; +} + +static struct usb_device *get_libusb_device(const char *uri) +{ + struct usb_bus *bus; + struct usb_device *dev; + + for (bus=usb_busses; bus; bus=bus->next) + for (dev=bus->devices; dev; dev=dev->next) + if (dev->descriptor.idVendor == 0x3f0 && is_interface(dev, 7)) + if (is_uri(dev, uri)) + return dev; /* found usb device that matches uri */ + + return NULL; +} + +static int device_id(int fd, char *buffer, int size) +{ + usb_dev_handle *hd; + int config,interface,alt; + int len=0, rlen, maxSize; + + hd = fd_table[fd].hd; + config = fd_table[fd].config; + interface = fd_table[fd].interface; + alt = fd_table[fd].alt_setting; + + if (hd == NULL) + { + BUG("invalid device_id state\n"); + goto bugout; + } + + maxSize = (size > 1024) ? 1024 : size; /* RH8 has a size limit for device id (usb) */ + + rlen = usb_control_msg(hd, + USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bmRequestType */ + USB_REQ_GET_STATUS, /* bRequest */ + config, /* wValue */ + interface, /* wIndex */ /* note firmware does not follow the USB Printer Class specification for wIndex */ + buffer, maxSize, LIBUSB_CONTROL_REQ_TIMEOUT); + + if (rlen < 0) + { +#if 0 /* Removed this PS A420 hack so a valid error is returned after USB reset. DES 10/1/09 */ + /* Following retry is necessary for a firmware problem with PS A420 products. DES 4/17/07 */ + BUG("invalid deviceid wIndex=%x, retrying wIndex=%x: %m\n", interface, interface << 8); + rlen = usb_control_msg(hd, + USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bmRequestType */ + USB_REQ_GET_STATUS, /* bRequest */ + config, /* wValue */ + interface << 8, /* wIndex */ + buffer, maxSize, LIBUSB_CONTROL_REQ_TIMEOUT); + if (rlen < 0) + { + BUG("invalid deviceid retry ret=%d: %m\n", rlen); + goto bugout; + } +#endif + BUG("invalid deviceid ret=%d: %m\n", rlen); + goto bugout; + } + + len = ntohs(*(short *)buffer); + if (len > (size-1)) + len = size-1; /* leave byte for zero termination */ + if (len > 2) + len -= 2; + memcpy(buffer, buffer+2, len); /* remove length */ + buffer[len]=0; + DBG("read actual device_id successfully fd=%d len=%d\n", fd, len); + +bugout: + return len; /* length does not include zero termination */ +} + +static int device_status(int fd, unsigned int *status) +{ + usb_dev_handle *hd; + int interface; + int len, stat=1; + char byte; + + hd = fd_table[fd].hd; + interface = fd_table[fd].interface; + + if (hd == NULL) + { + BUG("invalid device_status state\n"); + goto bugout; + } + + len = usb_control_msg(hd, + USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bmRequestType */ + USB_REQ_CLEAR_FEATURE, /* bRequest */ + 0, /* wValue */ + interface, /* wIndex */ + &byte, 1, LIBUSB_CONTROL_REQ_TIMEOUT); + + if (len < 0) + { + BUG("invalid device_status: %m\n"); + goto bugout; + } + + *status = (unsigned int)byte; + stat = 0; + DBG("read actual device_status successfully fd=%d\n", fd); + +bugout: + return stat; +} + +/* Get VStatus from S-field. */ +static int sfield_printer_state(const char *id) +{ + char *pSf; + int vstatus=0, ver; + + if ((pSf = strstr(id, ";S:")) == NULL) + { + BUG("invalid S-field\n"); + return vstatus; + } + + /* Valid S-field, get version number. */ + pSf+=3; + ver = 0; + HEX2INT(*pSf, ver); + pSf++; + ver = ver << 4; + HEX2INT(*pSf, ver); + pSf++; + + /* Position pointer to printer state subfield. */ + switch (ver) + { + case 0: + case 1: + case 2: + pSf+=12; + break; + case 3: + pSf+=14; + break; + case 4: + pSf+=18; + break; + default: + BUG("unknown S-field version=%d\n", ver); + pSf+=12; + break; + } + + /* Extract VStatus.*/ + vstatus = 0; + HEX2INT(*pSf, vstatus); + pSf++; + vstatus = vstatus << 4; + HEX2INT(*pSf, vstatus); + + return vstatus; +} + +/* + * Power up printer if necessary. Most all-in-ones have no power down state (ie: OJ K80), so they are already powered up. + * Newer single function printers power-up with the print job. May be called by other mud_device. + */ +int __attribute__ ((visibility ("hidden"))) power_up(mud_device *pd, int fd) +{ + const char *pSf; + + if ((pSf = strstr(pd->id, "CMD:LDL")) != NULL) + return 0; /* crossbow don't do power-up */ + + if ((pSf = strstr(pd->id, ";S:")) != NULL) + { + if (sfield_printer_state(pd->id) != 3) + return 0; /* already powered up */ + } + else if ((pSf = strstr(pd->id, "VSTATUS:")) != NULL) + { + /* DJ895C returns $XB0$XC0 (unknown pens) when powered off. */ + if (!(strstr(pSf+8, "OFFF") || strstr(pSf+8, "PWDN") || strstr(pSf+8, "$X"))) + return 0; /* already powered up */ + } + else + return 0; /* must be laserjet, don't do power-up */ + + (pd->vf.write)(fd, venice_power_on, sizeof(venice_power_on), HPMUD_EXCEPTION_TIMEOUT); + sleep(2); + + return 0; +} + +/* Create channel object given the requested socket id and service name. */ +static int new_channel(mud_device *pd, int index, const char *sn) +{ + int stat=1; + + /* Check for existing name service already open. */ + if (pd->channel[index].client_cnt) + { +#if 0 + if (index == HPMUD_EWS_CHANNEL) + { + pd->channel[index].client_cnt++; /* allow multiple clients for separate USB interfaces only */ + stat = 0; + DBG("reused %s channel=%d clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].client_cnt, pd->channel_cnt); + } + else +#endif + BUG("%s channel=%d is busy, used by [%d], clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].pid, pd->channel[index].client_cnt, pd->channel_cnt); + goto bugout; + } + + if (index == HPMUD_EWS_CHANNEL || index == HPMUD_EWS_LEDM_CHANNEL || + index == HPMUD_SOAPSCAN_CHANNEL || index == HPMUD_SOAPFAX_CHANNEL || + index == HPMUD_MARVELL_SCAN_CHANNEL || index == HPMUD_MARVELL_FAX_CHANNEL || + index == HPMUD_LEDM_SCAN_CHANNEL) { + pd->channel[index].vf = musb_comp_channel_vf; + } + else if (pd->io_mode == HPMUD_RAW_MODE || pd->io_mode == HPMUD_UNI_MODE) { + pd->channel[index].vf = musb_raw_channel_vf; + } + else if (pd->io_mode == HPMUD_MLC_GUSHER_MODE || pd->io_mode == HPMUD_MLC_MISER_MODE) { + pd->channel[index].vf = musb_mlc_channel_vf; + } + else { + pd->channel[index].vf = musb_dot4_channel_vf; + } + + pd->channel[index].index = index; + pd->channel[index].client_cnt = 1; + pd->channel[index].sockid = index; /* static socket id is valid for MLC but not 1284.4 */ + pd->channel[index].pid = getpid(); + pd->channel[index].dindex = pd->index; + pd->channel[index].fd = 0; + strcpy(pd->channel[index].sn, sn); + pd->channel_cnt++; + + stat = 0; + DBG("new %s channel=%d clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].client_cnt, pd->channel_cnt); + +bugout: + return stat; +} + +/* Remove channel object given the channel decriptor. */ +static int del_channel(mud_device *pd, mud_channel *pc) +{ + pc->client_cnt--; + + if (pc->client_cnt <= 0) + { + pd->channel_cnt--; + } + DBG("removed %s channel=%d clientCnt=%d channelCnt=%d\n", pc->sn, pc->index, pc->client_cnt, pd->channel_cnt); + return 0; +} + +static void write_thread(file_descriptor *pfd) +{ + int ep; + + pthread_detach(pthread_self()); + + if ((ep = get_out_ep(libusb_device, pfd->config, pfd->interface, pfd->alt_setting, USB_ENDPOINT_TYPE_BULK)) < 0) + { + BUG("invalid bulk out endpoint\n"); + goto bugout; + } + + /* Wait forever for write to complete (actually 72 hours in ms). */ + pfd->write_return = usb_bulk_write(pfd->hd, ep, (char *)pfd->write_buf, pfd->write_size, 72*3600*1000); + +bugout: + pthread_mutex_lock(&pfd->mutex); + pfd->write_buf = NULL; + pthread_cond_signal(&pfd->write_done_cond); /* signal write is complete */ + pthread_mutex_unlock(&pfd->mutex); + + return; +} + +/********************************************************************************************************************************* + * USB mud_device functions. + */ + +int __attribute__ ((visibility ("hidden"))) musb_write(int fd, const void *buf, int size, int usec) +{ + int len=-EIO; + + if (fd_table[fd].hd == NULL) + { + BUG("invalid musb_write state\n"); + goto bugout; + } + +#if 1 + struct timeval now; + struct timespec timeout; + int ret; + /* If write is still active, probably OOP condition, don't kick off a new write. */ + if (!fd_table[fd].write_active) + { + fd_table[fd].write_active = 1; + fd_table[fd].write_buf = buf; + fd_table[fd].write_size = size; + + /* Create usb_bulk_write thread so we can use our own timeout. Otherwise we cannot handle OOP condition. */ + if (pthread_create(&fd_table[fd].tid, NULL, (void *(*)(void*))write_thread, (void *)&fd_table[fd]) != 0) + { + BUG("unable to creat write_thread: %m\n"); + goto bugout; /* bail */ + } + } + + /* Wait for write to complete. */ + pthread_mutex_lock(&fd_table[fd].mutex); + gettimeofday(&now, NULL); + now.tv_usec += usec; + now.tv_sec += now.tv_usec / 1000000; + now.tv_usec %= 1000000; + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = now.tv_usec * 1000; + ret = 0; + while (fd_table[fd].write_buf && ret != ETIMEDOUT) + ret = pthread_cond_timedwait(&fd_table[fd].write_done_cond, &fd_table[fd].mutex, &timeout); + pthread_mutex_unlock(&fd_table[fd].mutex); + + if (ret == ETIMEDOUT) + { + len = -ETIMEDOUT; /* write timeout, let client know */ + goto bugout; + } + + fd_table[fd].write_active = 0; + + len = fd_table[fd].write_return; +#else + int ep; + if ((ep = get_out_ep(libusb_device, fd_table[fd].config, fd_table[fd].interface, fd_table[fd].alt_setting, USB_ENDPOINT_TYPE_BULK)) < 0) + { + BUG("invalid bulk out endpoint\n"); + goto bugout; + } + + len = usb_bulk_write(fd_table[fd].hd, ep, (char *)buf, size, usec); +#endif + + if (len < 0) + { + BUG("bulk_write failed buf=%p size=%d len=%d: %m\n", buf, size, len); + goto bugout; + } + + DBG("write fd=%d len=%d size=%d usec=%d\n", fd, len, size, usec); + DBG_DUMP(buf, len < 512 ? len : 512); + +bugout: + return len; +} + +int __attribute__ ((visibility ("hidden"))) musb_read(int fd, void *buf, int size, int usec) +{ + struct timeval t1, t2; + int total_usec, tmo_usec=usec; + int len=-EIO, ep; + + if (fd_table[fd].hd == NULL) + { + BUG("invalid musb_read state\n"); + goto bugout; + } + + gettimeofday (&t1, NULL); /* get start time */ + + if ((ep = get_in_ep(libusb_device, fd_table[fd].config, fd_table[fd].interface, fd_table[fd].alt_setting, USB_ENDPOINT_TYPE_BULK)) < 0) + { + BUG("invalid bulk in endpoint\n"); + goto bugout; + } + + while (1) + { + len = usb_bulk_read(fd_table[fd].hd, ep, (char *)buf, size, tmo_usec/1000); + + if (len == -ETIMEDOUT) + goto bugout; + + if (len < 0) + { + BUG("bulk_read failed: %m\n"); + goto bugout; + } + + if (len == 0) + { + /* Bulk_read has a timeout, but bulk_read can return zero byte packet(s), so we must use our own timeout here. */ + gettimeofday(&t2, NULL); /* get current time */ + + total_usec = (t2.tv_sec - t1.tv_sec)*1000000; + total_usec += (t2.tv_usec > t1.tv_usec) ? t2.tv_usec - t1.tv_usec : t1.tv_usec - t2.tv_usec; + if (total_usec > usec) + { + len = -ETIMEDOUT; /* timeout */ + goto bugout; + } + tmo_usec = usec - total_usec; /* decrease timeout */ + continue; + } + + break; + } + + DBG("read fd=%d len=%d size=%d usec=%d\n", fd, len, size, usec); + DBG_DUMP(buf, len < 32 ? len : 32); + +bugout: + return len; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_open(mud_device *pd) +{ + int len=0, fd=0; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + /* Find usb device for specified uri. */ + if ((libusb_device = get_libusb_device(pd->uri)) == NULL) + { + BUG("unable to open %s\n", pd->uri); + goto bugout; + } + + pthread_mutex_lock(&pd->mutex); + + if (pd->id[0] == 0) + { + /* First client. */ + + if ((fd = claim_id_interface(libusb_device)) == MAX_FD) + { + stat = HPMUD_R_DEVICE_BUSY; + goto blackout; + } + + len = device_id(fd, pd->id, sizeof(pd->id)); /* get new copy and cache it */ + + if (len > 0 && is_hp(pd->id)) + power_up(pd, fd); + + release_interface(&fd_table[fd]); + + if (len == 0) + goto blackout; + + pd->open_fd = fd; + } + + stat = HPMUD_R_OK; + +blackout: + pthread_mutex_unlock(&pd->mutex); + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_close(mud_device *pd) +{ + int i; + enum HPMUD_RESULT stat = HPMUD_R_OK; + + pthread_mutex_lock(&pd->mutex); + + for (i=1; i<MAX_FD; i++) + { + if (fd_table[i].hd != NULL) + release_interface(&fd_table[i]); + } + + pd->id[0] = 0; + + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_get_device_id(mud_device *pd, char *buf, int size, int *len) +{ + int i, fd=FD_NA; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + + *len=0; + + pthread_mutex_lock(&pd->mutex); + + if (pd->io_mode == HPMUD_DOT4_BRIDGE_MODE || pd->io_mode == HPMUD_UNI_MODE) + { + *len = strlen(pd->id); /* usb/parallel bridge chip, use cached copy */ + } + else + { + /* See if any interface is already claimed. */ + for (i=1; i<MAX_FD; i++) + { + if (fd_table[i].hd != NULL) + { + fd = i; + break; + } + } + + if (fd == FD_NA) + { + /* Device not in use. Claim interface, but release for other processes. */ + if ((fd = claim_id_interface(libusb_device)) != MAX_FD) + { + *len = device_id(fd, pd->id, sizeof(pd->id)); /* get new copy and cache it */ + release_interface(&fd_table[fd]); + } + else + { + /* Device is in use by another process, return cache copy. */ + *len = strlen(pd->id); + } + } + else + { + /* Device in use by current process, leave interface up. Other processes are blocked. */ + *len = device_id(fd, pd->id, sizeof(pd->id)); /* get new copy and cache it */ + } + } + + if (*len) + { + memcpy(buf, pd->id, *len > size ? size : *len); + stat = HPMUD_R_OK; + } + + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_get_device_status(mud_device *pd, unsigned int *status) +{ + int i, fd=FD_NA; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + int r=1; + + pthread_mutex_lock(&pd->mutex); + + if (pd->io_mode == HPMUD_DOT4_BRIDGE_MODE || pd->io_mode == HPMUD_UNI_MODE) + *status = NFAULT_BIT; /* usb/parallel bridge chip, fake status */ + else + { + /* See if any interface is already claimed. */ + for (i=1; i<MAX_FD; i++) + { + if (fd_table[i].hd != NULL) + { + fd = i; + break; + } + } + + if (fd == FD_NA) + { + /* Device not in use. Claim interface, but release for other processes. */ + if ((fd = claim_id_interface(libusb_device)) != MAX_FD) + { + r = device_status(fd, status); + release_interface(&fd_table[fd]); + } + } + else + { + /* Device in use by current process, leave interface up. Other processes are blocked. */ + r = device_status(fd, status); + } + } + + pthread_mutex_unlock(&pd->mutex); + + if (r != 0) + goto bugout; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_write(mud_device *pd, mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote) +{ + enum HPMUD_RESULT stat; + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.channel_write)(pc, buf, length, sec_timeout, bytes_wrote); + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_read(mud_device *pd, mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read) +{ + enum HPMUD_RESULT stat; + + if (pd->io_mode == HPMUD_UNI_MODE) + { + stat = HPMUD_R_INVALID_STATE; + BUG("invalid channel_read io_mode=%d\n", pd->io_mode); + goto bugout; + } + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.channel_read)(pc, buf, length, sec_timeout, bytes_read); + pthread_mutex_unlock(&pd->mutex); + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_open(mud_device *pd, const char *sn, HPMUD_CHANNEL *cd) +{ + int index; + enum HPMUD_RESULT stat; + + /* Check for valid service requests. */ + if ((stat = service_to_channel(pd, sn, &index)) != HPMUD_R_OK) + goto bugout; + + pthread_mutex_lock(&pd->mutex); + + if (new_channel(pd, index, sn)) + { + stat = HPMUD_R_DEVICE_BUSY; + } + else + { + if ((stat = (pd->channel[index].vf.open)(&pd->channel[index])) != HPMUD_R_OK) /* call transport specific open */ + del_channel(pd, &pd->channel[index]); /* open failed, cleanup */ + else + *cd = index; + } + + pthread_mutex_unlock(&pd->mutex); + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_close(mud_device *pd, mud_channel *pc) +{ + enum HPMUD_RESULT stat = HPMUD_R_OK; + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.close)(pc); /* call trasport specific close */ + del_channel(pd, pc); + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +/******************************************************************************************************************************* + * USB raw_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_open(mud_channel *pc) +{ + int fd = FD_7_1_2; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + + get_interface(libusb_device, fd, &fd_table[fd]); + + if (claim_interface(libusb_device, &fd_table[fd])) + goto bugout; + + pc->fd = fd; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_close(mud_channel *pc) +{ + int fd = pc->fd; + + // For New laserjet devices like Tsunami, end point was getting stall or halted, hence clearing it + int ep = -1; + if (( ep = get_in_ep(libusb_device, fd_table[fd].config, fd_table[fd].interface, fd_table[fd].alt_setting, USB_ENDPOINT_TYPE_BULK)) >= 0) + { + usb_clear_halt(fd_table[fd].hd, ep); + } + + if (( ep = get_out_ep(libusb_device, fd_table[fd].config, fd_table[fd].interface, fd_table[fd].alt_setting, USB_ENDPOINT_TYPE_BULK)) >= 0) + { + usb_clear_halt(fd_table[fd].hd, ep); + } + + release_interface(&fd_table[fd]); + + pc->fd = 0; + + return HPMUD_R_OK; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_write(mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote) +{ + int len, size, total=0; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_wrote=0; + size = length; + + while (size > 0) + { + len = (msp->device[pc->dindex].vf.write)(pc->fd, buf+total, size, sec_timeout*1000000); + if (len < 0) + { + if (len == -ETIMEDOUT) + { + stat = HPMUD_R_IO_TIMEOUT; + if (sec_timeout >= HPMUD_EXCEPTION_SEC_TIMEOUT) + BUG("unable to write data %s: %d second io timeout\n", msp->device[pc->dindex].uri, sec_timeout); + } + else + BUG("unable to write data %s: %m\n", msp->device[pc->dindex].uri); + goto bugout; + } + size-=len; + total+=len; + *bytes_wrote+=len; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/* + * Channel_read() tries to read "length" bytes from the peripheral. The returned read count may be zero + * (timeout, no data available), less than "length" or equal "length". + */ +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_read(mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read) +{ + int len=0, usec; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_read = 0; + + if (sec_timeout==0) + usec = 1000; /* minmum timeout is 1ms for libusb 0.1.12, hangs forever with zero */ + else + usec = sec_timeout*1000000; + + len = (msp->device[pc->dindex].vf.read)(pc->fd, buf, length, usec); + if (len < 0) + { + if (len == -ETIMEDOUT) + { + stat = HPMUD_R_IO_TIMEOUT; + if (sec_timeout >= HPMUD_EXCEPTION_SEC_TIMEOUT) + BUG("unable to read data %s: %d second io timeout\n", msp->device[pc->dindex].uri, sec_timeout); + } + else + BUG("unable to read data %s: %m\n", msp->device[pc->dindex].uri); + goto bugout; + } + + *bytes_read = len; + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/******************************************************************************************************************************* + * USB comp_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_comp_channel_open(mud_channel *pc) +{ + int fd; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + + /* Get requested composite interface. */ + switch (pc->index) + { + case HPMUD_EWS_CHANNEL: + fd = FD_ff_1_1; + break; + case HPMUD_EWS_LEDM_CHANNEL: + fd = FD_ff_4_1; + break; + case HPMUD_SOAPSCAN_CHANNEL: + fd = FD_ff_2_1; + break; + case HPMUD_SOAPFAX_CHANNEL: + fd = FD_ff_3_1; + break; + case HPMUD_MARVELL_SCAN_CHANNEL: + fd = FD_ff_ff_ff; + break; + case HPMUD_MARVELL_FAX_CHANNEL: //using vendor specific C/S/P codes for fax too + fd = FD_ff_1_0; + break; + case HPMUD_LEDM_SCAN_CHANNEL: //using vendor specific C/S/P codes for fax too + fd = FD_ff_cc_0; + break; + default: + stat = HPMUD_R_INVALID_SN; + BUG("invalid %s channel=%d\n", pc->sn, pc->index); + goto bugout; + break; + } + + if (get_interface(libusb_device, fd, &fd_table[fd])) + { + stat = HPMUD_R_INVALID_SN; + BUG("invalid %s channel=%d\n", pc->sn, pc->index); + goto bugout; + } + + if (claim_interface(libusb_device, &fd_table[fd])) + goto bugout; + + pc->fd = fd; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/******************************************************************************************************************************* + * USB mlc_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_open(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum FD_ID fd; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + /* Initialize MLC transport if this is the first MLC channel. */ + if (pd->channel_cnt==1) + { + if (get_interface(libusb_device, FD_7_1_3, &fd_table[FD_7_1_3]) == 0 && claim_interface(libusb_device, &fd_table[FD_7_1_3]) == 0) + fd = FD_7_1_3; /* mlc, dot4 */ + else if (get_interface(libusb_device, FD_ff_ff_ff, &fd_table[FD_ff_ff_ff]) == 0 && claim_interface(libusb_device, &fd_table[FD_ff_ff_ff]) == 0) + fd = FD_ff_ff_ff; /* mlc, dot4 */ + else if (get_interface(libusb_device, FD_ff_d4_0, &fd_table[FD_ff_d4_0]) == 0 && claim_interface(libusb_device, &fd_table[FD_ff_d4_0]) == 0) + fd = FD_ff_d4_0; /* mlc, dot4 */ + else if (get_interface(libusb_device, FD_7_1_2, &fd_table[FD_7_1_2]) == 0 && claim_interface(libusb_device, &fd_table[FD_7_1_2]) == 0) + fd = FD_7_1_2; /* raw, mlc, dot4 */ + else + { + stat = HPMUD_R_DEVICE_BUSY; + goto bugout; + } + + if (fd == FD_7_1_2) + { + /* Emulate 7/1/3 on 7/1/2 using vendor-specific ECP channel-77. */ + if (write_ecp_channel(&fd_table[fd], 77)) + goto bugout; + } + + unsigned int i; +#if 0 +// Removed reverse drain I seen it hang forever on read, one-time with PSC750 (FC5 64-bit). DES + int len; + unsigned char buf[255]; + + /* Drain any reverse data. */ + for (i=0,len=1; len > 0 && i < sizeof(buf); i++) + len = (pd->vf.read)(fd, buf+i, 1, 0); /* no blocking */ +#endif + + /* MLC initialize */ + if (MlcInit(pc, fd) != 0) + goto bugout; + + /* Reset transport attributes for all channels. */ + for (i=0; i<HPMUD_CHANNEL_MAX; i++) + memset(&pd->channel[i].ta, 0 , sizeof(transport_attributes)); + + pd->mlc_fd = fd; + pd->mlc_up=1; + + } /* if (pDev->ChannelCnt==1) */ + + if (MlcConfigSocket(pc, pd->mlc_fd)) + goto bugout; + + if (MlcOpenChannel(pc, pd->mlc_fd)) + goto bugout; + + pc->rcnt = pc->rindex = 0; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_close(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + unsigned char nullByte=0; + enum HPMUD_RESULT stat = HPMUD_R_OK; + + if (pd->mlc_up) + { + if (MlcCloseChannel(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + + /* Remove MLC transport if this is the last MLC channel. */ + if (pd->channel_cnt==1) + { + if (pd->mlc_up) + { + if (MlcExit(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + pd->mlc_up=0; + + if (pd->mlc_fd == FD_7_1_2) + { + write_ecp_channel(&fd_table[pd->mlc_fd], 78); + (pd->vf.write)(pd->mlc_fd, &nullByte, 1, HPMUD_EXCEPTION_TIMEOUT); + write_ecp_channel(&fd_table[pd->mlc_fd], 0); + } + + release_interface(&fd_table[pd->mlc_fd]); + + /* Delay for back-to-back scanning using scanimage (OJ 7110, OJ d135). */ + sleep(1); + } + + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_write(mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote) +{ + mud_device *pd = &msp->device[pc->dindex]; + int ret, len, size, dlen, total=0; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_wrote=0; + size = length; + dlen = pc->ta.h2psize - sizeof(MLCHeader); + while (size > 0) + { + len = (size > dlen) ? dlen : size; + + if (pc->ta.h2pcredit == 0 && pd->io_mode == HPMUD_MLC_MISER_MODE) + { + if (MlcCreditRequest(pc, pd->mlc_fd, 1)) /* Miser flow control */ + { + BUG("invalid MlcCreditRequest from peripheral\n"); + goto bugout; + } + } + + if (pc->ta.h2pcredit == 0) + { + ret = MlcReverseCmd(pc, pd->mlc_fd); + if (pc->ta.h2pcredit == 0) + { + if (ret == 0) + continue; /* Got a reverse command, but no MlcCredit, try again. */ + + if (pd->io_mode != HPMUD_MLC_MISER_MODE) + { + /* If miser flow control works for this device, set "miser" in models.dat. */ + BUG("invalid MlcCredit from peripheral, trying miser\n"); + pd->io_mode = HPMUD_MLC_MISER_MODE; + continue; + } + + BUG("invalid MlcCredit from peripheral\n"); + goto bugout; + } + } + + if (MlcForwardData(pc, pd->mlc_fd, buf+total, len, sec_timeout*1000000)) + { + goto bugout; + } + + pc->ta.h2pcredit--; + size-=len; + total+=len; + *bytes_wrote+=len; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/* + * Mlc_channel_read() tries to read "length" bytes from the peripheral. ReadData() reads data in packet size chunks. + * The returned read count may be zero (timeout, no data available), less than "length" or equal "length". + * + * Mlc_channel_read() may read more the "length" if the data packet is greater than "length". For this case the + * return value will equal "length" and the left over data will be buffered for the next ReadData() call. + * + * The "timeout" specifies how many seconds to wait for a data packet. Once the read of the data packet has + * started the "timeout" is no longer used. + * + * Note, if a "timeout" occurs one peripheral to host credit is left outstanding. Which means the peripheral + * can send unsolicited data later. + */ +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_read(mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_read=0; + if (pc->ta.p2hsize==0) + { + BUG("invalid channel_read state\n"); + goto bugout; + } + + if (pc->rcnt) + { + stat=HPMUD_R_OK; + *bytes_read = cut_buf(pc, buf, length); + goto bugout; + } + + if (pc->ta.p2hcredit == 0) + { + /* Issue enough credit to the peripheral to read one data packet. */ + if (MlcCredit(pc, pd->mlc_fd, 1)) + goto bugout; + } + + stat=HPMUD_R_OK; + pc->rcnt = MlcReverseData(pc, pd->mlc_fd, pc->rbuf, sizeof(pc->rbuf), sec_timeout*1000000); + if (pc->rcnt) + pc->ta.p2hcredit--; /* one data packet was read, decrement credit count */ + + *bytes_read = cut_buf(pc, buf, length); + +bugout: + return stat; +} + +/******************************************************************************************************************************* + * USB dot4_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_open(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum FD_ID fd; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + /* Initialize MLC transport if this is the first MLC channel. */ + if (pd->channel_cnt==1) + { + if (get_interface(libusb_device, FD_7_1_3, &fd_table[FD_7_1_3]) == 0 && claim_interface(libusb_device, &fd_table[FD_7_1_3]) == 0) + fd = FD_7_1_3; /* mlc, dot4 */ + else if (get_interface(libusb_device, FD_ff_ff_ff, &fd_table[FD_ff_ff_ff]) == 0 && claim_interface(libusb_device, &fd_table[FD_ff_ff_ff]) == 0) + fd = FD_ff_ff_ff; /* mlc, dot4 */ + else if (get_interface(libusb_device, FD_ff_d4_0, &fd_table[FD_ff_d4_0]) == 0 && claim_interface(libusb_device, &fd_table[FD_ff_d4_0]) == 0) + fd = FD_ff_d4_0; /* mlc, dot4 */ + else if (get_interface(libusb_device, FD_7_1_2, &fd_table[FD_7_1_2]) == 0 && claim_interface(libusb_device, &fd_table[FD_7_1_2]) == 0) + fd = FD_7_1_2; /* raw, mlc, dot4 */ + else + { + stat = HPMUD_R_DEVICE_BUSY; + goto bugout; + } + + if (fd == FD_7_1_2) + { + if (pd->io_mode == HPMUD_DOT4_BRIDGE_MODE) + { + /* Emulate 7/1/3 on 7/1/2 using the bridge chip set (ie: CLJ2500). */ + if (bridge_chip_up(&fd_table[fd])) + goto bugout; + } + else + { + /* Emulate 7/1/3 on 7/1/2 using vendor-specific ECP channel-77. */ + if (write_ecp_channel(&fd_table[fd], 77)) + goto bugout; + } + } + + if (pd->io_mode == HPMUD_DOT4_PHOENIX_MODE) + write_phoenix_setup(&fd_table[fd]); + + unsigned int i; +#if 0 +// Removed reverse drain LJ1015 can hang forever on read (FC5 64-bit). DES + unsigned char buf[255]; + int len; + + /* Drain any reverse data. */ + for (i=0,len=1; len > 0 && i < sizeof(buf); i++) + len = (pd->vf.read)(fd, buf+i, 1, 0); /* no blocking */ +#endif + /* DOT4 initialize */ + if (Dot4Init(pc, fd) != 0) + goto bugout; + + /* Reset transport attributes for all channels. */ + for (i=0; i<HPMUD_CHANNEL_MAX; i++) + memset(&pd->channel[i].ta, 0 , sizeof(transport_attributes)); + + pd->mlc_fd = fd; + pd->mlc_up=1; + + } /* if (pDev->ChannelCnt==1) */ + + if (Dot4GetSocket(pc, pd->mlc_fd)) + goto bugout; + + if (Dot4OpenChannel(pc, pd->mlc_fd)) + goto bugout; + + if (pd->io_mode == HPMUD_DOT4_PHOENIX_MODE) + { + /* Issue credit to peripheral. */ + if (Dot4Credit(pc, pd->mlc_fd, 2)) + { + BUG("invalid Dot4Credit to peripheral\n"); + goto bugout; + } + } + + pc->rcnt = pc->rindex = 0; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_close(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_OK; + + if (pd->mlc_up) + { + if (Dot4CloseChannel(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + + /* Remove 1284.4 transport if this is the last 1284.4 channel. */ + if (pd->channel_cnt==1) + { + if (pd->mlc_up) + { + if (Dot4Exit(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + pd->mlc_up=0; + + if (pd->mlc_fd == FD_7_1_2) + { + if (pd->io_mode == HPMUD_DOT4_BRIDGE_MODE) + { + bridge_chip_down(&fd_table[pd->mlc_fd]); + } + else + { + write_ecp_channel(&fd_table[pd->mlc_fd], 78); + write_ecp_channel(&fd_table[pd->mlc_fd], 0); + } + } + + release_interface(&fd_table[pd->mlc_fd]); + + /* Delay for back-to-back scanning using scanimage (OJ 7110, OJ d135). */ + sleep(1); + } + + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_write(mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote) +{ + mud_device *pd = &msp->device[pc->dindex]; + int ret, len, size, dlen, total=0, cnt=0; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_wrote=0; + size = length; + dlen = pc->ta.h2psize - sizeof(DOT4Header); + while (size > 0) + { + len = (size > dlen) ? dlen : size; + + if (pc->ta.h2pcredit == 0 && pd->io_mode == HPMUD_DOT4_PHOENIX_MODE) + { + /* Issue credit request to peripheral. */ + if (Dot4CreditRequest(pc, pd->mlc_fd, 1)) + { + BUG("invalid Dot4CreditRequest from peripheral\n"); + goto bugout; + } + if (pc->ta.h2pcredit == 0) + { + if (cnt++ > HPMUD_EXCEPTION_SEC_TIMEOUT) + { + BUG("invalid Dot4CreditRequest from peripheral\n"); + goto bugout; + } + sleep(1); + continue; /* Got a valid Dot4CreditRequest but no credit from peripheral, try again. */ + } + } + + if (pc->ta.h2pcredit == 0) + { + ret = Dot4ReverseCmd(pc, pd->mlc_fd); + if (pc->ta.h2pcredit == 0) + { + if (ret == 0) + continue; /* Got a reverse command, but no Dot4Credit, try again. */ + + BUG("invalid Dot4Credit from peripheral\n"); + goto bugout; + } + } + + if (Dot4ForwardData(pc, pd->mlc_fd, buf+total, len, sec_timeout*1000000)) + { + goto bugout; + } + + pc->ta.h2pcredit--; + size-=len; + total+=len; + *bytes_wrote+=len; + cnt=0; + } + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +/* + * dot4_read_data() tries to read "length" bytes from the peripheral. Read_data() reads data in packet size chunks. + * The returned read count may be zero (timeout, no data available), less than "length" or equal "length". + * + * dot4_read_data() may read more the "length" if the data packet is greater than "length". For this case the + * return value will equal "length" and the left over data will be buffered for the next read_data() call. + * + * The "timeout" specifies how many seconds to wait for a data packet. Once the read of the data packet has + * started the "timeout" is no longer used. + * + * Note, if a "timeout" occurs one peripheral to host credit is left outstanding. Which means the peripheral + * can send unsolicited data later. + */ +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_read(mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + *bytes_read=0; + if (pc->ta.p2hsize==0) + { + BUG("invalid channel_read state\n"); + goto bugout; + } + + if (pc->rcnt) + { + stat=HPMUD_R_OK; + *bytes_read = cut_buf(pc, buf, length); + goto bugout; + } + + if (pc->ta.p2hcredit == 0) + { + /* Issue enough credit to the peripheral to read one data packet. */ + if (Dot4Credit(pc, pd->mlc_fd, 1)) + goto bugout; + } + + stat=HPMUD_R_OK; + pc->rcnt = Dot4ReverseData(pc, pd->mlc_fd, pc->rbuf, sizeof(pc->rbuf), sec_timeout*1000000); + if (pc->rcnt) + pc->ta.p2hcredit--; /* one data packet was read, decrement credit count */ + + *bytes_read = cut_buf(pc, buf, length); + +bugout: + return stat; +} + +/******************************************************************************************************************************* + * USB probe devices, walk the USB bus(s) looking for HP products. + */ + +int __attribute__ ((visibility ("hidden"))) musb_probe_devices(char *lst, int lst_size, int *cnt) +{ + struct usb_bus *bus; + struct usb_device *dev; + usb_dev_handle *hd; + struct hpmud_model_attributes ma; + char rmodel[128]; + char rserial[128]; + char model[128]; + char serial[128]; + char mfg[128]; + char sz[HPMUD_LINE_SIZE]; + int r, size=0; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus=usb_busses; bus; bus=bus->next) + { + for (dev=bus->devices; dev; dev=dev->next) + { + + model[0] = serial[0] = rmodel[0] = rserial[0] = sz[0] = mfg[0] = 0; + + if (dev->descriptor.idVendor == 0x3f0 && is_interface(dev, 7)) + { + if((hd = usb_open(dev)) == NULL) + { + BUG("Invalid usb_open: %m\n"); + continue; + } + /* Found hp device. */ + if ((r=get_string_descriptor(hd, dev->descriptor.iProduct, rmodel, sizeof(rmodel))) < 0) + BUG("invalid product id string ret=%d\n", r); + else + generalize_model(rmodel, model, sizeof(model)); + + if ((r=get_string_descriptor(hd, dev->descriptor.iSerialNumber, rserial, sizeof(rserial))) < 0) + BUG("invalid serial id string ret=%d\n", r); + else + generalize_serial(rserial, serial, sizeof(serial)); + + if ((r=get_string_descriptor(hd, dev->descriptor.iManufacturer, sz, sizeof(sz))) < 0) + BUG("invalid manufacturer string ret=%d\n", r); + else + generalize_serial(sz, mfg, sizeof(serial)); + + if (!serial[0]) + strcpy(serial, "0"); /* no serial number, make it zero */ + + if (model[0]) + { + snprintf(sz, sizeof(sz), "hp:/usb/%s?serial=%s", model, serial); + + /* See if device is supported by hplip. */ + hpmud_query_model(sz, &ma); + if (ma.support != HPMUD_SUPPORT_TYPE_HPLIP) + { + BUG("ignoring %s support=%d\n", sz, ma.support); + continue; /* ignor, not supported */ + } + + /* + * For Cups 1.2 we append a dummy deviceid. A valid deviceid would require us to claim the USB interface, thus removing usblp. + * This will allow us to do discovery and not disable other CUPS backend(s) who use /dev/usb/lpx instead of libusb. + */ + if (strncasecmp(rmodel, "hp ", 3) == 0) + size += snprintf(lst+size, lst_size-size, "direct %s \"HP %s\" \"HP %s USB %s HPLIP\" \"MFG:%s;MDL:%s;CLS:PRINTER;DES:%s;SN:%s;\"\n", + sz, &rmodel[3], &rmodel[3], serial, mfg, rmodel, rmodel, rserial); + else + size += snprintf(lst+size, lst_size-size, "direct %s \"HP %s\" \"HP %s USB %s HPLIP\" \"MFG:%s;MDL:%s;CLS:PRINTER;DES:%s;SN:%s;\"\n", + sz, rmodel, rmodel, serial, mfg, rmodel, rmodel, rserial); + + *cnt+=1; + } + usb_close(hd); + } + } + } + + return size; +} + +enum HPMUD_RESULT hpmud_make_usb_uri(const char *busnum, const char *devnum, char *uri, int uri_size, int *bytes_read) +{ + struct usb_bus *bus; + struct usb_device *dev, *found_dev=NULL; + usb_dev_handle *hd=NULL; + char model[128]; + char serial[128]; + char sz[256]; + int r; + enum HPMUD_RESULT stat = HPMUD_R_INVALID_DEVICE_NODE; + + DBG("[%d] hpmud_make_usb_uri() bus=%s dev=%s\n", getpid(), busnum, devnum); + + *bytes_read=0; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus=usb_busses; bus && !found_dev; bus=bus->next) + if (strcmp(bus->dirname, busnum) == 0) + for (dev=bus->devices; dev && !found_dev; dev=dev->next) + if (strcmp(dev->filename, devnum) == 0) + found_dev = dev; /* found usb device that matches bus:device */ + + if (found_dev == NULL) + { + BUG("invalid busnum:devnum %s:%s\n", busnum, devnum); + goto bugout; + } + + dev = found_dev; + if ((hd = usb_open(dev)) == NULL) + { + BUG("invalid usb_open: %m\n"); + goto bugout; + } + + model[0] = serial[0] = sz[0] = 0; + + if (dev->descriptor.idVendor == 0x3f0) + { + /* Found hp device. */ + if ((r=get_string_descriptor(hd, dev->descriptor.iProduct, sz, sizeof(sz))) < 0) + BUG("invalid product id string ret=%d\n", r); + else + generalize_model(sz, model, sizeof(model)); + + if ((r=get_string_descriptor(hd, dev->descriptor.iSerialNumber, sz, sizeof(sz))) < 0) + BUG("invalid serial id string ret=%d\n", r); + else + generalize_serial(sz, serial, sizeof(serial)); + + if (!serial[0]) + strcpy(serial, "0"); /* no serial number, make it zero */ + } + else + { + BUG("invalid vendor id: %d\n", dev->descriptor.idVendor); + goto bugout; + } + + if (!model[0] || !serial[0]) + goto bugout; + + *bytes_read = snprintf(uri, uri_size, "hp:/usb/%s?serial=%s", model, serial); + stat = HPMUD_R_OK; + +bugout: + if (hd != NULL) + usb_close(hd); + + return stat; +} + +enum HPMUD_RESULT hpmud_make_usb_serial_uri(const char *sn, char *uri, int uri_size, int *bytes_read) +{ + struct usb_bus *bus; + struct usb_device *dev, *found_dev=NULL; + char model[128]; + enum HPMUD_RESULT stat = HPMUD_R_INVALID_DEVICE_NODE; + + DBG("[%d] hpmud_make_usb_serial_uri() sn=%s\n", getpid(), sn); + + *bytes_read=0; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus=usb_busses; bus && !found_dev; bus=bus->next) + for (dev=bus->devices; dev && !found_dev; dev=dev->next) + if (is_serial(dev, sn, model, sizeof(model))) + found_dev = dev; /* found usb device that matches serial number */ + + if (found_dev == NULL) + { + BUG("invalid sn %s\n", sn); + goto bugout; + } + + *bytes_read = snprintf(uri, uri_size, "hp:/usb/%s?serial=%s", model, sn); + stat = HPMUD_R_OK; + +bugout: + return stat; +} diff --git a/io/hpmud/musb.h b/io/hpmud/musb.h new file mode 100644 index 0000000..df4a8a6 --- /dev/null +++ b/io/hpmud/musb.h @@ -0,0 +1,121 @@ +/*****************************************************************************\ + + musb.h - USB support for multi-point transport driver + + (c) 2010 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. + + Author: Naga Samrat Chowdary Narla +\*****************************************************************************/ + +#ifndef _MUSB_H +#define _MUSB_H + +#include <usb.h> +#include "hpmud.h" +#include "hpmudi.h" + +#define LIBUSB_TIMEOUT 30000 /* milliseconds */ +#define LIBUSB_CONTROL_REQ_TIMEOUT 5000 + +enum FD_ID +{ + FD_NA=0, + FD_7_1_2, /* bi-di interface */ + FD_7_1_3, /* 1284.4 interface */ + FD_ff_1_1, /* HP EWS interface */ + FD_ff_2_1, /* HP Soap Scan interface */ + FD_ff_3_1, /* HP Soap Fax interface */ + FD_ff_ff_ff, /* HP dot4 interface */ + FD_ff_d4_0, /* HP dot4 interface */ + FD_ff_4_1, /* orblite scan / rest scan interface */ + FD_ff_1_0, /* Marvell fax support*/ + FD_ff_cc_0, + FD_ff_2_10, + MAX_FD +}; + +enum BRIGE_REG_ID +{ + ECRR=2, + CCTR=3, + ATAA=8 +}; + +/* USB file descriptor, one for each USB protocol. */ +typedef struct +{ + usb_dev_handle *hd; + enum FD_ID fd; + int config; + int interface; + int alt_setting; + + /* Write thread definitions. */ + int write_active; /* 0=no, 1=yes */ + const void *write_buf; + int write_size; + int write_return; /* return value, normally number bytes written */ + pthread_t tid; + pthread_mutex_t mutex; + pthread_cond_t write_done_cond; + + unsigned char ubuf[HPMUD_BUFFER_SIZE]; /* usb read packet buffer */ + int uindex; + int ucnt; +} file_descriptor; + +struct _mud_device; +struct _mud_channel; + +extern struct _mud_device_vf __attribute__ ((visibility ("hidden"))) musb_mud_device_vf; + +int __attribute__ ((visibility ("hidden"))) musb_write(int fd, const void *buf, int size, int usec_timout); +int __attribute__ ((visibility ("hidden"))) musb_read(int fd, void *buf, int size, int usec_timout); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_open(struct _mud_device *pd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_close(struct _mud_device *pd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_get_device_id(struct _mud_device *pd, char *buf, int size, int *len); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_get_device_status(struct _mud_device *pd, unsigned int *status); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_open(struct _mud_device *pd, const char *sn, HPMUD_CHANNEL *cd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_close(struct _mud_device *pd, struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_write(struct _mud_device *pd, struct _mud_channel *pc, const void *buf, int length, int timeout, int *bytes_wrote); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_channel_read(struct _mud_device *pd, struct _mud_channel *pc, void *buf, int length, int timeout, int *bytes_read); + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_close(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_write(struct _mud_channel *pc, const void *buf, int length, int timeout, int *bytes_wrote); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_raw_channel_read(struct _mud_channel *pc, void *buf, int length, int timeout, int *bytes_wrote); + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_comp_channel_open(struct _mud_channel *pc); + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_close(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_write(struct _mud_channel *pc, const void *buf, int length, int timeout, int *bytes_wrote); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_mlc_channel_read(struct _mud_channel *pc, void *buf, int length, int timeout, int *bytes_wrote); + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_close(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_write(struct _mud_channel *pc, const void *buf, int length, int sec_timeout, int *bytes_wrote); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) musb_dot4_channel_read(struct _mud_channel *pc, void *buf, int length, int sec_timeout, int *bytes_read); + +int __attribute__ ((visibility ("hidden"))) musb_probe_devices(char *lst, int lst_size, int *cnt); +int __attribute__ ((visibility ("hidden"))) power_up(struct _mud_device *pd, int fd); + +#endif // _MUSB_H + diff --git a/io/hpmud/pml.c b/io/hpmud/pml.c new file mode 100644 index 0000000..a84a9a2 --- /dev/null +++ b/io/hpmud/pml.c @@ -0,0 +1,520 @@ +/*****************************************************************************\ + + pml.c - get/set pml api for hpmud + + The get/set pml commands are a high level interface to hpmud. This hpmud system + interface sits on top of the hpmud core interface. The system interface does + not use the hpmud memory map file system. + + (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. + +\*****************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <string.h> +#include "hpmud.h" +#include "hpmudi.h" + +#ifdef HAVE_LIBNETSNMP +#ifdef HAVE_UCDSNMP +#include <ucd-snmp/ucd-snmp-config.h> +#include <ucd-snmp/ucd-snmp-includes.h> +#else +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#endif +static const char *SnmpPort[] = { "","public.1","public.2","public.3" }; +#endif + +static int PmlOidToHex(const char *szoid, unsigned char *oid, int oidSize) +{ + char *tail; + int i=0, val; + + if (szoid[0] == 0) + goto bugout; + + val = strtol(szoid, &tail, 10); + + while (i < oidSize) + { + if (val > 128) + { + BUG("invalid oid size: oid=%s\n", szoid); + goto bugout; + } + oid[i++] = (unsigned char)val; + + if (*tail == 0) + break; /* done */ + + val = strtol(tail+1, &tail, 10); + } + +bugout: + return i; +} + +/* Convert ascii snmp oid to pml hex oid. */ +static int SnmpToPml(const char *snmp_oid, unsigned char *oid, int oidSize) +{ + static const char hp_pml_mib_prefix[] = "1.3.6.1.4.1.11.2.3.9.4.2"; + static const char standard_printer_mib_prefix[] = "1.3.6.1.2.1.43"; + static const char host_resource_mib_prefix[] = "1.3.6.1.2.1.25"; + int len=0; + + if (strncmp(snmp_oid, hp_pml_mib_prefix, sizeof(hp_pml_mib_prefix)-1) == 0) + { + /* Strip out snmp prefix and convert to hex. */ + len = 0; + len += PmlOidToHex(&snmp_oid[sizeof(hp_pml_mib_prefix)], &oid[0], oidSize); + len--; /* remove trailing zero in pml mib */ + } + else if (strncmp(snmp_oid, standard_printer_mib_prefix, sizeof(standard_printer_mib_prefix)-1) == 0) + { + /* Replace snmp prefix with 2 and convert to hex. */ + len = 1; + oid[0] = 0x2; + len += PmlOidToHex(&snmp_oid[sizeof(standard_printer_mib_prefix)], &oid[1], oidSize); + } + else if (strncmp(snmp_oid, host_resource_mib_prefix, sizeof(host_resource_mib_prefix)-1) == 0) + { + /* Replace snmp prefix with 3 and convert to hex. */ + len = 1; + oid[0] = 0x3; + len += PmlOidToHex(&snmp_oid[sizeof(host_resource_mib_prefix)], &oid[1], oidSize); + } + else + BUG("SnmpToPml failed snmp oid=%s\n", snmp_oid); + + return len; +} + +#ifdef HAVE_LIBNETSNMP + +static int SnmpErrorToPml(int snmp_error) +{ + int err; + + switch (snmp_error) + { + case SNMP_ERR_NOERROR: + err = PML_EV_OK; + break; + case SNMP_ERR_TOOBIG: + err = PML_EV_ERROR_BUFFER_OVERFLOW; + break; + case SNMP_ERR_NOSUCHNAME: + err = PML_EV_ERROR_UNKNOWN_OBJECT_IDENTIFIER; + break; + case SNMP_ERR_BADVALUE: + err = PML_EV_ERROR_INVALID_OR_UNSUPPORTED_VALUE; + break; + case SNMP_ERR_READONLY: + err = PML_EV_ERROR_OBJECT_DOES_NOT_SUPPORT_REQUESTED_ACTION; + break; + case SNMP_ERR_GENERR: + default: + err = PML_EV_ERROR_UNKNOWN_REQUEST; + break; + } + + return err; +} + +static int SetSnmp(const char *ip, int port, const char *szoid, int type, void *buffer, unsigned int size, int *pml_result, int *result) +{ + struct snmp_session session, *ss=NULL; + struct snmp_pdu *pdu=NULL; + struct snmp_pdu *response=NULL; + oid anOID[MAX_OID_LEN]; + size_t anOID_len = MAX_OID_LEN; + unsigned int i, len=0; + uint32_t val; + + *result = HPMUD_R_IO_ERROR; + *pml_result = PML_EV_ERROR_UNKNOWN_REQUEST; + + init_snmp("snmpapp"); + + snmp_sess_init(&session ); /* set up defaults */ + session.peername = (char *)ip; + session.version = SNMP_VERSION_1; + session.community = (unsigned char *)SnmpPort[port]; + session.community_len = strlen((const char *)session.community); + ss = snmp_open(&session); /* establish the session */ + if (ss == NULL) + goto bugout; + + pdu = snmp_pdu_create(SNMP_MSG_SET); + read_objid(szoid, anOID, &anOID_len); + + switch (type) + { + case PML_DT_ENUMERATION: + case PML_DT_SIGNED_INTEGER: + /* Convert PML big-endian to SNMP little-endian byte stream. */ + for(i=0, val=0; i<size && i<sizeof(val); i++) + val = ((val << 8) | ((unsigned char *)buffer)[i]); + snmp_pdu_add_variable(pdu, anOID, anOID_len, ASN_INTEGER, (unsigned char *)&val, sizeof(val)); + break; + case PML_DT_REAL: + case PML_DT_STRING: + case PML_DT_BINARY: + case PML_DT_NULL_VALUE: + case PML_DT_COLLECTION: + default: + snmp_pdu_add_variable(pdu, anOID, anOID_len, ASN_OCTET_STR, buffer, size); + break; + } + + + /* Send the request and get response. */ + if (snmp_synch_response(ss, pdu, &response) != STAT_SUCCESS) + goto bugout; + + if (response->errstat == SNMP_ERR_NOERROR) + { + len = size; + } + + *pml_result = SnmpErrorToPml(response->errstat); + *result = HPMUD_R_OK; + +bugout: + if (response != NULL) + snmp_free_pdu(response); + if (ss != NULL) + snmp_close(ss); + return len; +} + +int __attribute__ ((visibility ("hidden"))) GetSnmp(const char *ip, int port, const char *szoid, void *buffer, unsigned int size, int *type, int *pml_result, int *result) +{ + struct snmp_session session, *ss=NULL; + struct snmp_pdu *pdu=NULL; + struct snmp_pdu *response=NULL; + unsigned int i, len=0; + oid anOID[MAX_OID_LEN]; + size_t anOID_len = MAX_OID_LEN; + struct variable_list *vars; + uint32_t val; + unsigned char tmp[sizeof(uint32_t)]; + + *result = HPMUD_R_IO_ERROR; + *type = PML_DT_NULL_VALUE; + *pml_result = PML_EV_ERROR_UNKNOWN_REQUEST; + + init_snmp("snmpapp"); + + snmp_sess_init(&session ); /* set up defaults */ + session.peername = (char *)ip; + session.version = SNMP_VERSION_1; + session.community = (unsigned char *)SnmpPort[port]; + session.community_len = strlen((const char *)session.community); + session.retries = 2; + session.timeout = 1000000; /* 1 second */ + ss = snmp_open(&session); /* establish the session */ + if (ss == NULL) + goto bugout; + + pdu = snmp_pdu_create(SNMP_MSG_GET); + read_objid(szoid, anOID, &anOID_len); + snmp_add_null_var(pdu, anOID, anOID_len); + + /* Send the request and get response. */ + if (snmp_synch_response(ss, pdu, &response) != STAT_SUCCESS) + goto bugout; + + if (response->errstat == SNMP_ERR_NOERROR) + { + vars = response->variables; + switch (vars->type) + { + case ASN_INTEGER: + *type = PML_DT_SIGNED_INTEGER; + + /* Convert SNMP little-endian to PML big-endian byte stream. */ + len = (sizeof(uint32_t) < size) ? sizeof(uint32_t) : size; + val = *vars->val.integer; + for(i=len; i>0; i--) + { + tmp[i-1] = val & 0xff; + val >>= 8; + } + + /* Remove any in-significant bytes. */ + for (; tmp[i]==0 && i<len; i++) + ; + len -= i; + + memcpy(buffer, tmp+i, len); + break; + case ASN_NULL: + *type = PML_DT_NULL_VALUE; + break; + case ASN_OCTET_STR: + *type = PML_DT_STRING; + len = (vars->val_len < size) ? vars->val_len : size; + memcpy(buffer, vars->val.string, len); + break; + default: + BUG("unable to GetSnmp: data type=%d\n", vars->type); + goto bugout; + break; + } + } + + *pml_result = SnmpErrorToPml(response->errstat); + *result = HPMUD_R_OK; + +bugout: + if (response != NULL) + snmp_free_pdu(response); + if (ss != NULL) + snmp_close(ss); + return len; +} + +#else + +int __attribute__ ((visibility ("hidden"))) SetSnmp(const char *ip, int port, const char *szoid, int type, void *buffer, unsigned int size, int *pml_result, int *result) +{ + BUG("no JetDirect support enabled\n"); + return 0; +} + +int __attribute__ ((visibility ("hidden"))) GetSnmp(const char *ip, int port, const char *szoid, void *buffer, unsigned int size, int *type, int *pml_result, int *result) +{ + BUG("no JetDirect support enabled\n"); + return 0; +} + +#endif /* HAVE_LIBSNMP */ + +/* Set a PML object in the hp device. */ +enum HPMUD_RESULT hpmud_set_pml(HPMUD_DEVICE device, HPMUD_CHANNEL channel, const char *snmp_oid, int type, void *data, int data_size, int *pml_result) +{ + unsigned char message[HPMUD_BUFFER_SIZE]; + unsigned char oid[HPMUD_LINE_SIZE]; + char ip[HPMUD_LINE_SIZE], *psz, *tail; + unsigned char *p=message; + int len, dLen, result, reply, status, port; + struct hpmud_dstat ds; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + DBG("[%d] hpmud_set_pml() dd=%d cd=%d oid=%s type=%d data=%p size=%d\n", getpid(), device, channel, snmp_oid, type, data, data_size); + + if ((result = hpmud_get_dstat(device, &ds)) != HPMUD_R_OK) + { + stat = result; + goto bugout; + } + + if (strcasestr(ds.uri, "net/") != NULL) + { + /* Process pml via snmp. */ + + hpmud_get_uri_datalink(ds.uri, ip, sizeof(ip)); + + if ((psz = strstr(ds.uri, "port=")) != NULL) + port = strtol(psz+5, &tail, 10); + else + port = 1; + + SetSnmp(ip, port, snmp_oid, type, data, data_size, &status, &result); + if (result != HPMUD_R_OK) + { + BUG("SetPml failed ret=%d\n", result); + stat = result; + goto bugout; + } + } + else + { + /* Process pml via local transport. */ + + /* Convert snmp ascii oid to pml hex oid. */ + dLen = SnmpToPml(snmp_oid, oid, sizeof(oid)); + + *p++ = PML_SET_REQUEST; + *p++ = PML_DT_OBJECT_IDENTIFIER; + *p++ = dLen; /* assume oid length is < 10 bits */ + memcpy(p, oid, dLen); + p+=dLen; + *p = type; + *p |= data_size >> 8; /* assume data length is 10 bits */ + *(p+1) = data_size & 0xff; + p += 2; + memcpy(p, data, data_size); + + result = hpmud_write_channel(device, channel, message, dLen+data_size+3+2, HPMUD_EXCEPTION_SEC_TIMEOUT, &len); + if (result != HPMUD_R_OK) + { + BUG("SetPml channel_write failed ret=%d\n", result); + stat = result; + goto bugout; + } + + result = hpmud_read_channel(device, channel, message, sizeof(message), HPMUD_EXCEPTION_SEC_TIMEOUT, &len); + if (result != HPMUD_R_OK || len == 0) + { + BUG("SetPml channel_read failed ret=%d len=%d\n", result, len); + goto bugout; + } + + p = message; + reply = *p++; /* read command reply */ + status = *p++; /* read execution outcome */ + + if (reply != (PML_SET_REQUEST | 0x80) && status & 0x80) + { + BUG("SetPml failed reply=%x outcome=%x\n", reply, status); + DBG_DUMP(p, len-2); + goto bugout; + } + } + + *pml_result = status; + stat = HPMUD_R_OK; + + DBG("set_pml result pmlresult=%x\n", status); + +bugout: + return stat; +} + +/* Get a PML object from the hp device. */ +enum HPMUD_RESULT hpmud_get_pml(HPMUD_DEVICE device, HPMUD_CHANNEL channel, const char *snmp_oid, void *buf, int buf_size, int *bytes_read, int *type, int *pml_result) +{ + unsigned char message[HPMUD_BUFFER_SIZE]; + unsigned char oid[HPMUD_LINE_SIZE]; + char ip[HPMUD_LINE_SIZE], *psz, *tail; + unsigned char *p=message; + int len, dLen, result, reply, status, dt, port; + struct hpmud_dstat ds; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + DBG("[%d] hpmud_get_pml() dd=%d cd=%d oid=%s data=%p size=%d\n", getpid(), device, channel, snmp_oid, buf, buf_size); + + if ((result = hpmud_get_dstat(device, &ds)) != HPMUD_R_OK) + { + stat = result; + goto bugout; + } + + if (strcasestr(ds.uri, "net/") != NULL) + { + /* Process pml via snmp. */ + + hpmud_get_uri_datalink(ds.uri, ip, sizeof(ip)); + + if ((psz = strstr(ds.uri, "port=")) != NULL) + port = strtol(psz+5, &tail, 10); + else + port = 1; + + dLen = GetSnmp(ip, port, snmp_oid, message, sizeof(message), &dt, &status, &result); + if (result != HPMUD_R_OK) + { + BUG("GetPml failed ret=%d\n", result); + stat = result; + goto bugout; + } + p = message; + } + else + { + /* Process pml via local transport. */ + + /* Convert snmp ascii oid to pml hex oid. */ + dLen = SnmpToPml(snmp_oid, oid, sizeof(oid)); + + *p++ = PML_GET_REQUEST; + *p++ = PML_DT_OBJECT_IDENTIFIER; + *p++ = dLen; /* assume oid length is < 10 bits */ + memcpy(p, oid, dLen); + result = hpmud_write_channel(device, channel, message, dLen+3, HPMUD_EXCEPTION_SEC_TIMEOUT, &len); + if (result != HPMUD_R_OK) + { + BUG("GetPml channel_write failed ret=%d\n", result); + stat = result; + goto bugout; + } + + result = hpmud_read_channel(device, channel, message, sizeof(message), HPMUD_EXCEPTION_SEC_TIMEOUT, &len); + if (result != HPMUD_R_OK || len == 0) + { + BUG("GetPml channel_read failed ret=%d len=%d\n", result, len); + goto bugout; + } + + p = message; + reply = *p++; /* read command reply */ + status = *p++; /* read execution outcome */ + + if (reply != (PML_GET_REQUEST | 0x80) && status & 0x80) + { + BUG("GetPml failed reply=%x outcome=%x\n", reply, status); + DBG_DUMP(p, len-2); + goto bugout; + } + + dt = *p++; /* read data type */ + + if (dt == PML_DT_ERROR_CODE) + { + /* Ok, but invalid data type requested, get new data type. */ + p += 2; /* eat length and err code */ + dt = *p++; /* read data type */ + } + + if (dt != PML_DT_OBJECT_IDENTIFIER) + { + BUG("GetPml failed data type=%x\n", dt); + goto bugout; + } + + dLen = *p++; /* read oid length */ + p += dLen; /* eat oid */ + + dt = *p; /* read data type. */ + dLen = ((*p & 0x3) << 8 | *(p+1)); /* read 10 bit len from 2 byte field */ + p += 2; /* eat type and length */ + } + + memcpy(buf, p, dLen); + *bytes_read = dLen; + *type = dt; + *pml_result = status; + stat = HPMUD_R_OK; + + DBG("get_pml result len=%d datatype=%x pmlresult=%x\n", dLen, dt, status); + DBG_DUMP(buf, dLen); + +bugout: + return stat; +} + + diff --git a/io/hpmud/pml.h b/io/hpmud/pml.h new file mode 100644 index 0000000..188d584 --- /dev/null +++ b/io/hpmud/pml.h @@ -0,0 +1,77 @@ +/*****************************************************************************\ + + pml.h - get/set pml api for hpmud + + (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. + (c) 2003-2004 Copyright Hewlett-Packard Development Company, LP + +\*****************************************************************************/ + +#ifndef _PML_H +#define _PML_H + +/* + * PML definitions + */ + +enum PML_REQUESTS +{ + PML_GET_REQUEST = 0, + PML_GET_NEXT_REQUEST = 0x1, + PML_BLOCK_REQUEST = 0x3, + PML_SET_REQUEST = 0x4, + PML_ENABLE_TRAP_REQUEST = 0x5, + PML_DISABLE_TRAP_REQUEST = 0x6, + PML_TRAP_REQUEST = 0x7 +}; + +enum PML_ERROR_VALUES +{ + PML_EV_OK = 0, + PML_EV_OK_END_OF_SUPPORTED_OBJECTS = 0x1, + PML_EV_OK_NEAREST_LEGAL_VALUE_SUBSTITUTED = 0x2, + PML_EV_ERROR_UNKNOWN_REQUEST = 0x80, + PML_EV_ERROR_BUFFER_OVERFLOW = 0x81, + PML_EV_ERROR_COMMAND_EXECUTION_ERROR = 0x82, + PML_EV_ERROR_UNKNOWN_OBJECT_IDENTIFIER = 0x83, + PML_EV_ERROR_OBJECT_DOES_NOT_SUPPORT_REQUESTED_ACTION = 0x84, + PML_EV_ERROR_INVALID_OR_UNSUPPORTED_VALUE = 0x85, + PML_EV_ERROR_PAST_END_OF_SUPPORTED_OBJECTS = 0x86, + PML_EV_ERROR_ACTION_CAN_NOT_BE_PERFORMED_NOW = 0x87 +}; + +enum PML_DATA_TYPES +{ + PML_DT_OBJECT_IDENTIFIER = 0, + PML_DT_ENUMERATION = 0x04, + PML_DT_SIGNED_INTEGER = 0x08, + PML_DT_REAL = 0x0C, + PML_DT_STRING = 0x10, + PML_DT_BINARY = 0x14, + PML_DT_ERROR_CODE = 0x18, + PML_DT_NULL_VALUE = 0x1C, + PML_DT_COLLECTION = 0x20, + PML_DT_UNKNOWN = 0xff +}; + +int __attribute__ ((visibility ("hidden"))) GetSnmp(const char *ip, int port, const char *szoid, void *buffer, unsigned int size, int *type, int *pml_result, int *result); + +#endif // _PML_H + diff --git a/io/hpmud/pp.c b/io/hpmud/pp.c new file mode 100644 index 0000000..3b95ab7 --- /dev/null +++ b/io/hpmud/pp.c @@ -0,0 +1,1310 @@ +/*****************************************************************************\ + + pp.c - parallel port support for multi-point transport 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. + Client/Server generic message format (see messaging-protocol.doc): + +\*****************************************************************************/ + +#ifdef HAVE_PPORT + +#include "hpmud.h" +#include "hpmudi.h" + +mud_device_vf __attribute__ ((visibility ("hidden"))) pp_mud_device_vf = +{ + .read = pp_read, + .write = pp_write, + .open = pp_open, + .close = pp_close, + .get_device_id = pp_get_device_id, + .get_device_status = pp_get_device_status, + .channel_open = pp_channel_open, + .channel_close = pp_channel_close, + .channel_write = musb_channel_write, + .channel_read = musb_channel_read +}; + +static mud_channel_vf pp_raw_channel_vf = +{ + .open = pp_raw_channel_open, + .close = pp_raw_channel_close, + .channel_write = musb_raw_channel_write, + .channel_read = musb_raw_channel_read +}; + +static mud_channel_vf pp_mlc_channel_vf = +{ + .open = pp_mlc_channel_open, + .close = pp_mlc_channel_close, + .channel_write = musb_mlc_channel_write, + .channel_read = musb_mlc_channel_read +}; + +static mud_channel_vf pp_dot4_channel_vf = +{ + .open = pp_dot4_channel_open, + .close = pp_dot4_channel_close, + .channel_write = musb_dot4_channel_write, + .channel_read = musb_dot4_channel_read +}; + +static int frob_control(int fd, unsigned char mask, unsigned char val) +{ + struct ppdev_frob_struct frob; + + /* Convert ieee1284 control values to PC-style (invert Strobe, AutoFd and Select) . */ + frob.val = val ^ (mask & (PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT)); + + frob.mask = mask; + return ioctl(fd, PPFCONTROL, &frob); +} + +static unsigned char read_status(int fd) +{ + unsigned char status; + if (ioctl(fd, PPRSTATUS, &status)) + BUG("read_status error: %m\n"); + + /* Convert PC-style status values to ieee1284 (invert Busy). */ + return (status ^ PARPORT_STATUS_BUSY); +} + +static int wait_status(int fd, unsigned char mask, unsigned char val, int usec) +{ + struct timeval tmo, now; + struct timespec min; + unsigned char status; + int cnt=0; + + gettimeofday (&tmo, NULL); + tmo.tv_usec += usec; + tmo.tv_sec += tmo.tv_usec / 1000000; + tmo.tv_usec %= 1000000; + + min.tv_sec = 0; + min.tv_nsec = 5000000; /* 5ms */ + + while (1) + { + status = read_status(fd); + if ((status & mask) == val) + { + // bug("found status=%x mask=%x val=%x cnt=%d: %s %d\n", status, mask, val, cnt, __FILE__, __LINE__); + return 0; + } + cnt++; + // nanosleep(&min, NULL); + gettimeofday(&now, NULL); + if ((now.tv_sec > tmo.tv_sec) || (now.tv_sec == tmo.tv_sec && now.tv_usec > tmo.tv_usec)) + { + DBG("wait_status timeout status=%x mask=%x val=%x us=%d\n", status, mask, val, usec); + return -1; /* timeout */ + } + } +} + +static int wait(int usec) +{ + struct timeval tmo, now; + int cnt=0; + + gettimeofday (&tmo, NULL); + tmo.tv_usec += usec; + tmo.tv_sec += tmo.tv_usec / 1000000; + tmo.tv_usec %= 1000000; + + while (1) + { + cnt++; + gettimeofday(&now, NULL); + if ((now.tv_sec > tmo.tv_sec) || (now.tv_sec == tmo.tv_sec && now.tv_usec > tmo.tv_usec)) + { + return 0; /* timeout */ + } + } +} + +static int ecp_is_fwd(int fd) +{ + unsigned char status; + + status = read_status(fd); + if ((status & PARPORT_STATUS_PAPEROUT) == PARPORT_STATUS_PAPEROUT) + return 1; + return 0; +} + +static int ecp_is_rev(int fd) +{ + unsigned char status; + + status = read_status(fd); + if ((status & PARPORT_STATUS_PAPEROUT) == 0) + return 1; + return 0; +} + +static int ecp_rev_to_fwd(int fd) +{ + int dir=0; + + if (ecp_is_fwd(fd)) + return 0; + + /* Event 47: write NReverseRequest/nInit=1 */ + frob_control(fd, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT); + + /* Event 48: wait PeriphClk/nAck=1, PeriphAck/Busy=0 */ + // wait_status(fd, PARPORT_STATUS_PAPEROUT | PARPORT_STATUS_BUSY, PARPORT_STATUS_PAPEROUT, SIGNAL_TIMEOUT); + + /* Event 49: wait nAckReverse/PError=1 */ + wait_status(fd, PARPORT_STATUS_PAPEROUT, PARPORT_STATUS_PAPEROUT, PP_SIGNAL_TIMEOUT); + + ioctl(fd, PPDATADIR, &dir); + + return 0; +} + +static int ecp_fwd_to_rev(int fd) +{ + int dir=1; + + if (ecp_is_rev(fd)) + return 0; + + /* Event 33: NPeriphRequest/nFault=0, PeriphAck/Busy=0 */ + wait_status(fd, PARPORT_STATUS_BUSY | PARPORT_STATUS_ERROR, 0, PP_DEVICE_TIMEOUT); + + /* Event 38: write HostAck/nAutoFd=0 */ + ioctl(fd, PPDATADIR, &dir); + frob_control(fd, PARPORT_CONTROL_AUTOFD, 0); + wait(PP_SETUP_TIMEOUT); + + /* Event 39: write NReverseRequest/nInit=0 (start bus reversal) */ + frob_control(fd, PARPORT_CONTROL_INIT, 0); + + /* Event 40: wait nAckReverse/PError=0 */ + wait_status(fd, PARPORT_STATUS_PAPEROUT, 0, PP_SIGNAL_TIMEOUT); + + return 0; +} + +static int ecp_write_addr(int fd, unsigned char data) +{ + int cnt=0, len=0; + unsigned d=(data | 0x80); /* set channel address bit */ + + ecp_rev_to_fwd(fd); + + /* Event 33: PeriphAck/Busy=0 */ + if (wait_status(fd, PARPORT_STATUS_BUSY, 0, PP_SIGNAL_TIMEOUT)) + { + BUG("ecp_write_addr transfer stalled\n"); + goto bugout; + } + + while (1) + { + /* Event 34: write HostAck/nAutoFD=0 (channel command), data */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, 0); + ioctl(fd, PPWDATA, &d); + + /* Event 35: write HostClk/NStrobe=0 */ + frob_control(fd, PARPORT_CONTROL_STROBE, 0); + + /* Event 36: wait PeriphAck/Busy=1 */ + if (wait_status(fd, PARPORT_STATUS_BUSY, PARPORT_STATUS_BUSY, PP_SIGNAL_TIMEOUT)) + { + + /* Event 72: write NReverseRequest/nInit=0 (Host Transfer Recovery) */ + frob_control(fd, PARPORT_CONTROL_INIT, 0); + + /* Event 73: wait nAckReverse/PError=0 */ + wait_status(fd, PARPORT_STATUS_PAPEROUT, 0, PP_SIGNAL_TIMEOUT); + + /* Event 74: write NReverseRequest/nInit=1 */ + frob_control(fd, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT); + + /* Event 75: wait nAckReverse/PError=1 */ + wait_status(fd, PARPORT_STATUS_PAPEROUT, PARPORT_STATUS_PAPEROUT, PP_SIGNAL_TIMEOUT); + + cnt++; + if (cnt > 4) + { + BUG("ecp_write_addr transfer stalled\n"); + goto bugout; + } + BUG("ecp_write_addr host transfer recovery cnt=%d\n", cnt); + continue; /* retry */ + } + break; /* done */ + } /* while (1) */ + + len = 1; + +bugout: + + /* Event 37: write HostClk/NStrobe=1 */ + frob_control(fd, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); + + return len; +} + +static int ecp_write_data(int fd, unsigned char data) +{ + int cnt=0, len=0; + + // ecp_rev_to_fwd(fd); + + /* Event 33: check PeriphAck/Busy=0 */ + if (wait_status(fd, PARPORT_STATUS_BUSY, 0, PP_SIGNAL_TIMEOUT)) + { + BUG("ecp_write_data transfer stalled\n"); + goto bugout; + } + + while (1) + { + /* Event 34: write HostAck/nAutoFD=1 (channel data), data */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, PARPORT_CONTROL_AUTOFD); + ioctl(fd, PPWDATA, &data); + + /* Event 35: write HostClk/NStrobe=0 */ + frob_control(fd, PARPORT_CONTROL_STROBE, 0); + + /* Event 36: wait PeriphAck/Busy=1 */ + if (wait_status(fd, PARPORT_STATUS_BUSY, PARPORT_STATUS_BUSY, PP_SIGNAL_TIMEOUT)) + { + + /* Event 72: write NReverseRequest/nInit=0 (Host Transfer Recovery) */ + frob_control(fd, PARPORT_CONTROL_INIT, 0); + + /* Event 73: wait nAckReverse/PError=0 */ + wait_status(fd, PARPORT_STATUS_PAPEROUT, 0, PP_SIGNAL_TIMEOUT); + + /* Event 74: write NReverseRequest/nInit=1 */ + frob_control(fd, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT); + + /* Event 75: wait nAckReverse/PError=1 */ + wait_status(fd, PARPORT_STATUS_PAPEROUT, PARPORT_STATUS_PAPEROUT, PP_SIGNAL_TIMEOUT); + + cnt++; + if (cnt > 4) + { + BUG("ecp_write_data transfer stalled\n"); + goto bugout; + } + BUG("ecp_write_data host transfer recovery cnt=%d\n", cnt); + continue; /* retry */ + } + break; /* done */ + } /* while (1) */ + + len = 1; + +bugout: + + /* Event 37: write HostClk/NStrobe=1 */ + frob_control(fd, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); + + return len; +} + +static int ecp_read_data(int fd, unsigned char *data) +{ + int len=0; + + // ecp_fwd_to_rev(fd); + + /* Event 43: wait PeriphClk/NAck=0 */ + if (wait_status(fd, PARPORT_STATUS_ACK, 0, PP_SIGNAL_TIMEOUT)) + { + len = -1; + goto bugout; + } + ioctl(fd, PPRDATA, data); + + /* Event 44: write HostAck/nAutoFd=1 */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, PARPORT_CONTROL_AUTOFD); + + /* Event 45: wait PeriphClk/NAck=1 */ + wait_status(fd, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK, PP_SIGNAL_TIMEOUT); + + /* Event 46: write HostAck/nAutoFd=0 */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, 0); + + len = 1; + +bugout: + + return len; +} + +static int ecp_read(int fd, void *buffer, int size, int usec) +{ + int i=0; + unsigned char *p = (unsigned char *)buffer; + + ecp_fwd_to_rev(fd); + + while (i < size) + { + if (ecp_read_data(fd, p+i) != 1) + { + usec-=PP_SIGNAL_TIMEOUT; + if (usec > 0) + continue; + +// return -1; + return -ETIMEDOUT; /* timeout */ + } + i++; + } + return i; +} + +static int ecp_write(int fd, const void *buffer, int size) +{ + int i; + unsigned char *p = (unsigned char *)buffer; + static int timeout=0; + + if (timeout) + { + timeout=0; + return -1; /* report timeout */ + } + + ecp_rev_to_fwd(fd); + + for (i=0; i < size; i++) + { + if (ecp_write_data(fd, p[i]) != 1) + { + if (i) + timeout=1; /* save timeout, report bytes written */ + else + i=-1; /* report timeout */ + break; + } + } + return i; +} + +static int nibble_read_data(int fd, unsigned char *data) +{ + int len=0; + unsigned char nibble; + + /* Event 7: write HostBusy/nAutoFd=0 */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 8: peripheral sets low-order nibble. */ + + /* Event 9: wait PtrClk/NAck=0 */ + if (wait_status(fd, PARPORT_STATUS_ACK, 0, PP_SIGNAL_TIMEOUT)) + { + len = -1; + goto bugout; + } + nibble = read_status(fd) >> 3; + nibble = ((nibble & 0x10) >> 1) | (nibble & 0x7); + *data = nibble; + + /* Event 10: write HostBusy/nAutoFd=1 */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, PARPORT_CONTROL_AUTOFD); + + /* Event 11: wait PtrClk/NAck=1 */ + wait_status(fd, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK, PP_SIGNAL_TIMEOUT); + + /* Event 7: write HostBusy/nAutoFd=0 */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 8: peripheral sets high-order nibble. */ + + /* Event 9: wait PtrClk/NAck=0 */ + if (wait_status(fd, PARPORT_STATUS_ACK, 0, PP_SIGNAL_TIMEOUT)) + { + len = -1; + goto bugout; + } + nibble = read_status(fd) >> 3; + nibble = ((nibble & 0x10) >> 1) | (nibble & 0x7); + *data |= (nibble<<4); + + /* Event 10: write HostBusy/nAutoFd=1 */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, PARPORT_CONTROL_AUTOFD); + + /* Event 11: wait PtrClk/NAck=1 */ + wait_status(fd, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK, PP_SIGNAL_TIMEOUT); + + len = 1; + +bugout: + + return len; +} + +static int nibble_read(int fd, int flag, void *buffer, int size, int usec) +{ + int i=0; + unsigned char *p = (unsigned char *)buffer; + int m = IEEE1284_MODE_NIBBLE | flag; + int mc = IEEE1284_MODE_COMPAT; + unsigned char status; + + ioctl (fd, PPNEGOT, &mc); + if (ioctl (fd, PPNEGOT, &m)) + { + DBG("nibble_read negotiation failed: %m\n"); + return -1; + } + + while (i < size) + { + if (nibble_read_data(fd, p+i) != 1) + { + usec-=PP_SIGNAL_TIMEOUT; + if (usec > 0) + continue; + +// return -1; + return -ETIMEDOUT; /* timeout */ + } + + i++; + + /* More data? */ + status = read_status(fd); + if (status & PARPORT_STATUS_ERROR) + { + /* Event 7: write HostBusy/nAutoFd=0, idle phase */ + frob_control(fd, PARPORT_CONTROL_AUTOFD, 0); + + break; /* done */ + } + } + + return i; +} + +static int compat_write_data(int fd, unsigned char data) +{ + int len=0; + + /* wait Busy=0 */ + if (wait_status(fd, PARPORT_STATUS_BUSY, 0, PP_DEVICE_TIMEOUT)) + { + BUG("compat_write_data transfer stalled\n"); + goto bugout; + } + + ioctl(fd, PPWDATA, &data); + wait(PP_SETUP_TIMEOUT); + + /* write NStrobe=0 */ + frob_control(fd, PARPORT_CONTROL_STROBE, 0); + + /* wait Busy=1 */ + if (wait_status(fd, PARPORT_STATUS_BUSY, PARPORT_STATUS_BUSY, PP_SIGNAL_TIMEOUT)) + { + BUG("compat_write_data transfer stalled\n"); + goto bugout; + } + + /* write nStrobe=1 */ + frob_control(fd, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); + + len = 1; + +bugout: + return len; +} + +static int compat_write(int fd, const void *buffer, int size) +{ + int i=0; + unsigned char *p = (unsigned char *)buffer; + int m = IEEE1284_MODE_COMPAT; + static int timeout=0; + + if (timeout) + { + timeout=0; + return -1; /* report timeout */ + } + + if (ioctl(fd, PPNEGOT, &m)) + { + BUG("compat_write failed: %m\n"); + goto bugout; + } + + for (i=0; i < size; i++) + { + if (compat_write_data(fd, p[i]) != 1) + { + if (i) + timeout=1; /* save timeout, report bytes written */ + else + i=-1; /* report timeout */ + break; + } + } + +bugout: + return i; +} + +static int claim_pp(int fd) +{ + int stat=1; + + /* Claim parallel port (can block forever). */ + if (ioctl(fd, PPCLAIM)) + { + BUG("failed claim_pp fd=%d: %m\n", fd); + goto bugout; + } + + DBG("claimed pp fd=%d\n", fd); + + stat=0; + +bugout: + return stat; +} + +static int release_pp(int fd) +{ + int stat=1, m=IEEE1284_MODE_COMPAT; + + /* Restore compat_mode (default), otherwise close(fd) may block restoring compat_mode. */ + if (ioctl(fd, PPNEGOT, &m)) + { + BUG("failed release_pp fd=%d: %m\n", fd); + goto bugout; + } + + ioctl(fd, PPRELEASE); + + DBG("released pp fd=%d\n", fd); + + stat=0; + +bugout: + return 0; +} + +static int device_id(int fd, char *buffer, int size) +{ + int len=0, maxSize; + + maxSize = (size > 1024) ? 1024 : size; /* RH8 has a size limit for device id */ + + len = nibble_read(fd, IEEE1284_DEVICEID, buffer, maxSize, 0); + if (len < 0) + { + BUG("unable to read device-id ret=%d\n", len); + len = 0; + goto bugout; + } + if (len > (size-1)) + len = size-1; /* leave byte for zero termination */ + if (len > 2) + len -= 2; + memcpy(buffer, buffer+2, len); /* remove length */ + buffer[len]=0; + + DBG("read actual device_id successfully fd=%d len=%d\n", fd, len); + +bugout: + return len; /* length does not include zero termination */ +} + +static int device_status(int fd, unsigned int *status) +{ + int m, stat=1; + unsigned char byte = NFAULT_BIT; /* set default */ + + m = IEEE1284_MODE_COMPAT; + if (ioctl (fd, PPNEGOT, &m)) + { + BUG("unable to read device_status: %m\n"); + stat = HPMUD_R_IO_ERROR; + goto bugout; + } + byte = read_status(fd); + + *status = (unsigned int)byte; + stat = 0; + DBG("read actual device_status successfully fd=%d\n", fd); + +bugout: + return stat; +} + +/* Create channel object given the requested socket id and service name. */ +static int new_channel(mud_device *pd, int index, const char *sn) +{ + int stat=1; + + /* Check for existing name service already open. */ + if (pd->channel[index].client_cnt) + { +#if 0 + if (index == HPMUD_EWS_CHANNEL) + { + pd->channel[index].client_cnt++; /* allow multiple clients for separate USB interfaces only */ + stat = 0; + DBG("reused %s channel=%d clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].client_cnt, pd->channel_cnt); + } + else +#endif + BUG("%s channel=%d is busy, used by [%d], clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].pid, pd->channel[index].client_cnt, pd->channel_cnt); + goto bugout; + } + + if (pd->io_mode == HPMUD_RAW_MODE || pd->io_mode == HPMUD_UNI_MODE) + pd->channel[index].vf = pp_raw_channel_vf; + else if (pd->io_mode == HPMUD_MLC_GUSHER_MODE || pd->io_mode == HPMUD_MLC_MISER_MODE) + pd->channel[index].vf = pp_mlc_channel_vf; + else + pd->channel[index].vf = pp_dot4_channel_vf; + + pd->channel[index].index = index; + pd->channel[index].client_cnt = 1; + pd->channel[index].sockid = index; /* static socket id is valid for MLC but not 1284.4 */ + pd->channel[index].pid = getpid(); + pd->channel[index].dindex = pd->index; + pd->channel[index].fd = -1; + strcpy(pd->channel[index].sn, sn); + pd->channel_cnt++; + + stat = 0; + DBG("new %s channel=%d clientCnt=%d channelCnt=%d\n", sn, index, pd->channel[index].client_cnt, pd->channel_cnt); + +bugout: + return stat; +} + +/* Remove channel object given the channel decriptor. */ +static int del_channel(mud_device *pd, mud_channel *pc) +{ + pc->client_cnt--; + + if (pc->client_cnt <= 0) + { + pd->channel_cnt--; + } + DBG("removed %s channel=%d clientCnt=%d channelCnt=%d\n", pc->sn, pc->index, pc->client_cnt, pd->channel_cnt); + return 0; +} + +/********************************************************************************************************************************* + * Parallel port mud_device functions. + */ + +int __attribute__ ((visibility ("hidden"))) pp_write(int fd, const void *buf, int size, int usec) +{ + int len=0, m; + + ioctl(fd, PPGETMODE, &m); + + if (m & (IEEE1284_MODE_ECPSWE | IEEE1284_MODE_ECP)) + { + len = ecp_write(fd, buf, size); + } + else + { + len = compat_write(fd, buf, size); + } + + DBG("write fd=%d len=%d size=%d\n", fd, len, size); + DBG_DUMP(buf, len < 32 ? len : 32); + + return len; +} + +int __attribute__ ((visibility ("hidden"))) pp_read(int fd, void *buf, int size, int usec) +{ + int len=0, m; +// int sec = usec/1000000; + + ioctl(fd, PPGETMODE, &m); + + if (m & (IEEE1284_MODE_ECPSWE | IEEE1284_MODE_ECP)) + { + len = ecp_read(fd, buf, size, usec); + } + else + { + len = nibble_read(fd, 0, buf, size, usec); + } + + DBG("read fd=%d len=%d size=%d usec=%d\n", fd, len, size, usec); + DBG_DUMP(buf, len < 32 ? len : 32); + + return len; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_open(mud_device *pd) +{ + char dev[255], uriModel[128], model[128]; + int len, m, fd; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + + pthread_mutex_lock(&pd->mutex); + + hpmud_get_uri_model(pd->uri, uriModel, sizeof(uriModel)); + + if (pd->id[0] == 0) + { + /* First client, open actual kernal device, use blocking i/o. */ + hpmud_get_uri_datalink(pd->uri, dev, sizeof(dev)); + if ((fd = open(dev, O_RDWR | O_NOCTTY)) < 0) + { + BUG("unable to open %s: %m\n", pd->uri); + goto bugout; + } + + /* Open can succeed with no connected device, see if this is a valid device. */ + if (ioctl(fd, PPGETMODES, &m)) + { + BUG("unable to open %s: %m\n", pd->uri); + goto bugout; + } + + /* Claim parallel port (can block forever). */ + if (claim_pp(fd)) + goto bugout; + + len = device_id(fd, pd->id, sizeof(pd->id)); /* get new copy and cache it */ + + if (len > 0 && is_hp(pd->id)) + power_up(pd, fd); + + release_pp(fd); + + if (len == 0) + goto bugout; + + pd->open_fd = fd; + } + + /* Make sure uri model matches device id model. */ + hpmud_get_model(pd->id, model, sizeof(model)); + if (strcmp(uriModel, model) != 0) + { + stat = HPMUD_R_INVALID_DEVICE_NODE; /* probably a laserjet, or different device plugged in */ + BUG("invalid model %s != %s\n", uriModel, model); + goto bugout; + } + + stat = HPMUD_R_OK; + +bugout: + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_close(mud_device *pd) +{ + enum HPMUD_RESULT stat = HPMUD_R_OK; + + pthread_mutex_lock(&pd->mutex); + + if (pd->open_fd >=0) + close(pd->open_fd); + + pd->open_fd = -1; + pd->id[0] = 0; + + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_get_device_id(mud_device *pd, char *buf, int size, int *len) +{ + int m, fd = pd->open_fd; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + + *len=0; + + pthread_mutex_lock(&pd->mutex); + + if (fd < 0) + { + stat = HPMUD_R_INVALID_STATE; + BUG("invalid get_device_id state\n"); + goto bugout; + } + + if (pd->io_mode == HPMUD_UNI_MODE) + { + *len = strlen(pd->id); /* use cached copy */ + DBG("using cached device_id io_mode=%d\n", pd->io_mode); + } + else + { + ioctl(fd, PPGETMODE, &m); + if (m & (IEEE1284_MODE_ECPSWE | IEEE1284_MODE_ECP)) + { + *len = strlen(pd->id); /* channel is busy, return cached copy. */ + DBG("using cached device_id m=%x\n", m); + } + else + { + if (pd->channel_cnt == 0) + { + /* Device not in use. Claim it, but release for other processes. */ + if (claim_pp(fd)) + goto bugout; + *len = device_id(fd, pd->id, sizeof(pd->id)); /* get new copy */ + release_pp(fd); + } + else + { + /* Device already claimed by open_channel. */ + *len = device_id(fd, pd->id, sizeof(pd->id)); /* get new copy */ + } + } + } + + if (*len) + { + memcpy(buf, pd->id, *len > size ? size : *len); + stat = HPMUD_R_OK; + } + +bugout: + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_get_device_status(mud_device *pd, unsigned int *status) +{ + int fd=pd->open_fd; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + int m, r=0; + + pthread_mutex_lock(&pd->mutex); + + if (fd < 0) + { + stat = HPMUD_R_INVALID_STATE; + BUG("invalid get_device_id state\n"); + goto bugout; + } + + if (pd->io_mode == HPMUD_UNI_MODE) + { + *status = NFAULT_BIT; /* fake status */ + DBG("using cached device_status io_mode=%d\n", pd->io_mode); + } + else + { + ioctl(fd, PPGETMODE, &m); + if (m & (IEEE1284_MODE_ECPSWE | IEEE1284_MODE_ECP)) + { + *status = NFAULT_BIT; /* channel is busy, fake 8-bit status */ + DBG("using cached device_status m=%x\n", m); + } + else + { + if (pd->channel_cnt == 0) + { + /* Device not in use. Claim it, but release for other processes. */ + if (claim_pp(fd)) + goto bugout; + r = device_status(fd, status); + release_pp(fd); + } + else + { + /* Device already claimed by open_channel. */ + r = device_status(fd, status); + } + } + } + + if (r != 0) + goto bugout; + + stat = HPMUD_R_OK; + +bugout: + pthread_mutex_unlock(&pd->mutex); + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_channel_open(mud_device *pd, const char *sn, HPMUD_CHANNEL *cd) +{ + int index; + enum HPMUD_RESULT stat = HPMUD_R_DEVICE_BUSY; + + /* Check for valid service requests. */ + if ((stat = service_to_channel(pd, sn, &index)) != HPMUD_R_OK) + goto bugout; + + pthread_mutex_lock(&pd->mutex); + + if (new_channel(pd, index, sn)) + { + stat = HPMUD_R_DEVICE_BUSY; + } + else + { + if ((stat = (pd->channel[index].vf.open)(&pd->channel[index])) != HPMUD_R_OK) /* call transport specific open */ + del_channel(pd, &pd->channel[index]); /* open failed, cleanup */ + else + *cd = index; + } + + pthread_mutex_unlock(&pd->mutex); + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_channel_close(mud_device *pd, mud_channel *pc) +{ + enum HPMUD_RESULT stat = HPMUD_R_OK; + + pthread_mutex_lock(&pd->mutex); + stat = (pc->vf.close)(pc); /* call trasport specific close */ + del_channel(pd, pc); + pthread_mutex_unlock(&pd->mutex); + + return stat; +} + +/******************************************************************************************************************************* + * Parallel port raw_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_raw_channel_open(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + if (claim_pp(pd->open_fd)) + return HPMUD_R_IO_ERROR; + pc->fd = pd->open_fd; + return HPMUD_R_OK; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_raw_channel_close(mud_channel *pc) +{ + if (pc->fd >= 0) + release_pp(pc->fd); + pc->fd = -1; + return HPMUD_R_OK; +} + +/******************************************************************************************************************************* + * Parallel port mlc_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_mlc_channel_open(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + int i, m; + + /* Initialize MLC transport if this is the first MLC channel. */ + if (pd->channel_cnt==1) + { + if (claim_pp(pd->open_fd)) + goto bugout; + + /* Negotiate ECP mode. */ + m = IEEE1284_MODE_ECPSWE; + if (ioctl(pd->open_fd, PPNEGOT, &m)) + { + BUG("unable to negotiate %s ECP mode: %m\n", pd->uri); + goto bugout; + } + + /* Enable MLC mode with ECP channel-77. */ + ecp_write_addr(pd->open_fd, 78); + ecp_write(pd->open_fd, "\0", 1); + ecp_write_addr(pd->open_fd, 77); + + /* MLC initialize */ + if (MlcInit(pc, pd->open_fd) != 0) + goto bugout; + + /* Reset transport attributes for all channels. */ + for (i=0; i<HPMUD_CHANNEL_MAX; i++) + memset(&pd->channel[i].ta, 0 , sizeof(transport_attributes)); + + pd->mlc_fd = pd->open_fd; + pd->mlc_up=1; + + } /* if (pDev->ChannelCnt==1) */ + + if (MlcConfigSocket(pc, pd->mlc_fd)) + goto bugout; + + if (MlcOpenChannel(pc, pd->mlc_fd)) + goto bugout; + + pc->rcnt = pc->rindex = 0; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_mlc_channel_close(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_OK; + int m; + + if (pd->mlc_up) + { + if (MlcCloseChannel(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + + /* Remove MLC transport if this is the last MLC channel. */ + if (pd->channel_cnt==1) + { + if (pd->mlc_up) + { + if (MlcExit(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + pd->mlc_up=0; + + ecp_write_addr(pd->mlc_fd, 78); /* disable MLC mode with ECP channel-78 */ + ecp_write(pd->mlc_fd, "\0", 1); + + m = IEEE1284_MODE_NIBBLE; + ioctl(pd->mlc_fd, PPNEGOT, &m); + release_pp(pd->mlc_fd); + + /* Delay for batch scanning. */ + sleep(1); + } + + return stat; +} + +/******************************************************************************************************************************* + * Parallel port dot4_channel functions. + */ + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_dot4_channel_open(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + int i, m; + + /* Initialize MLC transport if this is the first MLC channel. */ + if (pd->channel_cnt==1) + { + if (claim_pp(pd->open_fd)) + goto bugout; + + /* Negotiate ECP mode. */ + m = IEEE1284_MODE_ECPSWE; + if (ioctl(pd->open_fd, PPNEGOT, &m)) + { + BUG("unable to negotiate %s ECP mode: %m\n", pd->uri); + goto bugout; + } + + /* Enable MLC mode with ECP channel-77. */ + ecp_write_addr(pd->open_fd, 78); + ecp_write(pd->open_fd, "\0", 1); + ecp_write_addr(pd->open_fd, 77); + + /* DOT4 initialize */ + if (Dot4Init(pc, pd->open_fd) != 0) + goto bugout; + + /* Reset transport attributes for all channels. */ + for (i=0; i<HPMUD_CHANNEL_MAX; i++) + memset(&pd->channel[i].ta, 0 , sizeof(transport_attributes)); + + pd->mlc_fd = pd->open_fd; + pd->mlc_up=1; + + } /* if (pDev->ChannelCnt==1) */ + + if (Dot4GetSocket(pc, pd->mlc_fd)) + goto bugout; + + if (Dot4OpenChannel(pc, pd->mlc_fd)) + goto bugout; + + pc->rcnt = pc->rindex = 0; + + stat = HPMUD_R_OK; + +bugout: + return stat; +} + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_dot4_channel_close(mud_channel *pc) +{ + mud_device *pd = &msp->device[pc->dindex]; + enum HPMUD_RESULT stat = HPMUD_R_OK; + int m; + + if (pd->mlc_up) + { + if (Dot4CloseChannel(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + + /* Remove MLC transport if this is the last MLC channel. */ + if (pd->channel_cnt==1) + { + if (pd->mlc_up) + { + if (Dot4Exit(pc, pd->mlc_fd)) + stat = HPMUD_R_IO_ERROR; + } + pd->mlc_up=0; + + ecp_write_addr(pd->mlc_fd, 78); /* disable MLC mode with ECP channel-78 */ + ecp_write(pd->mlc_fd, "\0", 1); + + m = IEEE1284_MODE_NIBBLE; + ioctl(pd->mlc_fd, PPNEGOT, &m); + release_pp(pd->mlc_fd); + + /* Delay for batch scanning. */ + sleep(1); + } + + return stat; +} + +/******************************************************************************************************************************* + * Parallel port probe devices, walk the parallel port bus(s) looking for HP products. + */ + +int __attribute__ ((visibility ("hidden"))) pp_probe_devices(char *lst, int lst_size, int *cnt) +{ + struct hpmud_model_attributes ma; + char dev[HPMUD_LINE_SIZE]; + char rmodel[128]; + char model[128]; + char id[1024]; + int i, size=0, fd, m; + + for (i=0; i < 4; i++) + { + sprintf(dev, "/dev/parport%d", i); + + if ((fd = open(dev, O_RDONLY | O_NOCTTY)) < 0) + continue; + + /* Silently check the port for valid device (no syslog errors). */ + if (ioctl(fd, PPGETMODES, &m) == 0) + { + if (claim_pp(fd) == 0) + { + if (device_id(fd, id, sizeof(id)) > 0 && is_hp(id)) + { + hpmud_get_model(id, model, sizeof(model)); + hpmud_get_raw_model(id, rmodel, sizeof(rmodel)); + snprintf(dev, sizeof(dev), "hp:/par/%s?device=/dev/parport%d", model, i); + + /* See if device is supported by hplip. */ + hpmud_query_model(dev, &ma); + if (ma.support != HPMUD_SUPPORT_TYPE_HPLIP) + { + BUG("ignoring %s support=%d\n", dev, ma.support); + continue; /* ignor, not supported */ + } + + if (strncasecmp(rmodel, "hp ", 3) == 0) + size += sprintf(lst+size,"direct %s \"HP %s\" \"HP %s LPT parport%d HPLIP\" \"%s\"\n", dev, &rmodel[3], &rmodel[3], i, id); + else + size += sprintf(lst+size,"direct %s \"HP %s\" \"HP %s LPT parport%d HPLIP\" \"%s\"\n", dev, rmodel, rmodel, i, id); + *cnt+=1; + } + release_pp(fd); + } + else + { + BUG("unable to probe %s: %m\n", dev); /* device is busy */ + } + } + close(fd); + } + return size; +} + +enum HPMUD_RESULT hpmud_make_par_uri(const char *dnode, char *uri, int uri_size, int *bytes_read) +{ + char model[128]; + char id[1024]; + enum HPMUD_RESULT stat = HPMUD_R_IO_ERROR; + int fd=-1, m; + + DBG("[%d] hpmud_make_par_uri() dnode=%s\n", getpid(), dnode); + + *bytes_read=0; + + uri[0]=0; + + if ((fd = open(dnode, O_RDONLY | O_NOCTTY)) < 0) + { + BUG("unable to open %s: %m\n", dnode); + goto bugout; + } + + if (ioctl(fd, PPGETMODES, &m)) + { + BUG("unable to make uri %s: %m\n", dnode); + goto bugout; + } + + if (claim_pp(fd)) + { + BUG("unable to make uri %s: %m\n", dnode); /* device is busy */ + goto bugout; + } + + if (device_id(fd, id, sizeof(id)) > 0 && is_hp(id)) + { + hpmud_get_model(id, model, sizeof(model)); + *bytes_read = snprintf(uri, uri_size, "hp:/par/%s?device=%s", model, dnode); + } + release_pp(fd); + + stat = HPMUD_R_OK; + +bugout: + if (fd >= 0) + close(fd); + return stat; +} + +#endif /* HAVE_PPORT */ diff --git a/io/hpmud/pp.h b/io/hpmud/pp.h new file mode 100644 index 0000000..5a5198f --- /dev/null +++ b/io/hpmud/pp.h @@ -0,0 +1,100 @@ +/*****************************************************************************\ + + pp.h - parallel port support for multi-point transport 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. + +\*****************************************************************************/ + +#ifndef _PP_H +#define _PP_H + +#include <sys/ioctl.h> +#include <linux/parport.h> +#include <linux/ppdev.h> +#include "hpmud.h" +#include "hpmudi.h" + +/* + * PC-style parallel port bit definitions. + * + * Status + * bit + * 7 - Busy * + * 6 - NAck + * 5 - PError (PARPORT_STATUS_PAPEROUT) + * 4 - Select + * + * 3 - NFault (PARPORT_STATUS_ERROR) + * 2 - + * 1 - + * 0 - + * + * Control + * bit + * 7 - + * 6 - + * 5 - + * 4 - + * + * 3 - Select * + * 2 - Init + * 1 - AutoFD * + * 0 - Strobe * + * + * * inverted + * + * Notes: + * For ECP mode use low-level parport ioctl instead of high-level parport read/writes because its more reliable. High-level support + * for Compatible and Nibble modes are probably ok, but for consistency low-level parport ioctl is used. + * + */ + +#define PP_DEVICE_TIMEOUT 30000000 /* device timeout (us) */ +//#define PP_SIGNAL_TIMEOUT 1000000 /* signal timeout (us), too long for 1ms timeout, DES 8/18/08 */ +//#define PP_SIGNAL_TIMEOUT 1000 /* signal timeout (us), too short for DJ540, DES 8/18/08 */ +#define PP_SIGNAL_TIMEOUT 100000 /* signal timeout (us), DES 8/18/08 */ +#define PP_SETUP_TIMEOUT 10 /* setup timeout (us) */ + +struct _mud_device; +struct _mud_channel; + +extern struct _mud_device_vf __attribute__ ((visibility ("hidden"))) pp_mud_device_vf; + +int __attribute__ ((visibility ("hidden"))) pp_write(int fd, const void *buf, int size, int usec); +int __attribute__ ((visibility ("hidden"))) pp_read(int fd, void *buf, int size, int usec); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_open(struct _mud_device *pd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_close(struct _mud_device *pd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_get_device_id(struct _mud_device *pd, char *buf, int size, int *len); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_get_device_status(struct _mud_device *pd, unsigned int *status); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_channel_open(struct _mud_device *pd, const char *sn, HPMUD_CHANNEL *cd); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_channel_close(struct _mud_device *pd, struct _mud_channel *pc); + +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_raw_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_raw_channel_close(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_mlc_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_mlc_channel_close(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_dot4_channel_open(struct _mud_channel *pc); +enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) pp_dot4_channel_close(struct _mud_channel *pc); + +int __attribute__ ((visibility ("hidden"))) pp_probe_devices(char *lst, int lst_size, int *cnt); + +#endif // _PP_H + diff --git a/io/mudext/hpmudext.c b/io/mudext/hpmudext.c new file mode 100644 index 0000000..e387501 --- /dev/null +++ b/io/mudext/hpmudext.c @@ -0,0 +1,495 @@ +/*****************************************************************************\ + hpmudext - Python extension for HP multi-point transport driver (HPMUD) + + (c) Copyright 2010 Hewlett-Packard Development Company, L.P. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Requires: +Python 2.2+ + +Authors: Don Welch, David Suffield, Naga Samrat Chowdary Narla + +\*****************************************************************************/ + +#include <Python.h> +#include "hpmud.h" +#include "hpmudi.h" + +/* Ref: PEP 353 (Python 2.5) */ +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + +/* +HPMUDEXT API: + +result_code, dd = open_device(uri, io_mode) + +result_code = close_device(dd) + +result_code, data = get_device_id(dd) + +result_code, data = probe_devices(bus) + +result_code, cd = open_channel(dd, channel_name) + +result_code = close_channel(dd, cd) + +result_code, bytes_written = write_channel(dd, cd, data, [timeout]) + +result_code, data = read_channel(dd, cd, bytes_to_read, [timeout]) + +result_code, pml_result_code = set_pml(dd, cd, oid, type, data) + +result_code, data, pml_result_code = get_pml(dd, cd, oid, type) + +result_code, uri = make_usb_uri(busnum, devnum) + +result_code, uri = make_net_uri(ip, port) + +result_code, uri = make_zc_uri(ip, port) + +result_code, ip_address = get_zc_ip_address(hostname) + +result_code, uri = make_par_uri(devnode) + +*/ + + + static PyObject *open_device(PyObject *self, PyObject *args) +{ + char * uri; + enum HPMUD_IO_MODE io_mode; + HPMUD_DEVICE dd; + enum HPMUD_RESULT result = HPMUD_R_OK; + + if (!PyArg_ParseTuple(args, "si", &uri, &io_mode)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_open_device(uri, io_mode, &dd); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(ii)", result, dd); +} + +static PyObject *close_device(PyObject *self, PyObject *args) +{ + HPMUD_DEVICE dd; + enum HPMUD_RESULT result = HPMUD_R_OK; + + if (!PyArg_ParseTuple(args, "i", &dd)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_close_device(dd); + Py_END_ALLOW_THREADS + + return Py_BuildValue("i", result); +} + +static PyObject *get_device_id(PyObject *self, PyObject *args) +{ + HPMUD_DEVICE dd; + enum HPMUD_RESULT result = HPMUD_R_OK; + char buf[HPMUD_BUFFER_SIZE]; + int bytes_read = 0; + + if (!PyArg_ParseTuple(args, "i", &dd)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_get_device_id(dd, buf, HPMUD_BUFFER_SIZE, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, buf, bytes_read); + +} + +static PyObject *probe_devices(PyObject *self, PyObject *args) +{ + enum HPMUD_BUS_ID bus; + enum HPMUD_RESULT result = HPMUD_R_OK; + int cnt = 0; + int bytes_read = 0; + char buf[HPMUD_BUFFER_SIZE * 4]; + + if (!PyArg_ParseTuple(args, "i", &bus)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_probe_devices(bus, buf, HPMUD_BUFFER_SIZE * 4, &cnt, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, buf, bytes_read); +} + +static PyObject *open_channel(PyObject *self, PyObject *args) +{ + enum HPMUD_RESULT result = HPMUD_R_OK; + HPMUD_DEVICE dd = -1; + char * channel_name; + HPMUD_CHANNEL cd = -1; + + if (!PyArg_ParseTuple(args, "is", &dd, &channel_name)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_open_channel(dd, channel_name, &cd); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(ii)", result, cd); + +} + +static PyObject *close_channel(PyObject *self, PyObject *args) +{ + enum HPMUD_RESULT result = HPMUD_R_OK; + HPMUD_DEVICE dd; + HPMUD_CHANNEL cd; + + if (!PyArg_ParseTuple(args, "ii", &dd, &cd)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_close_channel(dd, cd); + Py_END_ALLOW_THREADS + + return Py_BuildValue("i", result); +} + +static PyObject *write_channel(PyObject *self, PyObject *args) +{ + enum HPMUD_RESULT result = HPMUD_R_OK; + HPMUD_DEVICE dd; + HPMUD_CHANNEL cd; + int timeout = 30; + char * buf; + int buf_size = 0; + int bytes_written = 0; + + if (!PyArg_ParseTuple(args, "iis#|i", &dd, &cd, &buf, &buf_size, &timeout)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_write_channel(dd, cd, buf, buf_size, timeout, &bytes_written); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(ii)", result, bytes_written); +} + +static PyObject *read_channel(PyObject *self, PyObject *args) +{ + enum HPMUD_RESULT result = HPMUD_R_OK; + HPMUD_DEVICE dd; + HPMUD_CHANNEL cd; + int timeout = 30; + char buf[HPMUD_BUFFER_SIZE]; + int bytes_read = 0; + int bytes_to_read; + + if (!PyArg_ParseTuple(args, "iii|i", &dd, &cd, &bytes_to_read, &timeout)) + return NULL; + + if (bytes_to_read > HPMUD_BUFFER_SIZE) + return Py_BuildValue("(is#)", HPMUD_R_INVALID_LENGTH, "", 0); + + Py_BEGIN_ALLOW_THREADS + result = hpmud_read_channel(dd, cd, (void *)buf, bytes_to_read, timeout, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, buf, bytes_read); +} + +static PyObject *set_pml(PyObject *self, PyObject *args) +{ + enum HPMUD_RESULT result = HPMUD_R_OK; + HPMUD_DEVICE dd; + HPMUD_CHANNEL cd; + char * oid; + int type; + char * data; + int data_size; + int pml_result; + + if (!PyArg_ParseTuple(args, "iisis#", &dd, &cd, &oid, &type, &data, &data_size)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_set_pml(dd, cd, oid, type, (void *)data, data_size, &pml_result); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(ii)", result, pml_result); +} + +static PyObject *get_pml(PyObject *self, PyObject *args) +{ + enum HPMUD_RESULT result = HPMUD_R_OK; + HPMUD_DEVICE dd; + HPMUD_CHANNEL cd; + char * oid; + int type; + char buf[HPMUD_BUFFER_SIZE * 4]; + int pml_result; + int bytes_read = 0; + + if (!PyArg_ParseTuple(args, "iisi", &dd, &cd, &oid, &type)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_get_pml(dd, cd, oid, (void *)buf, HPMUD_BUFFER_SIZE * 4, &bytes_read, &type, &pml_result); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#ii)", result, buf, bytes_read, type, pml_result); +} + +static PyObject *make_usb_uri(PyObject *self, PyObject *args) +{ + char * busnum; + char * devnum; + char uri[HPMUD_BUFFER_SIZE]; + enum HPMUD_RESULT result = HPMUD_R_OK; + int bytes_read = 0; + + if (!PyArg_ParseTuple(args, "ss", &busnum, &devnum)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_make_usb_uri(busnum, devnum, uri, HPMUD_BUFFER_SIZE, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, uri, bytes_read); +} + +#ifdef HAVE_LIBNETSNMP +static PyObject *make_net_uri(PyObject *self, PyObject *args) +{ + char * ip; + int port; + char uri[HPMUD_BUFFER_SIZE]; + enum HPMUD_RESULT result = HPMUD_R_OK; + int bytes_read = 0; + + if (!PyArg_ParseTuple(args, "si", &ip, &port)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_make_net_uri(ip, port, uri, HPMUD_BUFFER_SIZE, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, uri, bytes_read); +} +#else +static PyObject *make_net_uri(PyObject *self, PyObject *args) +{ + return Py_BuildValue("(is#)", HPMUD_R_INVALID_URI, "", 0); +} +#endif /* HAVE_LIBSNMP */ + +#ifdef HAVE_LIBNETSNMP +static PyObject *make_zc_uri(PyObject *self, PyObject *args) +{ + char *hn; + int port; + char uri[HPMUD_BUFFER_SIZE]; + enum HPMUD_RESULT result = HPMUD_R_OK; + int bytes_read = 0; + + if (!PyArg_ParseTuple(args, "si", &hn, &port)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_make_mdns_uri(hn, port, uri, HPMUD_BUFFER_SIZE, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, uri, bytes_read); +} +#else +static PyObject *make_zc_uri(PyObject *self, PyObject *args) +{ + return Py_BuildValue("(is#)", HPMUD_R_INVALID_URI, "", 0); +} +#endif /* HAVE_LIBSNMP */ + +#ifdef HAVE_LIBNETSNMP +static PyObject *get_zc_ip_address(PyObject *self, PyObject *args) +{ + char *hn; + char ip[HPMUD_BUFFER_SIZE]; + enum HPMUD_RESULT result = HPMUD_R_OK; + + if (!PyArg_ParseTuple(args, "s", &hn)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_mdns_lookup(hn, HPMUD_MDNS_TIMEOUT, ip); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is)", result, ip); +} +#else +static PyObject *get_zc_ip_address(PyObject *self, PyObject *args) +{ + return Py_BuildValue("(is)", HPMUD_R_INVALID_URI, ""); +} +#endif /* HAVE_LIBSNMP */ + +#ifdef HAVE_PPORT +static PyObject *make_par_uri(PyObject *self, PyObject *args) +{ + char * devnode; + char uri[HPMUD_BUFFER_SIZE]; + enum HPMUD_RESULT result = HPMUD_R_OK; + int bytes_read = 0; + + if (!PyArg_ParseTuple(args, "s", &devnode)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + result = hpmud_make_par_uri(devnode, uri, HPMUD_BUFFER_SIZE, &bytes_read); + Py_END_ALLOW_THREADS + + return Py_BuildValue("(is#)", result, uri, bytes_read); +} +#else +static PyObject *make_par_uri(PyObject *self, PyObject *args) +{ + return Py_BuildValue("(is#)", HPMUD_R_INVALID_URI, "", 0); +} +#endif /* HAVE_PPORT */ + +// Unwrapped MUD APIs: +// int hpmud_get_model(const char *id, char *buf, int buf_size); +// int hpmud_get_uri_model(const char *uri, char *buf, int buf_size); +// int hpmud_get_uri_datalink(const char *uri, char *buf, int buf_size); +// enum HPMUD_RESULT hpmud_get_model_attributes(char *uri, char *attr, int attrSize); +// enum HPMUD_RESULT hpmud_model_query(char *uri, struct hpmud_model_attributes *ma); +// enum HPMUD_RESULT hpmud_get_device_status(HPMUD_DEVICE dd, unsigned int *status); +// enum HPMUD_RESULT hpmud_get_dstat(HPMUD_DEVICE dd, struct hpmud_dstat *ds); + + +static PyMethodDef mudext_functions[] = +{ + {"open_device", (PyCFunction)open_device, METH_VARARGS }, + {"close_device", (PyCFunction)close_device, METH_VARARGS }, + {"get_device_id", (PyCFunction)get_device_id, METH_VARARGS }, + {"probe_devices", (PyCFunction)probe_devices, METH_VARARGS }, + {"open_channel", (PyCFunction)open_channel, METH_VARARGS }, + {"write_channel", (PyCFunction)write_channel, METH_VARARGS }, + {"read_channel", (PyCFunction)read_channel, METH_VARARGS }, + {"close_channel", (PyCFunction)close_channel, METH_VARARGS }, + {"set_pml", (PyCFunction)set_pml, METH_VARARGS }, + {"get_pml", (PyCFunction)get_pml, METH_VARARGS }, + {"make_usb_uri", (PyCFunction)make_usb_uri, METH_VARARGS }, + {"make_net_uri", (PyCFunction)make_net_uri, METH_VARARGS }, + {"make_zc_uri", (PyCFunction)make_zc_uri, METH_VARARGS }, + {"get_zc_ip_address", (PyCFunction)get_zc_ip_address, METH_VARARGS }, + {"make_par_uri", (PyCFunction)make_par_uri, METH_VARARGS }, + { NULL, NULL } +}; + + +static char mudext_documentation[] = "Python extension for HP multi-point transport driver"; + +static void insint(PyObject *d, char *name, int value) +{ + PyObject *v = PyInt_FromLong((long) value); + + if (!v || PyDict_SetItemString(d, name, v)) + Py_FatalError("Initialization failed."); + + Py_DECREF(v); +} + +static void insstr(PyObject *d, char *name, char *value) +{ + PyObject *v = PyString_FromString(value); + + if (!v || PyDict_SetItemString(d, name, v)) + Py_FatalError("Initialization failed."); + + Py_DECREF(v); +} + + +void inithpmudext(void) +{ + PyObject *mod = Py_InitModule3("hpmudext", mudext_functions, mudext_documentation); + + if (mod == NULL) + return; + + PyObject * d = PyModule_GetDict(mod); + + // enum HPMUD_RESULT + insint(d, "HPMUD_R_OK", HPMUD_R_OK); + insint(d, "HPMUD_R_INVALID_DEVICE", HPMUD_R_INVALID_DEVICE); + insint(d, "HPMUD_R_INVALID_DESCRIPTOR", HPMUD_R_INVALID_DESCRIPTOR); + insint(d, "HPMUD_R_INVALID_URI", HPMUD_R_INVALID_URI); + insint(d, "HPMUD_R_INVALID_LENGTH", HPMUD_R_INVALID_LENGTH); + insint(d, "HPMUD_R_IO_ERROR", HPMUD_R_IO_ERROR); + insint(d, "HPMUD_R_DEVICE_BUSY", HPMUD_R_DEVICE_BUSY); + insint(d, "HPMUD_R_INVALID_SN", HPMUD_R_INVALID_SN); + insint(d, "HPMUD_R_INVALID_CHANNEL_ID", HPMUD_R_INVALID_CHANNEL_ID); + insint(d, "HPMUD_R_INVALID_STATE", HPMUD_R_INVALID_STATE); + insint(d, "HPMUD_R_INVALID_DEVICE_OPEN", HPMUD_R_INVALID_DEVICE_OPEN); + insint(d, "HPMUD_R_INVALID_DEVICE_NODE", HPMUD_R_INVALID_DEVICE_NODE); + insint(d, "HPMUD_R_INVALID_IP", HPMUD_R_INVALID_IP); + insint(d, "HPMUD_R_INVALID_IP_PORT", HPMUD_R_INVALID_IP_PORT); + insint(d, "HPMUD_R_INVALID_TIMEOUT", HPMUD_R_INVALID_TIMEOUT); + insint(d, "HPMUD_R_DATFILE_ERROR", HPMUD_R_DATFILE_ERROR); + insint(d, "HPMUD_R_IO_TIMEOUT", HPMUD_R_IO_TIMEOUT); + + // enum HPMUD_IO_MODE + insint(d, "HPMUD_UNI_MODE", HPMUD_UNI_MODE); + insint(d, "HPMUD_RAW_MODE",HPMUD_RAW_MODE); + insint(d, "HPMUD_DOT4_MODE", HPMUD_DOT4_MODE); + insint(d, "HPMUD_DOT4_PHOENIX_MODE", HPMUD_DOT4_PHOENIX_MODE); + insint(d, "HPMUD_DOT4_BRIDGE_MODE", HPMUD_DOT4_BRIDGE_MODE); + insint(d, "HPMUD_MLC_GUSHER_MODE", HPMUD_MLC_GUSHER_MODE); + insint(d, "HPMUD_MLC_MISER_MODE", HPMUD_MLC_MISER_MODE); + + // enum HPMUD_BUS_ID + insint(d, "HPMUD_BUS_NA", HPMUD_BUS_NA); + insint(d, "HPMUD_BUS_USB", HPMUD_BUS_USB); + insint(d, "HPMUD_BUS_PARALLEL", HPMUD_BUS_PARALLEL); + insint(d, "HPMUD_BUS_ALL", HPMUD_BUS_ALL); + + // Channel names + insstr(d, "HPMUD_S_PRINT_CHANNEL", HPMUD_S_PRINT_CHANNEL); + insstr(d, "HPMUD_S_PML_CHANNEL", HPMUD_S_PML_CHANNEL); + insstr(d, "HPMUD_S_SCAN_CHANNEL", HPMUD_S_SCAN_CHANNEL); + insstr(d, "HPMUD_S_FAX_SEND_CHANNEL", HPMUD_S_FAX_SEND_CHANNEL); + insstr(d, "HPMUD_S_CONFIG_UPLOAD_CHANNEL", HPMUD_S_CONFIG_UPLOAD_CHANNEL); + insstr(d, "HPMUD_S_CONFIG_DOWNLOAD_CHANNEL", HPMUD_S_CONFIG_DOWNLOAD_CHANNEL); + insstr(d, "HPMUD_S_MEMORY_CARD_CHANNEL", HPMUD_S_MEMORY_CARD_CHANNEL); + insstr(d, "HPMUD_S_EWS_CHANNEL", HPMUD_S_EWS_CHANNEL); + insstr(d, "HPMUD_S_EWS_LEDM_CHANNEL", HPMUD_S_EWS_LEDM_CHANNEL); + insstr(d, "HPMUD_S_SOAP_SCAN", HPMUD_S_SOAP_SCAN); + insstr(d, "HPMUD_S_SOAP_FAX", HPMUD_S_SOAP_FAX); + insstr(d, "HPMUD_S_DEVMGMT_CHANNEL", HPMUD_S_DEVMGMT_CHANNEL); + insstr(d, "HPMUD_S_WIFI_CHANNEL", HPMUD_S_WIFI_CHANNEL); + insstr(d, "HPMUD_S_MARVELL_FAX_CHANNEL", HPMUD_S_MARVELL_FAX_CHANNEL); + insstr(d, "HPMUD_S_LEDM_SCAN", HPMUD_S_LEDM_SCAN); + + // Max buffer size + insint(d, "HPMUD_BUFFER_SIZE", HPMUD_BUFFER_SIZE); + +} + + |