]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/serial.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / serial.c
index 66a0354cf2b8bb5867e6a3dc33ca6f8b7a25a98f..a931389f846981f2821e3160534cd460d58cf175 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: serial.c 5099 2006-02-13 02:46:10Z mike $"
+ * "$Id: serial.c 6170 2007-01-02 17:26:41Z mike $"
  *
  *   Serial port backend for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *   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
  *
  *   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/backend.h>
-#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>
 #  include <unistd.h>
 #  include <fcntl.h>
 #  include <termios.h>
-#  include <sys/select.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 */
 #  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);
 
 
 /*
@@ -112,21 +117,23 @@ main(int  argc,                           /* I - Number of command-line arguments (6 or 7) */
                value[255],             /* Value of option */
                *ptr;                   /* Pointer into name or value */
   int          port;                   /* Port number (not used) */
-  int          fp;                     /* Print file */
   int          copies;                 /* Number of copies to print */
-  int          fd;                     /* Parallel device */
-  int          rbytes;                 /* Number of bytes read */
-  int          wbytes;                 /* Number of bytes written */
-  size_t       nbytes,                 /* Number of bytes read */
-               tbytes;                 /* Total number of bytes written */
+  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          bufsize;                /* Size of output buffer for writes */
-  char         buffer[8192],           /* Output buffer */
-               *bufptr;                /* Pointer into buffer */
+  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 */
-  fd_set       input,                  /* Input set for select() */
-               output;                 /* Output set for select() */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* Actions for POSIX signals */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
@@ -174,8 +181,8 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
 
   if (argc == 6)
   {
-    fp     = 0;
-    copies = 1;
+    print_fd = 0;
+    copies   = 1;
   }
   else
   {
@@ -183,7 +190,7 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
     * Try to open the print file...
     */
 
-    if ((fp = open(argv[6], O_RDONLY)) < 0)
+    if ((print_fd = open(argv[6], O_RDONLY)) < 0)
     {
       perror("ERROR: unable to open print file");
       return (CUPS_BACKEND_FAILED);
@@ -219,9 +226,12 @@ 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)
       {
@@ -257,22 +267,26 @@ main(int  argc,                           /* I - Number of command-line arguments (6 or 7) */
       }
     }
   }
-  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)
     {
      /*
@@ -297,7 +311,7 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
             *ptr++ = *options++;
        *ptr = '\0';
 
-       if (*options == '+')
+       if (*options == '+' || *options == '&')
          options ++;
       }
       else
@@ -313,7 +327,7 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
         * Set the baud rate...
        */
 
-        bufsize = atoi(value) / 100;
+        print_size = atoi(value) / 100;
 
 #if B19200 == 19200
         cfsetispeed(&opts, atoi(value));
@@ -475,9 +489,10 @@ main(int  argc,                            /* I - Number of command-line arguments (6 or 7) */
        }
       }
     }
+  }
 
-  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
@@ -486,7 +501,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);
@@ -502,133 +517,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);
 
-  wbytes = 0;
+  total_bytes = 0;
 
   while (copies > 0)
   {
     copies --;
 
-    if (fp != 0)
+    if (print_fd != 0)
     {
       fputs("PAGE: 1 1\n", stderr);
-      lseek(fp, 0, SEEK_SET);
+      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...
       */
 
-      int status;
+      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 (!ioctl(fd, TIOCMGET, &status))
-        if (!(status & TIOCM_DSR))
-       {
-        /*
-         * Wait for DSR to go high...
-         */
+      if (select(nfds, &input, &output, NULL, NULL) < 0)
+       continue;                       /* Ignore errors here */
 
-         fputs("DEBUG: DSR is low; waiting for device...\n", stderr);
-
-          do
-         {
-           sleep(1);
-           if (ioctl(fd, TIOCMGET, &status))
-             break;
-         }
-         while (!(status & TIOCM_DSR));
+     /*
+      * Check if we have a side-channel request ready...
+      */
 
-         fputs("DEBUG: DSR is high; writing to device...\n", stderr);
-        }
-    }
+      if (FD_ISSET(CUPS_SC_FD, &input))
+        side_cb(print_fd, device_fd, 1);
 
-    tbytes = 0;
-    while ((nbytes = read(fp, buffer, bufsize)) > 0)
-    {
      /*
-      * Write the print data to the printer...
+      * Check if we have back-channel data ready...
       */
 
-      tbytes += nbytes;
-      bufptr = buffer;
-
-      while (nbytes > 0)
+      if (FD_ISSET(device_fd, &input))
       {
-       /*
-        * See if we are ready to read or write...
-       */
-
-        do
+       if ((bc_bytes = read(device_fd, bc_buffer, sizeof(bc_buffer))) > 0)
        {
-          FD_ZERO(&input);
-         FD_SET(fd, &input);
-         FD_ZERO(&output);
-         FD_SET(fd, &output);
-        }
-       while (select(fd + 1, &input, &output, NULL, NULL) < 0);
+         fprintf(stderr,
+                 "DEBUG: Received " CUPS_LLFMT " bytes of back-channel data!\n",
+                 CUPS_LLCAST bc_bytes);
+          cupsBackChannelWrite(bc_buffer, bc_bytes, 1.0);
+       }
+      }
 
-        if (FD_ISSET(fd, &input))
+     /*
+      * Check if we have print data ready...
+      */
+
+      if (FD_ISSET(print_fd, &input))
+      {
+       if ((print_bytes = read(print_fd, print_buffer, print_size)) < 0)
        {
         /*
-         * Read backchannel data...
+          * Read error - bail if we don't see EAGAIN or EINTR...
          */
 
-         if ((rbytes = read(fd, resource, sizeof(resource))) > 0)
+         if (errno != EAGAIN || errno != EINTR)
          {
-           fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
-                   rbytes);
-            cupsBackChannelWrite(resource, rbytes, 1.0);
-          }
+           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);
+         }
+
+          print_bytes = 0;
+       }
+       else if (print_bytes == 0)
+       {
+        /*
+          * End of file, break out of the loop...
+         */
+
+          break;
        }
 
-        if (FD_ISSET(fd, &output))
+       print_ptr = print_buffer;
+      }
+
+     /*
+      * Check if the device is ready to receive data and we have data to
+      * send...
+      */
+
+      if (print_bytes && FD_ISSET(device_fd, &output))
+      {
+       if (dtrdsr)
        {
         /*
-         * Write print data...
+         * Check the port and sleep until DSR is set...
          */
 
-         if ((wbytes = write(fd, bufptr, nbytes)) < 0)
-           if (errno == ENOTTY)
-             wbytes = write(fd, bufptr, nbytes);
+         int status;
 
-         if (wbytes < 0)
-         {
-          /*
-           * Check for retryable errors...
-           */
 
-           if (errno != EAGAIN && errno != EINTR)
+         if (!ioctl(device_fd, TIOCMGET, &status))
+            if (!(status & TIOCM_DSR))
            {
-             perror("ERROR: Unable to send print file to printer");
-             break;
-           }
-         }
-         else
+            /*
+             * Wait for DSR to go high...
+             */
+
+             fputs("DEBUG: DSR is low; waiting for device...\n", stderr);
+
+              do
+             {
+              /*
+               * Poll every 100ms...
+               */
+
+               usleep(100000);
+
+               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)
+       {
+        /*
+          * Write error - bail if we don't see an error we can retry...
+         */
+
+         if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
          {
-          /*
-           * Update count and pointer...
-           */
+           perror("ERROR: Unable to write print data");
+
+            tcsetattr(device_fd, TCSADRAIN, &origopts);
+
+           close(device_fd);
+
+           if (print_fd != 0)
+             close(print_fd);
 
-           nbytes -= wbytes;
-           bufptr += wbytes;
+           return (CUPS_BACKEND_FAILED);
          }
        }
-      }
-
-      if (wbytes < 0)
-        break;
+       else
+       {
+          fprintf(stderr, "DEBUG: Wrote %d bytes...\n", (int)bytes);
 
-      if (argc > 6)
-       fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
-               (unsigned long)tbytes);
+          print_bytes -= bytes;
+         print_ptr   += bytes;
+         total_bytes += bytes;
+       }
+      }
     }
   }
 
@@ -636,13 +707,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(fd);
-  if (fp != 0)
-    close(fp);
+  close(device_fd);
 
-  return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+  if (print_fd != 0)
+    close(print_fd);
+
+  return (total_bytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
 }
 
 
@@ -650,26 +722,53 @@ 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__)
   static char  *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
-                               /* Funky hex numbering used for some devices */
+                                       /* Funky hex numbering used for some *
+                                        * devices                           */
 #endif /* __hpux || __sgi || __sun || __FreeBSD__ || __OpenBSD__ */
 
-#if defined(__linux) || defined(linux) || defined(__linux__)
-  int  i;              /* Looping var */
-  int  fd;             /* File descriptor */
-  char device[255];    /* Device filename */
+#ifdef __linux
+  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);
@@ -689,6 +788,29 @@ list_devices(void)
       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)
   int          i, j, n;        /* Looping vars */
@@ -801,13 +923,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 */
   }
 
  /*
@@ -903,7 +1025,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__)
   int  i, j;           /* Looping vars */
   int  fd;             /* File descriptor */
   char device[255];    /* Device filename */
@@ -1040,7 +1162,6 @@ list_devices(void)
   CFMutableDictionaryRef       classesToMatch;
   io_object_t                  serialService;
 
-  printf("serial serial \"Unknown\" \"Serial Printer (serial)\"\n");
 
   kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
   if (KERN_SUCCESS != kernResult)
@@ -1094,8 +1215,8 @@ list_devices(void)
              CFRelease(bsdPathAsCFString);
 
              if (result)
-               printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n", bsdPath,
-                      serialName);
+               printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n",
+                      bsdPath, serialName);
            }
          }
        }
@@ -1103,7 +1224,11 @@ list_devices(void)
        IOObjectRelease(serialService);
       }
 
-      IOObjectRelease(serialPortIterator);    /* Release the iterator. */
+     /*
+      * Release the iterator.
+      */
+
+      IOObjectRelease(serialPortIterator);
     }
   }
 #endif
@@ -1111,5 +1236,54 @@ list_devices(void)
 
 
 /*
- * End of "$Id: serial.c 5099 2006-02-13 02:46:10Z mike $".
+ * '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))
+  {
+    fputs("WARNING: Failed to read side-channel request!\n", stderr);
+    return;
+  }
+
+  switch (command)
+  {
+    case CUPS_SC_CMD_DRAIN_OUTPUT :
+        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: serial.c 6170 2007-01-02 17:26:41Z mike $".
  */