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