• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 static int	open_device(const char *uri, int *use_bc);
26 static 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 
34 int					/* O - Exit status */
print_device(const char * uri,const char * hostname,const char * resource,char * options,int print_fd,int copies,int argc,char * argv[])35 print_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 
195 void
list_devices(void)196 list_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 
298 static int				/* O - File descriptor or -1 on error */
open_device(const char * uri,int * use_bc)299 open_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 
533 static int				/* O - 0 on success, -1 on error */
side_cb(int print_fd,int device_fd,int snmp_fd,http_addr_t * addr,int use_bc)534 side_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 }
597