]> git.ipfire.org Git - thirdparty/cups.git/blame_incremental - backend/usb-unix.c
Merge pull request #1312 from weblate/weblate-cups-cups
[thirdparty/cups.git] / backend / usb-unix.c
... / ...
CommitLineData
1/*
2 * USB port backend for CUPS.
3 *
4 * This file is included from "usb.c" when compiled on UNIX/Linux.
5 *
6 * Copyright © 2020-2024 by OpenPrinting.
7 * Copyright © 2007-2013 by Apple Inc.
8 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
9 *
10 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * information.
12 */
13
14/*
15 * Include necessary headers.
16 */
17
18#include <sys/select.h>
19
20
21/*
22 * Local functions...
23 */
24
25static int open_device(const char *uri, int *use_bc);
26static int side_cb(int print_fd, int device_fd, int snmp_fd,
27 http_addr_t *addr, int use_bc);
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 */
38 char *options, /* I - Device options/serial number */
39 int print_fd, /* I - File descriptor to print */
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 */
43{
44 int use_bc; /* Use backchannel path? */
45 int device_fd; /* USB device */
46 ssize_t tbytes; /* Total number of bytes written */
47 struct termios opts; /* Parallel port options */
48
49
50 (void)argc;
51 (void)argv;
52
53 /*
54 * Open the USB port device...
55 */
56
57 fputs("STATE: +connecting-to-device\n", stderr);
58
59 do
60 {
61#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
62 /*
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()...
66 */
67
68 use_bc = 0;
69
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
78#else
79 /*
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...
84 */
85
86 use_bc = _cups_strcasecmp(hostname, "Brother") &&
87 _cups_strcasecmp(hostname, "Canon") &&
88 _cups_strncasecmp(hostname, "Konica", 6) &&
89 _cups_strncasecmp(hostname, "Minolta", 7);
90#endif /* __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __DragonFly__ */
91
92 if ((device_fd = open_device(uri, &use_bc)) == -1)
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
103 _cupsLangPrintFilter(stderr, "INFO",
104 _("Unable to contact printer, queuing on next "
105 "printer in class."));
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 {
118 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
119 sleep(10);
120 }
121 else if (errno == ENXIO || errno == EIO || errno == ENOENT ||
122 errno == ENODEV)
123 {
124 sleep(30);
125 }
126 else
127 {
128 _cupsLangPrintError("ERROR", _("Unable to open device file"));
129 return (CUPS_BACKEND_FAILED);
130 }
131 }
132 }
133 while (device_fd < 0);
134
135 fputs("STATE: -connecting-to-device\n", stderr);
136
137 /*
138 * Set any options provided...
139 */
140
141 tcgetattr(device_fd, &opts);
142
143 opts.c_lflag &= ~(unsigned)(ICANON | ECHO | ISIG); /* Raw mode */
144
145 /**** No options supported yet ****/
146
147 tcsetattr(device_fd, TCSANOW, &opts);
148
149 /*
150 * Finally, send the print file...
151 */
152
153 tbytes = 0;
154
155 while (copies > 0 && tbytes >= 0)
156 {
157 copies --;
158
159 if (print_fd != 0)
160 {
161 fputs("PAGE: 1 1\n", stderr);
162 lseek(print_fd, 0, SEEK_SET);
163 }
164
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
171 tbytes = backendRunLoop(print_fd, device_fd, -1, NULL, use_bc, 1, NULL);
172
173#else
174 tbytes = backendRunLoop(print_fd, device_fd, -1, NULL, use_bc, 1, side_cb);
175#endif /* __sun */
176
177 if (print_fd != 0 && tbytes >= 0)
178 _cupsLangPrintFilter(stderr, "INFO", _("Print file sent."));
179 }
180
181 /*
182 * Close the USB port and return...
183 */
184
185 close(device_fd);
186
187 return (CUPS_BACKEND_OK);
188}
189
190
191/*
192 * 'list_devices()' - List all USB devices.
193 */
194
195void
196list_devices(void)
197{
198#ifdef __linux
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 */
205
206
207 /*
208 * Try to open each USB device...
209 */
210
211 for (i = 0; i < 16; i ++)
212 {
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 */
217
218 snprintf(device, sizeof(device), "/dev/usblp%d", i);
219
220 if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
221 {
222 if (errno != ENOENT)
223 continue;
224
225 snprintf(device, sizeof(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 snprintf(device, sizeof(device), "/dev/usb/usblp%d", i);
233
234 if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
235 continue;
236 }
237 }
238
239 if (!backendGetDeviceID(fd, device_id, sizeof(device_id),
240 make_model, sizeof(make_model),
241 "usb", device_uri, sizeof(device_uri)))
242 cupsBackendReport("direct", device_uri, make_model, make_model,
243 device_id, NULL);
244
245 close(fd);
246 }
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 */
254
255
256 /*
257 * Open each USB device...
258 */
259
260 for (i = 0; i < 8; i ++)
261 {
262 snprintf(device, sizeof(device), "/dev/usb/printer%d", i);
263
264 if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0)
265 {
266 if (!backendGetDeviceID(fd, device_id, sizeof(device_id),
267 make_model, sizeof(make_model),
268 "usb", device_uri, sizeof(device_uri)))
269 cupsBackendReport("direct", device_uri, make_model, make_model,
270 device_id, NULL);
271
272 close(fd);
273 }
274 }
275#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
276 int i; /* Looping var */
277 char device[255]; /* Device filename */
278
279
280 for (i = 0; i < 8; i ++)
281 {
282 snprintf(device, sizeof(device), "/dev/ulpt%d", i);
283 if (!access(device, 0))
284 printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);
285
286 snprintf(device, sizeof(device), "/dev/unlpt%d", i);
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
298static int /* O - File descriptor or -1 on error */
299open_device(const char *uri, /* I - Device URI */
300 int *use_bc) /* O - Set to 0 for unidirectional */
301{
302 int fd; /* File descriptor */
303
304
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
313 {
314 /*
315 * Do not allow direct devices anymore...
316 */
317
318 errno = ENODEV;
319 return (-1);
320 }
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? */
329 char device[255], /* Device filename */
330 device_id[1024], /* Device ID string */
331 make_model[1024], /* Make and model */
332 device_uri[1024]; /* Device URI string */
333
334
335 /*
336 * Find the correct USB device...
337 */
338
339 for (;;)
340 {
341 for (busy = 0, i = 0; i < 16; i ++)
342 {
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
348 snprintf(device, sizeof(device), "/dev/usblp%d", i);
349
350 if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)
351 {
352 snprintf(device, sizeof(device), "/dev/usb/lp%d", i);
353
354 if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)
355 {
356 snprintf(device, sizeof(device), "/dev/usb/usblp%d", i);
357
358 if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)
359 continue;
360 }
361 }
362
363 if (fd >= 0)
364 {
365 backendGetDeviceID(fd, device_id, sizeof(device_id),
366 make_model, sizeof(make_model),
367 "usb", device_uri, sizeof(device_uri));
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
388 fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n",
389 device);
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)
408 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
409
410 sleep(5);
411 }
412 }
413#elif defined(__sun) && defined(ECPPIOC_GETDEVID)
414 {
415 /*
416 * Do not allow direct devices anymore...
417 */
418
419 errno = ENODEV;
420 return (-1);
421 }
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? */
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 */
434
435
436 /*
437 * Find the correct USB device...
438 */
439
440 do
441 {
442 for (i = 0, busy = 0; i < 8; i ++)
443 {
444 snprintf(device, sizeof(device), "/dev/usb/printer%d", i);
445
446 if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0)
447 backendGetDeviceID(fd, device_id, sizeof(device_id),
448 make_model, sizeof(make_model),
449 "usb", device_uri, sizeof(device_uri));
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))
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 }
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 {
491 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
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
506 {
507 if (*use_bc)
508 fd = open(uri + 4, O_RDWR | O_EXCL);
509 else
510 fd = -1;
511
512 if (fd < 0)
513 {
514 fd = open(uri + 4, O_WRONLY | O_EXCL);
515 *use_bc = 0;
516 }
517
518 return (fd);
519 }
520#endif /* __linux */
521 else
522 {
523 errno = ENODEV;
524 return (-1);
525 }
526}
527
528
529/*
530 * 'side_cb()' - Handle side-channel requests...
531 */
532
533static int /* O - 0 on success, -1 on error */
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? */
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
546 (void)snmp_fd;
547 (void)addr;
548
549 datalen = sizeof(data);
550
551 if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
552 return (-1);
553
554 switch (command)
555 {
556 case CUPS_SC_CMD_DRAIN_OUTPUT :
557 if (backendDrainOutput(print_fd, device_fd))
558 status = CUPS_SC_STATUS_IO_ERROR;
559 else if (tcdrain(device_fd))
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 :
568 status = CUPS_SC_STATUS_OK;
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
595 return (cupsSideChannelWrite(command, status, data, datalen, 1.0));
596}