]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/parallel.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / parallel.c
CommitLineData
ef416fc2 1/*
b423cd4c 2 * "$Id: parallel.c 5194 2006-02-27 20:57:07Z mike $"
ef416fc2 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>
ef416fc2 43#include "ieee1284.c"
44
b423cd4c 45#ifdef __hpux
46# include <sys/time.h>
47#else
48# include <sys/select.h>
49#endif /* __hpux */
50
ef416fc2 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
76void 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
87int /* O - Exit status */
88main(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
a4d04587 182 httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
183 method, sizeof(method), username, sizeof(username),
184 hostname, sizeof(hostname), &port,
ef416fc2 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 do
206 {
207 if ((fd = open(resource, O_WRONLY | O_EXCL)) == -1)
208 {
209 if (getenv("CLASS") != NULL)
210 {
211 /*
212 * If the CLASS environment variable is set, the job was submitted
213 * to a class and not to a specific queue. In this case, we want
214 * to abort immediately so that the job can be requeued on the next
215 * available printer in the class.
216 */
217
218 fputs("INFO: Unable to open parallel port, queuing on next printer in class...\n",
219 stderr);
220
221 /*
222 * Sleep 5 seconds to keep the job from requeuing too rapidly...
223 */
224
225 sleep(5);
226
227 return (CUPS_BACKEND_FAILED);
228 }
229
230 if (errno == EBUSY)
231 {
232 fputs("INFO: Parallel port busy; will retry in 30 seconds...\n", stderr);
233 sleep(30);
234 }
235 else if (errno == ENXIO || errno == EIO || errno == ENOENT)
236 {
237 fputs("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
238 sleep(30);
239 }
240 else
241 {
242 fprintf(stderr, "ERROR: Unable to open parallel port device file \"%s\": %s\n",
243 resource, strerror(errno));
244 return (CUPS_BACKEND_FAILED);
245 }
246 }
247 }
248 while (fd < 0);
249
250 /*
251 * Set any options provided...
252 */
253
254 tcgetattr(fd, &opts);
255
256 opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
257
258 /**** No options supported yet ****/
259
260 tcsetattr(fd, TCSANOW, &opts);
261
262 /*
263 * Check printer status...
264 */
265
266 paperout = 0;
267
268#if defined(__linux) && defined(LP_POUTPA)
269 /*
270 * Show the printer status before we send the file...
271 */
272
273 while (!ioctl(fd, LPGETSTATUS, &status))
274 {
275 fprintf(stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
276
277 if (status & LP_POUTPA)
278 {
279 fputs("WARNING: Media tray empty!\n", stderr);
280 fputs("STATUS: +media-tray-empty-error\n", stderr);
281
282 paperout = 1;
283 }
284
285 if (!(status & LP_PERRORP))
286 fputs("WARNING: Printer fault!\n", stderr);
287 else if (!(status & LP_PSELECD))
288 fputs("WARNING: Printer off-line.\n", stderr);
289 else
290 break;
291
292 sleep(5);
293 }
294#endif /* __linux && LP_POUTPA */
295
296 /*
297 * Now that we are "connected" to the port, ignore SIGTERM so that we
298 * can finish out any page data the driver sends (e.g. to eject the
299 * current page... Only ignore SIGTERM if we are printing data from
300 * stdin (otherwise you can't cancel raw jobs...)
301 */
302
303 if (argc < 7)
304 {
305#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
306 sigset(SIGTERM, SIG_IGN);
307#elif defined(HAVE_SIGACTION)
308 memset(&action, 0, sizeof(action));
309
310 sigemptyset(&action.sa_mask);
311 action.sa_handler = SIG_IGN;
312 sigaction(SIGTERM, &action, NULL);
313#else
314 signal(SIGTERM, SIG_IGN);
315#endif /* HAVE_SIGSET */
316 }
317
318 /*
319 * Finally, send the print file...
320 */
321
322 wbytes = 0;
323
324 while (copies > 0)
325 {
326 copies --;
327
328 if (fp != 0)
329 {
330 fputs("PAGE: 1 1\n", stderr);
331 lseek(fp, 0, SEEK_SET);
332 }
333
334 tbytes = 0;
335 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
336 {
337 /*
338 * Write the print data to the printer...
339 */
340
341 tbytes += nbytes;
342 bufptr = buffer;
343
344 while (nbytes > 0)
345 {
346 /*
347 * See if we are ready to read or write...
348 */
349
350 do
351 {
352 FD_ZERO(&input);
353 FD_SET(fd, &input);
354 FD_ZERO(&output);
355 FD_SET(fd, &output);
356 }
357 while (select(fd + 1, &input, &output, NULL, NULL) < 0);
358
359 if (FD_ISSET(fd, &input))
360 {
361 /*
362 * Read backchannel data...
363 */
364
365 if ((rbytes = read(fd, resource, sizeof(resource))) > 0)
366 {
367 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
368 rbytes);
bd7854cb 369 cupsBackChannelWrite(resource, rbytes, 1.0);
ef416fc2 370 }
371 }
372
373 if (FD_ISSET(fd, &output))
374 {
375 /*
376 * Write print data...
377 */
378
379 if ((wbytes = write(fd, bufptr, nbytes)) < 0)
380 if (errno == ENOTTY)
381 wbytes = write(fd, bufptr, nbytes);
382
383 if (wbytes < 0)
384 {
385 /*
386 * Check for retryable errors...
387 */
388
389 if (errno == ENOSPC)
390 {
391 paperout = 1;
392 fputs("ERROR: Out of paper!\n", stderr);
393 fputs("STATUS: +media-tray-empty-error\n", stderr);
394 }
395 else if (errno != EAGAIN && errno != EINTR)
396 {
397 perror("ERROR: Unable to send print file to printer");
398 break;
399 }
400 }
401 else
402 {
403 /*
404 * Update count and pointer...
405 */
406
407 if (paperout)
408 {
409 fputs("STATUS: -media-tray-empty-error\n", stderr);
410 paperout = 0;
411 }
412
413 nbytes -= wbytes;
414 bufptr += wbytes;
415 }
416 }
417 }
418
419 if (wbytes < 0)
420 break;
421
422 if (argc > 6)
423 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
424 (unsigned long)tbytes);
425 }
426 }
427
428 /*
429 * Close the socket connection and input file and return...
430 */
431
432 close(fd);
433 if (fp != 0)
434 close(fp);
435
436 return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
437}
438
439
440/*
441 * 'list_devices()' - List all parallel devices.
442 */
443
444void
445list_devices(void)
446{
447#if defined(__hpux) || defined(__sgi) || defined(__sun)
448 static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
449 /* Funky hex numbering used for some devices */
450#endif /* __hpux || __sgi || __sun */
451
452#ifdef __linux
453 int i; /* Looping var */
454 int fd; /* File descriptor */
455 char device[255], /* Device filename */
456 basedevice[255], /* Base device filename for ports */
457 device_id[1024], /* Device ID string */
458 make_model[1024]; /* Make and model */
459
460
461 if (!access("/dev/parallel/", 0))
462 strcpy(basedevice, "/dev/parallel/");
463 else if (!access("/dev/printers/", 0))
464 strcpy(basedevice, "/dev/printers/");
465 else if (!access("/dev/par0", 0))
466 strcpy(basedevice, "/dev/par");
467 else
468 strcpy(basedevice, "/dev/lp");
469
470 for (i = 0; i < 4; i ++)
471 {
472 /*
473 * Open the port, if available...
474 */
475
476 sprintf(device, "%s%d", basedevice, i);
477 if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
478 fd = open(device, O_WRONLY);
479
480 if (fd >= 0)
481 {
482 /*
483 * Now grab the IEEE 1284 device ID string...
484 */
485
486 if (!get_device_id(fd, device_id, sizeof(device_id),
487 make_model, sizeof(make_model),
488 NULL, NULL, 0))
489 printf("direct parallel:%s \"%s\" \"%s LPT #%d\" \"%s\"\n", device,
490 make_model, make_model, i + 1, device_id);
491 else
492 printf("direct parallel:%s \"Unknown\" \"LPT #%d\"\n", device, i + 1);
493
494 close(fd);
495 }
496 }
497#elif defined(__sgi)
498 int i, j, n; /* Looping vars */
499 char device[255]; /* Device filename */
500 inventory_t *inv; /* Hardware inventory info */
501
502
503 /*
504 * IRIX maintains a hardware inventory of most devices...
505 */
506
507 setinvent();
508
509 while ((inv = getinvent()) != NULL)
510 {
511 if (inv->inv_class == INV_PARALLEL &&
512 (inv->inv_type == INV_ONBOARD_PLP ||
513 inv->inv_type == INV_EPP_ECP_PLP))
514 {
515 /*
516 * Standard parallel port...
517 */
518
519 puts("direct parallel:/dev/plp \"Unknown\" \"Onboard Parallel Port\"");
520 }
521 else if (inv->inv_class == INV_PARALLEL &&
522 inv->inv_type == INV_EPC_PLP)
523 {
524 /*
525 * EPC parallel port...
526 */
527
528 printf("direct parallel:/dev/plp%d \"Unknown\" \"Integral EPC parallel port, Ebus slot %d\"\n",
529 inv->inv_controller, inv->inv_controller);
530 }
531 }
532
533 endinvent();
534
535 /*
536 * Central Data makes serial and parallel "servers" that can be
537 * connected in a number of ways. Look for ports...
538 */
539
540 for (i = 0; i < 10; i ++)
541 for (j = 0; j < 8; j ++)
542 for (n = 0; n < 32; n ++)
543 {
544 if (i == 8) /* EtherLite */
545 sprintf(device, "/dev/lpn%d%c", j, funky_hex[n]);
546 else if (i == 9) /* PCI */
547 sprintf(device, "/dev/lpp%d%c", j, funky_hex[n]);
548 else /* SCSI */
549 sprintf(device, "/dev/lp%d%d%c", i, j, funky_hex[n]);
550
551 if (access(device, 0) == 0)
552 {
553 if (i == 8)
554 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
555 device, j, n);
556 else if (i == 9)
557 printf("direct parallel:%s \"Unknown\" \"Central Data PCI Parallel Port, ID %d, port %d\"\n",
558 device, j, n);
559 else
560 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
561 device, i, j, n);
562 }
563 }
564#elif defined(__sun)
565 int i, j, n; /* Looping vars */
566 char device[255]; /* Device filename */
567
568
569 /*
570 * Standard parallel ports...
571 */
572
573 for (i = 0; i < 10; i ++)
574 {
575 sprintf(device, "/dev/ecpp%d", i);
576 if (access(device, 0) == 0)
577 printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
578 device, i + 1);
579 }
580
581 for (i = 0; i < 10; i ++)
582 {
583 sprintf(device, "/dev/bpp%d", i);
584 if (access(device, 0) == 0)
585 printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
586 device, i + 1);
587 }
588
589 for (i = 0; i < 3; i ++)
590 {
591 sprintf(device, "/dev/lp%d", i);
592
593 if (access(device, 0) == 0)
594 printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
595 device, i + 1);
596 }
597
598 /*
599 * MAGMA parallel ports...
600 */
601
602 for (i = 0; i < 40; i ++)
603 {
604 sprintf(device, "/dev/pm%02d", i);
605 if (access(device, 0) == 0)
606 printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
607 device, (i / 10) + 1, (i % 10) + 1);
608 }
609
610 /*
611 * Central Data parallel ports...
612 */
613
614 for (i = 0; i < 9; i ++)
615 for (j = 0; j < 8; j ++)
616 for (n = 0; n < 32; n ++)
617 {
618 if (i == 8) /* EtherLite */
619 sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
620 else
621 sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
622 funky_hex[n]);
623
624 if (access(device, 0) == 0)
625 {
626 if (i == 8)
627 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
628 device, j, n);
629 else
630 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
631 device, i, j, n);
632 }
633 }
634#elif defined(__hpux)
635 int i, j, n; /* Looping vars */
636 char device[255]; /* Device filename */
637
638
639 /*
640 * Standard parallel ports...
641 */
642
643 if (access("/dev/rlp", 0) == 0)
644 puts("direct parallel:/dev/rlp \"Unknown\" \"Standard Parallel Port (/dev/rlp)\"");
645
646 for (i = 0; i < 7; i ++)
647 for (j = 0; j < 7; j ++)
648 {
649 sprintf(device, "/dev/c%dt%dd0_lp", i, j);
650 if (access(device, 0) == 0)
651 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d,%d\"\n",
652 device, i, j);
653 }
654
655 /*
656 * Central Data parallel ports...
657 */
658
659 for (i = 0; i < 9; i ++)
660 for (j = 0; j < 8; j ++)
661 for (n = 0; n < 32; n ++)
662 {
663 if (i == 8) /* EtherLite */
664 sprintf(device, "/dev/lpN%d%c", j, funky_hex[n]);
665 else
666 sprintf(device, "/dev/lp%c%d%c", i + 'C', j,
667 funky_hex[n]);
668
669 if (access(device, 0) == 0)
670 {
671 if (i == 8)
672 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
673 device, j, n);
674 else
675 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
676 device, i, j, n);
677 }
678 }
679#elif defined(__osf__)
680 int i; /* Looping var */
681 int fd; /* File descriptor */
682 char device[255]; /* Device filename */
683
684
685 for (i = 0; i < 3; i ++)
686 {
687 sprintf(device, "/dev/lp%d", i);
688 if ((fd = open(device, O_WRONLY)) >= 0)
689 {
690 close(fd);
691 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
692 }
693 }
b423cd4c 694#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
ef416fc2 695 int i; /* Looping var */
696 int fd; /* File descriptor */
697 char device[255]; /* Device filename */
698
699
700 for (i = 0; i < 3; i ++)
701 {
702 sprintf(device, "/dev/lpt%d", i);
703 if ((fd = open(device, O_WRONLY)) >= 0)
704 {
705 close(fd);
706 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (interrupt-driven)\"\n", device, i + 1);
707 }
708
709 sprintf(device, "/dev/lpa%d", i);
710 if ((fd = open(device, O_WRONLY)) >= 0)
711 {
712 close(fd);
713 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (polled)\"\n", device, i + 1);
714 }
715 }
716#elif defined(_AIX)
717 int i; /* Looping var */
718 int fd; /* File descriptor */
719 char device[255]; /* Device filename */
720
721
722 for (i = 0; i < 8; i ++)
723 {
724 sprintf(device, "/dev/lp%d", i);
725 if ((fd = open(device, O_WRONLY)) >= 0)
726 {
727 close(fd);
728 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
729 }
730 }
731#endif
732}
733
734
735/*
b423cd4c 736 * End of "$Id: parallel.c 5194 2006-02-27 20:57:07Z mike $".
ef416fc2 737 */