]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/serial.c
Support for GNU/kFreeBSD (STR #2367)
[thirdparty/cups.git] / backend / serial.c
index 003bd766a3bcb76a76135a7391c15587d44ad340..420039d15bd499372db95bd366dee925a635654d 100644 (file)
@@ -1,57 +1,51 @@
 /*
- * "$Id: serial.c,v 1.38 2002/01/02 17:58:34 mike Exp $"
+ * "$Id$"
  *
  *   Serial port backend for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2002 by Easy Software Products, all rights reserved.
+ *   Copyright 2007 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
  *   "LICENSE" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636-3111 USA
- *
- *       Voice: (301) 373-9603
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   This file is subject to the Apple OS-Developed Software exception.
  *
  * Contents:
  *
  *   main()         - Send a file to the printer or server.
  *   list_devices() - List all serial devices.
+ *   side_cb()      - Handle side-channel requests...
  */
 
 /*
  * Include necessary headers.
  */
 
-#include <cups/cups.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <cups/string.h>
-#include <signal.h>
+#include "backend-private.h"
 
 #ifdef __hpux
 #  include <sys/modem.h>
 #endif /* __hpux */
 
-#if defined(WIN32) || defined(__EMX__)
+#ifdef WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
 #  include <fcntl.h>
 #  include <termios.h>
+#  ifdef __hpux
+#    include <sys/time.h>
+#  else
+#    include <sys/select.h>
+#  endif /* __hpux */
 #  ifdef HAVE_SYS_IOCTL_H
 #    include <sys/ioctl.h>
 #  endif /* HAVE_SYS_IOCTL_H */
-#endif /* WIN32 || __EMX__ */
+#endif /* WIN32 */
 
 #ifdef __sgi
 #  include <invent.h>
 #  endif /* CNEW_RTSCTS */
 #endif /* !CRTSCTS */
 
+#if defined(__APPLE__)
+#  include <CoreFoundation/CoreFoundation.h>
+#  include <IOKit/IOKitLib.h>
+#  include <IOKit/serial/IOSerialKeys.h>
+#  include <IOKit/IOBSD.h>
+#endif /* __APPLE__ */
+
+#if defined(__linux) && defined(TIOCGSERIAL)
+#  include <linux/serial.h>
+#  include <linux/ioctl.h>
+#endif /* __linux && TIOCGSERIAL */
+
 
 /*
  * Local functions...
  */
 
-void   list_devices(void);
+static void    list_devices(void);
+static void    side_cb(int print_fd, int device_fd, int use_bc);
 
 
 /*
@@ -88,33 +95,38 @@ void        list_devices(void);
  *    printer-uri job-id user title copies options [file]
  */
 
-int                    /* O - Exit status */
-main(int  argc,                /* I - Number of command-line arguments (6 or 7) */
-     char *argv[])     /* I - Command-line arguments */
+int                                    /* O - Exit status */
+main(int  argc,                                /* I - Number of command-line arguments (6 or 7) */
+     char *argv[])                     /* I - Command-line arguments */
 {
-  char         method[255],    /* Method in URI */
-               hostname[1024], /* Hostname */
-               username[255],  /* Username info (not used) */
-               resource[1024], /* Resource info (device and options) */
-               *options,       /* Pointer to options */
-               name[255],      /* Name of option */
-               value[255],     /* Value of option */
-               *ptr;           /* Pointer into name or value */
-  int          port;           /* Port number (not used) */
-  FILE         *fp;            /* Print file */
-  int          copies;         /* Number of copies to print */
-  int          fd;             /* Parallel device */
-  int          wbytes;         /* Number of bytes written */
-  size_t       nbytes,         /* Number of bytes read */
-               tbytes;         /* Total number of bytes written */
-  int          dtrdsr;         /* Do dtr/dsr flow control? */
-  int          bufsize;        /* Size of output buffer for writes */
-  char         buffer[8192],   /* Output buffer */
-               *bufptr;        /* Pointer into buffer */
-  struct termios opts;         /* Serial port options */
-  struct termios origopts;     /* Original port options */
+  char         method[255],            /* Method in URI */
+               hostname[1024],         /* Hostname */
+               username[255],          /* Username info (not used) */
+               resource[1024],         /* Resource info (device and options) */
+               *options,               /* Pointer to options */
+               name[255],              /* Name of option */
+               value[255],             /* Value of option */
+               *ptr;                   /* Pointer into name or value */
+  int          port;                   /* Port number (not used) */
+  int          copies;                 /* Number of copies to print */
+  int          print_fd,               /* Print file */
+               device_fd;              /* Serial device */
+  int          nfds;                   /* Maximum file descriptor value + 1 */
+  fd_set       input,                  /* Input set for reading */
+               output;                 /* Output set for writing */
+  ssize_t      print_bytes,            /* Print bytes read */
+               bc_bytes,               /* Backchannel bytes read */
+               total_bytes,            /* Total bytes written */
+               bytes;                  /* Bytes written */
+  int          dtrdsr;                 /* Do dtr/dsr flow control? */
+  int          print_size;             /* Size of output buffer for writes */
+  char         print_buffer[8192],     /* Print data buffer */
+               *print_ptr,             /* Pointer into print data buffer */
+               bc_buffer[1024];        /* Back-channel data buffer */
+  struct termios opts;                 /* Serial port options */
+  struct termios origopts;             /* Original port options */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
-  struct sigaction action;     /* Actions for POSIX signals */
+  struct sigaction action;             /* Actions for POSIX signals */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
 
 
@@ -124,6 +136,20 @@ main(int  argc,            /* I - Number of command-line arguments (6 or 7) */
 
   setbuf(stderr, NULL);
 
+ /*
+  * Ignore SIGPIPE signals...
+  */
+
+#ifdef HAVE_SIGSET
+  sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+  memset(&action, 0, sizeof(action));
+  action.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &action, NULL);
+#else
+  signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
  /*
   * Check command-line...
   */
@@ -131,12 +157,14 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
   if (argc == 1)
   {
     list_devices();
-    return (0);
+    return (CUPS_BACKEND_OK);
   }
   else if (argc < 6 || argc > 7)
   {
-    fputs("Usage: serial job-id user title copies options [file]\n", stderr);
-    return (1);
+    _cupsLangPrintf(stderr,
+                    _("Usage: %s job-id user title copies options [file]\n"),
+                   argv[0]);
+    return (CUPS_BACKEND_FAILED);
   }
 
  /*
@@ -146,8 +174,8 @@ main(int  argc,             /* I - Number of command-line arguments (6 or 7) */
 
   if (argc == 6)
   {
-    fp     = stdin;
-    copies = 1;
+    print_fd = 0;
+    copies   = 1;
   }
   else
   {
@@ -155,10 +183,10 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
     * Try to open the print file...
     */
 
-    if ((fp = fopen(argv[6], "rb")) == NULL)
+    if ((print_fd = open(argv[6], O_RDONLY)) < 0)
     {
       perror("ERROR: unable to open print file");
-      return (1);
+      return (CUPS_BACKEND_FAILED);
     }
 
     copies = atoi(argv[4]);
@@ -168,7 +196,10 @@ main(int  argc,            /* I - Number of command-line arguments (6 or 7) */
   * Extract the device name and options from the URI...
   */
 
-  httpSeparate(argv[0], method, username, hostname, &port, resource);
+  httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
+                  method, sizeof(method), username, sizeof(username),
+                 hostname, sizeof(hostname), &port,
+                 resource, sizeof(resource));
 
  /*
   * See if there are any options...
@@ -188,38 +219,70 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
   * Open the serial port device...
   */
 
+  fputs("STATE: +connecting-to-device\n", stderr);
+
   do
   {
-    if ((fd = open(resource, O_WRONLY | O_NOCTTY | O_EXCL | O_NDELAY)) == -1)
+    if ((device_fd = open(resource, O_RDWR | O_NOCTTY | O_EXCL |
+                                    O_NDELAY)) == -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 == EBUSY)
       {
-        fputs("INFO: Serial port busy; will retry in 30 seconds...\n", stderr);
+        _cupsLangPuts(stderr,
+                     _("INFO: Printer busy; will retry in 30 seconds...\n"));
        sleep(30);
       }
       else
       {
-       perror("ERROR: Unable to open serial port device file");
-       return (1);
+       _cupsLangPrintf(stderr,
+                       _("ERROR: Unable to open device file \"%s\": %s\n"),
+                       resource, strerror(errno));
+       return (CUPS_BACKEND_FAILED);
       }
     }
   }
-  while (fd < 0);
+  while (device_fd < 0);
+
+  fputs("STATE: -connecting-to-device\n", stderr);
 
  /*
   * Set any options provided...
   */
 
-  tcgetattr(fd, &origopts);
-  tcgetattr(fd, &opts);
+  tcgetattr(device_fd, &origopts);
+  tcgetattr(device_fd, &opts);
 
-  opts.c_lflag &= ~(ICANON | ECHO | ISIG);     /* Raw mode */
-  opts.c_oflag &= ~OPOST;                      /* Don't post-process */
+  opts.c_lflag &= ~(ICANON | ECHO | ISIG);
+                                       /* Raw mode */
+  opts.c_oflag &= ~OPOST;              /* Don't post-process */
 
-  bufsize = 96;                /* 9600 baud / 10 bits/char / 10Hz */
-  dtrdsr  = 0;         /* No dtr/dsr flow control */
+  print_size = 96;                     /* 9600 baud / 10 bits/char / 10Hz */
+  dtrdsr     = 0;                      /* No dtr/dsr flow control */
 
-  if (options != NULL)
+  if (options)
+  {
     while (*options)
     {
      /*
@@ -227,7 +290,8 @@ main(int  argc,             /* I - Number of command-line arguments (6 or 7) */
       */
 
       for (ptr = name; *options && *options != '=';)
-        *ptr++ = *options++;
+        if (ptr < (name + sizeof(name) - 1))
+          *ptr++ = *options++;
       *ptr = '\0';
 
       if (*options == '=')
@@ -238,11 +302,12 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
 
         options ++;
 
-       for (ptr = value; *options && *options != '+';)
-          *ptr++ = *options++;
+       for (ptr = value; *options && *options != '+' && *options != '&';)
+          if (ptr < (value + sizeof(value) - 1))
+            *ptr++ = *options++;
        *ptr = '\0';
 
-       if (*options == '+')
+       if (*options == '+' || *options == '&')
          options ++;
       }
       else
@@ -252,13 +317,13 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
       * Process the option...
       */
 
-      if (strcasecmp(name, "baud") == 0)
+      if (!strcasecmp(name, "baud"))
       {
        /*
         * Set the baud rate...
        */
 
-        bufsize = atoi(value) / 100;
+        print_size = atoi(value) / 100;
 
 #if B19200 == 19200
         cfsetispeed(&opts, atoi(value));
@@ -290,25 +355,32 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
              cfsetispeed(&opts, B38400);
              cfsetospeed(&opts, B38400);
              break;
-#ifdef B57600
+#  ifdef B57600
          case 57600 :
              cfsetispeed(&opts, B57600);
              cfsetospeed(&opts, B57600);
              break;
-#endif /* B57600 */
-#ifdef B115200
+#  endif /* B57600 */
+#  ifdef B115200
          case 115200 :
              cfsetispeed(&opts, B115200);
              cfsetospeed(&opts, B115200);
              break;
-#endif /* B115200 */
+#  endif /* B115200 */
+#  ifdef B230400
+         case 230400 :
+             cfsetispeed(&opts, B230400);
+             cfsetospeed(&opts, B230400);
+             break;
+#  endif /* B230400 */
           default :
-             fprintf(stderr, "WARNING: Unsupported baud rate %s!\n", value);
+             _cupsLangPrintf(stderr, _("WARNING: Unsupported baud rate %s!\n"),
+                             value);
              break;
        }
 #endif /* B19200 == 19200 */
       }
-      else if (strcasecmp(name, "bits") == 0)
+      else if (!strcasecmp(name, "bits"))
       {
        /*
         * Set number of data bits...
@@ -329,59 +401,95 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
              break;
        }
       }
-      else if (strcasecmp(name, "parity") == 0)
+      else if (!strcasecmp(name, "parity"))
       {
        /*
        * Set parity checking...
        */
 
-       if (strcasecmp(value, "even") == 0)
+       if (!strcasecmp(value, "even"))
        {
          opts.c_cflag |= PARENB;
           opts.c_cflag &= ~PARODD;
        }
-       else if (strcasecmp(value, "odd") == 0)
+       else if (!strcasecmp(value, "odd"))
        {
          opts.c_cflag |= PARENB;
           opts.c_cflag |= PARODD;
        }
-       else if (strcasecmp(value, "none") == 0)
+       else if (!strcasecmp(value, "none"))
+         opts.c_cflag &= ~PARENB;
+       else if (!strcasecmp(value, "space"))
+       {
+        /*
+         * Note: we only support space parity with 7 bits per character...
+         */
+
+         opts.c_cflag &= ~CSIZE;
+          opts.c_cflag |= CS8;
+         opts.c_cflag &= ~PARENB;
+        }
+       else if (!strcasecmp(value, "mark"))
+       {
+        /*
+         * Note: we only support mark parity with 7 bits per character
+         * and 1 stop bit...
+         */
+
+         opts.c_cflag &= ~CSIZE;
+          opts.c_cflag |= CS7;
          opts.c_cflag &= ~PARENB;
+          opts.c_cflag |= CSTOPB;
+        }
       }
-      else if (strcasecmp(name, "flow") == 0)
+      else if (!strcasecmp(name, "flow"))
       {
        /*
        * Set flow control...
        */
 
-       if (strcasecmp(value, "none") == 0)
+       if (!strcasecmp(value, "none"))
        {
-         opts.c_iflag &= ~(IXON | IXOFF | IXANY);
+         opts.c_iflag &= ~(IXON | IXOFF);
           opts.c_cflag &= ~CRTSCTS;
        }
-       else if (strcasecmp(value, "soft") == 0)
+       else if (!strcasecmp(value, "soft"))
        {
-         opts.c_iflag |= IXON | IXOFF | IXANY;
+         opts.c_iflag |= IXON | IXOFF;
           opts.c_cflag &= ~CRTSCTS;
        }
-       else if (strcasecmp(value, "hard") == 0 ||
-                strcasecmp(value, "rtscts") == 0)
+       else if (!strcasecmp(value, "hard") ||
+                !strcasecmp(value, "rtscts"))
         {
-         opts.c_iflag &= ~(IXON | IXOFF | IXANY);
+         opts.c_iflag &= ~(IXON | IXOFF);
           opts.c_cflag |= CRTSCTS;
        }
-       else if (strcasecmp(value, "dtrdsr") == 0)
+       else if (!strcasecmp(value, "dtrdsr"))
        {
-         opts.c_iflag &= ~(IXON | IXOFF | IXANY);
+         opts.c_iflag &= ~(IXON | IXOFF);
           opts.c_cflag &= ~CRTSCTS;
 
          dtrdsr = 1;
        }
       }
+      else if (!strcasecmp(name, "stop"))
+      {
+        switch (atoi(value))
+       {
+         case 1 :
+             opts.c_cflag &= ~CSTOPB;
+             break;
+
+         case 2 :
+             opts.c_cflag |= CSTOPB;
+             break;
+       }
+      }
     }
+  }
 
-  tcsetattr(fd, TCSANOW, &opts);
-  fcntl(fd, F_SETFL, 0);
+  tcsetattr(device_fd, TCSANOW, &opts);
+  fcntl(device_fd, F_SETFL, 0);
 
  /*
   * Now that we are "connected" to the port, ignore SIGTERM so that we
@@ -390,7 +498,7 @@ main(int  argc,             /* I - Number of command-line arguments (6 or 7) */
   * stdin (otherwise you can't cancel raw jobs...)
   */
 
-  if (argc < 7)
+  if (print_fd != 0)
   {
 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
     sigset(SIGTERM, SIG_IGN);
@@ -406,80 +514,189 @@ main(int  argc,          /* I - Number of command-line arguments (6 or 7) */
   }
 
  /*
-  * Finally, send the print file...
+  * Figure out the maximum file descriptor value to use with select()...
+  */
+
+  nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
+
+ /*
+  * Finally, send the print file.  Ordinarily we would just use the
+  * backendRunLoop() function, however since we need to use smaller
+  * writes and may need to do DSR/DTR flow control, we duplicate much
+  * of the code here instead...
   */
 
-  if (bufsize > sizeof(buffer))
-    bufsize = sizeof(buffer);
+  if (print_size > sizeof(print_buffer))
+    print_size = sizeof(print_buffer);
+
+  total_bytes = 0;
 
   while (copies > 0)
   {
     copies --;
 
-    if (fp != stdin)
+    if (print_fd != 0)
     {
       fputs("PAGE: 1 1\n", stderr);
-      rewind(fp);
+      lseek(print_fd, 0, SEEK_SET);
     }
 
-    if (dtrdsr)
+   /*
+    * Now loop until we are out of data from print_fd...
+    */
+
+    for (print_bytes = 0, print_ptr = print_buffer;;)
     {
      /*
-      * Check the port and sleep until DSR is set...
+      * Use select() to determine whether we have data to copy around...
+      */
+
+      FD_ZERO(&input);
+      if (!print_bytes)
+       FD_SET(print_fd, &input);
+      FD_SET(device_fd, &input);
+      FD_SET(CUPS_SC_FD, &input);
+
+      FD_ZERO(&output);
+      if (print_bytes)
+       FD_SET(device_fd, &output);
+
+      if (select(nfds, &input, &output, NULL, NULL) < 0)
+       continue;                       /* Ignore errors here */
+
+     /*
+      * Check if we have a side-channel request ready...
       */
 
-      int status;
+      if (FD_ISSET(CUPS_SC_FD, &input))
+        side_cb(print_fd, device_fd, 1);
 
+     /*
+      * Check if we have back-channel data ready...
+      */
 
-      if (!ioctl(fd, TIOCMGET, &status))
-        if (!(status & TIOCM_DSR))
+      if (FD_ISSET(device_fd, &input))
+      {
+       if ((bc_bytes = read(device_fd, bc_buffer, sizeof(bc_buffer))) > 0)
+       {
+         fprintf(stderr,
+                 "DEBUG: Received " CUPS_LLFMT " bytes of back-channel data!\n",
+                 CUPS_LLCAST bc_bytes);
+          cupsBackChannelWrite(bc_buffer, bc_bytes, 1.0);
+       }
+      }
+
+     /*
+      * Check if we have print data ready...
+      */
+
+      if (FD_ISSET(print_fd, &input))
+      {
+       if ((print_bytes = read(print_fd, print_buffer, print_size)) < 0)
        {
         /*
-         * Wait for DSR to go high...
+          * Read error - bail if we don't see EAGAIN or EINTR...
          */
 
-         fputs("DEBUG: DSR is low; waiting for device...\n", stderr);
-
-          do
+         if (errno != EAGAIN || errno != EINTR)
          {
-           sleep(1);
-           if (ioctl(fd, TIOCMGET, &status))
-             break;
+           perror("ERROR: Unable to read print data");
+
+            tcsetattr(device_fd, TCSADRAIN, &origopts);
+
+           close(device_fd);
+
+           if (print_fd != 0)
+             close(print_fd);
+
+           return (CUPS_BACKEND_FAILED);
          }
-         while (!(status & TIOCM_DSR));
 
-         fputs("DEBUG: DSR is high; writing to device...\n", stderr);
-        }
-    }
+          print_bytes = 0;
+       }
+       else if (print_bytes == 0)
+       {
+        /*
+          * End of file, break out of the loop...
+         */
+
+          break;
+       }
+
+       print_ptr = print_buffer;
+      }
 
-    tbytes = 0;
-    while ((nbytes = fread(buffer, 1, bufsize, fp)) > 0)
-    {
      /*
-      * Write the print data to the printer...
+      * Check if the device is ready to receive data and we have data to
+      * send...
       */
 
-      tbytes += nbytes;
-      bufptr = buffer;
-
-      while (nbytes > 0)
+      if (print_bytes && FD_ISSET(device_fd, &output))
       {
-       if ((wbytes = write(fd, bufptr, nbytes)) < 0)
-         if (errno == ENOTTY)
-           wbytes = write(fd, bufptr, nbytes);
+       if (dtrdsr)
+       {
+        /*
+         * Check the port and sleep until DSR is set...
+         */
+
+         int status;
+
+
+         if (!ioctl(device_fd, TIOCMGET, &status))
+            if (!(status & TIOCM_DSR))
+           {
+            /*
+             * Wait for DSR to go high...
+             */
+
+             fputs("DEBUG: DSR is low; waiting for device...\n", stderr);
+
+              do
+             {
+              /*
+               * Poll every 100ms...
+               */
+
+               usleep(100000);
 
-       if (wbytes < 0)
+               if (ioctl(device_fd, TIOCMGET, &status))
+                 break;
+             }
+             while (!(status & TIOCM_DSR));
+
+             fputs("DEBUG: DSR is high; writing to device...\n", stderr);
+            }
+       }
+
+       if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
        {
-         perror("ERROR: Unable to send print file to printer");
-         break;
+        /*
+          * Write error - bail if we don't see an error we can retry...
+         */
+
+         if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
+         {
+           perror("ERROR: Unable to write print data");
+
+            tcsetattr(device_fd, TCSADRAIN, &origopts);
+
+           close(device_fd);
+
+           if (print_fd != 0)
+             close(print_fd);
+
+           return (CUPS_BACKEND_FAILED);
+         }
        }
+       else
+       {
+          fprintf(stderr, "DEBUG: Wrote %d bytes...\n", (int)bytes);
 
-       nbytes -= wbytes;
-       bufptr += wbytes;
+          print_bytes -= bytes;
+         print_ptr   += bytes;
+         total_bytes += bytes;
+       }
       }
-
-      if (argc > 6)
-       fprintf(stderr, "INFO: Sending print file, %u bytes...\n", tbytes);
     }
   }
 
@@ -487,13 +704,14 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
   * Close the serial port and input file and return...
   */
 
-  tcsetattr(fd, TCSADRAIN, &origopts);
+  tcsetattr(device_fd, TCSADRAIN, &origopts);
+
+  close(device_fd);
 
-  close(fd);
-  if (fp != stdin)
-    fclose(fp);
+  if (print_fd != 0)
+    close(print_fd);
 
-  return (0);
+  return (total_bytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
 }
 
 
@@ -501,28 +719,94 @@ main(int  argc,           /* I - Number of command-line arguments (6 or 7) */
  * 'list_devices()' - List all serial devices.
  */
 
-void
+static void
 list_devices(void)
 {
-#if defined(__hpux) || defined(__sgi) || defined(__sun) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#if defined(__hpux) || defined(__sgi) || defined(__sun) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
   static char  *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
-                               /* Funky hex numbering used for some devices */
-#endif /* __hpux || __sgi || __sun || __FreeBSD__ || __OpenBSD__ */
+                                       /* Funky hex numbering used for some *
+                                        * devices                           */
+#endif /* __hpux || __sgi || __sun || __FreeBSD__ || __OpenBSD__ || __FreeBSD_kernel__ */
 
 #ifdef __linux
-  int  i;              /* Looping var */
-  int  fd;             /* File descriptor */
-  char device[255];    /* Device filename */
+  int                  i, j;           /* Looping vars */
+  int                  fd;             /* File descriptor */
+  char                 device[255];    /* Device filename */
+#  ifdef TIOCGSERIAL
+  struct serial_struct serinfo;        /* serial port info */
+#  endif /* TIOCGSERIAL */
 
 
   for (i = 0; i < 100; i ++)
   {
     sprintf(device, "/dev/ttyS%d", i);
+
     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
     {
+#  ifdef TIOCGSERIAL
+     /*
+      * See if this port exists...
+      */
+
+      serinfo.reserved_char[0] = 0;
+
+      if (!ioctl(fd, TIOCGSERIAL, &serinfo))
+      {
+       if (serinfo.type == PORT_UNKNOWN)
+       {
+        /*
+         * Nope...
+         */
+
+         close(fd);
+         continue;
+       }
+      }
+#  endif /* TIOCGSERIAL */
+
       close(fd);
+
+#  if defined(_ARCH_PPC) || defined(powerpc) || defined(__powerpc)
+      printf("serial serial:%s?baud=230400 \"Unknown\" \"Serial Port #%d\"\n",
+             device, i + 1);
+#  else
       printf("serial serial:%s?baud=115200 \"Unknown\" \"Serial Port #%d\"\n",
              device, i + 1);
+#  endif /* _ARCH_PPC || powerpc || __powerpc */
+    }
+  }
+
+  for (i = 0; i < 16; i ++)
+  {
+    sprintf(device, "/dev/usb/ttyUSB%d", i);
+    if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+    {
+      close(fd);
+      printf("serial serial:%s?baud=230400 \"Unknown\" \"USB Serial Port #%d\"\n",
+             device, i + 1);
+    }
+
+    sprintf(device, "/dev/ttyUSB%d", i);
+    if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+    {
+      close(fd);
+      printf("serial serial:%s?baud=230400 \"Unknown\" \"USB Serial Port #%d\"\n",
+             device, i + 1);
+    }
+  }
+
+  for (i = 0; i < 64; i ++)
+  {
+    for (j = 0; j < 8; j ++)
+    {
+      sprintf(device, "/dev/ttyQ%02de%d", i, j);
+      if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+      {
+        close(fd);
+        printf("serial serial:%s?baud=115200 \"Unknown\" "
+              "\"Equinox ESP %d Port #%d\"\n",
+               device, i, j + 1);
+      }
     }
   }
 #elif defined(__sgi)
@@ -636,13 +920,13 @@ list_devices(void)
   {
     sprintf(device, "/dev/cua/%c", 'a' + i);
     if (access(device, 0) == 0)
-#ifdef B115200
+#  ifdef B115200
       printf("serial serial:%s?baud=115200 \"Unknown\" \"Serial Port #%d\"\n",
              device, i + 1);
-#else
+#  else
       printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
              device, i + 1);
-#endif /* B115200 */
+#  endif /* B115200 */
   }
 
  /*
@@ -738,7 +1022,7 @@ list_devices(void)
       printf("serial serial:%s?baud=38400 \"Unknown\" \"Serial Port #%d\"\n",
              device, i + 1);
   }
-#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
   int  i, j;           /* Looping vars */
   int  fd;             /* File descriptor */
   char device[255];    /* Device filename */
@@ -773,6 +1057,14 @@ list_devices(void)
        printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
                device, i, j + 1);
       }
+
+      sprintf(device, "/dev/ttyC%d%c", i, funky_hex[j]);
+      if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
+      {
+       close(fd);
+       printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
+               device, i, j + 1);
+      }
     }
 
  /*
@@ -856,11 +1148,142 @@ list_devices(void)
               device, i, j + 1);
       }
     }
+#elif defined(__APPLE__)
+ /*
+  * Standard serial ports on MacOS X...
+  */
+
+  kern_return_t                        kernResult;
+  mach_port_t                  masterPort;
+  io_iterator_t                        serialPortIterator;
+  CFMutableDictionaryRef       classesToMatch;
+  io_object_t                  serialService;
 
+
+  kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
+  if (KERN_SUCCESS != kernResult)
+    return;
+
+ /*
+  * Serial devices are instances of class IOSerialBSDClient.
+  */
+
+  classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+  if (classesToMatch != NULL)
+  {
+    CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey),
+                         CFSTR(kIOSerialBSDRS232Type));
+
+    kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch,
+                                              &serialPortIterator);
+    if (kernResult == KERN_SUCCESS)
+    {
+      while ((serialService = IOIteratorNext(serialPortIterator)))
+      {
+       CFTypeRef       serialNameAsCFString;
+       CFTypeRef       bsdPathAsCFString;
+       char            serialName[128];
+       char            bsdPath[1024];
+       Boolean         result;
+
+
+       serialNameAsCFString =
+           IORegistryEntryCreateCFProperty(serialService,
+                                           CFSTR(kIOTTYDeviceKey),
+                                           kCFAllocatorDefault, 0);
+       if (serialNameAsCFString)
+       {
+         result = CFStringGetCString(serialNameAsCFString, serialName,
+                                     sizeof(serialName),
+                                     kCFStringEncodingASCII);
+         CFRelease(serialNameAsCFString);
+
+         if (result)
+         {
+           bsdPathAsCFString =
+               IORegistryEntryCreateCFProperty(serialService,
+                                               CFSTR(kIOCalloutDeviceKey),
+                                               kCFAllocatorDefault, 0);
+           if (bsdPathAsCFString)
+           {
+             result = CFStringGetCString(bsdPathAsCFString, bsdPath,
+                                         sizeof(bsdPath),
+                                         kCFStringEncodingASCII);
+             CFRelease(bsdPathAsCFString);
+
+             if (result)
+               printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n",
+                      bsdPath, serialName);
+           }
+         }
+       }
+
+       IOObjectRelease(serialService);
+      }
+
+     /*
+      * Release the iterator.
+      */
+
+      IOObjectRelease(serialPortIterator);
+    }
+  }
 #endif
 }
 
 
 /*
- * End of "$Id: serial.c,v 1.38 2002/01/02 17:58:34 mike Exp $".
+ * 'side_cb()' - Handle side-channel requests...
+ */
+
+static void
+side_cb(int print_fd,                  /* I - Print file */
+        int device_fd,                 /* I - Device file */
+       int use_bc)                     /* I - Using back-channel? */
+{
+  cups_sc_command_t    command;        /* Request command */
+  cups_sc_status_t     status;         /* Request/response status */
+  char                 data[2048];     /* Request/response data */
+  int                  datalen;        /* Request/response data size */
+
+
+  datalen = sizeof(data);
+
+  if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
+  {
+    _cupsLangPuts(stderr,
+                  _("WARNING: Failed to read side-channel request!\n"));
+    return;
+  }
+
+  switch (command)
+  {
+    case CUPS_SC_CMD_DRAIN_OUTPUT :
+        if (backendDrainOutput(print_fd, device_fd))
+         status = CUPS_SC_STATUS_IO_ERROR;
+       else if (tcdrain(device_fd))
+         status = CUPS_SC_STATUS_IO_ERROR;
+       else
+         status = CUPS_SC_STATUS_OK;
+
+       datalen = 0;
+        break;
+
+    case CUPS_SC_CMD_GET_BIDI :
+        data[0] = use_bc;
+        datalen = 1;
+        break;
+
+    default :
+        status  = CUPS_SC_STATUS_NOT_IMPLEMENTED;
+       datalen = 0;
+       break;
+  }
+
+  cupsSideChannelWrite(command, status, data, datalen, 1.0);
+}
+
+
+/*
+ * End of "$Id$".
  */