]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - backend/parallel.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / parallel.c
... / ...
CommitLineData
1/*
2 * "$Id: parallel.c 5241 2006-03-07 22:07:44Z 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
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
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
448void
449list_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 if (!access("/dev/par0", 0))
470 strcpy(basedevice, "/dev/par");
471 else
472 strcpy(basedevice, "/dev/lp");
473
474 for (i = 0; i < 4; i ++)
475 {
476 /*
477 * Open the port, if available...
478 */
479
480 sprintf(device, "%s%d", basedevice, i);
481 if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
482 fd = open(device, O_WRONLY);
483
484 if (fd >= 0)
485 {
486 /*
487 * Now grab the IEEE 1284 device ID string...
488 */
489
490 if (!get_device_id(fd, device_id, sizeof(device_id),
491 make_model, sizeof(make_model),
492 NULL, NULL, 0))
493 printf("direct parallel:%s \"%s\" \"%s LPT #%d\" \"%s\"\n", device,
494 make_model, make_model, i + 1, device_id);
495 else
496 printf("direct parallel:%s \"Unknown\" \"LPT #%d\"\n", device, i + 1);
497
498 close(fd);
499 }
500 }
501#elif defined(__sgi)
502 int i, j, n; /* Looping vars */
503 char device[255]; /* Device filename */
504 inventory_t *inv; /* Hardware inventory info */
505
506
507 /*
508 * IRIX maintains a hardware inventory of most devices...
509 */
510
511 setinvent();
512
513 while ((inv = getinvent()) != NULL)
514 {
515 if (inv->inv_class == INV_PARALLEL &&
516 (inv->inv_type == INV_ONBOARD_PLP ||
517 inv->inv_type == INV_EPP_ECP_PLP))
518 {
519 /*
520 * Standard parallel port...
521 */
522
523 puts("direct parallel:/dev/plp \"Unknown\" \"Onboard Parallel Port\"");
524 }
525 else if (inv->inv_class == INV_PARALLEL &&
526 inv->inv_type == INV_EPC_PLP)
527 {
528 /*
529 * EPC parallel port...
530 */
531
532 printf("direct parallel:/dev/plp%d \"Unknown\" \"Integral EPC parallel port, Ebus slot %d\"\n",
533 inv->inv_controller, inv->inv_controller);
534 }
535 }
536
537 endinvent();
538
539 /*
540 * Central Data makes serial and parallel "servers" that can be
541 * connected in a number of ways. Look for ports...
542 */
543
544 for (i = 0; i < 10; i ++)
545 for (j = 0; j < 8; j ++)
546 for (n = 0; n < 32; n ++)
547 {
548 if (i == 8) /* EtherLite */
549 sprintf(device, "/dev/lpn%d%c", j, funky_hex[n]);
550 else if (i == 9) /* PCI */
551 sprintf(device, "/dev/lpp%d%c", j, funky_hex[n]);
552 else /* SCSI */
553 sprintf(device, "/dev/lp%d%d%c", i, j, funky_hex[n]);
554
555 if (access(device, 0) == 0)
556 {
557 if (i == 8)
558 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
559 device, j, n);
560 else if (i == 9)
561 printf("direct parallel:%s \"Unknown\" \"Central Data PCI Parallel Port, ID %d, port %d\"\n",
562 device, j, n);
563 else
564 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
565 device, i, j, n);
566 }
567 }
568#elif defined(__sun)
569 int i, j, n; /* Looping vars */
570 char device[255]; /* Device filename */
571
572
573 /*
574 * Standard parallel ports...
575 */
576
577 for (i = 0; i < 10; i ++)
578 {
579 sprintf(device, "/dev/ecpp%d", i);
580 if (access(device, 0) == 0)
581 printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
582 device, i + 1);
583 }
584
585 for (i = 0; i < 10; i ++)
586 {
587 sprintf(device, "/dev/bpp%d", i);
588 if (access(device, 0) == 0)
589 printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
590 device, i + 1);
591 }
592
593 for (i = 0; i < 3; i ++)
594 {
595 sprintf(device, "/dev/lp%d", i);
596
597 if (access(device, 0) == 0)
598 printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
599 device, i + 1);
600 }
601
602 /*
603 * MAGMA parallel ports...
604 */
605
606 for (i = 0; i < 40; i ++)
607 {
608 sprintf(device, "/dev/pm%02d", i);
609 if (access(device, 0) == 0)
610 printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
611 device, (i / 10) + 1, (i % 10) + 1);
612 }
613
614 /*
615 * Central Data parallel ports...
616 */
617
618 for (i = 0; i < 9; i ++)
619 for (j = 0; j < 8; j ++)
620 for (n = 0; n < 32; n ++)
621 {
622 if (i == 8) /* EtherLite */
623 sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
624 else
625 sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
626 funky_hex[n]);
627
628 if (access(device, 0) == 0)
629 {
630 if (i == 8)
631 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
632 device, j, n);
633 else
634 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
635 device, i, j, n);
636 }
637 }
638#elif defined(__hpux)
639 int i, j, n; /* Looping vars */
640 char device[255]; /* Device filename */
641
642
643 /*
644 * Standard parallel ports...
645 */
646
647 if (access("/dev/rlp", 0) == 0)
648 puts("direct parallel:/dev/rlp \"Unknown\" \"Standard Parallel Port (/dev/rlp)\"");
649
650 for (i = 0; i < 7; i ++)
651 for (j = 0; j < 7; j ++)
652 {
653 sprintf(device, "/dev/c%dt%dd0_lp", i, j);
654 if (access(device, 0) == 0)
655 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d,%d\"\n",
656 device, i, j);
657 }
658
659 /*
660 * Central Data parallel ports...
661 */
662
663 for (i = 0; i < 9; i ++)
664 for (j = 0; j < 8; j ++)
665 for (n = 0; n < 32; n ++)
666 {
667 if (i == 8) /* EtherLite */
668 sprintf(device, "/dev/lpN%d%c", j, funky_hex[n]);
669 else
670 sprintf(device, "/dev/lp%c%d%c", i + 'C', j,
671 funky_hex[n]);
672
673 if (access(device, 0) == 0)
674 {
675 if (i == 8)
676 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
677 device, j, n);
678 else
679 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
680 device, i, j, n);
681 }
682 }
683#elif defined(__osf__)
684 int i; /* Looping var */
685 int fd; /* File descriptor */
686 char device[255]; /* Device filename */
687
688
689 for (i = 0; i < 3; i ++)
690 {
691 sprintf(device, "/dev/lp%d", i);
692 if ((fd = open(device, O_WRONLY)) >= 0)
693 {
694 close(fd);
695 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
696 }
697 }
698#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
699 int i; /* Looping var */
700 int fd; /* File descriptor */
701 char device[255]; /* Device filename */
702
703
704 for (i = 0; i < 3; i ++)
705 {
706 sprintf(device, "/dev/lpt%d", i);
707 if ((fd = open(device, O_WRONLY)) >= 0)
708 {
709 close(fd);
710 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (interrupt-driven)\"\n", device, i + 1);
711 }
712
713 sprintf(device, "/dev/lpa%d", i);
714 if ((fd = open(device, O_WRONLY)) >= 0)
715 {
716 close(fd);
717 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (polled)\"\n", device, i + 1);
718 }
719 }
720#elif defined(_AIX)
721 int i; /* Looping var */
722 int fd; /* File descriptor */
723 char device[255]; /* Device filename */
724
725
726 for (i = 0; i < 8; i ++)
727 {
728 sprintf(device, "/dev/lp%d", i);
729 if ((fd = open(device, O_WRONLY)) >= 0)
730 {
731 close(fd);
732 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d\"\n", device, i + 1);
733 }
734 }
735#endif
736}
737
738
739/*
740 * End of "$Id: parallel.c 5241 2006-03-07 22:07:44Z mike $".
741 */