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