1 /* PRINTER.C    (c) Copyright Roger Bowler, 1999-2010                */
2 /*              (c) Copyright Enrico Sorichetti, 2012                */
3 /*              ESA/390 Line Printer Device Handler                  */
4 
5 /*-------------------------------------------------------------------*/
6 /* This module contains device handling functions for emulated       */
7 /* System/370 line printer devices with fcb support and more         */
8 /*-------------------------------------------------------------------*/
9 
10 #include "hstdinc.h"
11 #include "hercules.h"
12 #include "devtype.h"
13 #include "opcode.h"
14 
15 /*-------------------------------------------------------------------*/
16 /* Ivan Warren 20040227                                              */
17 /* This table is used by channel.c to determine if a CCW code is an  */
18 /* immediate command or not                                          */
19 /* The tape is addressed in the DEVHND structure as 'DEVIMM immed'   */
20 /* 0 : Command is NOT an immediate command                           */
21 /* 1 : Command is an immediate command                               */
22 /* Note : An immediate command is defined as a command which returns */
23 /* CE (channel end) during initialisation (that is, no data is       */
24 /* actually transfered. In this case, IL is not indicated for a CCW  */
25 /* Format 0 or for a CCW Format 1 when IL Suppression Mode is in     */
26 /* effect                                                            */
27 /*-------------------------------------------------------------------*/
28 
29 /* Printer Specific : 1403 */
30 /* The following are considered IMMEDIATE commands : */
31 /* CTL-NOOP, Skip Channel 'n' Immediate, Block Data check , Allow Data Check
32  * Space 1,2,3 Lines Immediate, UCS Gate Load, Load UCS Buffer & Fold,
33  * Load UCS Buffer (No Fold)
34  */
35 
36 static BYTE printer_immed_commands[256]=
37 /*
38  *0 1 2 3 4 5 6 7 8 9 A B C D E F
39 */
40 
41 { 0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
42   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
43   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
44   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
45   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
46   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
47   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
48   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
49   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
50   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
51   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
52   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
53   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
54   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
55   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
56   0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0};
57 
58 /*-------------------------------------------------------------------*/
59 /* Internal macro definitions                                        */
60 /*-------------------------------------------------------------------*/
61 //#define LINE_LENGTH     150
62 #define BUFF_SIZE       1500
63 #define BUFF_OVFL       150
64 
65 int line;
66 int coun;
67 int chan;
68 int FCBMASK[] = {66,1,7,13,19,25,31,37,43,63,49,55,61};
69 int havechan;
70 
71 #define LINENUM(n)  ( 1 + ( ( (n)-1) % dev->lpp))
72 
73 #define WRITE_LINE() \
74 do { \
75     /* Start a new record if not data-chained from previous CCW */ \
76     if ((chained & CCW_FLAGS_CD) == 0) \
77     { \
78         dev->bufoff = 0; \
79         dev->bufres = BUFF_SIZE; \
80     } /* end if(!data-chained) */ \
81     if ( dev->index > 1 ) \
82     { \
83         for (i = 1; i < dev->index; i++) \
84         { \
85             dev->buf[dev->bufoff] = SPACE; \
86             dev->bufoff++; \
87             dev->bufres--; \
88         } /* end for(i) */ \
89     } /* end if ( dev->index > 1 )  */ \
90     /* Calculate number of bytes to write and set residual count */ \
91     num = (count < dev->bufres) ? count : dev->bufres; \
92     *residual = count - num; \
93     /* Copy data from channel buffer to print buffer */ \
94     for (i = 0; i < num; i++) \
95     { \
96         c = guest_to_host(iobuf[i]); \
97         if (dev->fold) c = toupper(c); \
98         if (c == 0) c = SPACE; \
99         dev->buf[dev->bufoff] = c; \
100         dev->bufoff++; \
101         dev->bufres--; \
102     } /* end for(i) */ \
103     /* Perform end of record processing if not data-chaining */ \
104     if ((flags & CCW_FLAGS_CD) == 0) \
105     { \
106         /* Truncate trailing blanks from print line */ \
107         for (i = dev->bufoff; i > 0; i--) \
108             if (dev->buf[i-1] != SPACE) break; \
109         /* Write print line */ \
110         write_buffer (dev, (char *)dev->buf, i, unitstat); \
111         if (*unitstat != 0) return; \
112         if ( dev->crlf ) \
113         { \
114             write_buffer (dev, "\r", 1, unitstat); \
115             if (*unitstat != 0) return; \
116         } \
117     } /* end if(!data-chaining) */ \
118     /* Return normal status */ \
119 } while(0)
120 
121 /* changed to                                                 */
122 /* search the fcb array starting at the CURRENT line position */
123 /* check if the previous operation was a write no space       */
124 #define SKIP_TO_CHAN() \
125 do { \
126     havechan = 0; \
127     for ( i = 0; i < dev->lpp; i++ ) \
128     { \
129         line = LINENUM( dev->currline + i ); \
130         if ( dev->fcb[line] != chan )  \
131             continue; \
132         havechan = 1; \
133         dev->destline = line; \
134         break; \
135     } \
136     if ( havechan == 1 ) \
137     { \
138         if ( ( dev->destline < dev->currline ) ||  \
139              ( dev->chskip == 1 && dev->destline <= dev->currline ) ) \
140         { \
141             dev->chskip = 0; \
142             write_buffer (dev, "\f", 1, unitstat); \
143             if (*unitstat != 0) return; \
144             dev->currline = 1; \
145         } \
146         for (; dev->currline < dev->destline; dev->currline++ ) \
147         { \
148             write_buffer (dev, "\n", 1, unitstat); \
149             if (*unitstat != 0) return; \
150         } \
151         *unitstat = CSW_CE | CSW_DE; \
152         return; \
153     } \
154     /* channel not found */ \
155     { \
156         if ( dev->nofcbcheck ) \
157         { \
158             if ( ( code & 0x02 ) != 0 ) \
159             { \
160                 write_buffer (dev, "\n", 1, unitstat); \
161                 if (*unitstat != 0) return; \
162             } \
163         } \
164         else \
165         { \
166             dev->sense[0] = (dev->devtype == 0x1403 ) ? SENSE_EC :SENSE_EC; \
167             *unitstat = CSW_CE | CSW_DE | CSW_UC; \
168             return; \
169         } \
170     } \
171 } while (0)
172 
173 static void* spthread (DEVBLK* dev);        /*  (forward reference)  */
174 
175 /*-------------------------------------------------------------------*/
176 /* Dump the FCB info                                                 */
177 /*-------------------------------------------------------------------*/
fcb_dump(DEVBLK * dev,char * buf,unsigned int buflen)178 static void fcb_dump(DEVBLK* dev, char *buf, unsigned int buflen)
179 {
180     int i;
181     char wrk[16];
182     char sep[1];
183     sep[0] = '=';
184     snprintf(buf, buflen, "LOADED lpi=%d index=%d lpp=%d fcb", dev->lpi, dev->index, dev->lpp );
185     for (i = 1; i <= dev->lpp; i++)
186     {
187         if (dev->fcb[i] != 0)
188         {
189             sprintf(wrk, "%c%d:%d", sep[0], i, dev->fcb[i]);
190             sep[0] = ',';
191             if (strlen(buf) + strlen(wrk) >= buflen - 4)
192             {
193                 /* Too long, truncate it */
194                 strcat(buf, ",...");
195                 return;
196             }
197             strcat(buf, wrk);
198         }
199     }
200     return;
201 }
202 
203 /*-------------------------------------------------------------------*/
204 /* Sockdev "OnConnection" callback function                          */
205 /*-------------------------------------------------------------------*/
onconnect_callback(DEVBLK * dev)206 static int onconnect_callback (DEVBLK* dev)
207 {
208     TID tid;
209     if (create_thread( &tid, DETACHED, spthread, dev, NULL ))
210     {
211         logmsg(_("HHCPR015E Create spthread failed for %4.4X: errno=%d: %s\n" ),
212             dev->devnum, errno, strerror( errno ) );
213         return 0;
214     }
215     return 1;
216 }
217 
218 /*-------------------------------------------------------------------*/
219 /* Thread to monitor the sockdev remote print spooler connection     */
220 /*-------------------------------------------------------------------*/
spthread(DEVBLK * dev)221 static void* spthread (DEVBLK* dev)
222 {
223     BYTE byte;
224     fd_set readset, errorset;
225     struct timeval tv;
226     int rc, fd = dev->fd;           // (save original fd)
227 
228     /* Fix thread name */
229     {
230         char thread_name[32];
231         thread_name[sizeof(thread_name)-1] = 0;
232         snprintf( thread_name, sizeof(thread_name)-1,
233             "spthread %4.4X", dev->devnum );
234         SET_THREAD_NAME( thread_name );
235     }
236 
237     // Looooop...  until shutdown or disconnect...
238 
239     // PROGRAMMING NOTE: we do our select specifying an immediate
240     // timeout to prevent our select from holding up (slowing down)
241     // the device thread (which does the actual writing of data to
242     // the client). The only purpose for our thread even existing
243     // is to detect a severed connection (i.e. to detect when the
244     // client disconnects)...
245 
246     while ( !sysblk.shutdown && dev->fd == fd )
247     {
248         if (dev->busy)
249         {
250             SLEEP(3);
251             continue;
252         }
253 
254         FD_ZERO( &readset );
255         FD_ZERO( &errorset );
256 
257         FD_SET( fd, &readset );
258         FD_SET( fd, &errorset );
259 
260         tv.tv_sec = 0;
261         tv.tv_usec = 0;
262 
263         rc = select( fd+1, &readset, NULL, &errorset, &tv );
264 
265         if (rc < 0)
266             break;
267 
268         if (rc == 0)
269         {
270             SLEEP(3);
271             continue;
272         }
273 
274         if (FD_ISSET( fd, &errorset ))
275             break;
276 
277         // Read and ignore any data they send us...
278         // Note: recv should complete immediately
279         // as we know data is waiting to be read.
280 
281         ASSERT( FD_ISSET( fd, &readset ) );
282 
283         rc = recv( fd, &byte, sizeof(byte), 0 );
284 
285         if (rc <= 0)
286             break;
287     }
288 
289     obtain_lock( &dev->lock );
290 
291     // PROGRAMMING NOTE: the following tells us whether we detected
292     // the error or if the device thread already did. If the device
293     // thread detected it while we were sleeping (and subsequently
294     // closed the connection) then we don't need to do anything at
295     // all; just exit. If we were the ones that detected the error
296     // however, then we need to close the connection so the device
297     // thread can learn of it...
298 
299     if (dev->fd == fd)
300     {
301         dev->fd = -1;
302         close_socket( fd );
303         logmsg (_("HHCPR016I %s (%s) disconnected from device %4.4X (%s)\n"),
304             dev->bs->clientname, dev->bs->clientip, dev->devnum, dev->bs->spec);
305     }
306 
307     release_lock( &dev->lock );
308 
309     return NULL;
310 
311 } /* end function spthread */
312 
313 /*-------------------------------------------------------------------*/
314 /* Initialize the device handler                                     */
315 /*-------------------------------------------------------------------*/
printer_init_handler(DEVBLK * dev,int argc,char * argv[])316 static int printer_init_handler (DEVBLK *dev, int argc, char *argv[])
317 {
318 int     iarg,i,j;                       /* Array subscripts          */
319 char   *ptr;
320 char   *nxt;
321 int     sockdev = 0;                    /* 1 == is socket device     */
322 
323     /* Forcibly disconnect anyone already currently connected */
324     if (dev->bs && !unbind_device_ex(dev,1))
325         return -1; // (error msg already issued)
326 
327     /* The first argument is the file name */
328     if (argc == 0 || strlen(argv[0]) > sizeof(dev->filename)-1)
329     {
330         logmsg (_("HHCPR001E File name missing or invalid for printer %4.4X\n"),
331                  dev->devnum);
332         return -1;
333     }
334 
335     /* Save the file name in the device block */
336     strncpy (dev->filename, argv[0], sizeof(dev->filename));
337 
338     if(!sscanf(dev->typname,"%hx",&(dev->devtype)))
339         dev->devtype = 0x3211;
340 
341     /* Initialize device dependent fields */
342     dev->fd = -1;
343     dev->diaggate = 0;
344     dev->fold = 0;
345     dev->crlf = 0;
346     dev->stopprt = 0;
347     dev->notrunc = 0;
348     dev->ispiped = (dev->filename[0] == '|');
349 
350     /* initialize the new fields for FCB+ support */
351     dev->fcbsupp = 1;
352     dev->cc = 0;
353     dev->rawcc = 0;
354     dev->fcbcheck = 1;
355     dev->nofcbcheck = 0;
356     dev->ccpend = 0;
357     dev->chskip = 0;
358 
359     dev->prevline = 1;
360     dev->currline = 1;
361     dev->destline = 1;
362 
363     dev->print = 1;
364     dev->browse = 0;
365 
366     dev->lpi = 6;
367     dev->index = 0;
368     dev->ffchan = 1;
369     for (i = 0; i < FCBSIZE; i++)  dev->fcb[i] = 0;
370     for (i = 1; i <= 12; i++ )
371     {
372         if ( FCBMASK[i] != 0 )
373             dev->fcb[FCBMASK[i]] = i;
374     }
375     dev->lpp = FCBMASK[0];
376     dev->fcbisdef = 0;
377 
378     /* Process the driver arguments */
379     for (iarg = 1; iarg < argc; iarg++)
380     {
381         if (strcasecmp(argv[iarg], "crlf") == 0)
382         {
383             dev->crlf = 1;
384             continue;
385         }
386 
387         /* sockdev means the device file is actually
388            a connected socket instead of a disk file.
389            The file name is the socket_spec (host:port)
390            to listen for connections on.
391         */
392         if (!dev->ispiped && strcasecmp(argv[iarg], "sockdev") == 0)
393         {
394             sockdev = 1;
395             continue;
396         }
397 
398         if (strcasecmp(argv[iarg], "noclear") == 0)
399         {
400             dev->notrunc = 1;
401             continue;
402         }
403 
404         if (strcasecmp(argv[iarg], "cc") == 0)
405         {
406             dev->cc = 1;
407             dev->rawcc = 0;
408             continue;
409         }
410         if (strcasecmp(argv[iarg], "rawcc") == 0)
411         {
412             dev->cc = 0;
413             dev->rawcc = 1;
414             continue;
415         }
416 
417         if (strcasecmp(argv[iarg], "nofcbcheck") == 0)
418         {
419             dev->fcbcheck = 0;
420             dev->nofcbcheck = 1;
421             continue;
422         }
423 
424         if (strcasecmp(argv[iarg], "fcbcheck") == 0)
425         {
426             dev->fcbcheck = 1;
427             dev->nofcbcheck = 0;
428             continue;
429         }
430 
431         if ( (strcasecmp(argv[iarg], "browse") == 0) ||
432              (strcasecmp(argv[iarg], "optbrowse") == 0 ) )
433         {
434             dev->print = 0;
435             dev->browse = 1;
436             continue;
437         }
438 
439         if ( (strcasecmp(argv[iarg], "print") == 0 ) ||
440              (strcasecmp(argv[iarg], "optprint") == 0) )
441         {
442             dev->print = 1;
443             dev->browse = 0;
444             continue;
445         }
446 
447         if (strncasecmp("lpi=", argv[iarg], 4) == 0)
448         {
449             ptr = argv[iarg]+4;
450             errno = 0;
451             dev->lpi = (int) strtoul(ptr,&nxt,10);
452             if (errno != 0 || nxt == ptr || *nxt != 0 || ( dev->lpi != 6 && dev->lpi != 8 ) )
453             {
454                 j = ptr - argv[iarg];
455                 logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
456                         SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
457                 return -1;
458             }
459             continue;
460         }
461 
462         if (strncasecmp("index=", argv[iarg], 6) == 0)
463         {
464             if (dev->devtype != 0x3211 )
465             {
466                 logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
467                         SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, 1);
468                 return -1;
469             }
470             ptr = argv[iarg]+6;
471             errno = 0;
472             dev->index = (int) strtoul(ptr,&nxt,10);
473             if (errno != 0 || nxt == ptr || *nxt != 0 || ( dev->index < 0 || dev->index > 15) )
474             {
475                 j = ptr - argv[iarg];
476                 logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
477                         SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
478                 return -1;
479             }
480             continue;
481         }
482 
483         if (strncasecmp("lpp=", argv[iarg], 4) == 0)
484         {
485             ptr = argv[iarg]+4;
486             errno = 0;
487             dev->lpp = (int) strtoul(ptr,&nxt,10);
488             if (errno != 0 || nxt == ptr || *nxt != 0 ||dev->lpp > FCBSIZE)
489             {
490                 j = ptr - argv[iarg];
491                 logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
492                         SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
493                 return -1;
494             }
495             continue;
496         }
497 #if 0
498         if (strncasecmp("ffchan=", argv[iarg], 7) == 0)
499         {
500             ptr = argv[iarg]+7;
501             errno = 0;
502             dev->ffchan = (int) strtoul(ptr,&nxt,10);
503             if (errno != 0 || nxt == ptr || *nxt != 0 ||  dev->ffchan < 1 || dev->ffchan > 12)
504             {
505                 j = ptr - argv[iarg];
506                 logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
507                         SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
508                 return -1;
509             }
510             continue;
511         }
512 #endif
513 
514         if (strncasecmp("fcb=", argv[iarg], 4) == 0)
515         {
516             for (line = 0; line <= FCBSIZE; line++)  dev->fcb[line] = 0;
517             /* check for simple mode */
518             if  ( strstr(argv[iarg],":") )
519             {
520                 /* ':" found  ==> new mode */
521                 ptr = argv[iarg]+4;
522                 while (*ptr)
523                 {
524                     errno = 0;
525                     line = (int) strtoul(ptr,&nxt,10);
526                     if (errno != 0 || *nxt != ':' || nxt == ptr || line > dev->lpp || dev->fcb[line] != 0 )
527                     {
528                         j = ptr - argv[iarg];
529                         logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
530                                 SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
531                         return -1;
532                     }
533 
534                     ptr = nxt + 1;
535                     errno = 0;
536                     chan = (int) strtoul(ptr,&nxt,10);
537                     if (errno != 0 || (*nxt != ',' && *nxt != 0) || nxt == ptr || chan < 1 || chan > 12 )
538                     {
539                         j = ptr - argv[iarg];
540                         logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
541                                 SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
542                         return -1;
543                     }
544                     dev->fcb[line] = chan;
545                     if ( *nxt == 0 )
546                         break;
547                     ptr = nxt + 1;
548                 }
549 
550             }
551             else
552             {
553                 /* ':" NOT found  ==> old mode */
554                 ptr = argv[iarg]+4;
555                 chan = 0;
556                 while (*ptr)
557                 {
558                     errno = 0;
559                     line = (int) strtoul(ptr,&nxt,10);
560                     if (errno != 0 || (*nxt != ',' && *nxt != 0) || nxt == ptr || line > dev->lpp || dev->fcb[line] != 0 )
561                     {
562                         j = ptr - argv[iarg];
563                         logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
564                                 SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
565                         return -1;
566                     }
567                     chan += 1;
568                     if ( chan > 12 )
569                     {
570                         j = ptr - argv[iarg];
571                         logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
572                                 SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
573                         return -1;
574                     }
575                     dev->fcb[line] = chan;
576                     if ( *nxt == 0 )
577                         break;
578                     ptr = nxt + 1;
579                 }
580                 if ( chan != 12 )
581                 {
582                     j = 5;
583                     logmsg("HHCPR103E %d:%4.4X Printer: parameter %s in argument %d at position %d is invalid\n",
584                             SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1, j);
585                     return -1;
586                 }
587             }
588 
589             continue;
590         }
591 
592         logmsg("HHCPR102E %d:%4.4X Printer: parameter %s in argument %d is invalid\n",
593                 SSID_TO_LCSS(dev->ssid), dev->devnum, argv[iarg], iarg + 1);
594         return -1;
595     }
596 
597     /* Check for incompatible options */
598     if (dev->rawcc && dev->browse)
599     {
600         logmsg("HHCPR104E %d:%4.4X Printer: option %s is incompatible\n",
601                 SSID_TO_LCSS(dev->ssid), dev->devnum, "rawcc/browse");
602         return -1;
603     }
604 
605     if (sockdev && dev->crlf)
606     {
607         logmsg("HHCPR104E %d:%4.4X Printer: option %s is incompatible\n",
608                 SSID_TO_LCSS(dev->ssid), dev->devnum, "sockdev/crlf");
609         return -1;
610     }
611 
612     if (sockdev && dev->notrunc)
613     {
614         logmsg("HHCPR104E %d:%4.4X Printer: option %s is incompatible\n",
615                 SSID_TO_LCSS(dev->ssid), dev->devnum, "sockdev/noclear");
616         return -1;
617     }
618 
619     /* If socket device, create a listening socket
620        to accept connections on.
621     */
622     if (sockdev && !bind_device_ex( dev,
623         dev->filename, onconnect_callback, dev ))
624     {
625         return -1;  // (error msg already issued)
626     }
627 
628     /* Set length of print buffer */
629 //  dev->bufsize = LINE_LENGTH + 8;
630     dev->bufsize = BUFF_SIZE + BUFF_OVFL;
631     dev->bufres = BUFF_SIZE;
632     dev->bufoff = 0;
633 
634     /* Set number of sense bytes */
635     dev->numsense = 1;
636 
637     /* Initialize the device identifier bytes */
638     dev->devid[0] = 0xFF;
639     dev->devid[1] = 0x28; /* Control unit type is 2821-1 */
640     dev->devid[2] = 0x21;
641     dev->devid[3] = 0x01;
642     dev->devid[4] = dev->devtype >> 8;
643     dev->devid[5] = dev->devtype & 0xFF;
644     dev->devid[6] = 0x01;
645     dev->numdevid = 7;
646 
647     /* Activate I/O tracing */
648 //  dev->ccwtrace = 1;
649 
650     return 0;
651 } /* end function printer_init_handler */
652 
653 /*-------------------------------------------------------------------*/
654 /* Query the device definition                                       */
655 /*-------------------------------------------------------------------*/
printer_query_device(DEVBLK * dev,char ** class,int buflen,char * buffer)656 static void printer_query_device (DEVBLK *dev, char **class,
657                 int buflen, char *buffer)
658 {
659     BEGIN_DEVICE_CLASS_QUERY( "PRT", dev, class, buflen, buffer );
660 
661     snprintf (buffer, buflen, "%s%s%s%s%s%s%s",
662                  dev->filename,
663                 (dev->bs         ? " sockdev"      : ""),
664                 (dev->crlf       ? " crlf"         : ""),
665                 (dev->notrunc    ? " noclear"      : ""),
666                 (dev->rawcc      ? " rawcc"        : dev->browse  ? " brwse"    : " print"),
667                 (dev->nofcbcheck ? " nofcbck"   : " fcbck"),
668                 (dev->stopprt    ? " (stopped)"    : ""));
669 
670 } /* end function printer_query_device */
671 
672 /*-------------------------------------------------------------------*/
673 /* Subroutine to open the printer file or pipe                       */
674 /*-------------------------------------------------------------------*/
675 static int
open_printer(DEVBLK * dev)676 open_printer (DEVBLK *dev)
677 {
678 pid_t           pid;                    /* Child process identifier  */
679 char            pathname[MAX_PATH];     /* file path in host format  */
680 int             open_flags;             /* File open flags           */
681 #if !defined( _MSVC_ )
682 int             pipefd[2];              /* Pipe descriptors          */
683 int             rc;                     /* Return code               */
684 #endif
685 
686     /* Regular open if 1st char of filename is not vertical bar */
687     if (!dev->ispiped)
688     {
689         int fd;
690 
691         /* Socket printer? */
692         if (dev->bs)
693             return (dev->fd < 0 ? -1 : 0);
694 
695         /* Normal printer */
696         hostpath(pathname, dev->filename, sizeof(pathname));
697         open_flags = O_BINARY | O_WRONLY | O_CREAT /* | O_SYNC */;
698         if (dev->notrunc != 1)
699         {
700             open_flags |= O_TRUNC;
701         }
702         fd = hopen(pathname, open_flags,
703                     S_IRUSR | S_IWUSR | S_IRGRP);
704         if (fd < 0)
705         {
706             logmsg (_("HHCPR004E Error opening file %s: %s\n"),
707                     dev->filename, strerror(errno));
708             return -1;
709         }
710 
711         /* Save file descriptor in device block and return */
712         dev->fd = fd;
713         return 0;
714     }
715 
716     /* Filename is in format |xxx, set up pipe to program xxx */
717 
718 #if defined( _MSVC_ )
719 
720     /* "Poor man's" fork... */
721     pid = w32_poor_mans_fork ( dev->filename+1, &dev->fd );
722     if (pid < 0)
723     {
724         logmsg (_("HHCPR006E %4.4X device initialization error: fork: %s\n"),
725                 dev->devnum, strerror(errno));
726         return -1;
727     }
728 
729     /* Log start of child process */
730     logmsg (_("HHCPR007I pipe receiver (pid=%d) starting for %4.4X\n"),
731             pid, dev->devnum);
732     dev->ptpcpid = pid;
733 
734 #else /* !defined( _MSVC_ ) */
735 
736     /* Create a pipe */
737     rc = create_pipe (pipefd);
738     if (rc < 0)
739     {
740         logmsg (_("HHCPR005E %4.4X device initialization error: pipe: %s\n"),
741                 dev->devnum, strerror(errno));
742         return -1;
743     }
744 
745     /* Fork a child process to receive the pipe data */
746     pid = fork();
747     if (pid < 0)
748     {
749         logmsg (_("HHCPR006E %4.4X device initialization error: fork: %s\n"),
750                 dev->devnum, strerror(errno));
751         close_pipe ( pipefd[0] );
752         close_pipe ( pipefd[1] );
753         return -1;
754     }
755 
756     /* The child process executes the pipe receiver program... */
757     if (pid == 0)
758     {
759         /* Log start of child process */
760         logmsg (_("HHCPR007I pipe receiver (pid=%d) starting for %4.4X\n"),
761                 getpid(), dev->devnum);
762 
763         /* Close the write end of the pipe */
764         close_pipe ( pipefd[1] );
765 
766         /* Duplicate the read end of the pipe onto STDIN */
767         if (pipefd[0] != STDIN_FILENO)
768         {
769             rc = dup2 (pipefd[0], STDIN_FILENO);
770             if (rc != STDIN_FILENO)
771             {
772                 logmsg (_("HHCPR008E %4.4X dup2 error: %s\n"),
773                         dev->devnum, strerror(errno));
774                 close_pipe ( pipefd[0] );
775                 _exit(127);
776             }
777         } /* end if(pipefd[0] != STDIN_FILENO) */
778 
779         /* Close the original descriptor now duplicated to STDIN */
780         close_pipe ( pipefd[0] );
781 
782         /* Redirect stderr (screen) to hercules log task */
783         dup2(STDOUT_FILENO, STDERR_FILENO);
784 
785         /* Relinquish any ROOT authority before calling shell */
786         SETMODE(TERM);
787 
788         /* Execute the specified pipe receiver program */
789         rc = system (dev->filename+1);
790 
791         if (rc == 0)
792         {
793             /* Log end of child process */
794             logmsg (_("HHCPR011I pipe receiver (pid=%d) terminating for %4.4X\n"),
795                     getpid(), dev->devnum);
796         }
797         else
798         {
799             /* Log error */
800             logmsg (_("HHCPR012E %4.4X Unable to execute %s: %s\n"),
801                     dev->devnum, dev->filename+1, strerror(errno));
802         }
803 
804         /* The child process terminates using _exit instead of exit
805            to avoid invoking the panel atexit cleanup routine */
806         _exit(rc);
807 
808     } /* end if(pid==0) */
809 
810     /* The parent process continues as the pipe sender */
811 
812     /* Close the read end of the pipe */
813     close_pipe ( pipefd[0] );
814 
815     /* Save pipe write descriptor in the device block */
816     dev->fd = pipefd[1];
817     dev->ptpcpid = pid;
818 
819 #endif /* defined( _MSVC_ ) */
820 
821     return 0;
822 
823 } /* end function open_printer */
824 
825 /*-------------------------------------------------------------------*/
826 /* Subroutine to write data to the printer                           */
827 /*-------------------------------------------------------------------*/
828 static void
write_buffer(DEVBLK * dev,char * buf,int len,BYTE * unitstat)829 write_buffer (DEVBLK *dev, char *buf, int len, BYTE *unitstat)
830 {
831 int             rc;                     /* Return code               */
832 
833     /* Write data to the printer file */
834     if (dev->bs)
835     {
836         /* (socket printer) */
837         rc = write_socket (dev->fd, buf, len);
838 
839         /* Check for socket error */
840         if (rc < len)
841         {
842             /* Close the connection */
843             if (dev->fd != -1)
844             {
845                 int fd = dev->fd;
846                 dev->fd = -1;
847                 close_socket( fd );
848                 logmsg (_("HHCPR017I %s (%s) disconnected from device %4.4X (%s)\n"),
849                     dev->bs->clientname, dev->bs->clientip, dev->devnum, dev->bs->spec);
850             }
851 
852             /* Set unit check with intervention required */
853             dev->sense[0] = SENSE_IR;
854             *unitstat = CSW_CE | CSW_DE | CSW_UC;
855         }
856     }
857     else
858     {
859         /* Write data to the printer file */
860         rc = write (dev->fd, buf, len);
861 
862         /* Equipment check if error writing to printer file */
863         if (rc < len)
864         {
865             logmsg (_("HHCPR003E %4.4X Error writing to %s: %s\n"),
866                     dev->devnum, dev->filename,
867                     (errno == 0 ? _("incomplete"): strerror(errno)));
868             dev->sense[0] = SENSE_EC;
869             *unitstat = CSW_CE | CSW_DE | CSW_UC;
870         }
871     }
872 
873 } /* end function write_buffer */
874 
875 /*-------------------------------------------------------------------*/
876 /* Close the device                                                  */
877 /*-------------------------------------------------------------------*/
printer_close_device(DEVBLK * dev)878 static int printer_close_device ( DEVBLK *dev )
879 {
880 int fd = dev->fd;
881 
882     if (fd == -1)
883         return 0;
884 
885     dev->fd      = -1;
886     dev->stopprt =  0;
887 
888     /* Close the device file */
889     if ( dev->ispiped )
890     {
891 #if !defined( _MSVC_ )
892         close_pipe (fd);
893 #else /* defined( _MSVC_ ) */
894         close (fd);
895         /* Log end of child process */
896         logmsg (_("HHCPR011I pipe receiver (pid=%d) terminating for %4.4X\n"),
897                 dev->ptpcpid, dev->devnum);
898 #endif /* defined( _MSVC_ ) */
899         dev->ptpcpid = 0;
900     }
901     else
902     {
903         if (dev->bs)
904         {
905             /* Socket printer */
906             close_socket (fd);
907             logmsg (_("HHCPR018I %s (%s) disconnected from device %4.4X (%s)\n"),
908                 dev->bs->clientname, dev->bs->clientip, dev->devnum, dev->bs->spec);
909         }
910         else
911         {
912             /* Regular printer */
913             close (fd);
914         }
915     }
916 
917     return 0;
918 } /* end function printer_close_device */
919 
920 /*-------------------------------------------------------------------*/
921 /* Execute a Channel Command Word                                    */
922 /*-------------------------------------------------------------------*/
printer_execute_ccw(DEVBLK * dev,BYTE code,BYTE flags,BYTE chained,U16 count,BYTE prevcode,int ccwseq,BYTE * iobuf,BYTE * more,BYTE * unitstat,U16 * residual)923 static void printer_execute_ccw (DEVBLK *dev, BYTE code, BYTE flags,
924         BYTE chained, U16 count, BYTE prevcode, int ccwseq,
925         BYTE *iobuf, BYTE *more, BYTE *unitstat, U16 *residual)
926 {
927 int             rc = 0;                 /* Return code               */
928 int             i;                      /* Loop counter              */
929 int             num;                    /* Number of bytes to move   */
930 char           *eor;                    /* -> end of record string   */
931 char           *nls = "\n\n\n";         /* -> new lines              */
932 BYTE            c;                      /* Print character           */
933 char            hex[3];                 /* for hex conversion        */
934 char            wbuf[150];
935 
936     /* Reset flags at start of CCW chain */
937     if (chained == 0)
938     {
939         dev->diaggate = 0;
940     }
941 
942     /* Open the device file if necessary */
943     if (dev->fd < 0 && !IS_CCW_SENSE(code))
944         rc = open_printer (dev);
945     else
946     {
947         /* If printer stopped, return intervention required */
948         if (dev->stopprt && !IS_CCW_SENSE(code))
949             rc = -1;
950         else
951             rc = 0;
952     }
953 
954     if (rc < 0)
955     {
956         /* Set unit check with intervention required */
957         dev->sense[0] = SENSE_IR;
958         *unitstat = CSW_UC;
959         return;
960     }
961 
962     /* Process depending on CCW opcode */
963 
964     switch (code) {
965 
966     case 0x01: /* Write     No Space             */
967     case 0x09: /* Write and Space 1 Line         */
968     case 0x11: /* Write and Space 2 Lines        */
969     case 0x19: /* Write and Space 3 Lines        */
970 
971 
972     case 0x89: /* Write and Skip to Channel 1    */
973     case 0x91: /* Write and Skip to Channel 2    */
974     case 0x99: /* Write and Skip to Channel 3    */
975     case 0xA1: /* Write and Skip to Channel 4    */
976     case 0xA9: /* Write and Skip to Channel 5    */
977     case 0xB1: /* Write and Skip to Channel 6    */
978     case 0xB9: /* Write and Skip to Channel 7    */
979     case 0xC1: /* Write and Skip to Channel 8    */
980     case 0xC9: /* Write and Skip to Channel 9    */
981     case 0xD1: /* Write and Skip to Channel 10   */
982     case 0xD9: /* Write and Skip to Channel 11   */
983     case 0xE1: /* Write and Skip to Channel 12   */
984         if (dev->rawcc)
985         {
986             sprintf(hex,"%02x",code);
987             write_buffer(dev, hex, 2, unitstat);
988             if (*unitstat != 0) return;
989             WRITE_LINE();
990             write_buffer(dev, "\n", 1, unitstat);
991             if (*unitstat == 0)
992                 *unitstat = CSW_CE | CSW_DE;
993             return;
994         }
995 
996         if ( dev->browse && dev->ccpend && ((chained & CCW_FLAGS_CD) == 0) )
997         {
998             dev->ccpend = 0;
999             /* dev->currline++; */
1000             write_buffer(dev, "\n", 1, unitstat);
1001             if (*unitstat != 0) return;
1002         }
1003         WRITE_LINE();
1004         if ((flags & CCW_FLAGS_CD) == 0)
1005         {
1006             if    ( code <= 0x80 ) /* line control */
1007             {
1008                 coun = code / 8;
1009                 if  ( coun == 0 )
1010                 {
1011                     dev->chskip = 1;
1012                     if ( dev->browse )
1013                     {
1014                         dev->ccpend = 1;
1015                         *unitstat = 0;
1016                     }
1017                     else
1018                         write_buffer(dev, "\r", 1, unitstat);
1019                     if (*unitstat == 0)
1020                         *unitstat = CSW_CE | CSW_DE;
1021                     return;
1022                 }
1023 
1024                 dev->ccpend = 0;
1025                 dev->currline += coun;
1026                 write_buffer(dev, nls, coun, unitstat);
1027                 if (*unitstat == 0)
1028                     *unitstat = CSW_CE | CSW_DE;
1029                 return;
1030             }
1031             else  /*code >  0x80*/ /* chan control */
1032             {
1033                 /*
1034                 if ( dev->browse )
1035                 {
1036                     dev->currline++;
1037                     write_buffer(dev, "\n", 1, unitstat);
1038                     if (*unitstat != 0) return;
1039                 }
1040                 */
1041                 chan = ( code - 128 ) / 8;
1042                 if ( chan == 1 )
1043                 {
1044                     write_buffer(dev, "\r", 1, unitstat);
1045                     if (*unitstat != 0)
1046                         return;
1047                 }
1048                 SKIP_TO_CHAN();
1049                 if (*unitstat == 0)
1050                     *unitstat = CSW_CE | CSW_DE;
1051                 return;
1052             }
1053 
1054         }
1055         *unitstat = CSW_CE | CSW_DE;
1056         return;
1057 
1058     case 0x03: /* No Operation                   */
1059         *unitstat = CSW_CE | CSW_DE;
1060         break;
1061 
1062     case 0x0B: /*           Space 1 Line         */
1063     case 0x13: /*           Space 2 Lines        */
1064     case 0x1B: /*           Space 3 Lines        */
1065 
1066     case 0x8B: /*           Skip to Channel 1    */
1067     case 0x93: /*           Skip to Channel 2    */
1068     case 0x9B: /*           Skip to Channel 3    */
1069     case 0xA3: /*           Skip to Channel 4    */
1070     case 0xAB: /*           Skip to Channel 5    */
1071     case 0xB3: /*           Skip to Channel 6    */
1072     case 0xBB: /*           Skip to Channel 7    */
1073     case 0xC3: /*           Skip to Channel 8    */
1074     case 0xCB: /*           Skip to Channel 9    */
1075     case 0xD3: /*           Skip to Channel 10   */
1076     case 0xDB: /*           Skip to Channel 11   */
1077     case 0xE3: /*           Skip to Channel 12   */
1078         if (dev->rawcc)
1079         {
1080             sprintf(hex,"%02x",code);
1081             write_buffer(dev, hex, 2, unitstat);
1082             if (*unitstat != 0) return;
1083             eor = (dev->crlf) ? "\r\n" : "\n";
1084             write_buffer(dev, eor, strlen(eor), unitstat);
1085             if (*unitstat == 0)
1086                 *unitstat = CSW_CE | CSW_DE;
1087             return;
1088         }
1089 
1090         if    ( code <= 0x80 ) /* line control */
1091         {
1092             coun = code / 8;
1093             dev->ccpend = 0;
1094             dev->currline += coun;
1095             write_buffer(dev, nls, coun, unitstat);
1096             if (*unitstat == 0)
1097                 *unitstat = CSW_CE | CSW_DE;
1098             return;
1099         }
1100         else  /*code >  0x80*/ /* chan control */
1101         {
1102             /*
1103             if ( dev->browse && dev->ccpend)
1104             {
1105                 coun = 1;
1106                 dev->ccpend = 0;
1107                 dev->currline += coun;
1108                 write_buffer(dev, nls, coun, unitstat);
1109                 if (*unitstat != 0) return;
1110             }
1111             */
1112             chan = ( code - 128 ) / 8;
1113             SKIP_TO_CHAN();
1114             if (*unitstat == 0)
1115                 *unitstat = CSW_CE | CSW_DE;
1116             return;
1117         }
1118         break;
1119 
1120     case 0x63:
1121     /*---------------------------------------------------------------*/
1122     /* LOAD FORMS CONTROL BUFFER                                     */
1123     /*---------------------------------------------------------------*/
1124         if (dev->rawcc)
1125         {
1126             sprintf(hex,"%02x",code);
1127             write_buffer(dev, hex, 2, unitstat);
1128             if (*unitstat != 0) return;
1129             for (i = 0; i < count; i++)
1130             {
1131                 sprintf(hex,"%02x",iobuf[i]);
1132                 dev->buf[i*2] = hex[0];
1133                 dev->buf[i*2+1] = hex[1];
1134             } /* end for(i) */
1135             write_buffer(dev, (char *)dev->buf, i*2, unitstat);
1136             if (*unitstat != 0) return;
1137             eor = (dev->crlf) ? "\r\n" : "\n";
1138             write_buffer(dev, eor, strlen(eor), unitstat);
1139             if (*unitstat != 0) return;
1140         }
1141         else
1142         {
1143             int i = 0;
1144             int j = 1;
1145             int more = 1;
1146             for (i = 0; i <= FCBSIZE; i++) dev->fcb[i] = 0;
1147 
1148             dev->lpi = 6;
1149             dev->index = 0;
1150             i = 0;
1151             if (iobuf[0] & 0xc0)
1152             {
1153                 /* First byte is a print position index */
1154                 if ((iobuf[0] & 0xc0) == 0x80)
1155                     /* Indexing right */
1156                     dev->index = iobuf[0] & 0x1f;
1157                 else
1158                     /* Indexing left */
1159                     dev->index = - (iobuf[0] & 0x1f);
1160                 i = 1;
1161             }
1162 
1163             for (; i < count && j <= FCBSIZE && more; i++, j++)
1164             {
1165                 dev->fcb[i] = iobuf[i] & 0x0f;
1166                 if (dev->fcb[j] > 12)
1167                 {
1168                     *residual = count - i;
1169                     *unitstat = CSW_CE | CSW_DE | CSW_UC;
1170                     dev->sense[0] = SENSE_CC;
1171                     return;
1172                 }
1173 
1174                 if (iobuf[i] & 0x10)
1175                 {
1176                     /* Flag bit is on */
1177                     if (j == 1)
1178                         /* Flag bit in first byte means eight lines per inch */
1179                         dev->lpi = 8;
1180                     else
1181                         more = 0;
1182                 }
1183             }
1184             if (more)
1185             {
1186                 /* No flag in last byte or too many bytes */
1187                 *residual = count - i;
1188                 *unitstat = CSW_CE | CSW_DE | CSW_UC;
1189                 dev->sense[0] = SENSE_CC;
1190                 return;
1191             }
1192             *residual = count - i;
1193             dev->lpp = j - 1;
1194 
1195             fcb_dump(dev, wbuf, 150);
1196             logmsg("HHCPN210I %d:%4.4X %s\n",
1197                     SSID_TO_LCSS(dev->ssid), dev->devnum, wbuf);
1198         }
1199         /* Return normal status */
1200         *residual = 0;
1201         *unitstat = CSW_CE | CSW_DE;
1202         break;
1203 
1204     case 0x06:
1205     /*---------------------------------------------------------------*/
1206     /* DIAGNOSTIC CHECK READ                                         */
1207     /*---------------------------------------------------------------*/
1208         /* If not 1403, reject if not preceded by DIAGNOSTIC GATE */
1209         if (dev->devtype != 0x1403 && dev->diaggate == 0)
1210         {
1211             dev->sense[0] = SENSE_CR;
1212             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1213             break;
1214         }
1215 
1216         /* Return normal status */
1217         *unitstat = CSW_CE | CSW_DE;
1218         break;
1219 
1220     case 0x07:
1221     /*---------------------------------------------------------------*/
1222     /* DIAGNOSTIC GATE                                               */
1223     /*---------------------------------------------------------------*/
1224         /* Command reject if 1403, or if chained to another CCW
1225            except a no-operation at the start of the CCW chain */
1226         if (dev->devtype == 0x1403 || ccwseq > 1
1227             || (chained && prevcode != 0x03))
1228         {
1229             dev->sense[0] = SENSE_CR;
1230             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1231             break;
1232         }
1233 
1234         /* Set diagnostic gate flag */
1235         dev->diaggate = 1;
1236 
1237         /* Return normal status */
1238         *unitstat = CSW_CE | CSW_DE;
1239         break;
1240 
1241     case 0x0A:
1242     /*---------------------------------------------------------------*/
1243     /* DIAGNOSTIC READ UCS BUFFER                                    */
1244     /*---------------------------------------------------------------*/
1245         /* Reject if 1403 or not preceded by DIAGNOSTIC GATE */
1246         if (dev->devtype == 0x1403 || dev->diaggate == 0)
1247         {
1248             dev->sense[0] = SENSE_CR;
1249             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1250             break;
1251         }
1252 
1253         /* Return normal status */
1254         *unitstat = CSW_CE | CSW_DE;
1255         break;
1256 
1257     case 0x12:
1258     /*---------------------------------------------------------------*/
1259     /* DIAGNOSTIC READ fcb                                           */
1260     /*---------------------------------------------------------------*/
1261         /* Reject if 1403 or not preceded by DIAGNOSTIC GATE */
1262         if (dev->devtype == 0x1403 || dev->diaggate == 0)
1263         {
1264             dev->sense[0] = SENSE_CR;
1265             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1266             break;
1267         }
1268 
1269         /* Return normal status */
1270         *unitstat = CSW_CE | CSW_DE;
1271         break;
1272 
1273     case 0x23:
1274     /*---------------------------------------------------------------*/
1275     /* UNFOLD                                                        */
1276     /*---------------------------------------------------------------*/
1277         dev->fold = 0;
1278         *unitstat = CSW_CE | CSW_DE;
1279         break;
1280 
1281     case 0x43:
1282     /*---------------------------------------------------------------*/
1283     /* FOLD                                                          */
1284     /*---------------------------------------------------------------*/
1285         dev->fold = 1;
1286         *unitstat = CSW_CE | CSW_DE;
1287         break;
1288 
1289     case 0x73:
1290     /*---------------------------------------------------------------*/
1291     /* BLOCK DATA CHECK                                              */
1292     /*---------------------------------------------------------------*/
1293     /*
1294         *residual = 0;
1295     */
1296         *unitstat = CSW_CE | CSW_DE;
1297         break;
1298 
1299     case 0x7B:
1300     /*---------------------------------------------------------------*/
1301     /* ALLOW DATA CHECK                                              */
1302     /*---------------------------------------------------------------*/
1303     /*
1304         *residual = 0;
1305     */
1306         *unitstat = CSW_CE | CSW_DE;
1307         break;
1308 
1309     case 0xEB:
1310     /*---------------------------------------------------------------*/
1311     /* UCS GATE LOAD                                                 */
1312     /*---------------------------------------------------------------*/
1313         /* Command reject if not first command in chain */
1314         if (chained != 0)
1315         {
1316             dev->sense[0] = SENSE_CR;
1317             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1318             break;
1319         }
1320 
1321         /* Return normal status */
1322         *unitstat = CSW_CE | CSW_DE;
1323         break;
1324 
1325     case 0xF3:
1326     /*---------------------------------------------------------------*/
1327     /* LOAD UCS BUFFER AND FOLD                                      */
1328     /*---------------------------------------------------------------*/
1329         /* For 1403, command reject if not chained to UCS GATE */
1330         /* Also allow ALLOW DATA CHECK to get TSS/370 working  */
1331         /* -- JRM 11/28/2007 */
1332         if (dev->devtype == 0x1403 &&
1333             ((prevcode != 0xEB) && (prevcode != 0x7B)))
1334         {
1335             dev->sense[0] = SENSE_CR;
1336             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1337             break;
1338         }
1339 
1340         /* Set fold indicator and return normal status */
1341         dev->fold = 1;
1342         dev->chskip = 1;
1343     /*
1344         *residual = 0;
1345     */
1346         *unitstat = CSW_CE | CSW_DE;
1347         break;
1348 
1349     case 0xFB:
1350     /*---------------------------------------------------------------*/
1351     /* LOAD UCS BUFFER (NO FOLD)                                     */
1352     /*---------------------------------------------------------------*/
1353         /* For 1403, command reject if not chained to UCS GATE */
1354         if (dev->devtype == 0x1403 && prevcode != 0xEB)
1355         {
1356             dev->sense[0] = SENSE_CR;
1357             *unitstat = CSW_CE | CSW_DE | CSW_UC;
1358             break;
1359         }
1360 
1361         /* Reset fold indicator and return normal status */
1362         dev->fold = 0;
1363         dev->chskip = 1;
1364 
1365     /*
1366         *residual = 0;
1367     */
1368         *unitstat = CSW_CE | CSW_DE;
1369         break;
1370 
1371     case 0x04:
1372     /*---------------------------------------------------------------*/
1373     /* SENSE                                                         */
1374     /*---------------------------------------------------------------*/
1375         /* Calculate residual byte count */
1376         num = (count < dev->numsense) ? count : dev->numsense;
1377         *residual = count - num;
1378         if (count < dev->numsense) *more = 1;
1379 
1380         /* Copy device sense bytes to channel I/O buffer */
1381         memcpy (iobuf, dev->sense, num);
1382 
1383         /* Clear the device sense bytes */
1384         memset (dev->sense, 0, sizeof(dev->sense));
1385 
1386         /* Return unit status */
1387         *unitstat = CSW_CE | CSW_DE;
1388         break;
1389 
1390     case 0xE4:
1391     /*---------------------------------------------------------------*/
1392     /* SENSE ID                                                      */
1393     /*---------------------------------------------------------------*/
1394         /* Calculate residual byte count */
1395         num = (count < dev->numdevid) ? count : dev->numdevid;
1396         *residual = count - num;
1397         if (count < dev->numdevid) *more = 1;
1398 
1399         /* Copy device identifier bytes to channel I/O buffer */
1400         memcpy (iobuf, dev->devid, num);
1401 
1402         /* Return unit status */
1403         *unitstat = CSW_CE | CSW_DE;
1404         break;
1405 
1406     default:
1407     /*---------------------------------------------------------------*/
1408     /* INVALID OPERATION                                             */
1409     /*---------------------------------------------------------------*/
1410         /* Set command reject sense byte, and unit check status */
1411         dev->sense[0] = SENSE_CR;
1412         *unitstat = CSW_UC;
1413 
1414     } /* end switch(code) */
1415 
1416 } /* end function printer_execute_ccw */
1417 
1418 
1419 #if defined(OPTION_DYNAMIC_LOAD)
1420 static
1421 #endif
1422 DEVHND printer_device_hndinfo = {
1423         &printer_init_handler,         /* Device Initialisation      */
1424         &printer_execute_ccw,          /* Device CCW execute         */
1425         &printer_close_device,         /* Device Close               */
1426         &printer_query_device,         /* Device Query               */
1427         NULL,                          /* Device Start channel pgm   */
1428         NULL,                          /* Device End channel pgm     */
1429         NULL,                          /* Device Resume channel pgm  */
1430         NULL,                          /* Device Suspend channel pgm */
1431         NULL,                          /* Device Read                */
1432         NULL,                          /* Device Write               */
1433         NULL,                          /* Device Query used          */
1434         NULL,                          /* Device Reserve             */
1435         NULL,                          /* Device Release             */
1436         NULL,                          /* Device Attention           */
1437         printer_immed_commands,        /* Immediate CCW Codes        */
1438         NULL,                          /* Signal Adapter Input       */
1439         NULL,                          /* Signal Adapter Output      */
1440         NULL,                          /* Hercules suspend           */
1441         NULL                           /* Hercules resume            */
1442 };
1443 
1444 /* Libtool static name colision resolution */
1445 /* note : lt_dlopen will look for symbol & modulename_LTX_symbol */
1446 #if !defined(HDL_BUILD_SHARED) && defined(HDL_USE_LIBTOOL)
1447 #define hdl_ddev hdt1403_LTX_hdl_ddev
1448 #define hdl_depc hdt1403_LTX_hdl_depc
1449 #define hdl_reso hdt1403_LTX_hdl_reso
1450 #define hdl_init hdt1403_LTX_hdl_init
1451 #define hdl_fini hdt1403_LTX_hdl_fini
1452 #endif
1453 
1454 #if defined(OPTION_DYNAMIC_LOAD)
1455 HDL_DEPENDENCY_SECTION;
1456 {
1457      HDL_DEPENDENCY(HERCULES);
1458      HDL_DEPENDENCY(DEVBLK);
1459 }
1460 END_DEPENDENCY_SECTION
1461 
1462 
1463 HDL_DEVICE_SECTION;
1464 {
1465     HDL_DEVICE(1403, printer_device_hndinfo );
1466     HDL_DEVICE(3211, printer_device_hndinfo );
1467 }
1468 END_DEVICE_SECTION
1469 #endif
1470