scsi-linux.c   [plain text]


/*
 * "$Id: scsi-linux.c 6835 2007-08-22 18:34:34Z mike $"
 *
 *   Linux SCSI printer support for the Common UNIX Printing System (CUPS).
 *
 *   Copyright 2007 by Apple Inc.
 *   Copyright 2003-2005 by Easy Software Products, all rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the
 *   following conditions are met:
 *
 *     1. Redistributions of source code must retain the above
 *	  copyright notice, this list of conditions and the
 *	  following disclaimer.
 *
 *     2. Redistributions in binary form must reproduce the
 *	  above copyright notice, this list of conditions and
 *	  the following disclaimer in the documentation and/or
 *	  other materials provided with the distribution.
 *
 *     3. All advertising materials mentioning features or use
 *	  of this software must display the following
 *	  acknowledgement:
 *
 *	    This product includes software developed by Easy
 *	    Software Products.
 *
 *     4. The name of Easy Software Products may not be used to
 *	  endorse or promote products derived from this software
 *	  without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS
 *   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 *   BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
 *   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 *   DAMAGE.
 *
 * Contents:
 *
 *   list_devices() - List the available SCSI printer devices.
 *   print_device() - Print a file to a SCSI device.
 */

/*
 * Include necessary headers.
 */

#include <scsi/sg.h>
#include <cups/i18n.h>


/*
 * We currently only support the Linux 2.4 generic SCSI interface.
 */

#ifndef SG_DXFER_TO_DEV
/*
 * Dummy functions that do nothing on unsupported platforms...
 */
void	list_devices(void) {}
int	print_device(const char *resource, int fd, int copies) { return (1); }
#else


/*
 * 'list_devices()' - List the available SCSI printer devices.
 */

void
list_devices(void)
{
  puts("direct scsi \"Unknown\" \"SCSI Printer\"");
}


/*
 * 'print_device()' - Print a file to a SCSI device.
 */

int					/* O - Print status */
print_device(const char *resource,	/* I - SCSI device */
             int        fd,		/* I - File to print */
	     int        copies)		/* I - Number of copies to print */
{
  int		scsi_fd;		/* SCSI file descriptor */
  char		buffer[8192];		/* Data buffer */
  int		bytes;			/* Number of bytes */
  int		try;			/* Current try */
  sg_io_hdr_t	scsi_req;		/* SCSI request */
  unsigned char	scsi_cmd[6],		/* SCSI command data */
		scsi_sense[32];		/* SCSI sense data */
#  if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
  struct sigaction action;		/* Actions for POSIX signals */
#  endif /* HAVE_SIGACTION && !HAVE_SIGSET */


 /*
  * Make sure we have a valid resource name...
  */

  if (strncmp(resource, "/dev/sg", 7) != 0)
  {
    _cupsLangPrintf(stderr, _("ERROR: Bad SCSI device file \"%s\"!\n"),
                    resource);
    return (CUPS_BACKEND_STOP);
  }

 /*
  * Open the SCSI device file...
  */

  fputs("STATE: +connecting-to-device\n", stderr);

  do
  {
    if ((scsi_fd = open(resource, O_RDWR | O_EXCL)) == -1)
    {
      if (getenv("CLASS") != NULL)
      {
       /*
        * If the CLASS environment variable is set, the job was submitted
	* to a class and not to a specific queue.  In this case, we want
	* to abort immediately so that the job can be requeued on the next
	* available printer in the class.
	*/

        _cupsLangPuts(stderr,
	              _("INFO: Unable to contact printer, queuing on next "
		        "printer in class...\n"));

       /*
        * Sleep 5 seconds to keep the job from requeuing too rapidly...
	*/

	sleep(5);

        return (CUPS_BACKEND_FAILED);
      }

      if (errno != EAGAIN && errno != EBUSY)
      {
	_cupsLangPrintf(stderr,
	                _("ERROR: Unable to open device file \"%s\": %s\n"),
			resource, strerror(errno));
	return (CUPS_BACKEND_FAILED);
      }
      else
      {
        _cupsLangPuts(stderr,
	              _("INFO: Printer busy; will retry in 30 seconds...\n"));
        sleep(30);
      }
    }
  }
  while (scsi_fd == -1);

  fputs("STATE: -connecting-to-device\n", stderr);

 /*
  * Now that we are "connected" to the port, ignore SIGTERM so that we
  * can finish out any page data the driver sends (e.g. to eject the
  * current page...  Only ignore SIGTERM if we are printing data from
  * stdin (otherwise you can't cancel raw jobs...)
  */

  if (fd != 0)
  {
#  ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
    sigset(SIGTERM, SIG_IGN);
#  elif defined(HAVE_SIGACTION)
    memset(&action, 0, sizeof(action));

    sigemptyset(&action.sa_mask);
    action.sa_handler = SIG_IGN;
    sigaction(SIGTERM, &action, NULL);
#  else
    signal(SIGTERM, SIG_IGN);
#  endif /* HAVE_SIGSET */
  }

 /*
  * Copy the print file to the device...
  */

  while (copies > 0)
  {
    if (fd != 0)
      lseek(fd, 0, SEEK_SET);

    while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
    {
      memset(&scsi_req, 0, sizeof(scsi_req));

      scsi_req.interface_id    = 'S';
      scsi_req.dxfer_direction = SG_DXFER_TO_DEV;
      scsi_req.cmd_len         = 6;
      scsi_req.mx_sb_len       = sizeof(scsi_sense);
      scsi_req.iovec_count     = 0;
      scsi_req.dxfer_len       = bytes;
      scsi_req.dxferp          = buffer;
      scsi_req.cmdp            = scsi_cmd;
      scsi_req.sbp             = scsi_sense;
      scsi_req.timeout         = 60 * 1000;
      
      scsi_cmd[0] = 0x0a;	/* Group 0 print command */
      scsi_cmd[1] = 0x00;
      scsi_cmd[2] = bytes / 65536;
      scsi_cmd[3] = bytes / 256;
      scsi_cmd[4] = bytes;
      scsi_cmd[5] = 0x00;

      for (try = 0; try < 10; try ++)
	if (ioctl(scsi_fd, SG_IO, &scsi_req) < 0 ||
            scsi_req.status != 0)
        {
	  _cupsLangPrintf(stderr,
			  _("WARNING: SCSI command timed out (%d); "
			    "retrying...\n"), scsi_req.status);
          sleep(try + 1);
	}
	else
          break;

      if (try >= 10)
      {
	_cupsLangPrintf(stderr, _("ERROR: Unable to send print data (%d)\n"),
			scsi_req.status);
        close(scsi_fd);
	return (CUPS_BACKEND_FAILED);
      }
    }

    copies --;
  }

 /*
  * Close the device and return...
  */

  close(fd);

  return (CUPS_BACKEND_OK);
}
#endif /* !SG_DXFER_TO_DEV */


/*
 * End of "$Id: scsi-linux.c 6835 2007-08-22 18:34:34Z mike $".
 */