]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/parallel.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / parallel.c
1 /*
2 * "$Id: parallel.c 5387 2006-04-10 02:51:12Z mike $"
3 *
4 * Parallel port backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * main() - Send a file to the specified parallel port.
29 * list_devices() - List all parallel devices.
30 */
31
32 /*
33 * Include necessary headers.
34 */
35
36 #include <cups/backend.h>
37 #include <cups/cups.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <cups/string.h>
42 #include <signal.h>
43 #include "ieee1284.c"
44
45 #ifdef __hpux
46 # include <sys/time.h>
47 #else
48 # include <sys/select.h>
49 #endif /* __hpux */
50
51 #ifdef WIN32
52 # include <io.h>
53 #else
54 # include <unistd.h>
55 # include <fcntl.h>
56 # include <termios.h>
57 # include <sys/socket.h>
58 #endif /* WIN32 */
59
60 #ifdef __sgi
61 # include <invent.h>
62 # ifndef INV_EPP_ECP_PLP
63 # define INV_EPP_ECP_PLP 6 /* From 6.3/6.4/6.5 sys/invent.h */
64 # define INV_ASO_SERIAL 14 /* serial portion of SGI ASO board */
65 # define INV_IOC3_DMA 16 /* DMA mode IOC3 serial */
66 # define INV_IOC3_PIO 17 /* PIO mode IOC3 serial */
67 # define INV_ISA_DMA 19 /* DMA mode ISA serial -- O2 */
68 # endif /* !INV_EPP_ECP_PLP */
69 #endif /* __sgi */
70
71
72 /*
73 * Local functions...
74 */
75
76 void list_devices(void);
77
78
79 /*
80 * 'main()' - Send a file to the specified parallel port.
81 *
82 * Usage:
83 *
84 * printer-uri job-id user title copies options [file]
85 */
86
87 int /* O - Exit status */
88 main(int argc, /* I - Number of command-line arguments (6 or 7) */
89 char *argv[]) /* I - Command-line arguments */
90 {
91 char method[255], /* Method in URI */
92 hostname[1024], /* Hostname */
93 username[255], /* Username info (not used) */
94 resource[1024], /* Resource info (device and options) */
95 *options; /* Pointer to options */
96 int port; /* Port number (not used) */
97 int fp; /* Print file */
98 int copies; /* Number of copies to print */
99 int fd; /* Parallel device */
100 int rbytes; /* Number of bytes read */
101 int wbytes; /* Number of bytes written */
102 size_t nbytes, /* Number of bytes read */
103 tbytes; /* Total number of bytes written */
104 char buffer[8192], /* Output buffer */
105 *bufptr; /* Pointer into buffer */
106 struct termios opts; /* Parallel port options */
107 fd_set input, /* Input set for select() */
108 output; /* Output set for select() */
109 int paperout; /* Paper out? */
110 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
111 struct sigaction action; /* Actions for POSIX signals */
112 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
113 #ifdef __linux
114 unsigned int status; /* Port status (off-line, out-of-paper, etc.) */
115 #endif /* __linux */
116
117
118 /*
119 * Make sure status messages are not buffered...
120 */
121
122 setbuf(stderr, NULL);
123
124 /*
125 * Ignore SIGPIPE signals...
126 */
127
128 #ifdef HAVE_SIGSET
129 sigset(SIGPIPE, SIG_IGN);
130 #elif defined(HAVE_SIGACTION)
131 memset(&action, 0, sizeof(action));
132 action.sa_handler = SIG_IGN;
133 sigaction(SIGPIPE, &action, NULL);
134 #else
135 signal(SIGPIPE, SIG_IGN);
136 #endif /* HAVE_SIGSET */
137
138 /*
139 * Check command-line...
140 */
141
142 if (argc == 1)
143 {
144 list_devices();
145 return (CUPS_BACKEND_OK);
146 }
147 else if (argc < 6 || argc > 7)
148 {
149 fputs("Usage: parallel job-id user title copies options [file]\n", stderr);
150 return (CUPS_BACKEND_FAILED);
151 }
152
153 /*
154 * If we have 7 arguments, print the file named on the command-line.
155 * Otherwise, send stdin instead...
156 */
157
158 if (argc == 6)
159 {
160 fp = 0;
161 copies = 1;
162 }
163 else
164 {
165 /*
166 * Try to open the print file...
167 */
168
169 if ((fp = open(argv[6], O_RDONLY)) < 0)
170 {
171 perror("ERROR: unable to open print file");
172 return (CUPS_BACKEND_FAILED);
173 }
174
175 copies = atoi(argv[4]);
176 }
177
178 /*
179 * Extract the device name and options from the URI...
180 */
181
182 httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
183 method, sizeof(method), username, sizeof(username),
184 hostname, sizeof(hostname), &port,
185 resource, sizeof(resource));
186
187 /*
188 * See if there are any options...
189 */
190
191 if ((options = strchr(resource, '?')) != NULL)
192 {
193 /*
194 * Yup, terminate the device name string and move to the first
195 * character of the options...
196 */
197
198 *options++ = '\0';
199 }
200
201 /*
202 * Open the parallel port device...
203 */
204
205 fputs("STATE: +connecting-to-device\n", stderr);
206
207 do
208 {
209 if ((fd = open(resource, O_WRONLY | O_EXCL)) == -1)
210 {
211 if (getenv("CLASS") != NULL)
212 {
213 /*
214 * If the CLASS environment variable is set, the job was submitted
215 * to a class and not to a specific queue. In this case, we want
216 * to abort immediately so that the job can be requeued on the next
217 * available printer in the class.
218 */
219
220 fputs("INFO: Unable to open parallel port, queuing on next printer in class...\n",
221 stderr);
222
223 /*
224 * Sleep 5 seconds to keep the job from requeuing too rapidly...
225 */
226
227 sleep(5);
228
229 return (CUPS_BACKEND_FAILED);
230 }
231
232 if (errno == EBUSY)
233 {
234 fputs("INFO: Parallel port busy; will retry in 30 seconds...\n", stderr);
235 sleep(30);
236 }
237 else if (errno == ENXIO || errno == EIO || errno == ENOENT)
238 {
239 fputs("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
240 sleep(30);
241 }
242 else
243 {
244 fprintf(stderr, "ERROR: Unable to open parallel port device file \"%s\": %s\n",
245 resource, strerror(errno));
246 return (CUPS_BACKEND_FAILED);
247 }
248 }
249 }
250 while (fd < 0);
251
252 fputs("STATE: -connecting-to-device\n", stderr);
253
254 /*
255 * Set any options provided...
256 */
257
258 tcgetattr(fd, &opts);
259
260 opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
261
262 /**** No options supported yet ****/
263
264 tcsetattr(fd, TCSANOW, &opts);
265
266 /*
267 * Check printer status...
268 */
269
270 paperout = 0;
271
272 #if defined(__linux) && defined(LP_POUTPA)
273 /*
274 * Show the printer status before we send the file...
275 */
276
277 while (!ioctl(fd, LPGETSTATUS, &status))
278 {
279 fprintf(stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
280
281 if (status & LP_POUTPA)
282 {
283 fputs("WARNING: Media tray empty!\n", stderr);
284 fputs("STATUS: +media-tray-empty-error\n", stderr);
285
286 paperout = 1;
287 }
288
289 if (!(status & LP_PERRORP))
290 fputs("WARNING: Printer fault!\n", stderr);
291 else if (!(status & LP_PSELECD))
292 fputs("WARNING: Printer off-line.\n", stderr);
293 else
294 break;
295
296 sleep(5);
297 }
298 #endif /* __linux && LP_POUTPA */
299
300 /*
301 * Now that we are "connected" to the port, ignore SIGTERM so that we
302 * can finish out any page data the driver sends (e.g. to eject the
303 * current page... Only ignore SIGTERM if we are printing data from
304 * stdin (otherwise you can't cancel raw jobs...)
305 */
306
307 if (argc < 7)
308 {
309 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
310 sigset(SIGTERM, SIG_IGN);
311 #elif defined(HAVE_SIGACTION)
312 memset(&action, 0, sizeof(action));
313
314 sigemptyset(&action.sa_mask);
315 action.sa_handler = SIG_IGN;
316 sigaction(SIGTERM, &action, NULL);
317 #else
318 signal(SIGTERM, SIG_IGN);
319 #endif /* HAVE_SIGSET */
320 }
321
322 /*
323 * Finally, send the print file...
324 */
325
326 wbytes = 0;
327
328 while (copies > 0)
329 {
330 copies --;
331
332 if (fp != 0)
333 {
334 fputs("PAGE: 1 1\n", stderr);
335 lseek(fp, 0, SEEK_SET);
336 }
337
338 tbytes = 0;
339 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
340 {
341 /*
342 * Write the print data to the printer...
343 */
344
345 tbytes += nbytes;
346 bufptr = buffer;
347
348 while (nbytes > 0)
349 {
350 /*
351 * See if we are ready to read or write...
352 */
353
354 do
355 {
356 FD_ZERO(&input);
357 FD_SET(fd, &input);
358 FD_ZERO(&output);
359 FD_SET(fd, &output);
360 }
361 while (select(fd + 1, &input, &output, NULL, NULL) < 0);
362
363 if (FD_ISSET(fd, &input))
364 {
365 /*
366 * Read backchannel data...
367 */
368
369 if ((rbytes = read(fd, resource, sizeof(resource))) > 0)
370 {
371 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
372 rbytes);
373 cupsBackChannelWrite(resource, rbytes, 1.0);
374 }
375 }
376
377 if (FD_ISSET(fd, &output))
378 {
379 /*
380 * Write print data...
381 */
382
383 if ((wbytes = write(fd, bufptr, nbytes)) < 0)
384 if (errno == ENOTTY)
385 wbytes = write(fd, bufptr, nbytes);
386
387 if (wbytes < 0)
388 {
389 /*
390 * Check for retryable errors...
391 */
392
393 if (errno == ENOSPC)
394 {
395 paperout = 1;
396 fputs("ERROR: Out of paper!\n", stderr);
397 fputs("STATUS: +media-tray-empty-error\n", stderr);
398 }
399 else if (errno != EAGAIN && errno != EINTR)
400 {
401 perror("ERROR: Unable to send print file to printer");
402 break;
403 }
404 }
405 else
406 {
407 /*
408 * Update count and pointer...
409 */
410
411 if (paperout)
412 {
413 fputs("STATUS: -media-tray-empty-error\n", stderr);
414 paperout = 0;
415 }
416
417 nbytes -= wbytes;
418 bufptr += wbytes;
419 }
420 }
421 }
422
423 if (wbytes < 0)
424 break;
425
426 if (argc > 6)
427 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
428 (unsigned long)tbytes);
429 }
430 }
431
432 /*
433 * Close the socket connection and input file and return...
434 */
435
436 close(fd);
437 if (fp != 0)
438 close(fp);
439
440 return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
441 }
442
443
444 /*
445 * 'list_devices()' - List all parallel devices.
446 */
447
448 void
449 list_devices(void)
450 {
451 #if defined(__hpux) || defined(__sgi) || defined(__sun)
452 static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
453 /* Funky hex numbering used for some devices */
454 #endif /* __hpux || __sgi || __sun */
455
456 #ifdef __linux
457 int i; /* Looping var */
458 int fd; /* File descriptor */
459 char device[255], /* Device filename */
460 basedevice[255], /* Base device filename for ports */
461 device_id[1024], /* Device ID string */
462 make_model[1024]; /* Make and model */
463
464
465 if (!access("/dev/parallel/", 0))
466 strcpy(basedevice, "/dev/parallel/");
467 else if (!access("/dev/printers/", 0))
468 strcpy(basedevice, "/dev/printers/");
469 else
470 strcpy(basedevice, "/dev/lp");
471
472 for (i = 0; i < 4; i ++)
473 {
474 /*
475 * Open the port, if available...
476 */
477
478 sprintf(device, "%s%d", basedevice, i);
479 if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
480 fd = open(device, O_WRONLY);
481
482 if (fd >= 0)
483 {
484 /*
485 * Now grab the IEEE 1284 device ID string...
486 */
487
488 if (!get_device_id(fd, device_id, sizeof(device_id),
489 make_model, sizeof(make_model),
490 NULL, NULL, 0))
491 printf("direct parallel:%s \"%s\" \"%s LPT #%d\" \"%s\"\n", device,
492 make_model, make_model, i + 1, device_id);
493 else
494 printf("direct parallel:%s \"Unknown\" \"LPT #%d\"\n", device, i + 1);
495
496 close(fd);
497 }
498 }
499 #elif defined(__sgi)
500 int i, j, n; /* Looping vars */
501 char device[255]; /* Device filename */
502 inventory_t *inv; /* Hardware inventory info */
503
504
505 /*
506 * IRIX maintains a hardware inventory of most devices...
507 */
508
509 setinvent();
510
511 while ((inv = getinvent()) != NULL)
512 {
513 if (inv->inv_class == INV_PARALLEL &&
514 (inv->inv_type == INV_ONBOARD_PLP ||
515 inv->inv_type == INV_EPP_ECP_PLP))
516 {
517 /*
518 * Standard parallel port...
519 */
520
521 puts("direct parallel:/dev/plp \"Unknown\" \"Onboard Parallel Port\"");
522 }
523 else if (inv->inv_class == INV_PARALLEL &&
524 inv->inv_type == INV_EPC_PLP)
525 {
526 /*
527 * EPC parallel port...
528 */
529
530 printf("direct parallel:/dev/plp%d \"Unknown\" \"Integral EPC parallel port, Ebus slot %d\"\n",
531 inv->inv_controller, inv->inv_controller);
532 }
533 }
534
535 endinvent();
536
537 /*
538 * Central Data makes serial and parallel "servers" that can be
539 * connected in a number of ways. Look for ports...
540 */
541
542 for (i = 0; i < 10; i ++)
543 for (j = 0; j < 8; j ++)
544 for (n = 0; n < 32; n ++)
545 {
546 if (i == 8) /* EtherLite */
547 sprintf(device, "/dev/lpn%d%c", j, funky_hex[n]);
548 else if (i == 9) /* PCI */
549 sprintf(device, "/dev/lpp%d%c", j, funky_hex[n]);
550 else /* SCSI */
551 sprintf(device, "/dev/lp%d%d%c", i, j, funky_hex[n]);
552
553 if (access(device, 0) == 0)
554 {
555 if (i == 8)
556 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
557 device, j, n);
558 else if (i == 9)
559 printf("direct parallel:%s \"Unknown\" \"Central Data PCI Parallel Port, ID %d, port %d\"\n",
560 device, j, n);
561 else
562 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
563 device, i, j, n);
564 }
565 }
566 #elif defined(__sun)
567 int i, j, n; /* Looping vars */
568 char device[255]; /* Device filename */
569
570
571 /*
572 * Standard parallel ports...
573 */
574
575 for (i = 0; i < 10; i ++)
576 {
577 sprintf(device, "/dev/ecpp%d", i);
578 if (access(device, 0) == 0)
579 printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
580 device, i + 1);
581 }
582
583 for (i = 0; i < 10; i ++)
584 {
585 sprintf(device, "/dev/bpp%d", i);
586 if (access(device, 0) == 0)
587 printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
588 device, i + 1);
589 }
590
591 for (i = 0; i < 3; i ++)
592 {
593 sprintf(device, "/dev/lp%d", i);
594
595 if (access(device, 0) == 0)
596 printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
597 device, i + 1);
598 }
599
600 /*
601 * MAGMA parallel ports...
602 */
603
604 for (i = 0; i < 40; i ++)
605 {
606 sprintf(device, "/dev/pm%02d", i);
607 if (access(device, 0) == 0)
608 printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
609 device, (i / 10) + 1, (i % 10) + 1);
610 }
611
612 /*
613 * Central Data parallel ports...
614 */
615
616 for (i = 0; i < 9; i ++)
617 for (j = 0; j < 8; j ++)
618 for (n = 0; n < 32; n ++)
619 {
620 if (i == 8) /* EtherLite */
621 sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
622 else
623 sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
624 funky_hex[n]);
625
626 if (access(device, 0) == 0)
627 {
628 if (i == 8)
629 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
630 device, j, n);
631 else
632 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
633 device, i, j, n);
634 }
635 }
636 #elif defined(__hpux)
637 int i, j, n; /* Looping vars */
638 char device[255]; /* Device filename */
639
640
641 /*
642 * Standard parallel ports...
643 */
644
645 if (access("/dev/rlp", 0) == 0)
646 puts("direct parallel:/dev/rlp \"Unknown\" \"Standard Parallel Port (/dev/rlp)\"");
647
648 for (i = 0; i < 7; i ++)
649 for (j = 0; j < 7; j ++)
650 {
651 sprintf(device, "/dev/c%dt%dd0_lp", i, j);
652 if (access(device, 0) == 0)
653 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d,%d\"\n",
654 device, i, j);
655 }
656
657 /*
658 * Central Data parallel ports...
659 */
660
661 for (i = 0; i < 9; i ++)
662 for (j = 0; j < 8; j ++)
663 for (n = 0; n < 32; n ++)
664 {
665 if (i == 8) /* EtherLite */
666 sprintf(device, "/dev/lpN%d%c", j, funky_hex[n]);
667 else
668 sprintf(device, "/dev/lp%c%d%c", i + 'C', j,
669 funky_hex[n]);
670
671 if (access(device, 0) == 0)
672 {
673 if (i == 8)
674 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
675 device, j, n);
676 else
677 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
678 device, i, j, n);
679 }
680 }
681 #elif defined(__osf__)
682 int i; /* Looping var */
683 int fd; /* File descriptor */
684 char device[255]; /* Device filename */
685
686
687 for (i = 0; i < 3; i ++)
688 {
689 sprintf(device, "/dev/lp%d", i);
690 if ((fd = open(device, O_WRONLY)) >= 0)
691 {
692 close(fd);
693 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
694 }
695 }
696 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
697 int i; /* Looping var */
698 int fd; /* File descriptor */
699 char device[255]; /* Device filename */
700
701
702 for (i = 0; i < 3; i ++)
703 {
704 sprintf(device, "/dev/lpt%d", i);
705 if ((fd = open(device, O_WRONLY)) >= 0)
706 {
707 close(fd);
708 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (interrupt-driven)\"\n", device, i + 1);
709 }
710
711 sprintf(device, "/dev/lpa%d", i);
712 if ((fd = open(device, O_WRONLY)) >= 0)
713 {
714 close(fd);
715 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (polled)\"\n", device, i + 1);
716 }
717 }
718 #elif defined(_AIX)
719 int i; /* Looping var */
720 int fd; /* File descriptor */
721 char device[255]; /* Device filename */
722
723
724 for (i = 0; i < 8; i ++)
725 {
726 sprintf(device, "/dev/lp%d", i);
727 if ((fd = open(device, O_WRONLY)) >= 0)
728 {
729 close(fd);
730 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
731 }
732 }
733 #endif
734 }
735
736
737 /*
738 * End of "$Id: parallel.c 5387 2006-04-10 02:51:12Z mike $".
739 */