]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/serial.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / serial.c
index 0442c29261730254b4270744b7cd30a63c1476c9..a931389f846981f2821e3160534cd460d58cf175 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: serial.c 5553 2006-05-20 12:22:27Z 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>
@@ -97,7 +92,8 @@
  * Local functions...
  */
 
-void   list_devices(void);
+static void    list_devices(void);
+static void    side_cb(int print_fd, int device_fd, int use_bc);
 
 
 /*
@@ -121,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 */
@@ -183,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
   {
@@ -192,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);
@@ -232,7 +230,8 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
 
   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)
       {
@@ -268,7 +267,7 @@ 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);
 
@@ -276,16 +275,18 @@ main(int  argc,                           /* I - Number of command-line arguments (6 or 7) */
   * 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)
     {
      /*
@@ -326,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));
@@ -488,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
@@ -499,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);
@@ -515,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()...
   */
 
-  if (bufsize > sizeof(buffer))
-    bufsize = sizeof(buffer);
+  nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
 
-  wbytes = 0;
+ /*
+  * 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 (print_size > sizeof(print_buffer))
+    print_size = sizeof(print_buffer);
+
+  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...
-         */
-
-         fputs("DEBUG: DSR is low; waiting for device...\n", stderr);
+      if (select(nfds, &input, &output, NULL, NULL) < 0)
+       continue;                       /* Ignore errors here */
 
-          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);
+       }
+      }
+
+     /*
+      * Check if we have print data ready...
+      */
 
-        if (FD_ISSET(fd, &input))
+      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);
 
-           nbytes -= wbytes;
-           bufptr += wbytes;
+           if (print_fd != 0)
+             close(print_fd);
+
+           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;
+       }
+      }
     }
   }
 
@@ -649,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(device_fd);
 
-  close(fd);
-  if (fp != 0)
-    close(fp);
+  if (print_fd != 0)
+    close(print_fd);
 
-  return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
+  return (total_bytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
 }
 
 
@@ -663,7 +722,7 @@ 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__) || defined(__DragonFly__)
@@ -729,6 +788,14 @@ 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 ++)
@@ -1169,5 +1236,54 @@ list_devices(void)
 
 
 /*
- * End of "$Id: serial.c 5553 2006-05-20 12:22:27Z 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 $".
  */