1 /*****************************************************************************\
3 dot4.c - 1284.4 support multi-point tranport driver
5 (c) 2005-2007 Copyright Hewlett-Packard Development Company, LP
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11 of the Software, and to permit persons to whom the Software is furnished to do
12 so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 \*****************************************************************************/
30 * This 1284.4 implementation does not support "Multiple Outstanding Transactions" which is optional.
33 /* Write command reply back to peripheral. */
34 static int Dot4ForwardReply(mud_channel *pc, int fd, unsigned char *buf, int size)
36 mud_device *pd = &msp->device[pc->dindex];
39 if ((len = (pd->vf.write)(fd, buf, size, HPMUD_EXCEPTION_TIMEOUT)) != size)
41 BUG("unable to Dot4ForwarReply: %m\n");
46 /* Execute command from peripheral. */
47 static int Dot4ExecReverseCmd(mud_channel *pc, int fd, unsigned char *buf)
49 mud_device *pd = &msp->device[pc->dindex];
50 mud_channel *out_of_bound_channel;
54 DOT4CreditReply *pCreditReply;
55 DOT4CreditRequest *pCreditReq;
56 DOT4CreditRequestReply *pCreditReqReply;
62 pCmd = (DOT4Cmd *)buf;
64 /* See if this packet is a command packet. */
65 if (!(pCmd->h.psid == 0 && pCmd->h.ssid == 0))
67 if (pCmd->h.psid == pCmd->h.ssid)
69 /* Got a valid data packet handle it. This can happen when channel_read timeouts and p2hcredit=1. */
70 out_of_bound_channel = &pd->channel[pCmd->h.psid];
72 if (out_of_bound_channel->ta.p2hcredit <= 0)
74 BUG("invalid data packet credit=%d\n", out_of_bound_channel->ta.p2hcredit);
78 size = ntohs(pCmd->h.length) - sizeof(DOT4Header);
79 if (size > (HPMUD_BUFFER_SIZE - out_of_bound_channel->rcnt))
81 BUG("invalid data packet size=%d\n", size);
84 memcpy(&out_of_bound_channel->rbuf[out_of_bound_channel->rcnt], buf+sizeof(MLCHeader), size);
85 out_of_bound_channel->rcnt += size;
87 out_of_bound_channel->ta.h2pcredit += pCmd->h.credit; /* note, piggy back credit is 1 byte wide */
88 out_of_bound_channel->ta.p2hcredit--; /* one data packet was read, decrement credit count */
92 len = ntohs(pCmd->h.length);
93 BUG("unsolicited data packet: psid=%x, ssid=%x, length=%d, credit=%d, status=%x\n", pCmd->h.psid,
94 pCmd->h.ssid, len, pCmd->h.credit, pCmd->h.control);
100 /* Process any command. */
104 pCredit = (DOT4Credit *)buf;
105 out_of_bound_channel = &pd->channel[pCredit->psocket];
106 out_of_bound_channel->ta.h2pcredit += ntohs(pCredit->credit);
107 pCreditReply = (DOT4CreditReply *)buf;
108 pCreditReply->h.length = htons(sizeof(DOT4CreditReply));
109 pCreditReply->h.credit = 1; /* transaction credit for next command */
110 pCreditReply->h.control = 0;
111 pCreditReply->cmd |= 0x80;
112 pCreditReply->result = 0;
113 pCreditReply->psocket = out_of_bound_channel->sockid;
114 pCreditReply->ssocket = out_of_bound_channel->sockid;
115 Dot4ForwardReply(pc, fd, (unsigned char *)pCreditReply, sizeof(DOT4CreditReply));
117 case DOT4_CREDIT_REQUEST:
118 pCreditReq = (DOT4CreditRequest *)buf;
120 BUG("unexpected DOT4CreditRequest: cmd=%x, hid=%x, pid=%x, maxcredit=%d\n", pCreditReq->cmd,
121 pCreditReq->psocket, pCreditReq->ssocket, ntohs(pCreditReq->maxcredit));
122 socket = pCreditReq->ssocket;
123 pCreditReqReply = (DOT4CreditRequestReply *)buf;
124 pCreditReqReply->h.length = htons(sizeof(DOT4CreditRequestReply));
125 pCreditReqReply->h.credit = 1; /* transaction credit for next command */
126 pCreditReqReply->h.control = 0;
127 pCreditReqReply->cmd |= 0x80;
128 pCreditReqReply->result = 0;
129 pCreditReqReply->psocket = socket;
130 pCreditReqReply->ssocket = socket;
131 pCreditReqReply->credit = 0;
132 Dot4ForwardReply(pc, fd, (unsigned char *)pCreditReqReply, sizeof(DOT4CreditRequestReply));
135 pError = (DOT4Error *)buf;
136 BUG("unexpected DOT4Error: cmd=%x, psocket=%d, ssocket=%d, error=%x\n", pError->cmd, pError->psocket, pError->ssocket, pError->error);
139 pReply = (DOT4Reply *)buf;
140 BUG("unexpected command: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
141 pReply->h.length = htons(sizeof(DOT4Reply));
142 pReply->h.credit = 1; /* transaction credit for next command */
143 pReply->h.control = 0;
146 Dot4ForwardReply(pc, fd, (unsigned char *)pReply, sizeof(DOT4Reply));
152 /* Get command from peripheral and processes the reverse command. */
153 int __attribute__ ((visibility ("hidden"))) Dot4ReverseCmd(mud_channel *pc, int fd)
155 mud_device *pd = &msp->device[pc->dindex];
156 unsigned char buf[HPMUD_BUFFER_SIZE];
157 int stat=0, len, size;
162 pPk = (DOT4Reply *)buf;
166 /* Read packet header. */
167 size = sizeof(DOT4Header);
170 if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
172 BUG("unable to read Dot4ReverseCmd header: %m\n");
180 /* Determine packet size. */
181 if ((pklen = ntohs(pPk->h.length)) > sizeof(buf))
183 BUG("invalid Dot4ReverseCmd packet size: size=%d\n", pklen);
188 /* Read packet data field. */
189 size = pklen - sizeof(DOT4Header);
192 if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
194 BUG("unable to read Dot4ReverseCmd data: %m exp=%zd act=%zd\n", pklen-sizeof(DOT4Header), pklen-sizeof(DOT4Header)-size);
202 stat = Dot4ExecReverseCmd(pc, fd, buf);
209 * Get command reply from peripheral. Waits for reply then returns. Processes any reverse commands
210 * while waiting for a reply.
212 static int Dot4ReverseReply(mud_channel *pc, int fd, unsigned char *buf, int bufsize)
214 mud_device *pd = &msp->device[pc->dindex];
215 int stat=0, len, size, pklen;
219 pPk = (DOT4Reply *)buf;
225 /* Read packet header. */
226 size = sizeof(DOT4Header);
229 if ((len = (pd->vf.read)(fd, pBuf, size, 4000000)) < 0) /* wait 4 seconds, 2 fails on PS2575 1200dpi uncompressed scanning */
231 BUG("unable to read Dot4ReverseReply header: %m bytesRead=%zd\n", sizeof(DOT4Header)-size);
232 stat = 2; /* short timeout */
239 /* Determine packet size. */
240 pklen = ntohs(pPk->h.length);
241 if (pklen <= 0 || pklen > bufsize)
243 BUG("invalid Dot4ReverseReply packet size: size=%d, buf=%d\n", pklen, bufsize);
248 /* Read packet data field. */
249 size = pklen - sizeof(DOT4Header);
252 if ((len = (pd->vf.read)(fd, pBuf, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
254 BUG("unable to read Dot4ReverseReply data: %m exp=%zd act=%zd\n", pklen-sizeof(DOT4Header), pklen-sizeof(DOT4Header)-size);
262 /* Check for reply. */
266 stat = Dot4ExecReverseCmd(pc, fd, buf);
277 int __attribute__ ((visibility ("hidden"))) Dot4Init(mud_channel *pc, int fd)
279 mud_device *pd = &msp->device[pc->dindex];
280 unsigned char buf[HPMUD_BUFFER_SIZE];
281 int stat=0, len, n, cnt;
283 DOT4InitReply *pReply;
285 memset(buf, 0, sizeof(DOT4Init));
286 pCmd = (DOT4Init *)buf;
287 n = sizeof(DOT4Init);
288 pCmd->h.length = htons(n);
289 pCmd->h.credit = 1; /* transaction credit for reply */
290 pCmd->cmd = DOT4_INIT;
293 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
295 BUG("unable to write DOT4Init: %m\n");
303 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
304 pReply = (DOT4InitReply *)buf;
306 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_INIT)) || (pReply->result != 0))
308 if (errno == EIO && cnt<1)
310 /* hack for usblp.c 2.6.5 */
311 BUG("invalid DOT4InitReply retrying...\n");
316 if (stat == 2 && cnt<1)
318 /* hack for Fullhouse, Swami and Northstar */
319 BUG("invalid DOT4InitReply retrying command...\n");
320 memset(buf, 0, sizeof(DOT4Init));
321 n = sizeof(DOT4Init);
322 pCmd->h.length = htons(n);
323 pCmd->h.credit = 1; /* transaction credit for reply */
324 pCmd->cmd = DOT4_INIT;
326 (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT);
330 BUG("invalid DOT4InitReply: cmd=%x, result=%x\n, revision=%x\n", pReply->cmd, pReply->result, pReply->rev);
341 int __attribute__ ((visibility ("hidden"))) Dot4Exit(mud_channel *pc, int fd)
343 mud_device *pd = &msp->device[pc->dindex];
344 unsigned char buf[HPMUD_BUFFER_SIZE];
347 DOT4ExitReply *pReply;
349 memset(buf, 0, sizeof(DOT4Exit));
350 pCmd = (DOT4Exit *)buf;
351 n = sizeof(DOT4Exit);
352 pCmd->h.length = htons(n);
354 pCmd->cmd = DOT4_EXIT;
356 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
358 BUG("unable to write DOT4Exit: %m\n");
363 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
364 pReply = (DOT4ExitReply *)buf;
366 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_EXIT)) || (pReply->result != 0))
368 BUG("invalid DOT4ExitReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
377 int __attribute__ ((visibility ("hidden"))) Dot4GetSocket(mud_channel *pc, int fd)
379 mud_device *pd = &msp->device[pc->dindex];
380 unsigned char buf[HPMUD_BUFFER_SIZE];
383 DOT4GetSocketReply *pReply;
385 memset(buf, 0, sizeof(DOT4GetSocket));
386 pCmd = (DOT4GetSocket *)buf;
387 n = sizeof(DOT4GetSocket);
388 len = strlen(pc->sn);
389 memcpy(buf+sizeof(DOT4GetSocket), pc->sn, len);
391 pCmd->h.length = htons(n);
393 pCmd->cmd = DOT4_GET_SOCKET;
395 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
397 BUG("unable to write DOT4GetSocket: %m\n");
402 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
403 pReply = (DOT4GetSocketReply *)buf;
405 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_GET_SOCKET)) || (pReply->result != 0))
407 BUG("invalid DOT4GetSocketReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
412 pc->sockid = pReply->socket;
414 if (pc->sockid != pc->index)
415 BUG("invalid sockid match sockid=%d index=%d\n", pc->sockid, pc->index);
421 /* Write data to peripheral. */
422 int __attribute__ ((visibility ("hidden"))) Dot4ForwardData(mud_channel *pc, int fd, const void *buf, int size, int usec_timeout)
424 mud_device *pd = &msp->device[pc->dindex];
428 memset(&h, 0, sizeof(h));
429 n = sizeof(DOT4Header) + size;
434 if ((len = (pd->vf.write)(fd, &h, sizeof(DOT4Header), usec_timeout)) != sizeof(DOT4Header))
436 BUG("unable to write Dot4ForwardData header: %m\n");
441 if ((len = (pd->vf.write)(fd, buf, size, usec_timeout)) != size)
443 BUG("unable to write Dot4ForwardData: %m\n");
452 /* Read data from peripheral. */
453 int __attribute__ ((visibility ("hidden"))) Dot4ReverseData(mud_channel *pc, int fd, void *buf, int length, int usec_timeout)
455 mud_device *pd = &msp->device[pc->dindex];
456 mud_channel *out_of_bound_channel;
457 int len, size, total;
460 pPk = (DOT4Header *)buf;
466 /* Read packet header. */
467 size = sizeof(DOT4Header);
470 /* Use requested client timeout until we start reading. */
472 len = (pd->vf.read)(fd, buf+total, size, usec_timeout);
474 len = (pd->vf.read)(fd, buf+total, size, HPMUD_EXCEPTION_TIMEOUT);
478 /* Got a timeout, if exception timeout or timeout occured after read started thats an error. */
479 if (usec_timeout >= HPMUD_EXCEPTION_TIMEOUT || total > 0)
480 BUG("unable to read Dot4ReverseData header: %m %s\n", pd->uri);
487 /* Determine data size. */
488 size = ntohs(pPk->length) - sizeof(DOT4Header);
492 BUG("invalid Dot4ReverseData size: size=%d, buf=%d\n", size, length);
496 /* Make sure data packet is for this channel. */
497 if (pPk->psid != pc->sockid && pPk->ssid != pc->sockid)
499 if (pPk->psid == 0 && pPk->ssid == 0)
501 /* Ok, got a command channel packet instead of a data packet, handle it... */
504 if ((len = (pd->vf.read)(fd, buf+total, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
506 BUG("unable to read Dot4ReverseData command: %m\n");
512 Dot4ExecReverseCmd(pc, fd, buf);
513 continue; /* try again for data packet */
515 else if (pPk->psid == pPk->ssid)
517 /* Got a valid data packet for another channel handle it. This can happen when ReadData timeouts and p2hcredit=1. */
518 out_of_bound_channel = &pd->channel[pPk->psid];
521 if (out_of_bound_channel->ta.p2hcredit <= 0)
523 BUG("invalid data packet credit=%d\n", out_of_bound_channel->ta.p2hcredit);
527 if (size > (HPMUD_BUFFER_SIZE - out_of_bound_channel->rcnt))
529 BUG("invalid data packet size=%d\n", size);
534 pBuf = &out_of_bound_channel->rbuf[out_of_bound_channel->rcnt];
537 if ((len = (pd->vf.read)(fd, pBuf+total, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
539 BUG("unable to read MlcReverseData: %m\n");
546 out_of_bound_channel->rcnt += total;
548 out_of_bound_channel->ta.h2pcredit += pPk->credit; /* note, piggy back credit is 1 byte wide */
549 out_of_bound_channel->ta.p2hcredit--; /* one data packet was read, decrement credit count */
550 continue; /* try again for data packet */
554 DOT4Cmd *pCmd = (DOT4Cmd *)buf;
555 BUG("invalid Dot4ReverseData state: unexpected packet psid=%x, ssid=%x, cmd=%x\n", pPk->psid, pPk->ssid, pCmd->cmd);
562 pc->ta.h2pcredit += pPk->credit; /* note, piggy back credit is 1 byte wide */
565 total = 0; /* eat packet header */
567 /* Read packet data field with exception_timeout. */
570 if ((len = (pd->vf.read)(fd, buf+total, size, HPMUD_EXCEPTION_TIMEOUT)) < 0)
572 BUG("unable to read Dot4ReverseData: %m\n");
578 break; /* done reading data packet */
585 int __attribute__ ((visibility ("hidden"))) Dot4OpenChannel(mud_channel *pc, int fd)
587 mud_device *pd = &msp->device[pc->dindex];
588 unsigned char buf[HPMUD_BUFFER_SIZE];
590 DOT4OpenChannel *pCmd;
591 DOT4OpenChannelReply *pReply;
593 memset(buf, 0, sizeof(DOT4OpenChannel));
594 pCmd = (DOT4OpenChannel *)buf;
595 n = sizeof(DOT4OpenChannel);
596 pCmd->h.length = htons(n);
598 pCmd->cmd = DOT4_OPEN_CHANNEL;
599 pCmd->psocket = pc->sockid;
600 pCmd->ssocket = pc->sockid;
601 pCmd->maxp2s = htons(HPMUD_BUFFER_SIZE); /* max primary to secondary packet size in bytes */
602 pCmd->maxs2p = htons(HPMUD_BUFFER_SIZE); /* max secondary to primary packet size in bytes */
603 pCmd->maxcredit = htons(0xffff); /* "unlimited credit" mode, give primary (sender) as much credit as possible */
605 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
607 BUG("unable to write Dot4OpenChannel: %m\n");
612 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
613 pReply = (DOT4OpenChannelReply *)buf;
615 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_OPEN_CHANNEL)) || (pReply->result != 0))
617 BUG("invalid Dot4OpenChannelReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
622 pc->ta.h2psize = ntohs(pReply->maxp2s);
623 pc->ta.p2hsize = ntohs(pReply->maxs2p);
624 pc->ta.h2pcredit = ntohs(pReply->credit);
630 int __attribute__ ((visibility ("hidden"))) Dot4CloseChannel(mud_channel *pc, int fd)
632 mud_device *pd = &msp->device[pc->dindex];
633 unsigned char buf[HPMUD_BUFFER_SIZE];
635 DOT4CloseChannel *pCmd;
636 DOT4CloseChannelReply *pReply;
638 memset(buf, 0, sizeof(DOT4CloseChannel));
639 pCmd = (DOT4CloseChannel *)buf;
640 n = sizeof(DOT4CloseChannel);
641 pCmd->h.length = htons(n);
643 pCmd->cmd = DOT4_CLOSE_CHANNEL;
644 pCmd->psocket = pc->sockid;
645 pCmd->ssocket = pc->sockid;
647 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
649 BUG("unable to write Dot4CloseChannel: %m\n");
654 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
655 pReply = (DOT4CloseChannelReply *)buf;
657 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_CLOSE_CHANNEL)) || (pReply->result != 0))
659 BUG("invalid Dot4CloseChannelReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
668 int __attribute__ ((visibility ("hidden"))) Dot4Credit(mud_channel *pc, int fd, unsigned short credit)
670 mud_device *pd = &msp->device[pc->dindex];
671 unsigned char buf[HPMUD_BUFFER_SIZE];
674 DOT4CreditReply *pReply;
676 memset(buf, 0, sizeof(DOT4Credit));
677 pCmd = (DOT4Credit *)buf;
678 n = sizeof(DOT4Credit);
679 pCmd->h.length = htons(n);
681 pCmd->cmd = DOT4_CREDIT;
682 pCmd->psocket = pc->sockid;
683 pCmd->ssocket = pc->sockid;
684 pCmd->credit = htons(credit); /* set peripheral to host credit */
686 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
688 BUG("unable to write Dot4Credit: %m\n");
693 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
694 pReply = (DOT4CreditReply *)buf;
696 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_CREDIT)) || (pReply->result != 0))
698 BUG("invalid Dot4CreditReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
703 pc->ta.p2hcredit += credit;
709 int __attribute__ ((visibility ("hidden"))) Dot4CreditRequest(mud_channel *pc, int fd, unsigned short credit)
711 mud_device *pd = &msp->device[pc->dindex];
712 unsigned char buf[HPMUD_BUFFER_SIZE];
714 DOT4CreditRequest *pCmd;
715 DOT4CreditRequestReply *pReply;
717 memset(buf, 0, sizeof(DOT4CreditRequest));
718 pCmd = (DOT4CreditRequest *)buf;
719 n = sizeof(DOT4CreditRequest);
720 pCmd->h.length = htons(n);
722 pCmd->cmd = DOT4_CREDIT_REQUEST;
723 pCmd->psocket = pc->sockid;
724 pCmd->ssocket = pc->sockid;
725 // pCmd->maxcredit = htons(credit); /* request host to peripheral credit */
726 pCmd->maxcredit = htons(0xffff); /* request host to peripheral credit */
728 if ((len = (pd->vf.write)(fd, pCmd, n, HPMUD_EXCEPTION_TIMEOUT)) != n)
730 BUG("unable to write Dot4CreditRequest: %m\n");
735 stat = Dot4ReverseReply(pc, fd, buf, sizeof(buf));
736 pReply = (DOT4CreditRequestReply *)buf;
738 if ((stat != 0) || (pReply->cmd != (0x80 | DOT4_CREDIT_REQUEST)) || (pReply->result != 0))
740 BUG("invalid Dot4CreditRequestReply: cmd=%x, result=%x\n", pReply->cmd, pReply->result);
745 pc->ta.h2pcredit += ntohs(pReply->credit);