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