1/* d4lib.c
2 * Copyright (C) 2001 Jean-Jacques Sarton jj.sarton@t-online.de
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19/* this file will be a library, which will allow to use the EPSON
20 * Stylus Scanner and also allow to send commands to the
21 * Stylus Color 480/580.
22 *
23 * At this stage a lot of things are not implemented and the
24 * first goal is to get the Stylus Scanner printing.
25 * This may be reached if this file is compiled with the TEST
26 * option set.
27 *
28 * I don��t own a Stylus Scanner and I am not able to test this
29 * code, as desired. Printing on a Stylus Photo 1290 work fine
30 * with this.
31 *
32 * The best way to get the Stylus Scanner working is to test
33 * this and also correct my possibly errors.
34 * Programming knowledge will be helpfull for this.
35 *
36 */
37
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <signal.h>
44#include <fcntl.h>
45#include <sys/time.h>
46#include <unistd.h>
47#include <errno.h>
48#include <string.h>
49#include <ctype.h>
50
51#include "d4lib.h"
52
53
54#ifndef RDTIMEOUT
55#define RDTIMEOUT     10000
56#define WRTIMEOUT     10000
57#define RDDATATIMEOUT 1000
58#define MICROTIMEOUT  1
59#endif
60
61int d4WrTimeout     = WRTIMEOUT;
62int d4RdTimeout     = RDTIMEOUT;
63int d4RdDataTimeout = RDDATATIMEOUT;
64int d4MicroTimeout  = 1;
65int ppid        = 0;
66
67int debugD4     = 1;
68
69typedef void (*signalHandler_t)(int);
70static signalHandler_t sig;
71static int timeoutGot = 0;
72static int _readData(int fd, unsigned char *buf, int len);
73
74static int d4Errno = 0;
75
76/* commands for the D4 protocol
77
78Transaction    Cmd    Reply
79-------------- ----   -----
80Init           0x00   0x80
81OpenChannel    0x01   0x81
82CloseChannel   0x02   0x82
83Credit         0x03   0x83
84CreditRequest  0x04   0x84
85Exit           0x08   0x88
86GetSocketID    0x09   0x89
87GetServiceName 0x0a   0x8a
88Error          0x7f   -
89
90*/
91
92typedef struct cmdHeader_s
93{
94   unsigned char psid;
95   unsigned char ssid;
96   unsigned char lengthH;
97   unsigned char lengthL;
98   unsigned char credit;
99   unsigned char control;
100   unsigned char command;
101} cmdHeader_t;
102
103typedef struct replyHeader_s
104{
105   unsigned char psid;
106   unsigned char ssid;
107   unsigned char lengthH;
108   unsigned char lengthL;
109   unsigned char credit;
110   unsigned char control;
111   unsigned char command;
112   unsigned char result;
113} replyHeader_t;
114
115typedef struct init_s
116{
117   cmdHeader_t head;
118   unsigned char          revision;
119} init_t;
120
121typedef struct initReply_s
122{
123   replyHeader_t head;
124   unsigned char            revision;
125} initReply_t;
126
127typedef struct error_s
128{
129   replyHeader_t head;
130   unsigned char            epsid;
131   unsigned char            essid;
132   unsigned char            ecode;
133} error_t;
134
135/* results */
136typedef struct errorMessage_s
137{
138   unsigned char    result;
139   const char *message;
140   int   errorClass;
141}  errorMessage_t;
142
143#define RECOVERABLE 0
144#define FATAL       1
145
146static errorMessage_t errorMessage[] =
147{
148   { 0x01, "Unable to begin conversation, try later."              ,0 },
149   { 0x02, "Protocol revision not supported."                      ,1 },
150   { 0x03, "Transaction channel can��t be closed."                  ,0 },
151   { 0x04, "No sufficient resources available now."                ,0 },
152   { 0x05, "Connection denied."                                    ,1 },
153   { 0x06, "Channel allready open."                                ,0 },
154   { 0x07, "Credit overflow, previous credit remain valid."        ,0 },
155   { 0x08, "Channel is not open."                                  ,1 },
156   { 0x09, "Service not available on specified socket."            ,1 },
157   { 0x0a, "Service name to socket ID failed."                     ,1 },
158   { 0x0b, "Init transaction failed."                              ,1 },
159   { 0x0c, "Invalid packet size."                                  ,1 },
160   { 0x0d, "Requested packed size is 0, no data can be transfered.",0 },
161   { 0x80, "Malformed packet, ignored."                            ,1 },
162   { 0x81, "No credit for received packet, ignored"                ,0 },
163   { 0x82, "Reply don��t match with outstanding command, ignored."  ,1 },
164   { 0x83, "Packet size greater as negotiated size."               ,1 },
165   { 0x84, "Data received for a non opened channel."               ,1 },
166   { 0x85, "Reply with unknown result value received."             ,1 },
167   { 0x86, "Piggybacked credit in data packet cause overflow."     ,1 },
168   { 0x87, "Unknown 1284.4 Reply."                                 ,0 },
169   { 0x00, NULL                                                    ,0 }
170};
171
172#define RESET_TIMER(ti,oti) { signal(SIGALRM, sig); \
173                              memset((void*)&ti,0,sizeof(ti)); \
174                              memset((void*)&oti,0,sizeof(oti)); \
175                              setitimer( ITIMER_REAL ,&ti, &oti); \
176                            }
177
178#define SET_TIMER(ti,oti,val) { memset((void*)&ti,0,sizeof(ti)); \
179                                memset((void*)&oti,0,sizeof(oti)); \
180                                ti.it_value.tv_sec  = val/1000; \
181                                ti.it_value.tv_usec = (val%1000)*1000; \
182                                setitimer( ITIMER_REAL ,&ti, &oti); \
183                                sig = signal(SIGALRM, sigAlarm); \
184                              }
185
186/*******************************************************************/
187/* Function printHexValues                                         */
188/*                                                                 */
189/* Print hex code contained in the passed buffer                   */
190/*                                                                 */
191/*******************************************************************/
192
193static void printHexValues(const char *dir, const unsigned char *buf, int len)
194{
195   int i, j;
196   int printable_count = 0;
197   int longest_printable_run = 0;
198   int current_printable_run = 0;
199   int print_strings = 0;
200   int blocks = (len + 15) / 16;
201#if 0
202   len = len > 30 ? 30 : len;
203#endif
204   printf("%s\n",dir);
205   for (i = 0; i < len; i++)
206     {
207       if (isprint(buf[i]))
208	 {
209	   if (!isspace(buf[i]))
210	     printable_count++;
211	   current_printable_run++;
212	 }
213       else
214	 {
215	   if (current_printable_run > longest_printable_run)
216	     longest_printable_run = current_printable_run;
217	 }
218     }
219   if (current_printable_run > longest_printable_run)
220     longest_printable_run = current_printable_run;
221   if (longest_printable_run >= 8 ||
222       ((float) printable_count / (float) len > .75))
223     print_strings = 1;
224   if (print_strings)
225     {
226       for (i = 0; i < len; i++)
227	 {
228	   printf("%c",isprint(buf[i])||isspace(buf[i])?buf[i]:'*');
229	   if (buf[i] == ';' && i < len - 1)
230	     printf("\n");
231	 }
232       printf("\n");
233     }
234   for (j = 0; j < blocks; j++)
235     {
236       int baseidx = j * 16;
237       int count = len;
238       if (count > baseidx + 16)
239	 count =  baseidx + 16;
240       printf("%4d: ", baseidx);
241       for ( i = baseidx; i < count;i++)
242	 {
243	   if (i % 4 == 0)
244	     printf(" ");
245	   printf(" %02x",buf[i]);
246	 }
247       if (print_strings)
248	 {
249	   printf("\n      ");
250	   for ( i = baseidx; i < count;i++)
251	     {
252	       if (i % 4 == 0)
253		 printf(" ");
254	       printf("  %c",
255		       isprint(buf[i]) && !isspace(buf[i]) ? buf[i] : ' ');
256	     }
257	 }
258       printf("\n");
259     }
260}
261
262int SafeWrite(int fd, const void *data, int len)
263{
264  int status;
265  int retries=30;
266  if (debugD4)
267    printHexValues("SafeWrite: ", data, len);
268  do
269    {
270      status = write(fd, data, len);
271      if(status < len)
272	usleep(d4WrTimeout);
273      retries--;
274    }
275  while ((status < len) && (retries > 0));
276  return(status);
277}
278
279/*******************************************************************/
280/* Function sigAlarm(int code)                                     */
281/*                                                                 */
282/* do nothing, avoid printing of undesired messages and/or         */
283/* other not desirable actions                                     */
284/*                                                                 */
285/*******************************************************************/
286
287static void sigAlarm(int code)
288{
289   timeoutGot = -1;
290}
291
292
293/*******************************************************************/
294/* Function printError()                                           */
295/*    print an error message on stdout                             */
296/*                                                                 */
297/* Input:  unsigned char errorNb the error number                  */
298/*                                                                 */
299/* Return: fatal = 1 or recoverable = 0                            */
300/*                                                                 */
301/*******************************************************************/
302
303static int printError(unsigned char errorNb)
304{
305   errorMessage_t *msg = errorMessage;
306   if ( errorNb == 0 )
307   {
308      return 0;
309   }
310   while (  msg->result )
311   {
312      if ( msg->result == errorNb )
313      {
314	 if (debugD4)
315	   printf("%s\n", msg->message);
316         return msg->errorClass;
317      }
318      msg++;
319   }
320   printf("Unknown IEEE 1284.4 error number %d\n",errorNb);
321   return 0;
322}
323
324
325/*******************************************************************/
326/* Function printCmdType()                                         */
327/*        print on stdout the command name                         */
328/* Input:  unsigned char *cmd   the data are to be put here        */
329/*                                                                 */
330/* Return: -                                                       */
331/*                                                                 */
332/*******************************************************************/
333
334static void printCmdType(unsigned char *cmd)
335{
336   if (cmd[6] & 0x80)
337     printf(">>>");
338   if ( cmd[0] == 0 && cmd[1] == 0 )
339   {
340      switch(cmd[6] & 0x7f)
341      {
342         case    0: printf("--- Init           ---\n");break;
343         case    1: printf("--- OpenChannel    ---\n");break;
344         case    2: printf("--- CloseChannel   ---\n");break;
345         case    3: printf("--- Credit         ---\n");break;
346         case    4: printf("--- CreditRequest  ---\n");break;
347         case    8: printf("--- Exit           ---\n");break;
348         case    9: printf("--- GetSocketID    ---\n");break;
349         case   10: printf("--- GetServiceName ---\n");break;
350         case 0x45: printf("--- EnterD4Mode    ---\n");break;
351         case 0x7f: printf("--- Error          ---\n");break;
352         default:   printf("--- ?????????????? ---\n");break;
353      }
354   }
355   else
356   {
357      printf("--- Send Data      ---\n");
358   }
359}
360
361
362/*******************************************************************/
363/* Function writeCmd()                                             */
364/*        write a commmand                                         */
365/* Input:  int   fd    file handle                                 */
366/*         char *cmd   the data are to be put here                 */
367/*         int   len   the number of bytes to read                 */
368/*                                                                 */
369/* Return: number of bytes write or -1                             */
370/*                                                                 */
371/*******************************************************************/
372
373static int writeCmd(int fd, unsigned char *cmd, int len)
374{
375   int w;
376   int i = 0;
377   struct  itimerval ti, oti;
378
379# if PTIME
380   struct timeval beg, end;
381   long dt;
382# endif
383   if ( debugD4 )
384   {
385      printCmdType(cmd);
386# if PTIME
387      gettimeofday(&beg, NULL);
388# endif
389      if ( cmd[0] == 0 && cmd[1] == 0 )
390      {
391         printHexValues("Send: ", cmd, len);
392      }
393      else
394      {
395         printHexValues("Send: ", cmd, 6);
396      }
397   }
398
399   /* according to Glen Steward, this will solve problems  */
400   /* for the cartridge exchange with the Stylus Color 580 */
401   usleep(d4MicroTimeout);
402
403   timeoutGot = 0;
404   errno = 0;
405   while ( i < len )
406   {
407      SET_TIMER(ti,oti,d4WrTimeout);
408      w = SafeWrite(fd, cmd+i,len-i);
409      RESET_TIMER(ti,oti);
410      if ( w < 0 )
411      {
412         if ( debugD4 )
413         {
414	   perror("Write error");
415         }
416         i= -1;
417         break;
418      }
419      else
420         i += w;
421   }
422
423   if ( debugD4 )
424   {
425# if PTIME
426      gettimeofday(&end, NULL);
427      dt = (end.tv_sec  - beg.tv_sec) * 1000000;
428      dt += end.tv_usec - beg.tv_usec;
429      printf("Write time %5.3f s\n",(double)dt/1000000);
430# endif
431   }
432
433   if ( timeoutGot )
434      return -1;
435   return i;
436}
437
438/*******************************************************************/
439/* Function readAnswer()                                           */
440/*        Read the datas returned by the printer                   */
441/* Input:  int   fd    file handle                                 */
442/*         char *buf   the data are to be put here                 */
443/*         int   len   the number of bytes to read                 */
444/*                                                                 */
445/* Return: number of bytes read. -1 on error                       */
446/*                                                                 */
447/*******************************************************************/
448
449int readAnswer(int fd, unsigned char *buf, int len, int allowExtra)
450{
451   int rd    = 0;
452   int total = 0;
453   struct timeval beg, end;
454   struct itimerval ti, oti;
455   long dt;
456   int count = 0;
457   int first_read = 1;
458   int excess = 0;
459   /* wait a little bit before reading an answer */
460   usleep(d4RdTimeout);
461
462   /* for error handling in case of timeout */
463   timeoutGot = 0;
464
465   /* set errno to 0 in order to get correct informations */
466   /* in case of error                                    */
467   errno = 0;
468
469   gettimeofday(&beg, NULL);
470
471   if (debugD4)
472     printf("length: %i\n", len);
473   while ( total < len )
474   {
475      SET_TIMER(ti,oti, d4RdTimeout);
476      rd = read(fd, buf+total, len-total);
477      if (debugD4)
478	{
479	  if (first_read)
480	    {
481	      printf("read: ");
482	      first_read = 0;
483	    }
484	  if (rd < 0)
485	    {
486	      printf("%i %s\n", rd, errno != 0 ?strerror(errno) : "");
487	      first_read = 1;
488	    }
489	  else
490	    printf("%i ", rd);
491	}
492      RESET_TIMER(ti,oti);
493      if ( rd <= 0 )
494      {
495         gettimeofday(&end, NULL);
496         dt  = (end.tv_sec  - beg.tv_sec) * 1000;
497         dt += (end.tv_usec - beg.tv_usec) / 1000;
498         if ( dt > d4RdTimeout * 2 )
499         {
500            if ( debugD4 )
501               printf("Timeout 1 at readAnswer() rcv %d bytes\n",total);
502            timeoutGot = 1;
503            break;
504         }
505         count++;
506         if ( count >= 100 )
507         {
508             timeoutGot = 1;
509             if ( rd == 0 )
510                errno = -1; /* tell that there is an abnormal condition */
511             break;
512         }
513         errno = 0;
514      } else {
515         total += rd;
516         if ( total > 3 )
517         {
518            /* the bytes idx 2 and 3 contain the length */
519            /* in case of errors this may differ from   */
520            /* the expected lenght. Setting len to this */
521            /* value will avoid waiting for timeout     */
522	    int newlen = (buf[2] << 8) + buf[3];
523	    if (len > newlen)
524	      {
525		if (debugD4)
526		  printf("Changing len from %d to %d\n", len, newlen);
527		len = newlen;
528	      }
529	    else if (len < newlen)
530	      {
531		excess = newlen - len;
532		if (debugD4)
533		  printf("Expected %d, getting %d, %sflushing %d\n",
534			  len, newlen, allowExtra ? "not " : "", excess);
535	      }
536         }
537      }
538      usleep(d4RdTimeout);
539   }
540   if (! allowExtra)
541     {
542       int retry_count = 0;
543       while (excess > 0)
544	 {
545	   char wastebuf[256];
546	   int bytes = excess > 256 ? 256 : excess;
547	   int status = read(fd, wastebuf, bytes);
548	   if (status < 0)
549	     break;
550	   else if (status == 0 && retry_count > 2)
551	     break;
552	   else if (status == 0)
553	     retry_count++;
554	   else
555	     retry_count = 0;
556	   if (status < bytes)
557	     usleep(d4RdTimeout);
558	   if (debugD4)
559	     printHexValues("waste", (const unsigned char *) wastebuf, status);
560	   excess -= status;
561	 }
562     }
563
564   if ( debugD4 )
565   {
566      printCmdType(buf);
567#  if PTIME
568      gettimeofday(&end, NULL);
569#  endif
570      printf("total: %i\n", total);
571      printHexValues("Recv: ",buf,total);
572#  if PTIME
573      dt = (end.tv_sec  - beg.tv_sec) * 1000000;
574      dt += end.tv_usec - beg.tv_usec;
575      printf("Read time %5.3f s\n",(double)dt/1000000);
576#  endif
577   }
578   if ( timeoutGot )
579   {
580      if ( debugD4 )
581         printf("Timeout 2 at readAnswer()\n");
582      return -1;
583   }
584   return total;
585}
586
587static void _flushData(int fd)
588{
589   int rd    = 0;
590   struct itimerval ti, oti;
591   char buf[1024];
592   int len = 1023;
593   int count = 200;
594   usleep(d4RdTimeout);
595
596   /* for error handling in case of timeout */
597   timeoutGot = 0;
598
599   /* set errno to 0 in order to get correct informations */
600   /* in case of error                                    */
601   errno = 0;
602
603   if (debugD4)
604     printf("flush data: length: %i\n", len);
605   do
606     {
607       usleep(d4RdTimeout);
608       SET_TIMER(ti,oti, d4RdTimeout);
609       rd = read(fd, buf, len);
610       if (debugD4)
611	 printf("flush: read: %i %s\n", rd,
612		 rd < 0 && errno != 0 ?strerror(errno) : "");
613       RESET_TIMER(ti,oti);
614       count--;
615     } while ( count > 0 && (rd > 0 || (rd < 0 && errno == EAGAIN)));
616}
617
618/*******************************************************************/
619/* Function _readData()                                            */
620/*        Read the datas returned by the printer                   */
621/* Input:  int   fd    file handle                                 */
622/*         char *buf   the data are to be put here                 */
623/*         int   len   the number of bytes to read                 */
624/*                                                                 */
625/* Return: number of bytes read. -1 on error                       */
626/*                                                                 */
627/*******************************************************************/
628
629static int _readData(int fd, unsigned char *buf, int len)
630{
631   int rd    = 0;
632   int total = 0;
633   int toGet = 0;
634   unsigned char  header[6];
635   struct timeval beg, end;
636   long dt;
637   struct itimerval ti, oti;
638
639   /* set errno to 0 in order to get correct informations */
640   /* in case of error                                    */
641   errno = 0;
642
643   /* read the first 6 bytes */
644   gettimeofday(&beg, NULL);
645   while ( total < 6 )
646   {
647      SET_TIMER(ti,oti, d4RdTimeout);
648      rd = read(fd, header+total, 6-total);
649      RESET_TIMER(ti,oti);
650      if ( rd <= 0 )
651      {
652         gettimeofday(&end, NULL);
653         dt  = (end.tv_sec  - beg.tv_sec) * 1000;
654         dt += (end.tv_usec - beg.tv_usec) / 1000;
655         if ( dt > d4RdTimeout*3 )
656         {
657            if ( debugD4 )
658               printf("Timeout at _readData(), dt = %ld ms\n", dt);
659            return -1;
660            break;
661         }
662         continue;
663      }
664      else
665      {
666         total += rd;
667      }
668   }
669
670   if ( debugD4 )
671      printHexValues("Recv: ",header,total);
672
673   if ( total == 6 )
674   {
675      toGet = (header[2] >> 8) + header[3] - 6;
676      if (debugD4)
677	printf("toGet: %i\n", toGet);
678      total = 0;
679      gettimeofday(&beg, NULL);
680      while ( total < toGet )
681      {
682         SET_TIMER(ti,oti, d4RdTimeout);
683         rd = read(fd, buf+total, toGet-total);
684         RESET_TIMER(ti,oti);
685         if ( rd <= 0 )
686         {
687            gettimeofday(&end, NULL);
688            dt  = (end.tv_sec  - beg.tv_sec) * 1000;
689            dt += (end.tv_usec - beg.tv_usec) / 1000;
690            if ( dt > d4RdTimeout*3 )
691            {
692               if ( debugD4 )
693                  printf("Timeout at _readData(), dt = %ld ms\n",dt);
694               return -1;
695               break;
696            }
697            continue;
698         }
699         else
700         {
701            total += rd;
702         }
703      }
704      if ( debugD4 )
705         printHexValues("Recv: ",buf,total);
706      return total;
707   }
708
709   return -1;
710}
711
712/*******************************************************************/
713/* Function sendReceiveCmd()                                       */
714/*        send a command and get the answer.                       */
715/* Input:  int   fd    file handle                                 */
716/*         char *buf   the data are to be put here                 */
717/*         int   len   the number of bytes to read                 */
718/*                                                                 */
719/* Return: number of bytes read                                    */
720/*                                                                 */
721/*******************************************************************/
722
723static int sendReceiveCmd(int fd, unsigned char *cmd, int len, unsigned char *answer, int expectedlen, int allowExtra)
724{
725   int rd;
726   d4Errno = 0;
727   if ( (rd = writeCmd(fd, cmd, len ) ) != len )
728   {
729      if ( rd < 0 ) return -1;
730      return 0;
731   }
732   rd = readAnswer(fd, answer, expectedlen, allowExtra );
733   if ( rd == 0 )
734   {
735      /* no answer from device */
736      return 0;
737   }
738   else if ( rd < 0 )
739   {
740      /* interrupted write call */
741      if ( debugD4 )
742         printf("interrupt received\n");
743      return -1;
744   }
745   else
746   {
747      /* check result */
748      if ( answer[6] == 0x7f )
749      {
750         printError(answer[9]);
751	 d4Errno = answer[9];
752         return -1;
753      }
754      else if (  answer[7] != 0 )
755      {
756	 d4Errno = answer[7];
757         if ( printError(answer[7]) )
758         {
759            return -1;
760         }
761         return 0;
762      }
763      else
764      {
765         return rd;
766      }
767   }
768}
769
770/*******************************************************************/
771/* Function EnterIEEE()                                            */
772/*        send a command and get the answer.                       */
773/* Input:  int   fd    file handle                                 */
774/*                                                                 */
775/* Return: 0 on error 1 if all is OK                               */
776/*                                                                 */
777/*******************************************************************/
778
779int EnterIEEE(int fd)
780{
781   unsigned char buf[200];
782   unsigned char cmd[] =
783   {
784      0x00, 0x00, 0x00, 0x1b, 0x01, '@', 'E', 'J', 'L', ' ',
785      '1', '2', '8', '4', '.', '4', 0x0a, '@', 'E', 'J',
786      'L', 0x0a, '@', 'E', 'J', 'L', 0x0a
787   };
788   int rd;
789   memset(buf, 0, sizeof(buf));
790Loop:
791   if ( writeCmd(fd, cmd, sizeof(cmd) ) != sizeof(cmd) )
792   {
793      return 0;
794   }
795   rd = readAnswer(fd, buf, 8, 0);
796   if ( rd == 0 )
797   {
798      /* no answer from device */
799      if (debugD4)
800        printf(">>>No answer from printer\n");
801      return 0;
802   }
803   else if ( rd < 0 )
804   {
805      if (debugD4)
806        printf(">>>Interrupted write\n");
807      /* interrupted write call */
808      return 0;
809   }
810   else
811   {
812      int i;
813      /* check result */
814      for (i=0; i < rd; i++ )
815        if ( buf[i] != 0 )
816           break;
817      if ( i == rd ) goto Loop;
818      return 1;
819   }
820}
821
822/*******************************************************************/
823/* Function Init()                                                 */
824/*        handle the init command                                  */
825/* Input:  int   fd    file handle                                 */
826/*                                                                 */
827/* Return: 0 on error 1 if all is OK                               */
828/*                                                                 */
829/*******************************************************************/
830
831int Init(int fd)
832{
833   unsigned char buf[20];
834   init_t cmd;
835   int rd;
836
837   cmd.head.psid     = 0;
838   cmd.head.ssid     = 0;
839   cmd.head.lengthH  = 0;
840   cmd.head.lengthL  = 8;
841   cmd.head.credit   = 1;
842   cmd.head.control  = 0;
843   cmd.head.command  = 0;
844   cmd.revision      = 0x10;
845
846   rd = sendReceiveCmd(fd, (unsigned char*)&cmd, sizeof(cmd), buf, 9, 0 );
847   return rd == 9 ? 1 : 0;
848}
849
850/*******************************************************************/
851/* Function Exit()                                                 */
852/*        handle the Exit command                                  */
853/* Input:  int   fd    file handle                                 */
854/*                                                                 */
855/* Return: 0 on error 1 if all is OK                               */
856/*                                                                 */
857/*******************************************************************/
858
859int Exit(int fd)
860{
861   int rd;
862   unsigned char  buf[20];
863   cmdHeader_t cmd;
864   cmd.psid     = 0;
865   cmd.ssid     = 0;
866   cmd.lengthH  = 0;
867   cmd.lengthL  = 7;
868   cmd.credit   = 1;
869   cmd.control  = 0;
870   cmd.command  = 8;
871
872   rd = sendReceiveCmd(fd, (unsigned char*)&cmd, sizeof(cmd), buf, 8, 0 );
873   return rd > 0 ? 1 : rd;
874}
875
876/*******************************************************************/
877/* Function GetSocketID()                                          */
878/*        handle the GetSocketID command                           */
879/* Input:  int   fd    file handle                                 */
880/*         char *serviceName  name of wanted service               */
881/*                                                                 */
882/* Return: 0 on error else the socket ID                           */
883/*                                                                 */
884/*******************************************************************/
885
886int GetSocketID(int fd, const char *serviceName)
887{
888   /* the service name may not be longer as 40 bytes */
889   int len = sizeof(cmdHeader_t) + strlen(serviceName);
890   char buf[100];
891   unsigned char rBuf[100];
892   int rd;
893   cmdHeader_t *cmd = (cmdHeader_t*)buf;
894   cmd->psid     = 0;
895   cmd->ssid     = 0;
896   cmd->lengthH  = 0;
897   cmd->lengthL  = len & 0xff;
898   cmd->credit   = 1;
899   cmd->control  = 0;
900   cmd->command  = 0x09;
901   strcpy(buf + sizeof(cmdHeader_t), serviceName);
902
903   rd = sendReceiveCmd(fd, (unsigned char*)buf, len, rBuf, len + 2, 0);
904   if ( rd > 0 )
905   {
906      return rBuf[8];
907   }
908   else
909   {
910      return 0;
911   }
912}
913
914/*******************************************************************/
915/* Function OpenChannel()                                          */
916/*        handle the OpenChannel command                           */
917/* Input:  int   fd    file handle                                 */
918/* I/O:    int  *sndSz The size for sendig of datas                */
919/*         int  *recSz The size for receiving of datas             */
920/*                                                                 */
921/* Return: -1 on error or 1 if all is OK                           */
922/*                                                                 */
923/*******************************************************************/
924
925int OpenChannel(int fd, unsigned char sockId, int *sndSz, int *rcvSz)
926{
927   unsigned char  cmd[17];
928   unsigned char  buf[20];
929   int rd;
930   int i;
931
932   for(i = 0; i < 5; i++)	/* Retry count */
933   {
934      cmd[0]  = 0;       /* transaction sockets */
935      cmd[1]  = 0;
936      cmd[2]  = 0;       /* len */
937      cmd[3]  = 17;      /* len */
938      cmd[4]  = 1;       /* credit */
939      cmd[5]  = 0;       /* control */
940      cmd[6]  = 1;       /* command */
941      cmd[7]  = sockId;  /* sockets # */
942      cmd[8]  = sockId;  /* sockets # */
943      cmd[9]  = *sndSz >> 8;   /* packet size in send dir */
944      cmd[10] = *sndSz & 0xff;
945      cmd[11] = *rcvSz >> 8;   /* packet size in recv dir */
946      cmd[12] = *rcvSz & 0xff;
947      cmd[13] = 0;    /* max outstanding Credit, must be 0 */
948      cmd[14] = 0;
949      cmd[15] = 0;    /* initial credit for us ? */
950      cmd[16] = 0;
951
952      rd = sendReceiveCmd(fd, cmd, 17, buf, 16, 0);
953      if ( rd == -1 )
954      {
955	if (debugD4)
956	  printf("OpenChannel %d fails, error %d\n", sockId, d4Errno);
957	if (d4Errno == 6)	/* channel already open */
958	  {
959	    if ( debugD4 )
960	      printf("Channel %d already open, closing\n", sockId);
961	    CloseChannel(fd, sockId);
962	    continue;
963	  }
964	else
965	  return -1;
966      }
967      else if ( rd == 16 )
968      {
969         if ( buf[7] == 4 )
970         {
971            /* device can��t allocate resources now */
972            continue;
973         }
974         else if ( buf[7] != 0 )
975         {
976	   if (debugD4)
977	     printf("OpenChannel %d fails, hard error\n", sockId);
978            /* hard error */
979            return -1;
980         }
981         *sndSz = (buf[10]<<8) + buf[11];
982         *rcvSz = (buf[12]<<8) + buf[13];
983         break;
984      }
985      else
986      {
987	if (d4Errno == 6)	/* channel already open */
988	  {
989	    if ( debugD4 )
990	      printf("Channel %d already open, closing\n", sockId);
991	    CloseChannel(fd, sockId);
992	    continue;
993	  }
994         /* at this stage we can only have an error */
995	if (debugD4)
996	  printf("OpenChannel %d fails, wrong count %d\n", sockId, rd);
997         return -1;
998      }
999   }
1000   return 1;
1001}
1002
1003/*******************************************************************/
1004/* Function CloseChannel()                                         */
1005/*        handle the CloseChannel command                          */
1006/* Input:  int   fd    file handle                                 */
1007/*         unsigned char    socketID  he socket to close                      */
1008/*                                                                 */
1009/* Return: -1 on error or 1 if all is OK                           */
1010/*                                                                 */
1011/*******************************************************************/
1012
1013int CloseChannel(int fd, unsigned char socketID)
1014{
1015   unsigned char buf[100];
1016   int           rd;
1017   cmdHeader_t *cmd = (cmdHeader_t *)buf;
1018   cmd->psid     =  0;
1019   cmd->ssid     =  0;
1020   cmd->lengthH  =  0;
1021   cmd->lengthL  = 10;
1022   cmd->credit   =  1;
1023   cmd->control  =  0;
1024   cmd->command  =  2;
1025   buf[sizeof(cmdHeader_t)+0] = socketID;
1026   buf[sizeof(cmdHeader_t)+1] = socketID;
1027   buf[sizeof(cmdHeader_t)+2] = 0;
1028   rd = sendReceiveCmd(fd, buf,10, buf, 10, 0);
1029   return rd == 10 ? 1 : rd;
1030}
1031
1032/*******************************************************************/
1033/* Function CreditRequest()                                        */
1034/*        handle the CreditRequest command                         */
1035/* Input:  int   fd    file handle                                 */
1036/*         unsigned char    socketID  he socket to close                      */
1037/*                                                                 */
1038/* Return: -1 on error else the credit got                         */
1039/*                                                                 */
1040/*******************************************************************/
1041
1042int CreditRequest(int fd, unsigned char socketID)
1043{
1044   int           rd;
1045   unsigned char            buf[100];
1046   unsigned char            rBuf[100];
1047   cmdHeader_t *cmd = (cmdHeader_t *)buf;
1048   cmd->psid     = 0;
1049   cmd->ssid     = 0;
1050   cmd->lengthH  = 0;
1051   cmd->lengthL  = 13;
1052   cmd->credit   = 1;
1053   cmd->control  = 0;
1054   cmd->command  = 4;
1055   buf[sizeof(cmdHeader_t)+0] = socketID;
1056   buf[sizeof(cmdHeader_t)+1] = socketID;
1057   buf[sizeof(cmdHeader_t)+2] = 0x00;
1058   buf[sizeof(cmdHeader_t)+3] = 0x80;
1059   buf[sizeof(cmdHeader_t)+4] = 0xff;
1060   buf[sizeof(cmdHeader_t)+5] = 0xff;
1061   rd = sendReceiveCmd(fd, buf, 13, rBuf, 12, 0);
1062   if ( rd == 12 )
1063   {
1064      /* this is the credit */
1065      return (rBuf[10]*256)+rBuf[11];
1066   }
1067   else
1068   {
1069      return rd > 0 ? 0 : rd; /* there was an error */
1070   }
1071}
1072
1073/*******************************************************************/
1074/* Function Credit()                                               */
1075/*        give credit to the attached device                       */
1076/* Input:  int   fd    file handle                                 */
1077/*         unsigned char    socketID  the socket to close                     */
1078/*                                                                 */
1079/* Return: -1 on error or 1 if all is OK                           */
1080/*                                                                 */
1081/*******************************************************************/
1082
1083/* needed for sending of commands (channel 2) or scanning */
1084int Credit(int fd, unsigned char socketID, int credit)
1085{
1086   int rd;
1087   unsigned char buf[100];
1088   unsigned char rBuf[100];
1089   cmdHeader_t *cmd = (cmdHeader_t*)buf;
1090   cmd->psid     = 0;
1091   cmd->ssid     = 0;
1092   cmd->lengthH  = 0;
1093   cmd->lengthL  = 0x0b;
1094   cmd->credit   = 1;
1095   cmd->control  = 0;
1096   cmd->command  = 0x03;
1097
1098   buf[sizeof(cmdHeader_t)+0] = socketID;
1099   buf[sizeof(cmdHeader_t)+1] = socketID;
1100   buf[sizeof(cmdHeader_t)+2] = credit >> 8;
1101   buf[sizeof(cmdHeader_t)+3] = credit & 0xff;
1102   rd = sendReceiveCmd(fd, buf, 11, rBuf, 10, 0);
1103   if ( rd == 10 )
1104   {
1105      return 1;
1106   }
1107   else
1108   {
1109      return 0;
1110   }
1111}
1112
1113/*******************************************************************/
1114/* Function askForCredit()                                         */
1115/*        Convenience function                                     */
1116/*        handle the CreditRequest command                         */
1117/* Input:  int   fd    file handle                                 */
1118/*         unsigned char    socketID                               */
1119/* IN/Out  int  *sndSize  for error handling                       */
1120/* IN/Out  int  *rcvSize  for error handling                       */
1121/*                                                                 */
1122/* Return: credit                                                  */
1123/*                                                                 */
1124/* Remark: CreditRequest() will be called in a loop as long as     */
1125/*         the returned credit is 0                                */
1126/*                                                                 */
1127/*******************************************************************/
1128#define MAX_CREDIT_REQUEST 2
1129int askForCredit(int fd, unsigned char socketID, int *sndSize, int *rcvSize)
1130{
1131   int credit = 0;
1132   int count  = 0;
1133   int retries = 10;
1134
1135   while (credit == 0 && retries-- >= 0 )
1136   {
1137      while((credit=CreditRequest(fd,socketID)) == 0  && count < MAX_CREDIT_REQUEST && retries-- >= 0)
1138         usleep(d4RdTimeout);
1139
1140      if ( credit == -1 )
1141      {
1142         if ( errno == ENODEV || count == MAX_CREDIT_REQUEST )
1143         {
1144            break;
1145         }
1146         credit = 0;
1147         /* init printer and reopen the printer channel */
1148         CloseChannel(fd, socketID);
1149	 socketID = GetSocketID(fd, "EPSON-CTRL");
1150         if ( Init(fd) )
1151         {
1152	   if (debugD4)
1153	     printf("askForCredit init succeeded, now try to open\n");
1154            OpenChannel(fd, socketID, sndSize, rcvSize);
1155         }
1156      }
1157      /* if the parent died, live this loop if credit not got */
1158      if ( credit == 0 && getppid() == ppid )
1159         return 0;
1160      count++;
1161   }
1162   return credit;
1163}
1164
1165/*******************************************************************/
1166/* Function writeData()                                            */
1167/*        Convenience function                                     */
1168/*        write the data to the device                             */
1169/* Input:  int   fd    file handle                                 */
1170/*         unsigned char    socketID  the deetination socket       */
1171/*         unsigned char   *buf       the datas to be send         */
1172/*         int   len       how many datas are to we send           */
1173/*         int   eoj       set out of band flag if eoj set         */
1174/*                                                                 */
1175/* Return: number of bytes written or -1;                          */
1176/*                                                                 */
1177/*******************************************************************/
1178
1179int writeData(int fd, unsigned char socketID, const unsigned char *buf, int len, int eoj)
1180{
1181   unsigned char  cmd[6];
1182   int wr = 0;
1183   int ret = 0;
1184   struct  itimerval ti, oti;
1185   struct timeval beg;
1186   static unsigned char *buffer = NULL;
1187   static int bLen   = 0;
1188   if ( debugD4 )
1189   {
1190      printf("--- Send Data      ---\n");
1191      gettimeofday(&beg, NULL);
1192   }
1193   len += 6;
1194   if ( len > bLen )
1195   {
1196      if ( buffer == NULL )
1197         buffer = (unsigned char*)malloc(len);
1198      else
1199         buffer = (unsigned char*)realloc(buffer, len);
1200      if ( buffer == NULL )
1201         return -1;
1202      bLen = len;
1203   }
1204   cmd[0] = socketID;
1205   cmd[1] = socketID;
1206   cmd[2] = len >> 8;
1207   cmd[3] = len & 0xff;
1208   cmd[4] = 0;
1209   cmd[5] = eoj ? 1 : 0;
1210
1211   memcpy(buffer, cmd, 6);
1212   memcpy(buffer + 6, buf, len - 6 );
1213   while( ret > -1 && wr != len )
1214   {
1215      SET_TIMER(ti,oti,d4WrTimeout);
1216      ret = SafeWrite(fd, buffer+wr, len-wr );
1217      RESET_TIMER(ti,oti);
1218      if ( ret == -1 )
1219      {
1220         perror("write: ");
1221      }
1222      else
1223      {
1224         wr += ret;
1225      }
1226   }
1227
1228   if ( debugD4 )
1229   {
1230# if PTIME
1231      gettimeofday(&end, NULL);
1232      dt = (end.tv_sec  - beg.tv_sec) * 1000000;
1233      dt += end.tv_usec - beg.tv_usec;
1234# endif
1235      printf("Send: ");
1236      for ( ret = 0; (wr > 0) && (ret < ((wr > 20) ? 20 : wr)) ; ret++ )
1237         printf("%02x ", buffer[ret]);
1238      printf("\n      ");
1239      for ( ret = 0; (wr > 0) && (ret < ((wr > 20) ? 20 : wr)) ; ret++ )
1240         printf("%c  ", isprint(buffer[ret])&&!isspace(buffer[ret])?buffer[ret]:' ');
1241      printf("\n");
1242# if PTIME
1243       printf("Write time %5.3f s\n",(double)dt/1000000);
1244# endif
1245   }
1246
1247   if (  wr > 6 )
1248      wr -= 6;
1249   else
1250      wr = -1;
1251   return wr;
1252
1253}
1254
1255/*******************************************************************/
1256/* Function readData()                                             */
1257/*        Convenience function                                     */
1258/*        give credit and read then the expected datas             */
1259/* Input:  int   fd    file handle                                 */
1260/*         unsigned char    socketID  the destination socket       */
1261/*         unsigned char   *buf       the datas to be send         */
1262/*         int   len       howmany datas are to we send            */
1263/*                                                                 */
1264/* Return: number of bytes read or -1;                             */
1265/*                                                                 */
1266/*******************************************************************/
1267
1268int readData(int fd, unsigned char socketID, unsigned char *buf, int len)
1269{
1270   int ret;
1271   /* give credit */
1272   if ( Credit(fd, socketID, 1) == 1 )
1273   {
1274      /* wait a little bit */
1275      usleep(d4RdDataTimeout);
1276      ret = _readData(fd, buf, len);
1277      return ret;
1278   }
1279   else
1280   {
1281      return -1;
1282   }
1283}
1284
1285/*******************************************************************/
1286/* Function writeAndReadData()                                     */
1287/*        Convenience function                                     */
1288/*        give credit and read then the expected datas             */
1289/* Input:  int   fd    file handle                                 */
1290/*         unsigned char    socketID  the destination socket       */
1291/*         unsigned char   *cmd       the datas to be send         */
1292/*         int   cmd_len       howmany datas are to we send        */
1293/*         int   eoj       set out of band flag if eoj set         */
1294/*         unsigned char   *buf       the datas to be send         */
1295/*         int   *sndSz    Send buffer size                        */
1296/*         int   *rcvSz    Receive buffer size                     */
1297/*         int   len       how many datas are to we send           */
1298/*         fptr  test      function to verify buffer contents      */
1299/*                                                                 */
1300/* Return: number of bytes read or -1;                             */
1301/*                                                                 */
1302/* This allows us to give credit before sending the command.       */
1303/* Sending the command and then giving credit sometimes causes     */
1304/* the actual data to be sent as a reply to the credit command.    */
1305/*                                                                 */
1306/*******************************************************************/
1307
1308int writeAndReadData(int fd, unsigned char socketID,
1309		     const unsigned char *cmd, int cmd_len, int eoj,
1310		     unsigned char *buf, int len, int *sndSz, int *rcvSz,
1311		     int (*test)(const unsigned char *buf))
1312{
1313   int ret;
1314   int retry = 5;
1315   int credit = askForCredit(fd, socketID, sndSz, rcvSz);
1316   if (credit < 0)
1317     return -1;
1318   /* give credit */
1319   if ( Credit(fd, socketID, 1) == 1 )
1320     {
1321       if (writeData(fd, socketID, cmd, cmd_len, eoj) <= 0)
1322	 return -1;
1323       /* wait a little bit */
1324       do
1325	 {
1326	   usleep(d4RdDataTimeout);
1327	   ret = _readData(fd, buf, len);
1328	   if (ret < 0)
1329	     return ret;
1330	 } while (retry-- >= 0 && (!test || !(*test)(buf)));
1331       return ret;
1332     }
1333   else
1334     return -1;
1335}
1336
1337/*******************************************************************/
1338/* Function readData()                                             */
1339/*        Convenience function                                     */
1340/*        give credit and read then the expected datas             */
1341/* Input:  int   fd    file handle                                 */
1342/*         unsigned char    socketID  the destination socket                  */
1343/*         unsigned char   *buf       the datas to be send                    */
1344/*         int   len       howmany datas are to we send            */
1345/*                                                                 */
1346/* Return: number of bytes written or -1;                          */
1347/*                                                                 */
1348/*******************************************************************/
1349
1350void flushData(int fd, unsigned char socketID)
1351{
1352  if (debugD4)
1353    printf("flushData %d\n", socketID);
1354   /* give credit */
1355   if (socketID != (unsigned char) -1)
1356     {
1357       if ( Credit(fd, socketID, 1) == 1 )
1358	 {
1359	   /* wait a little bit */
1360	   usleep(d4RdDataTimeout);
1361	   _flushData(fd);
1362	 }
1363     }
1364   else
1365     _flushData(fd);
1366}
1367
1368/*******************************************************************/
1369/* Function clearSndBuf()                                          */
1370/*        Convenience function                                     */
1371/*                                                                 */
1372/* Input:  int   fd    file handle                                 */
1373/*                                                                 */
1374/*                                                                 */
1375/*******************************************************************/
1376
1377static void clearSndBuf(int fd)
1378{
1379   char             buf[256];
1380   struct itimerval ti, oti;
1381
1382   SET_TIMER(ti,oti, d4RdTimeout);
1383   while ( read(fd, buf, sizeof(buf) ) > 0 )
1384      SET_TIMER(ti,oti, d4RdTimeout);
1385   RESET_TIMER(ti,oti);
1386}
1387
1388void setDebug(int debug)
1389{
1390  debugD4 = debug;
1391}
1392
1393#if 0 /* implementation later ? */
1394int InitReply(int fd)
1395{
1396   unsigned char buf[20];
1397   initReply_t cmd;
1398
1399   cmd.head.psid     = 0;
1400   cmd.head.ssid     = 0;
1401   cmd.head.lengthH  = 0;
1402   cmd.head.lengthL  = 9;
1403   cmd.head.credit   = 1;
1404   cmd.head.control  = 0;
1405   cmd.head.command  = 0x80;
1406   cmd.head.result   = 0;   /* put the correct value here, 0,1,2 or 0x0b */
1407   cmd.revision      = 0x10;
1408
1409}
1410
1411int ExitReply(int fd)
1412{
1413   replyHeader_t cmd;
1414   cmd.psid     = 0;
1415   cmd.ssid     = 0;
1416   cmd.lengthH  = 0;
1417   cmd.lengthL  = 7;
1418   cmd.credit   = 1;    /* always ignored */
1419   cmd.control  = 0;
1420   cmd.command  = 0x88;
1421   cmd.result   = 0x0;  /* put the correct value here always 0 */
1422}
1423
1424int Error(int fd)
1425{
1426   char
1427   error_t cmd;
1428   cmd.head.psid     = 0;
1429   cmd.head.ssid     = 0;
1430   cmd.head.lengthH  = 0;
1431   cmd.head.lengthL  = 0x0a;
1432   cmd.head.credit   = 0;
1433   cmd.head.control  = 0;
1434   cmd.head.command  = 0x7f;
1435   cmd.epsid         = psid;
1436   cmd.essid         = ssid;
1437   cmd.ecode         = 0x80; /* + 1...7 */
1438}
1439
1440int GetSocketIDReply(int fd, unsigned char socketID)
1441{
1442   /* the service name may not be longer as 40 bytes */
1443   int len = sizeof(replyHeader_t) + 1 + strlen(serviceName);
1444   unsigned char buf[100];
1445   replyHeader_t *cmd = buf;
1446   cmd->psid     = 0;
1447   cmd->ssid     = 0;
1448   cmd->lengthH  = 0;
1449   cmd->lengthL  = len & 0xff;
1450   cmd->credit   = 1;
1451   cmd->control  = 0;
1452   cmd->command  = 0x89;
1453   cmd->result   = 0; /* put the correct vale here 0 or 0x0a */
1454   buf[sizeof(cmdHeader_t)] = socketID;
1455   strcpy(buf+1+sizeof(cmdHeader_t), serviceName);
1456
1457}
1458
1459int GetServiceName(inf fd, unsigned char socketID)
1460{
1461   int rd;
1462   unsigned char buf[100];
1463   cmdHeader_t *cmd = buf;
1464   cmd->psid     = 0;
1465   cmd->ssid     = 0;
1466   cmd->lengthH  = 0;
1467   cmd->lengthL  = 8;
1468   cmd->credit   = 1;
1469   cmd->control  = 0;
1470   cmd->command  = 0x0a;
1471   buf[sizeof(cmdHeader_t)] = socketID;
1472}
1473
1474/* as GetSocketIDReply but with command 0x8a instead 0f 0x89 */
1475int GetServiceNameReply(inf fd)
1476{
1477   /* the service name may not be longer as 40 bytes */
1478   int len = sizeof(replyHeader_t) + 1 + strlen(serviceName);
1479   unsigned char buf[100];
1480   replyHeader_t *cmd = buf;
1481   cmd->psid     = 0;
1482   cmd->ssid     = 0;
1483   cmd->lengthH  = 0;
1484   cmd->lengthL  = len & 0xff;
1485   cmd->credit   = 1;
1486   cmd->control  = 0;
1487   cmd->command  = 0x8a;
1488   cmd->result   = 0; /* put the correct vale here 0 or 0x0a */
1489   buf[sizeof(replyHeader_t)] = socketID;
1490   strcpy(buf+1+sizeof(replyHeader_t), serviceName);
1491
1492}
1493
1494int CreditReply(int fd, unsigned char socketID, int credit)
1495{
1496   unsigned char buf[100];
1497   replyHeader_t *cmd = buf;
1498   cmd->psid     = 0;
1499   cmd->ssid     = 0;
1500   cmd->lengthH  = 0;
1501   cmd->lengthL  = 0x0b;
1502   cmd->credit   = 1;
1503   cmd->control  = 0;
1504   cmd->command  = 0x83;
1505   buf[sizeof(replyHeader_t)]   = socketID;
1506   buf[sizeof(replyHeader_t)+1] = socketID;
1507}
1508
1509#endif
1510
1511