]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/usb.c
If no description attribute is found, use the make and model attributes
[thirdparty/cups.git] / backend / usb.c
1 /*
2 * "$Id: usb.c,v 1.45 2003/07/06 20:19:34 mike Exp $"
3 *
4 * USB port backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2003 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-3111 USA
19 *
20 * Voice: (301) 373-9603
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 USB port.
29 * list_devices() - List all USB devices.
30 */
31
32 /*
33 * Include necessary headers.
34 */
35
36 #include <cups/cups.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <cups/string.h>
41 #include <signal.h>
42
43 #ifdef WIN32
44 # include <io.h>
45 #else
46 # include <unistd.h>
47 # include <fcntl.h>
48 # include <termios.h>
49 #endif /* WIN32 */
50
51 #ifdef __linux
52 # include <sys/ioctl.h>
53 # include <linux/lp.h>
54 # define IOCNR_GET_DEVICE_ID 1
55
56 /*
57 * Get device_id string
58 */
59 # define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
60 #endif /* __linux */
61
62 #ifdef __sun
63 # ifdef __sparc
64 # include <sys/ecppio.h>
65 # else
66 # include <sys/ecppsys.h>
67 # endif /* __sparc */
68 #endif /* __sun */
69
70
71 /*
72 * Local functions...
73 */
74
75 void decode_device_id(int port, const char *device_id,
76 char *make_model, int mmsize,
77 char *uri, int urisize);
78 void list_devices(void);
79 int open_device(const char *uri);
80
81
82 /*
83 * 'main()' - Send a file to the specified USB port.
84 *
85 * Usage:
86 *
87 * printer-uri job-id user title copies options [file]
88 */
89
90 int /* O - Exit status */
91 main(int argc, /* I - Number of command-line arguments (6 or 7) */
92 char *argv[]) /* I - Command-line arguments */
93 {
94 int fp; /* Print file */
95 int copies; /* Number of copies to print */
96 int fd; /* Parallel device */
97 int wbytes; /* Number of bytes written */
98 size_t nbytes, /* Number of bytes read */
99 tbytes; /* Total number of bytes written */
100 char buffer[8192], /* Output buffer */
101 *bufptr; /* Pointer into buffer */
102 struct termios opts; /* Parallel port options */
103 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
104 struct sigaction action; /* Actions for POSIX signals */
105 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
106 #ifdef __linux
107 unsigned char status; /* Port status (off-line, out-of-paper, etc.) */
108 #endif /* __linux */
109
110
111 /*
112 * Make sure status messages are not buffered...
113 */
114
115 setbuf(stderr, NULL);
116
117 /*
118 * Ignore SIGPIPE signals...
119 */
120
121 #ifdef HAVE_SIGSET
122 sigset(SIGPIPE, SIG_IGN);
123 #elif defined(HAVE_SIGACTION)
124 memset(&action, 0, sizeof(action));
125 action.sa_handler = SIG_IGN;
126 sigaction(SIGPIPE, &action, NULL);
127 #else
128 signal(SIGPIPE, SIG_IGN);
129 #endif /* HAVE_SIGSET */
130
131 /*
132 * Check command-line...
133 */
134
135 if (argc == 1)
136 {
137 list_devices();
138 return (0);
139 }
140 else if (argc < 6 || argc > 7)
141 {
142 fputs("Usage: USB job-id user title copies options [file]\n", stderr);
143 return (1);
144 }
145
146 /*
147 * If we have 7 arguments, print the file named on the command-line.
148 * Otherwise, send stdin instead...
149 */
150
151 if (argc == 6)
152 {
153 fp = 0;
154 copies = 1;
155 }
156 else
157 {
158 /*
159 * Try to open the print file...
160 */
161
162 if ((fp = open(argv[6], O_RDONLY)) < 0)
163 {
164 perror("ERROR: unable to open print file");
165 return (1);
166 }
167
168 copies = atoi(argv[4]);
169 }
170
171 /*
172 * Open the USB port device...
173 */
174
175 do
176 {
177 if ((fd = open_device(argv[0])) == -1)
178 {
179 if (errno == EBUSY)
180 {
181 fputs("INFO: USB port busy; will retry in 30 seconds...\n", stderr);
182 sleep(30);
183 }
184 else if (errno == ENXIO || errno == EIO || errno == ENOENT)
185 {
186 fputs("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
187 sleep(30);
188 }
189 else
190 {
191 fprintf(stderr, "ERROR: Unable to open USB device \"%s\": %s\n",
192 argv[0], strerror(errno));
193 return (1);
194 }
195 }
196 }
197 while (fd < 0);
198
199 /*
200 * Set any options provided...
201 */
202
203 tcgetattr(fd, &opts);
204
205 opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
206
207 /**** No options supported yet ****/
208
209 tcsetattr(fd, TCSANOW, &opts);
210
211 /*
212 * Now that we are "connected" to the port, ignore SIGTERM so that we
213 * can finish out any page data the driver sends (e.g. to eject the
214 * current page... Only ignore SIGTERM if we are printing data from
215 * stdin (otherwise you can't cancel raw jobs...)
216 */
217
218 if (argc < 7)
219 {
220 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
221 sigset(SIGTERM, SIG_IGN);
222 #elif defined(HAVE_SIGACTION)
223 memset(&action, 0, sizeof(action));
224
225 sigemptyset(&action.sa_mask);
226 action.sa_handler = SIG_IGN;
227 sigaction(SIGTERM, &action, NULL);
228 #else
229 signal(SIGTERM, SIG_IGN);
230 #endif /* HAVE_SIGSET */
231 }
232
233 #ifdef __linux
234 /*
235 * Show the printer status before we send the file; normally, we'd
236 * do this while we write data to the printer, however at least some
237 * Linux kernels have buggy USB drivers which don't like to be
238 * queried while sending data to the printer...
239 */
240
241 if (ioctl(fd, LPGETSTATUS, &status) == 0)
242 {
243 fprintf(stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
244
245 if (status & LP_NOPA)
246 fputs("WARNING: Media tray empty!\n", stderr);
247 else if (status & LP_ERR)
248 fputs("WARNING: Printer fault!\n", stderr);
249 else if (status & LP_OFFL)
250 fputs("WARNING: Printer off-line.\n", stderr);
251 }
252 #endif /* __linux */
253
254 /*
255 * Finally, send the print file...
256 */
257
258 while (copies > 0)
259 {
260 copies --;
261
262 if (fp != 0)
263 {
264 fputs("PAGE: 1 1\n", stderr);
265 lseek(fp, 0, SEEK_SET);
266 }
267
268 tbytes = 0;
269 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
270 {
271 /*
272 * Write the print data to the printer...
273 */
274
275 tbytes += nbytes;
276 bufptr = buffer;
277
278 while (nbytes > 0)
279 {
280
281 if ((wbytes = write(fd, bufptr, nbytes)) < 0)
282 if (errno == ENOTTY)
283 wbytes = write(fd, bufptr, nbytes);
284
285 if (wbytes < 0)
286 {
287 perror("ERROR: Unable to send print file to printer");
288 break;
289 }
290
291 nbytes -= wbytes;
292 bufptr += wbytes;
293 }
294
295 if (argc > 6)
296 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
297 (unsigned long)tbytes);
298 }
299 }
300
301 /*
302 * Close the socket connection and input file and return...
303 */
304
305 close(fd);
306 if (fp != 0)
307 close(fp);
308
309 fputs("INFO: Ready to print.\n", stderr);
310
311 return (0);
312 }
313
314
315 /*
316 * 'decode_device_id()' - Decode the IEEE-1284 device ID string.
317 */
318
319 void
320 decode_device_id(int port, /* I - Port number */
321 const char *device_id, /* I - 1284 device ID string */
322 char *make_model, /* O - Make/model */
323 int mmsize, /* I - Size of buffer */
324 char *uri, /* O - Device URI */
325 int urisize) /* I - Size of buffer */
326 {
327 char *attr, /* 1284 attribute */
328 *delim, /* 1284 delimiter */
329 *uriptr, /* Pointer into URI */
330 *mfg, /* Manufacturer string */
331 *mdl, /* Model string */
332 serial_number[1024]; /* Serial number string */
333
334
335 /*
336 * Look for the description field...
337 */
338
339 if ((attr = strstr(device_id, "DES:")) != NULL)
340 attr += 4;
341 else if ((attr = strstr(device_id, "DESCRIPTION:")) != NULL)
342 attr += 12;
343
344 if ((mfg = strstr(device_id, "MANUFACTURER:")) != NULL)
345 mfg += 13;
346 else if ((mfg = strstr(device_id, "MFG:")) != NULL)
347 mfg += 4;
348
349 if ((mdl = strstr(device_id, "MODEL:")) != NULL)
350 mdl += 6;
351 else if ((mdl = strstr(device_id, "MDL:")) != NULL)
352 mdl += 4;
353
354 if (attr)
355 {
356 if (strncasecmp(attr, "Hewlett-Packard ", 16) == 0)
357 {
358 strlcpy(make_model, "HP ", mmsize);
359 strlcpy(make_model + 3, attr + 16, mmsize - 3);
360 }
361 else
362 {
363 strlcpy(make_model, attr, mmsize);
364 }
365
366 if ((delim = strchr(make_model, ';')) != NULL)
367 *delim = '\0';
368 }
369 else if (mfg && mdl)
370 {
371 /*
372 * Build a make-model string from the manufacturer and model attributes...
373 */
374
375 strlcpy(make_model, mfg, mmsize);
376
377 if ((delim = strchr(make_model, ';')) != NULL)
378 *delim = '\0';
379
380 strlcat(make_model, " ", mmsize);
381 strlcat(make_model, mdl, mmsize);
382
383 if ((delim = strchr(make_model, ';')) != NULL)
384 *delim = '\0';
385 }
386 else
387 {
388 /*
389 * Use "Unknown" as the printer make and model...
390 */
391
392 strlcpy(make_model, "Unknown", mmsize);
393 }
394
395 /*
396 * Look for the serial number field...
397 */
398
399 if ((attr = strstr(device_id, "SERN:")) != NULL)
400 attr += 5;
401 else if ((attr = strstr(device_id, "SERIALNUMBER:")) != NULL)
402 attr += 13;
403
404 if (attr)
405 {
406 strlcpy(serial_number, attr, sizeof(serial_number));
407
408 if ((delim = strchr(serial_number, ';')) != NULL)
409 *delim = '\0';
410 }
411 else
412 serial_number[0] = '\0';
413
414 /*
415 * Generate the device URI from the make_model and serial number strings.
416 */
417
418 strlcpy(uri, "usb://", urisize);
419 for (uriptr = uri + 6, delim = make_model;
420 *delim && uriptr < (uri + urisize - 1);
421 delim ++)
422 if (*delim == ' ')
423 {
424 delim ++;
425 *uriptr++ = '/';
426 break;
427 }
428 else
429 *uriptr++ = *delim;
430
431 for (; *delim && uriptr < (uri + urisize - 3); delim ++)
432 if (*delim == ' ')
433 {
434 *uriptr++ = '%';
435 *uriptr++ = '2';
436 *uriptr++ = '0';
437 }
438 else
439 *uriptr++ = *delim;
440
441 *uriptr = '\0';
442
443 if (serial_number[0])
444 {
445 /*
446 * Add the serial number to the URI...
447 */
448
449 strlcat(uri, "?serial=", urisize);
450 strlcat(uri, serial_number, urisize);
451 }
452 }
453
454
455 /*
456 * 'list_devices()' - List all USB devices.
457 */
458
459 void
460 list_devices(void)
461 {
462 #ifdef __linux
463 int i; /* Looping var */
464 int length; /* Length of device ID info */
465 int fd; /* File descriptor */
466 char format[255], /* Format for device filename */
467 device[255], /* Device filename */
468 device_id[1024], /* Device ID string */
469 device_uri[1024], /* Device URI string */
470 make_model[1024]; /* Make and model */
471
472
473 /*
474 * First figure out which USB printer filename to use...
475 */
476
477 if (access("/dev/usb/lp0", 0) == 0)
478 strcpy(format, "/dev/usb/lp%d");
479 else if (access("/dev/usb/usblp0", 0) == 0)
480 strcpy(format, "/dev/usb/usblp%d");
481 else
482 strcpy(format, "/dev/usblp%d");
483
484 /*
485 * Then open each USB device...
486 */
487
488 for (i = 0; i < 16; i ++)
489 {
490 sprintf(device, format, i);
491
492 if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
493 {
494 if (ioctl(fd, LPIOC_GET_DEVICE_ID(sizeof(device_id)), device_id) == 0)
495 {
496 length = (((unsigned)device_id[0] & 255) << 8) +
497 ((unsigned)device_id[1] & 255);
498
499 /*
500 * Check to see if the length is larger than our buffer; first
501 * assume that the vendor incorrectly implemented the 1284 spec,
502 * and then limit the length to the size of our buffer...
503 */
504
505 if (length > (sizeof(device_id) - 2))
506 length = (((unsigned)device_id[1] & 255) << 8) +
507 ((unsigned)device_id[0] & 255);
508
509 if (length > (sizeof(device_id) - 2))
510 length = sizeof(device_id) - 2;
511
512 memcpy(device_id, device_id + 2, length);
513 device_id[length] = '\0';
514 }
515 else
516 device_id[0] = '\0';
517
518 close(fd);
519 }
520 else
521 device_id[0] = '\0';
522
523 if (device_id[0])
524 {
525 decode_device_id(i, device_id, make_model, sizeof(make_model),
526 device_uri, sizeof(device_uri));
527
528 printf("direct %s \"%s\" \"USB Printer #%d\"\n", device_uri,
529 make_model, i + 1);
530 }
531 else
532 printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
533 }
534 #elif defined(__sgi)
535 #elif defined(__sun)
536 int i; /* Looping var */
537 int fd; /* File descriptor */
538 char device[255], /* Device filename */
539 device_id[1024], /* Device ID string */
540 device_uri[1024], /* Device URI string */
541 make_model[1024]; /* Make and model */
542 # ifdef ECPPIOC_GETDEVID
543 struct ecpp_device_id did; /* Device ID buffer */
544 # endif /* ECPPIOC_GETDEVID */
545
546
547 /*
548 * Open each USB device...
549 */
550
551 for (i = 0; i < 8; i ++)
552 {
553 sprintf(device, "/dev/usb/printer%d", i);
554
555 # ifndef ECPPIOC_GETDEVID
556 if (!access(device, 0))
557 printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
558 # else
559 if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
560 {
561 did.mode = ECPP_CENTRONICS;
562 did.len = sizeof(device_id);
563 did.rlen = 0;
564 did.addr = device_id;
565
566 if (ioctl(fd, ECPPIOC_GETDEVID, &did) == 0)
567 {
568 if (did.rlen < (sizeof(device_id) - 1))
569 device_id[did.rlen] = '\0';
570 else
571 device_id[sizeof(device_id) - 1] = '\0';
572 }
573 else
574 device_id[0] = '\0';
575
576 close(fd);
577 }
578 else
579 device_id[0] = '\0';
580
581 if (device_id[0])
582 {
583 decode_device_id(i, device_id, make_model, sizeof(make_model),
584 device_uri, sizeof(device_uri));
585
586 printf("direct %s \"%s\" \"USB Printer #%d\"\n", device_uri,
587 make_model, i + 1);
588 }
589 else
590 printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
591 # endif /* !ECPPIOC_GETDEVID */
592 }
593 #elif defined(__hpux)
594 #elif defined(__osf)
595 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
596 int i; /* Looping var */
597 char device[255]; /* Device filename */
598
599
600 for (i = 0; i < 8; i ++)
601 {
602 sprintf(device, "/dev/ulpt%d", i);
603 if (!access(device, 0))
604 printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
605
606 sprintf(device, "/dev/unlpt%d", i);
607 if (!access(device, 0))
608 printf("direct usb:%s \"Unknown\" \"USB Printer #%d (no reset)\"\n", device, i + 1);
609 }
610 #endif
611 }
612
613
614 /*
615 * 'open_device()' - Open a USB device...
616 */
617
618 int /* O - File descriptor or -1 on error */
619 open_device(const char *uri) /* I - Device URI */
620 {
621 /*
622 * The generic implementation just treats the URI as a device filename...
623 * Specific operating systems may also support using the device serial
624 * number and/or make/model.
625 */
626
627 if (strncmp(uri, "usb:/dev/", 9) == 0)
628 return (open(uri + 4, O_RDWR | O_EXCL));
629 #ifdef __linux
630 else if (strncmp(uri, "usb://", 6) == 0)
631 {
632 /*
633 * For Linux, try looking up the device serial number or model...
634 */
635
636 int i; /* Looping var */
637 int length; /* Length of device ID info */
638 int fd; /* File descriptor */
639 char format[255], /* Format for device filename */
640 device[255], /* Device filename */
641 device_id[1024], /* Device ID string */
642 make_model[1024], /* Make and model */
643 device_uri[1024]; /* Device URI string */
644
645
646 /*
647 * First figure out which USB printer filename to use...
648 */
649
650 if (access("/dev/usb/lp0", 0) == 0)
651 strcpy(format, "/dev/usb/lp%d");
652 else if (access("/dev/usb/usblp0", 0) == 0)
653 strcpy(format, "/dev/usb/usblp%d");
654 else
655 strcpy(format, "/dev/usblp%d");
656
657 /*
658 * Then find the correct USB device...
659 */
660
661 for (i = 0; i < 16; i ++)
662 {
663 sprintf(device, format, i);
664
665 if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
666 {
667 if (ioctl(fd, LPIOC_GET_DEVICE_ID(sizeof(device_id)), device_id) == 0)
668 {
669 length = (((unsigned)device_id[0] & 255) << 8) +
670 ((unsigned)device_id[1] & 255);
671 memcpy(device_id, device_id + 2, length);
672 device_id[length] = '\0';
673 }
674 else
675 device_id[0] = '\0';
676 }
677 else
678 device_id[0] = '\0';
679
680 if (device_id[0])
681 {
682 /*
683 * Got the device ID - is this the one?
684 */
685
686 decode_device_id(i, device_id, make_model, sizeof(make_model),
687 device_uri, sizeof(device_uri));
688
689 if (strcmp(uri, device_uri) == 0)
690 {
691 /*
692 * Yes, return this file descriptor...
693 */
694
695 fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n", device);
696
697 return (fd);
698 }
699 }
700
701 /*
702 * This wasn't the one...
703 */
704
705 close(fd);
706 }
707
708 /*
709 * Couldn't find the printer, return "no such device or address"...
710 */
711
712 errno = ENODEV;
713
714 return (-1);
715 }
716 #elif defined(__sun) && defined(ECPPIOC_GETDEVID)
717 else if (strncmp(uri, "usb://", 6) == 0)
718 {
719 /*
720 * For Solaris, try looking up the device serial number or model...
721 */
722
723 int i; /* Looping var */
724 int fd; /* File descriptor */
725 char device[255], /* Device filename */
726 device_id[1024], /* Device ID string */
727 make_model[1024], /* Make and model */
728 device_uri[1024]; /* Device URI string */
729 struct ecpp_device_id did; /* Device ID buffer */
730
731
732 /*
733 * Find the correct USB device...
734 */
735
736 for (i = 0; i < 8; i ++)
737 {
738 sprintf(device, "/dev/usb/printer%d", i);
739
740 if ((fd = open(device, O_RDWR | O_EXCL)) >= 0)
741 {
742 did.mode = ECPP_CENTRONICS;
743 did.len = sizeof(device_id);
744 did.rlen = 0;
745 did.addr = device_id;
746
747 if (ioctl(fd, ECPPIOC_GETDEVID, &did) == 0)
748 {
749 if (did.rlen < (sizeof(device_id) - 1))
750 device_id[did.rlen] = '\0';
751 else
752 device_id[sizeof(device_id) - 1] = '\0';
753 }
754 else
755 device_id[0] = '\0';
756 }
757 else
758 device_id[0] = '\0';
759
760 if (device_id[0])
761 {
762 /*
763 * Got the device ID - is this the one?
764 */
765
766 decode_device_id(i, device_id, make_model, sizeof(make_model),
767 device_uri, sizeof(device_uri));
768
769 if (strcmp(uri, device_uri) == 0)
770 return (fd); /* Yes, return this file descriptor... */
771 }
772
773 /*
774 * This wasn't the one...
775 */
776
777 close(fd);
778 }
779
780 /*
781 * Couldn't find the printer, return "no such device or address"...
782 */
783
784 errno = ENODEV;
785
786 return (-1);
787 }
788 #endif /* __linux */
789 else
790 {
791 errno = ENODEV;
792 return (-1);
793 }
794 }
795
796
797 /*
798 * End of "$Id: usb.c,v 1.45 2003/07/06 20:19:34 mike Exp $".
799 */