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