]>
Commit | Line | Data |
---|---|---|
8e3807e7 | 1 | /* |
c9d3f842 | 2 | * "$Id$" |
8e3807e7 | 3 | * |
4 | * USB port backend for the Common UNIX Printing System (CUPS). | |
5 | * | |
6 | * This file is included from "usb.c" when compiled on UNIX/Linux. | |
7 | * | |
761a542e | 8 | * Copyright 1997-2007 by Easy Software Products, all rights reserved. |
8e3807e7 | 9 | * |
10 | * These coded instructions, statements, and computer programs are the | |
11 | * property of Easy Software Products and are protected by Federal | |
12 | * copyright law. Distribution and use rights are outlined in the file | |
13 | * "LICENSE" which should have been included with this file. If this | |
14 | * file is missing or damaged please contact Easy Software Products | |
15 | * at: | |
16 | * | |
17 | * Attn: CUPS Licensing Information | |
18 | * Easy Software Products | |
19 | * 44141 Airport View Drive, Suite 204 | |
c9d3f842 | 20 | * Hollywood, Maryland 20636 USA |
8e3807e7 | 21 | * |
22 | * Voice: (301) 373-9600 | |
23 | * EMail: cups-info@cups.org | |
24 | * WWW: http://www.cups.org | |
25 | * | |
26 | * This file is subject to the Apple OS-Developed Software exception. | |
27 | * | |
28 | * Contents: | |
29 | * | |
74584961 | 30 | * print_device() - Print a file to a USB device. |
8e3807e7 | 31 | * list_devices() - List all USB devices. |
74584961 | 32 | * open_device() - Open a USB device... |
761a542e | 33 | * side_cb() - Handle side-channel requests... |
8e3807e7 | 34 | */ |
35 | ||
36 | /* | |
37 | * Include necessary headers. | |
38 | */ | |
39 | ||
9ef960f1 | 40 | #include "ieee1284.c" |
5baa731f | 41 | #include <sys/select.h> |
42 | ||
8e3807e7 | 43 | |
44 | /* | |
45 | * Local functions... | |
46 | */ | |
47 | ||
761a542e | 48 | static int open_device(const char *uri, int *use_bc); |
49 | static void side_cb(int print_fd, int device_fd, int use_bc); | |
8e3807e7 | 50 | |
51 | ||
52 | /* | |
53 | * 'print_device()' - Print a file to a USB device. | |
54 | */ | |
55 | ||
56 | int /* O - Exit status */ | |
57 | print_device(const char *uri, /* I - Device URI */ | |
58 | const char *hostname, /* I - Hostname/manufacturer */ | |
59 | const char *resource, /* I - Resource/modelname */ | |
60 | const char *options, /* I - Device options/serial number */ | |
43b5ad0d | 61 | int print_fd, /* I - File descriptor to print */ |
7e826c4a | 62 | int copies, /* I - Copies to print */ |
63 | int argc, /* I - Number of command-line arguments (6 or 7) */ | |
64 | char *argv[]) /* I - Command-line arguments */ | |
8e3807e7 | 65 | { |
43b5ad0d | 66 | int use_bc; /* Use backchannel path? */ |
67 | int device_fd; /* USB device */ | |
68 | size_t tbytes; /* Total number of bytes written */ | |
8e3807e7 | 69 | struct termios opts; /* Parallel port options */ |
43b5ad0d | 70 | |
8e3807e7 | 71 | |
7e826c4a | 72 | (void)argc; |
73 | (void)argv; | |
8e3807e7 | 74 | |
75 | /* | |
76 | * Open the USB port device... | |
77 | */ | |
78 | ||
22a980cc | 79 | fputs("STATE: +connecting-to-device\n", stderr); |
80 | ||
8e3807e7 | 81 | do |
82 | { | |
acc0a9db | 83 | /* |
e4b02d8f | 84 | * Disable backchannel data when printing to Brother, Canon, or |
85 | * Minolta USB printers - apparently these printers will return | |
86 | * the IEEE-1284 device ID over and over and over when they get | |
87 | * a read request... | |
acc0a9db | 88 | */ |
89 | ||
e4b02d8f | 90 | use_bc = strcasecmp(hostname, "Brother") && |
91 | strcasecmp(hostname, "Canon") && | |
c740586e | 92 | strcasecmp(hostname, "Konica Minolta") && |
e6b5af05 | 93 | strcasecmp(hostname, "Minolta"); |
acc0a9db | 94 | |
95 | if ((device_fd = open_device(uri, &use_bc)) == -1) | |
8e3807e7 | 96 | { |
b7b63780 | 97 | if (getenv("CLASS") != NULL) |
98 | { | |
99 | /* | |
100 | * If the CLASS environment variable is set, the job was submitted | |
101 | * to a class and not to a specific queue. In this case, we want | |
102 | * to abort immediately so that the job can be requeued on the next | |
103 | * available printer in the class. | |
104 | */ | |
105 | ||
ff0295f0 | 106 | fputs(_("INFO: Unable to contact printer, queuing on next " |
107 | "printer in class...\n"), stderr); | |
b7b63780 | 108 | |
109 | /* | |
110 | * Sleep 5 seconds to keep the job from requeuing too rapidly... | |
111 | */ | |
112 | ||
113 | sleep(5); | |
114 | ||
6248387b | 115 | return (CUPS_BACKEND_FAILED); |
b7b63780 | 116 | } |
117 | ||
8e3807e7 | 118 | if (errno == EBUSY) |
119 | { | |
ff0295f0 | 120 | fputs(_("INFO: Printer busy; will retry in 10 seconds...\n"), stderr); |
121 | sleep(10); | |
8e3807e7 | 122 | } |
43b5ad0d | 123 | else if (errno == ENXIO || errno == EIO || errno == ENOENT || |
124 | errno == ENODEV) | |
8e3807e7 | 125 | { |
ff0295f0 | 126 | fputs(_("INFO: Printer not connected; will retry in 30 seconds...\n"), |
127 | stderr); | |
8e3807e7 | 128 | sleep(30); |
129 | } | |
130 | else | |
131 | { | |
ff0295f0 | 132 | fprintf(stderr, _("ERROR: Unable to open device file \"%s\": %s\n"), |
133 | resource, strerror(errno)); | |
6248387b | 134 | return (CUPS_BACKEND_FAILED); |
8e3807e7 | 135 | } |
136 | } | |
137 | } | |
43b5ad0d | 138 | while (device_fd < 0); |
8e3807e7 | 139 | |
22a980cc | 140 | fputs("STATE: -connecting-to-device\n", stderr); |
141 | ||
8e3807e7 | 142 | /* |
143 | * Set any options provided... | |
144 | */ | |
145 | ||
43b5ad0d | 146 | tcgetattr(device_fd, &opts); |
8e3807e7 | 147 | |
148 | opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */ | |
149 | ||
150 | /**** No options supported yet ****/ | |
151 | ||
43b5ad0d | 152 | tcsetattr(device_fd, TCSANOW, &opts); |
9ef960f1 | 153 | |
8e3807e7 | 154 | /* |
155 | * Finally, send the print file... | |
156 | */ | |
157 | ||
43b5ad0d | 158 | tbytes = 0; |
897922a9 | 159 | |
43b5ad0d | 160 | while (copies > 0 && tbytes >= 0) |
8e3807e7 | 161 | { |
162 | copies --; | |
163 | ||
43b5ad0d | 164 | if (print_fd != 0) |
8e3807e7 | 165 | { |
166 | fputs("PAGE: 1 1\n", stderr); | |
43b5ad0d | 167 | lseek(print_fd, 0, SEEK_SET); |
8e3807e7 | 168 | } |
169 | ||
761a542e | 170 | tbytes = backendRunLoop(print_fd, device_fd, use_bc, side_cb); |
8e3807e7 | 171 | |
43b5ad0d | 172 | if (print_fd != 0 && tbytes >= 0) |
ff0295f0 | 173 | fprintf(stderr, |
174 | #ifdef HAVE_LONG_LONG | |
175 | _("INFO: Sent print file, %lld bytes...\n"), | |
176 | #else | |
177 | _("INFO: Sent print file, %ld bytes...\n"), | |
178 | #endif /* HAVE_LONG_LONG */ | |
179 | CUPS_LLCAST tbytes); | |
8e3807e7 | 180 | } |
181 | ||
182 | /* | |
183 | * Close the USB port and return... | |
184 | */ | |
185 | ||
43b5ad0d | 186 | close(device_fd); |
8e3807e7 | 187 | |
43b5ad0d | 188 | return (tbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK); |
8e3807e7 | 189 | } |
190 | ||
191 | ||
8e3807e7 | 192 | /* |
193 | * 'list_devices()' - List all USB devices. | |
194 | */ | |
195 | ||
196 | void | |
197 | list_devices(void) | |
198 | { | |
199 | #ifdef __linux | |
7c76593b | 200 | int i; /* Looping var */ |
201 | int fd; /* File descriptor */ | |
202 | char device[255], /* Device filename */ | |
203 | device_id[1024], /* Device ID string */ | |
204 | device_uri[1024], /* Device URI string */ | |
205 | make_model[1024]; /* Make and model */ | |
8e3807e7 | 206 | |
207 | /* | |
7c76593b | 208 | * Try to open each USB device... |
8e3807e7 | 209 | */ |
210 | ||
211 | for (i = 0; i < 16; i ++) | |
212 | { | |
7c76593b | 213 | /* |
214 | * Linux has a long history of changing the standard filenames used | |
215 | * for USB printer devices. We get the honor of trying them all... | |
216 | */ | |
8e3807e7 | 217 | |
7c76593b | 218 | sprintf(device, "/dev/usblp%d", i); |
219 | ||
220 | if ((fd = open(device, O_RDWR | O_EXCL)) < 0) | |
8e3807e7 | 221 | { |
7c76593b | 222 | if (errno != ENOENT) |
223 | continue; | |
8e3807e7 | 224 | |
7c76593b | 225 | sprintf(device, "/dev/usb/lp%d", i); |
226 | ||
227 | if ((fd = open(device, O_RDWR | O_EXCL)) < 0) | |
228 | { | |
229 | if (errno != ENOENT) | |
230 | continue; | |
231 | ||
232 | sprintf(device, "/dev/usb/usblp%d", i); | |
233 | ||
234 | if ((fd = open(device, O_RDWR | O_EXCL)) < 0) | |
235 | continue; | |
236 | } | |
8e3807e7 | 237 | } |
7c76593b | 238 | |
239 | if (!backendGetDeviceID(fd, device_id, sizeof(device_id), | |
240 | make_model, sizeof(make_model), | |
241 | "usb", device_uri, sizeof(device_uri))) | |
242 | printf("direct %s \"%s\" \"%s USB #%d\" \"%s\"\n", device_uri, | |
243 | make_model, make_model, i + 1, device_id); | |
244 | ||
245 | close(fd); | |
8e3807e7 | 246 | } |
247 | #elif defined(__sgi) | |
74ef1ffb | 248 | #elif defined(__sun) && defined(ECPPIOC_GETDEVID) |
8e3807e7 | 249 | int i; /* Looping var */ |
250 | int fd; /* File descriptor */ | |
251 | char device[255], /* Device filename */ | |
252 | device_id[1024], /* Device ID string */ | |
253 | device_uri[1024], /* Device URI string */ | |
254 | make_model[1024]; /* Make and model */ | |
8e3807e7 | 255 | |
256 | ||
257 | /* | |
258 | * Open each USB device... | |
259 | */ | |
260 | ||
261 | for (i = 0; i < 8; i ++) | |
262 | { | |
263 | sprintf(device, "/dev/usb/printer%d", i); | |
264 | ||
acc0a9db | 265 | if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0) |
8e3807e7 | 266 | { |
43b5ad0d | 267 | if (!backendGetDeviceID(fd, device_id, sizeof(device_id), |
268 | make_model, sizeof(make_model), | |
269 | "usb", device_uri, sizeof(device_uri))) | |
9ef960f1 | 270 | printf("direct %s \"%s\" \"%s USB #%d\" \"%s\"\n", device_uri, |
271 | make_model, make_model, i + 1, device_id); | |
8e3807e7 | 272 | |
273 | close(fd); | |
274 | } | |
8e3807e7 | 275 | } |
276 | #elif defined(__hpux) | |
277 | #elif defined(__osf) | |
fe688a06 | 278 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) |
8e3807e7 | 279 | int i; /* Looping var */ |
280 | char device[255]; /* Device filename */ | |
281 | ||
282 | ||
283 | for (i = 0; i < 8; i ++) | |
284 | { | |
285 | sprintf(device, "/dev/ulpt%d", i); | |
286 | if (!access(device, 0)) | |
287 | printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1); | |
288 | ||
289 | sprintf(device, "/dev/unlpt%d", i); | |
290 | if (!access(device, 0)) | |
291 | printf("direct usb:%s \"Unknown\" \"USB Printer #%d (no reset)\"\n", device, i + 1); | |
292 | } | |
293 | #endif | |
294 | } | |
295 | ||
296 | ||
297 | /* | |
298 | * 'open_device()' - Open a USB device... | |
299 | */ | |
300 | ||
761a542e | 301 | static int /* O - File descriptor or -1 on error */ |
acc0a9db | 302 | open_device(const char *uri, /* I - Device URI */ |
303 | int *use_bc) /* O - Set to 0 for unidirectional */ | |
8e3807e7 | 304 | { |
acc0a9db | 305 | int fd; /* File descriptor */ |
306 | ||
307 | ||
8e3807e7 | 308 | /* |
309 | * The generic implementation just treats the URI as a device filename... | |
310 | * Specific operating systems may also support using the device serial | |
311 | * number and/or make/model. | |
312 | */ | |
313 | ||
9ef960f1 | 314 | if (!strncmp(uri, "usb:/dev/", 9)) |
8e3807e7 | 315 | #ifdef __linux |
74584961 | 316 | { |
317 | /* | |
318 | * Do not allow direct devices anymore... | |
319 | */ | |
320 | ||
321 | errno = ENODEV; | |
322 | return (-1); | |
323 | } | |
9ef960f1 | 324 | else if (!strncmp(uri, "usb://", 6)) |
8e3807e7 | 325 | { |
326 | /* | |
327 | * For Linux, try looking up the device serial number or model... | |
328 | */ | |
329 | ||
330 | int i; /* Looping var */ | |
331 | int busy; /* Are any ports busy? */ | |
7c76593b | 332 | char device[255], /* Device filename */ |
8e3807e7 | 333 | device_id[1024], /* Device ID string */ |
334 | make_model[1024], /* Make and model */ | |
335 | device_uri[1024]; /* Device URI string */ | |
336 | ||
337 | ||
338 | /* | |
7c76593b | 339 | * Find the correct USB device... |
8e3807e7 | 340 | */ |
341 | ||
342 | do | |
343 | { | |
344 | for (busy = 0, i = 0; i < 16; i ++) | |
345 | { | |
7c76593b | 346 | /* |
347 | * Linux has a long history of changing the standard filenames used | |
348 | * for USB printer devices. We get the honor of trying them all... | |
349 | */ | |
350 | ||
351 | sprintf(device, "/dev/usblp%d", i); | |
352 | ||
353 | if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT) | |
354 | { | |
355 | sprintf(device, "/dev/usb/lp%d", i); | |
356 | ||
357 | if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT) | |
358 | { | |
359 | sprintf(device, "/dev/usb/usblp%d", i); | |
360 | ||
361 | if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT) | |
362 | continue; | |
363 | } | |
364 | } | |
8e3807e7 | 365 | |
7c76593b | 366 | if (fd >= 0) |
8e3807e7 | 367 | { |
43b5ad0d | 368 | backendGetDeviceID(fd, device_id, sizeof(device_id), |
369 | make_model, sizeof(make_model), | |
370 | "usb", device_uri, sizeof(device_uri)); | |
8e3807e7 | 371 | } |
372 | else | |
373 | { | |
374 | /* | |
375 | * If the open failed because it was busy, flag it so we retry | |
376 | * as needed... | |
377 | */ | |
378 | ||
379 | if (errno == EBUSY) | |
380 | busy = 1; | |
381 | ||
9ef960f1 | 382 | device_uri[0] = '\0'; |
8e3807e7 | 383 | } |
384 | ||
9ef960f1 | 385 | if (!strcmp(uri, device_uri)) |
8e3807e7 | 386 | { |
387 | /* | |
9ef960f1 | 388 | * Yes, return this file descriptor... |
8e3807e7 | 389 | */ |
390 | ||
ff0295f0 | 391 | fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n", |
392 | device); | |
8e3807e7 | 393 | |
9ef960f1 | 394 | return (fd); |
8e3807e7 | 395 | } |
396 | ||
397 | /* | |
398 | * This wasn't the one... | |
399 | */ | |
400 | ||
401 | if (fd >= 0) | |
402 | close(fd); | |
403 | } | |
404 | ||
405 | /* | |
406 | * If we get here and at least one of the printer ports showed up | |
407 | * as "busy", then sleep for a bit and retry... | |
408 | */ | |
409 | ||
410 | if (busy) | |
411 | { | |
ff0295f0 | 412 | fputs(_("INFO: Printer is busy; will retry in 5 seconds...\n"), |
413 | stderr); | |
8e3807e7 | 414 | sleep(5); |
415 | } | |
416 | } | |
417 | while (busy); | |
418 | ||
419 | /* | |
420 | * Couldn't find the printer, return "no such device or address"... | |
421 | */ | |
422 | ||
423 | errno = ENODEV; | |
424 | ||
425 | return (-1); | |
426 | } | |
427 | #elif defined(__sun) && defined(ECPPIOC_GETDEVID) | |
74584961 | 428 | { |
429 | /* | |
430 | * Do not allow direct devices anymore... | |
431 | */ | |
432 | ||
433 | errno = ENODEV; | |
434 | return (-1); | |
435 | } | |
9ef960f1 | 436 | else if (!strncmp(uri, "usb://", 6)) |
8e3807e7 | 437 | { |
438 | /* | |
439 | * For Solaris, try looking up the device serial number or model... | |
440 | */ | |
441 | ||
442 | int i; /* Looping var */ | |
443 | int busy; /* Are any ports busy? */ | |
8e3807e7 | 444 | char device[255], /* Device filename */ |
445 | device_id[1024], /* Device ID string */ | |
446 | make_model[1024], /* Make and model */ | |
447 | device_uri[1024]; /* Device URI string */ | |
8e3807e7 | 448 | |
449 | ||
450 | /* | |
451 | * Find the correct USB device... | |
452 | */ | |
453 | ||
454 | do | |
455 | { | |
63412dfd | 456 | for (i = 0, busy = 0; i < 8; i ++) |
8e3807e7 | 457 | { |
458 | sprintf(device, "/dev/usb/printer%d", i); | |
459 | ||
acc0a9db | 460 | if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0) |
43b5ad0d | 461 | backendGetDeviceID(fd, device_id, sizeof(device_id), |
462 | make_model, sizeof(make_model), | |
463 | "usb", device_uri, sizeof(device_uri)); | |
8e3807e7 | 464 | else |
465 | { | |
466 | /* | |
467 | * If the open failed because it was busy, flag it so we retry | |
468 | * as needed... | |
469 | */ | |
470 | ||
471 | if (errno == EBUSY) | |
472 | busy = 1; | |
473 | ||
9ef960f1 | 474 | device_uri[0] = '\0'; |
8e3807e7 | 475 | } |
476 | ||
9ef960f1 | 477 | if (!strcmp(uri, device_uri)) |
acc0a9db | 478 | { |
479 | /* | |
480 | * Yes, return this file descriptor... | |
481 | */ | |
482 | ||
483 | fputs("DEBUG: Setting use_bc to 0!\n", stderr); | |
484 | ||
485 | *use_bc = 0; | |
486 | ||
487 | return (fd); | |
488 | } | |
8e3807e7 | 489 | |
490 | /* | |
491 | * This wasn't the one... | |
492 | */ | |
493 | ||
494 | if (fd >= 0) | |
495 | close(fd); | |
496 | } | |
497 | ||
498 | /* | |
499 | * If we get here and at least one of the printer ports showed up | |
500 | * as "busy", then sleep for a bit and retry... | |
501 | */ | |
502 | ||
503 | if (busy) | |
504 | { | |
ff0295f0 | 505 | fputs(_("INFO: Printer is busy; will retry in 5 seconds...\n"), |
506 | stderr); | |
8e3807e7 | 507 | sleep(5); |
508 | } | |
509 | } | |
510 | while (busy); | |
511 | ||
512 | /* | |
513 | * Couldn't find the printer, return "no such device or address"... | |
514 | */ | |
515 | ||
516 | errno = ENODEV; | |
517 | ||
518 | return (-1); | |
519 | } | |
d4102150 | 520 | #else |
acc0a9db | 521 | { |
522 | if ((fd = open(uri + 4, O_RDWR | O_EXCL)) < 0) | |
523 | { | |
524 | fd = open(uri + 4, O_WRONLY | O_EXCL); | |
525 | *use_bc = 0; | |
526 | } | |
527 | ||
528 | return (fd); | |
529 | } | |
d4102150 | 530 | #endif /* __linux */ |
8e3807e7 | 531 | else |
532 | { | |
533 | errno = ENODEV; | |
534 | return (-1); | |
535 | } | |
536 | } | |
537 | ||
538 | ||
761a542e | 539 | /* |
540 | * 'side_cb()' - Handle side-channel requests... | |
541 | */ | |
542 | ||
543 | static void | |
544 | side_cb(int print_fd, /* I - Print file */ | |
545 | int device_fd, /* I - Device file */ | |
546 | int use_bc) /* I - Using back-channel? */ | |
547 | { | |
548 | cups_sc_command_t command; /* Request command */ | |
549 | cups_sc_status_t status; /* Request/response status */ | |
550 | char data[2048]; /* Request/response data */ | |
551 | int datalen; /* Request/response data size */ | |
552 | ||
553 | ||
554 | datalen = sizeof(data); | |
555 | ||
556 | if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0)) | |
557 | { | |
ff0295f0 | 558 | fputs(_("WARNING: Failed to read side-channel request!\n"), stderr); |
761a542e | 559 | return; |
560 | } | |
561 | ||
562 | switch (command) | |
563 | { | |
564 | case CUPS_SC_CMD_DRAIN_OUTPUT : | |
565 | if (tcdrain(device_fd)) | |
566 | status = CUPS_SC_STATUS_IO_ERROR; | |
567 | else | |
568 | status = CUPS_SC_STATUS_OK; | |
569 | ||
570 | datalen = 0; | |
571 | break; | |
572 | ||
573 | case CUPS_SC_CMD_GET_BIDI : | |
574 | data[0] = use_bc; | |
575 | datalen = 1; | |
576 | break; | |
577 | ||
578 | case CUPS_SC_CMD_GET_DEVICE_ID : | |
579 | memset(data, 0, sizeof(data)); | |
580 | ||
581 | if (backendGetDeviceID(device_fd, data, sizeof(data) - 1, | |
582 | NULL, 0, NULL, NULL, 0)) | |
583 | { | |
584 | status = CUPS_SC_STATUS_NOT_IMPLEMENTED; | |
585 | datalen = 0; | |
586 | } | |
587 | else | |
588 | { | |
589 | status = CUPS_SC_STATUS_OK; | |
590 | datalen = strlen(data); | |
591 | } | |
592 | break; | |
593 | ||
594 | default : | |
595 | status = CUPS_SC_STATUS_NOT_IMPLEMENTED; | |
596 | datalen = 0; | |
597 | break; | |
598 | } | |
599 | ||
600 | cupsSideChannelWrite(command, status, data, datalen, 1.0); | |
601 | } | |
602 | ||
603 | ||
8e3807e7 | 604 | /* |
c9d3f842 | 605 | * End of "$Id$". |
8e3807e7 | 606 | */ |