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