]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/usb-darwin.c
Full sweep of all Clang warnings, plus some bug fixes for incorrect memcpy usage.
[thirdparty/cups.git] / backend / usb-darwin.c
index 5c2ee4f4f6425fcdad1fabf66a87c9a01af3a0d9..302ae98168c6a60e0f0325fd208469fd36347bda 100644 (file)
@@ -1,68 +1,51 @@
 /*
- * "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $"
+ * "$Id$"
  *
- *   USB port on Darwin backend for the Common UNIX Printing System (CUPS).
+ * Copyright 2005-2014 Apple Inc. All rights reserved.
  *
- *   This file is included from "usb.c" when compiled on MacOS X or Darwin.
+ * IMPORTANT:  This Apple software is supplied to you by Apple Computer,
+ * Inc. ("Apple") in consideration of your agreement to the following
+ * terms, and your use, installation, modification or redistribution of
+ * this Apple software constitutes acceptance of these terms.  If you do
+ * not agree with these terms, please do not use, install, modify or
+ * redistribute this Apple software.
  *
- *   Copyright 2004 Apple Computer, Inc. All rights reserved.
+ * In consideration of your agreement to abide by the following terms, and
+ * subject to these terms, Apple grants you a personal, non-exclusive
+ * license, under Apple's copyrights in this original Apple software (the
+ * "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ * Software, with or without modifications, in source and/or binary forms;
+ * provided that if you redistribute the Apple Software in its entirety and
+ * without modifications, you must retain this notice and the following
+ * text and disclaimers in all such redistributions of the Apple Software.
+ * Neither the name, trademarks, service marks or logos of Apple Computer,
+ * Inc. may be used to endorse or promote products derived from the Apple
+ * Software without specific prior written permission from Apple.  Except
+ * as expressly stated in this notice, no other rights or licenses, express
+ * or implied, are granted by Apple herein, including but not limited to
+ * any patent rights that may be infringed by your derivative works or by
+ * other works in which the Apple Software may be incorporated.
  *
- *   IMPORTANT:         This Apple software is supplied to you by Apple Computer,
- *   Inc. ("Apple") in consideration of your agreement to the following
- *   terms, and your use, installation, modification or redistribution of
- *   this Apple software constitutes acceptance of these terms.         If you do
- *   not agree with these terms, please do not use, install, modify or
- *   redistribute this Apple software.
+ * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
+ * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
  *
- *   In consideration of your agreement to abide by the following terms, and
- *   subject to these terms, Apple grants you a personal, non-exclusive
- *   license, under Apple/s copyrights in this original Apple software (the
- *   "Apple Software"), to use, reproduce, modify and redistribute the Apple
- *   Software, with or without modifications, in source and/or binary forms;
- *   provided that if you redistribute the Apple Software in its entirety and
- *   without modifications, you must retain this notice and the following
- *   text and disclaimers in all such redistributions of the Apple Software. 
- *   Neither the name, trademarks, service marks or logos of Apple Computer,
- *   Inc. may be used to endorse or promote products derived from the Apple
- *   Software without specific prior written permission from Apple.  Except
- *   as expressly stated in this notice, no other rights or licenses, express
- *   or implied, are granted by Apple herein, including but not limited to
- *   any patent rights that may be infringed by your derivative works or by
- *   other works in which the Apple Software may be incorporated.
- *
- *   The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
- *   MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
- *   THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
- *   FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
- *   OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
- *
- *   IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
- *   OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *   INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
- *   MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
- *   AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
- *   STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
- *   POSSIBILITY OF SUCH DAMAGE.
- *
- * Contents:
- *
- *   print_device() - Send a file to the specified USB port.
- *   list_devices() - List all USB devices.
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  */
 
 /*
- * Include necessary headers...
+ * Include necessary headers.
  */
 
-#include <CoreFoundation/CoreFoundation.h>
-#include <ApplicationServices/ApplicationServices.h>
-
-#include <IOKit/usb/IOUSBLib.h>
-#include <IOKit/IOCFPlugIn.h>
-#include <mach/mach.h> 
-#include <mach/mach_error.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <termios.h>
 #include <unistd.h>
-#include <pthread.h>           /* Used for writegReadMutex */
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <libgen.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/mach_time.h>
+#include <cups/debug-private.h>
+#include <cups/file-private.h>
+#include <cups/sidechannel.h>
+#include <cups/language-private.h>
+#include "backend-private.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <libproc.h>
 
-#ifndef kPMPrinterURI
-#  define kPMPrinterURI CFSTR("Printer URI")
-#endif
+#include <spawn.h>
+#include <pthread.h>
 
-/*
- * Panther/10.3 kIOUSBInterfaceInterfaceID190
- * Jaguar/10.2 kIOUSBInterfaceInterfaceID182
- */
+extern char **environ;
 
-#define USB_INTERFACE_KIND  CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
-#define kUSBLanguageEnglish 0x409
 
 /*
- * Section 5.3 USB Printing Class spec
+ * DEBUG_WRITES, if defined, causes the backend to write data to the printer in
+ * 512 byte increments, up to 8192 bytes, to make debugging with a USB bus
+ * analyzer easier.
  */
 
-#define kUSBPrintingSubclass           1
-#define kUSBPrintingProtocolNoOpen     0
-#define kUSBPrintingProtocolUnidirectional  1
-#define kUSBPrintingProtocolBidirectional   2
-
-#define kUSBPrintClassGetDeviceID      0
-#define kUSBPrintClassGetCentronicsStatus   1
-#define kUSBPrintClassSoftReset                2
+#define DEBUG_WRITES 0
 
 /*
- *  Apple MacOS X printer-class plugins
+ * WAIT_EOF_DELAY is number of seconds we'll wait for responses from
+ * the printer after we've finished sending all the data
  */
+#define WAIT_EOF_DELAY                 7
+#define WAIT_SIDE_DELAY                        3
+#define DEFAULT_TIMEOUT                        5000L
 
-#define kUSBPrinterClassTypeID         (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
+#define        USB_INTERFACE_KIND              CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
+#define kUSBLanguageEnglish            0x409
 
-#define kUSBPrinterClassInterfaceID    (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
+#define PRINTER_POLLING_INTERVAL       5                       /* seconds */
+#define INITIAL_LOG_INTERVAL           PRINTER_POLLING_INTERVAL
+#define SUBSEQUENT_LOG_INTERVAL                3 * INITIAL_LOG_INTERVAL
 
-#define kUSBGenericPrinterClassDriver      CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
-#define kUSBGenericTOPrinterClassDriver            CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
+#define kUSBPrinterClassTypeID         CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92)
+#define        kUSBPrinterClassInterfaceID     CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92)
 
 #define kUSBClassDriverProperty                CFSTR("USB Printing Class")
-#define kUSBPrinterClassDeviceNotOpen      -9664   /*kPMInvalidIOMContext*/
 
-typedef union
+#define kUSBGenericTOPrinterClassDriver        CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
+#define kUSBPrinterClassDeviceNotOpen  -9664   /*kPMInvalidIOMContext*/
+
+#define CRSetCrashLogMessage(m) _crc_make_setter(message, m)
+#define _crc_make_setter(attr, arg) (gCRAnnotations.attr = (uint64_t)(unsigned long)(arg))
+#define CRASH_REPORTER_CLIENT_HIDDEN __attribute__((visibility("hidden")))
+#define CRASHREPORTER_ANNOTATIONS_VERSION 4
+#define CRASHREPORTER_ANNOTATIONS_SECTION "__crash_info"
+
+struct crashreporter_annotations_t {
+       uint64_t version;               // unsigned long
+       uint64_t message;               // char *
+       uint64_t signature_string;      // char *
+       uint64_t backtrace;             // char *
+       uint64_t message2;              // char *
+       uint64_t thread;                // uint64_t
+       uint64_t dialog_mode;           // unsigned int
+};
+
+CRASH_REPORTER_CLIENT_HIDDEN
+struct crashreporter_annotations_t gCRAnnotations
+       __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION)))
+       = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Section 5.3 USB Printing Class spec
+ */
+#define kUSBPrintingSubclass                   1
+#define kUSBPrintingProtocolNoOpen             0
+#define kUSBPrintingProtocolUnidirectional     1
+#define kUSBPrintingProtocolBidirectional      2
+
+typedef IOUSBInterfaceInterface190     **printer_interface_t;
+
+typedef struct iodevice_request_s      /**** Device request ****/
+{
+  UInt8                requestType;
+  UInt8                request;
+  UInt16       value;
+  UInt16       index;
+  UInt16       length;
+  void         *buffer;
+} iodevice_request_t;
+
+typedef union                          /**** Centronics status byte ****/
 {
-  char     b;
+  char         b;
   struct
   {
-    unsigned   reserved0 : 2;
-    unsigned   paperError : 1;
-    unsigned   select : 1;
-    unsigned   notError : 1;
-    unsigned   reserved1 : 3;
+    unsigned   reserved0:2;
+    unsigned   paperError:1;
+    unsigned   select:1;
+    unsigned   notError:1;
+    unsigned   reserved1:3;
   } status;
-} CentronicsStatusByte;
-
-typedef struct
-{
-  CFStringRef  manufacturer;   /* manufacturer name */
-  CFStringRef  product;        /* product name */
-  CFStringRef  compatible;     /* compatible product name */
-  CFStringRef  serial;         /* serial number */
-  CFStringRef  command;        /* command set */
-  CFStringRef  ppdURL;         /* url of the selected PPD, if any */
-} USBPrinterAddress;
+} centronics_status_t;
 
-typedef IOUSBInterfaceInterface190 **USBPrinterInterface;
-
-typedef struct 
-{
-  UInt8            requestType;
-  UInt8            request;
-  UInt16    value;
-  UInt16    index;
-  UInt16    length;
-  void     *buffer;    
-} USBIODeviceRequest;
-
-typedef struct classDriverContext
+typedef struct classdriver_s           /**** g.classdriver context ****/
 {
   IUNKNOWN_C_GUTS;
-  CFPlugInRef      plugin;     /* release plugin */
-  IUnknownVTbl     **factory;  
-  void         *vendorReference;/* vendor class specific usage */
-  UInt32       location;   /* unique location in bus topology */
-  UInt8                interfaceNumber;
-  UInt16       vendorID;
-  UInt16       productID;
-  USBPrinterInterface  interface;  /* identify the device to IOKit */
-  UInt8                outpipe;    /* mandatory bulkOut pipe */
-  UInt8                inpipe;     /* optional bulkIn pipe */
-  /*
-  **   general class requests
-  */
-  kern_return_t            (*DeviceRequest)( struct classDriverContext **printer, USBIODeviceRequest *iorequest, UInt16 timeout );
-  kern_return_t (*GetString)( struct classDriverContext **printer, UInt8 whichString, UInt16 language, UInt16 timeout, CFStringRef *result );
-  /*
-  **   standard printer class requests
-  */
-  kern_return_t (*SoftReset)( struct classDriverContext **printer, UInt16 timeout );
-  kern_return_t (*GetCentronicsStatus)( struct classDriverContext **printer, CentronicsStatusByte *result, UInt16 timeout );
-  kern_return_t (*GetDeviceID)( struct classDriverContext **printer, CFStringRef *devid, UInt16 timeout );
-  /*
-  **   standard bulk device requests
-  */
-  kern_return_t            (*ReadPipe)( struct classDriverContext **printer, UInt8 *buffer, UInt32 *count );
-  kern_return_t            (*WritePipe)( struct classDriverContext **printer, UInt8 *buffer, UInt32 *count, Boolean eoj );
-  /*
-  **   interface requests
-  */
-  kern_return_t            (*Open)( struct classDriverContext **printer, UInt32 location, UInt8 protocol );
-  kern_return_t            (*Abort)( struct classDriverContext **printer );
-  kern_return_t            (*Close)( struct classDriverContext **printer );
-  /*
-  **   initialize and terminate
-  */
-  kern_return_t            (*Initialize)( struct classDriverContext **printer, struct classDriverContext **baseclass );
-  kern_return_t            (*Terminate)( struct classDriverContext **printer );
-} USBPrinterClassContext;
-
+  CFPlugInRef          plugin;                 /* release plugin */
+  IUnknownVTbl         **factory;              /* Factory */
+  void                 *vendorReference;       /* vendor class specific usage */
+  UInt32               location;               /* unique location in bus topology */
+  UInt8                        interfaceNumber;        /* Interface number */
+  UInt16               vendorID;               /* Vendor id */
+  UInt16               productID;              /* Product id */
+  printer_interface_t  interface;              /* identify the device to IOKit */
+  UInt8                        outpipe;                /* mandatory bulkOut pipe */
+  UInt8                        inpipe;                 /* optional bulkIn pipe */
+
+  /* general class requests */
+  kern_return_t (*DeviceRequest)(struct classdriver_s **printer, iodevice_request_t *iorequest, UInt16 timeout);
+  kern_return_t        (*GetString)(struct classdriver_s **printer, UInt8 whichString, UInt16 language, UInt16 timeout, CFStringRef *result);
+
+  /* standard printer class requests */
+  kern_return_t        (*SoftReset)(struct classdriver_s **printer, UInt16 timeout);
+  kern_return_t        (*GetCentronicsStatus)(struct classdriver_s **printer, centronics_status_t *result, UInt16 timeout);
+  kern_return_t        (*GetDeviceID)(struct classdriver_s **printer, CFStringRef *devid, UInt16 timeout);
+
+  /* standard bulk device requests */
+  kern_return_t (*ReadPipe)(struct classdriver_s **printer, UInt8 *buffer, UInt32 *count);
+  kern_return_t (*WritePipe)(struct classdriver_s **printer, UInt8 *buffer, UInt32 *count, Boolean eoj);
+
+  /* interface requests */
+  kern_return_t (*Open)(struct classdriver_s **printer, UInt32 location, UInt8 protocol);
+  kern_return_t (*Abort)(struct classdriver_s **printer);
+  kern_return_t (*Close)(struct classdriver_s **printer);
+
+  /* initialize and terminate */
+  kern_return_t (*Initialize)(struct classdriver_s **printer, struct classdriver_s **baseclass);
+  kern_return_t (*Terminate)(struct classdriver_s **printer);
+
+} classdriver_t;
+
+typedef Boolean (*iterator_callback_t)(void *refcon, io_service_t obj);
+
+typedef struct iterator_reference_s    /**** Iterator reference data */
+{
+  iterator_callback_t callback;
+  void         *userdata;
+  Boolean      keepRunning;
+} iterator_reference_t;
 
-typedef struct usbPrinterClassType
+typedef struct globals_s
 {
-    USBPrinterClassContext  *classdriver;
-    CFUUIDRef              factoryID;
-    UInt32                 refCount;
-} USBPrinterClassType;
+  io_service_t         printer_obj;
+  classdriver_t                **classdriver;
 
+  pthread_mutex_t      read_thread_mutex;
+  pthread_cond_t       read_thread_cond;
+  int                  read_thread_stop;
+  int                  read_thread_done;
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-       Constants
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+  pthread_mutex_t      readwrite_lock_mutex;
+  pthread_cond_t       readwrite_lock_cond;
+  int                  readwrite_lock;
 
-/*
-    Debugging output to Console
-    DEBUG undefined or
-    DEBUG=0    production code: suppress console output
-
-    DEBUG=1    report errors (non-zero results)
-    DEBUG=2    report all results, generate dumps
-*/
-#if DEBUG==2
-#define DEBUG_ERR(c, x)                            showint(x, c)
-#define DEBUG_DUMP( text, buf, len )       dump( text, buf, len )
-#define DEBUG_CFString( text, a )          showcfstring( text, a )
-#define DEBUG_CFCompareString( text, a, b ) cmpcfs( text, a, b )
-#elif DEBUG==1
-#define DEBUG_ERR(c, x)                            if (c) fprintf(stderr, x, c)
-#define DEBUG_DUMP( text, buf, len )
-#define DEBUG_CFString( text, a )
-#define DEBUG_CFCompareString( text, a, b )
-#else
-#define DEBUG_ERR(c, x)
-#define DEBUG_DUMP( text, buf, len )
-#define DEBUG_CFString( text, a )
-#define DEBUG_CFCompareString( text, a, b )
-#endif
+  CFStringRef          make;
+  CFStringRef          model;
+  CFStringRef          serial;
+  UInt32               location;
+  UInt8                        interfaceNum;
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-       Type Definitions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+  CFRunLoopTimerRef    status_timer;
 
-typedef struct
-{
-    /*
-     * Tagged/Tranparent Binary Communications Protocol
-     * TBCP read
-     */
-    Boolean                tbcpQuoteReads;         /* enable tbcp on reads */
-    Boolean                escapeNextRead;         /* last char of last read buffer was escape */
-    UInt8                  *tbcpReadData;          /* read buffer */
-    UInt32                 readLength;             /* read buffer length (all used) */
-    int                            match_endoffset,        /* partial match of end TBCP sequence */
-                           match_startoffset;      /* partial match of start TBCP sequence */
-    /*
-     * TBCP write
-     */
-    UInt8                  *tbcpWriteData;         /* write buffer */
-    UInt32                 tbcpBufferLength,       /* write buffer allocated length */
-                           tbcpBufferRemaining;    /* write buffer not used */
+  int                  print_fd;       /* File descriptor to print */
+  ssize_t              print_bytes;    /* Print bytes read */
+#if DEBUG_WRITES
+  ssize_t              debug_bytes;    /* Current bytes to read */
+#endif /* DEBUG_WRITES */
 
-    Boolean                sendStatusNextWrite;
+  Boolean              wait_eof;
+  int                  drain_output;   /* Drain all pending output */
+  int                  bidi_flag;      /* 0=unidirectional, 1=bidirectional */
 
-} PostScriptData;
+  pthread_mutex_t      sidechannel_thread_mutex;
+  pthread_cond_t       sidechannel_thread_cond;
+  int                  sidechannel_thread_stop;
+  int                  sidechannel_thread_done;
+} globals_t;
 
-typedef struct 
-{
-    CFPlugInRef                    plugin;         /* valid until plugin is release  */
-    USBPrinterClassContext  **classdriver;  /* usb printer class in user space */
-    CFStringRef                    bundle;         /* class driver URI */
-    UInt32                 location;       /* unique location in USB topology */
-    USBPrinterAddress      address;        /* topology independent bus address */
-    CFURLRef               reference;      /* internal use */
-} USBPrinterInfo;
-
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-       Functions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
 /*
-**  IOKit to CF functions
-*/
-USBPrinterInfo     *UsbCopyPrinter( USBPrinterInfo *aPrinter );
-CFMutableArrayRef   UsbGetAllPrinters( void );
-void               UsbReleasePrinter( USBPrinterInfo *aPrinter );
-void               UsbReleaseAllPrinters( CFMutableArrayRef printers );
-kern_return_t      UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result );
-kern_return_t      UsbUnloadClassDriver( USBPrinterInfo *printer );
-kern_return_t      UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle );
-CFStringRef        UsbMakeFullUriAddress( USBPrinterInfo *aPrinter );
-
-int        UsbSamePrinter( const USBPrinterAddress *lastTime, const USBPrinterAddress *thisTime ); 
-
-OSStatus    UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout );
-
-
-/*******************************************************************************
-    Contains:  Support IEEE-1284 DeviceID as a CFString.
-
-    Copyright 2000-2005 by Apple Computer, Inc., all rights reserved.
-
-    Description:
-       IEEE-1284 Device ID is referenced in USB and PPDT (1394.3). It allows
-       a computer peripheral to convey information about its required software
-       to the host system.
-       
-       DeviceID is defined as a stream of ASCII bytes, commencing with one 16-bit
-       binary integer in Little-Endian format which describes how many bytes
-       of data are required by the entire DeviceID.
-       
-       The stream of bytes is further characterized as a series of
-       key-value list pairs. In other words each key can be followed by one
-       or more values. Multiple key-value list pairs fill out the DeviceID stream.
-       
-       Some keys are required: COMMAND SET (or CMD), MANUFACTURER (or MFG),
-       and MODEL (or MDL).
-       
-       One needs to read the first two bytes of DeviceID to allocate storage
-       for the complete DeviceID string. Then a second read operation can
-       retrieve the entire string.
-       
-       Often DeviceID is not very large. By allocating a reasonable buffer one
-       can fetch most device's DeviceID string on the first read.
-    
-    A more formal definition of DeviceID.
-
-       <DeviceID> = <Length><Key_ValueList_Pair>+
-
-       <Length> = <low byte of 16 bit integer><high byte of 16 bit integer>
-       <Key_ValueList_Pair> = <Key>:<Value>[,<Value>]*;
-
-       <Key> = <ASCII Byte>+
-       <Value> = <ASCII Byte>+
-       
-       Some keys are defined in the standard. The standard specifies that
-       keys are case sensitive. White space is allowed in the key.
-       
-       The standard does not say that values are case-sensitive.
-       Lexmark is known to ship printers with mixed-case value:
-           i.e., 'CLASS:Printer'
-
-       Required Keys:
-           'COMMAND SET' or CMD
-           MANUFACTURER or MFG
-           MODEL or MDL
-       
-       Optional Keys:
-           CLASS
-               Value PRINTER is referenced in the standard.
-               
-       Observed Keys:
-           SN,SERN
-               Used by Hewlett-Packard for the serial number.
-       
-       
-*******************************************************************************/
-
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Pragmas
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Constants
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-#define kDeviceIDKeyCommand            CFSTR("COMMAND SET:")
-#define kDeviceIDKeyCommandAbbrev      CFSTR( "CMD:" )
-
-#define kDeviceIDKeyManufacturer       CFSTR("MANUFACTURER:")
-#define kDeviceIDKeyManufacturerAbbrev CFSTR( "MFG:" )
-
-#define kDeviceIDKeyModel              CFSTR("MODEL:")
-#define kDeviceIDKeyModelAbbrev                CFSTR( "MDL:" )
-
-#define kDeviceIDKeySerial             CFSTR("SN:")
-#define kDeviceIDKeySerialAbbrev       CFSTR("SERN:")
-
-#define kDeviceIDKeyCompatible         CFSTR("COMPATIBLITY ID:")
-#define kDeviceIDKeyCompatibleAbbrev   CFSTR("CID:")
-
-/* delimiters */
-#define kDeviceIDKeyValuePairDelimiter CFSTR(";")
-
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Type definitions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Function prototypes
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-static CFStringRef CreateEncodedCFString(CFStringRef string);
-static CFRange DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options );
-static void parseOptions(const char *options, char *serial);
-
-CFStringRef
-DeviceIDCreateValueList(    const CFStringRef deviceID,
-                           const CFStringRef abbrevKey,
-                           const CFStringRef key );
-
-static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax);
-static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax);
-
-/* Required to suppress redefinition warnings for these two symbols 
-*/
-#if defined(TCP_NODELAY)
-#undef TCP_NODELAY
-#endif
-#if defined(TCP_MAXSEG)
-#undef TCP_MAXSEG
-#endif
-
-#include <cups/cups.h>
-
-
-#define PRINTER_POLLING_INTERVAL       5   /* seconds */
-#define INITIAL_LOG_INTERVAL           (PRINTER_POLLING_INTERVAL)
-#define SUBSEQUENT_LOG_INTERVAL                (3*INITIAL_LOG_INTERVAL)
+ * Globals...
+ */
 
-/* WAITEOF_DELAY is number of seconds we'll wait for responses from the printer */
-/*  after we've finished sending all the data */
-#define WAITEOF_DELAY          7
+globals_t g = { 0 };                   /* Globals */
+int Iterating = 0;                     /* Are we iterating the bus? */
 
-#define USB_MAX_STR_SIZE       1024
 
+/*
+ * Local functions...
+ */
 
-static volatile int done = 0;
-static int gWaitEOF = false;
-static pthread_cond_t *gReadCompleteConditionPtr = NULL;
-static pthread_mutex_t *gReadMutexPtr = NULL;
+static Boolean find_device_cb(void *refcon, io_service_t obj);
+static Boolean list_device_cb(void *refcon, io_service_t obj);
+static CFStringRef cfstr_create_trim(const char *cstr);
+static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys);
+static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t interface, classdriver_t ***printerDriver);
+static kern_return_t load_printerdriver(CFStringRef *driverBundlePath);
+static kern_return_t registry_close(void);
+static kern_return_t registry_open(CFStringRef *driverBundlePath);
+static kern_return_t unload_classdriver(classdriver_t ***classdriver);
+static OSStatus copy_deviceid(classdriver_t **printer, CFStringRef *deviceID);
+static void *read_thread(void *reference);
+static void *sidechannel_thread(void *reference);
+static void copy_deviceinfo(CFStringRef deviceIDString, CFStringRef *make, CFStringRef *model, CFStringRef *serial);
+static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation, UInt8 *interfaceNum);
+static void device_added(void *userdata, io_iterator_t iterator);
+static void get_device_id(cups_sc_status_t *status, char *data, int *datalen);
+static void iterate_printers(iterator_callback_t callBack, void *userdata);
+static void parse_options(char *options, char *serial, int serial_size, UInt32 *location, Boolean *wait_eof);
+static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial);
+static void setup_cfLanguage(void);
+static void soft_reset(void);
+static void status_timer_cb(CFRunLoopTimerRef timer, void *info);
+
+#if defined(__i386__) || defined(__x86_64__)
+static pid_t   child_pid;              /* Child PID */
+static void run_legacy_backend(int argc, char *argv[], int fd) __attribute__((noreturn));      /* Starts child backend process running as a ppc executable */
+#endif /* __i386__ || __x86_64__ */
+static void sigterm_handler(int sig);  /* SIGTERM handler */
+static void sigquit_handler(int sig, siginfo_t *si, void *unused) __attribute__((noreturn));
 
+#ifdef PARSE_PS_ERRORS
+static const char *next_line (const char *buffer);
+static void parse_pserror (char *sockBuffer, int len);
+#endif /* PARSE_PS_ERRORS */
 
+#pragma mark -
 
-#if DEBUG==2  
+/*
+ * 'list_devices()' - List all USB devices.
+ */
 
-static char
-hexdigit( char c )
+void list_devices()
 {
-    return ( c < 0 || c > 15 )? '?': (c < 10)? c + '0': c - 10 + 'A';
+  iterate_printers(list_device_cb, NULL);
 }
 
-static char
-asciidigit( char c )
-{
-    return (c< 20 || c > 0x7E)? '.': c;
-}
 
-void
-dump( char *text, void *s, int len )
+/*
+ * 'print_device()' - Print a file to a USB device.
+ */
+
+int                                    /* O - Exit status */
+print_device(const char *uri,          /* I - Device URI */
+             const char *hostname,     /* I - Hostname/manufacturer */
+             const char *resource,     /* I - Resource/modelname */
+            char       *options,       /* I - Device options/serial number */
+            int        print_fd,       /* I - File descriptor to print */
+            int        copies,         /* I - Copies to print */
+            int        argc,           /* I - Number of command-line arguments (6 or 7) */
+            char       *argv[])        /* I - Command-line arguments */
 {
-    int i;
-    char *p = (char *) s;
-    char m[1+2*16+1+16+1];
+  char           serial[1024];         /* Serial number buffer */
+  OSStatus       status;               /* Function results */
+  IOReturn       iostatus;             /* Current IO status */
+  pthread_t      read_thread_id,       /* Read thread */
+                 sidechannel_thread_id;/* Side-channel thread */
+  int            have_sidechannel = 0; /* Was the side-channel thread started? */
+  struct stat     sidechannel_info;    /* Side-channel file descriptor info */
+  char           print_buffer[8192],   /* Print data buffer */
+                 *print_ptr;           /* Pointer into print data buffer */
+  UInt32         location;             /* Unique location in bus topology */
+  fd_set         input_set;            /* Input set for select() */
+  CFStringRef    driverBundlePath;     /* Class driver path */
+  int            countdown,            /* Logging interval */
+                 nfds;                 /* Number of file descriptors */
+  ssize_t        total_bytes;          /* Total bytes written */
+  UInt32         bytes;                /* Bytes written */
+  struct timeval  *timeout,            /* Timeout pointer */
+                 tv;                   /* Time value */
+  struct timespec cond_timeout;                /* pthread condition timeout */
+  struct sigaction action;             /* Actions for POSIX signals */
+
+
+  (void)uri;
+
+ /*
+  * Catch SIGQUIT to determine who is sending it...
+  */
 
-    fprintf( stderr, "%s pointer %x len %d\n", text, (unsigned int) p, len );
+  memset(&action, 0, sizeof(action));
+  action.sa_sigaction = sigquit_handler;
+  action.sa_flags = SA_SIGINFO;
+  sigaction(SIGQUIT, &action, NULL);
 
-    for ( ; len > 0; len -= 16 )
-    {
-       char *q = p;
-       char *out = m;
-       *out++ = '\t';
-       for ( i = 0; i < 16 && i < len; ++i, ++p )
-       {
-           *out++ = hexdigit( (*p >> 4) & 0x0F );
-                                *out++ = hexdigit( *p & 0x0F );
-       }
-       for ( ;i < 16; ++i )
-       {
-           *out++ = ' ';
-           *out++ = ' ';
-       }
-       *out++ = '\t';
-       for ( i = 0; i < 16 && i < len; ++i, ++q )
-           *out++ = asciidigit( *q );
-       *out = 0;
-       m[ strlen( m ) ] = '\0';
-       fprintf( stderr,  "%s\n", m );
-    }
-}
+ /*
+  * See if the side-channel descriptor is valid...
+  */
 
-void 
-printcfs( char *text, CFStringRef s )
-{
-    char dest[1024];
-    if ( s != NULL )
-    {
-       if ( CFStringGetCString(s, dest, sizeof(dest), kCFStringEncodingUTF8) )
-           sprintf( dest,  "%s <%s>\n", text, dest );
-       else
-           sprintf( dest,  "%s [Unknown string]\n", text );
-    } else {
-       sprintf( dest,  "%s [NULL]\n", text );
-    }
-    perror( dest );
-}
+  have_sidechannel = !fstat(CUPS_SC_FD, &sidechannel_info) &&
+                     S_ISSOCK(sidechannel_info.st_mode);
 
-void
-cmpcfs( char *text, CFStringRef a, CFStringRef b )
-{
-    CFRange found = {0, 0};
-    
-    printcfs( text, a );
-    printcfs( " ", b );
-
-    if (a != NULL && b != NULL) {
-       found = CFStringFind( a, b, kCFCompareCaseInsensitive );
-    
-    } else if (a == NULL && b == NULL) {
-       found.length = 1;   /* Match */
-       found.location = 0;
-    } else {
-       found.length = 0;   /* No match. */
-    }
-    
-    if ( found.length > 0 )
-       fprintf( stderr,  "matched @%d:%d\n", (int) found.location, (int) found.length);
-    else
-       fprintf( stderr,  "not matched\n" );
-}
-#endif /*DEBUG==2 */
+ /*
+  * Localize using CoreFoundation...
+  */
 
-#ifdef PARSE_PS_ERRORS
-static const char *nextLine (const char *buffer);
-static void parsePSError (char *sockBuffer, int len);
+  setup_cfLanguage();
 
+  parse_options(options, serial, sizeof(serial), &location, &g.wait_eof);
 
-static const char *nextLine (const char *buffer)
-{
-    const char *cptr, *lptr = NULL;
-    for (cptr = buffer; *cptr && lptr == NULL; cptr++)
-       if (*cptr == '\n' || *cptr == '\r')
-           lptr = cptr;
-    return lptr;
-}
+  if (resource[0] == '/')
+    resource++;
 
-static void parsePSError (char *sockBuffer, int len)
-{
-    static char         gErrorBuffer[1024] = "";
-    static char *gErrorBufferPtr = gErrorBuffer;
-    static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
+  g.print_fd   = print_fd;
+  g.make       = cfstr_create_trim(hostname);
+  g.model      = cfstr_create_trim(resource);
+  g.serial     = cfstr_create_trim(serial);
+  g.location   = location;
+
+  if (!g.make || !g.model)
+  {
+    fprintf(stderr, "DEBUG: Fatal USB error.\n");
+    _cupsLangPrintFilter(stderr, "ERROR",
+                         _("There was an unrecoverable USB error."));
 
-    char *pCommentBegin, *pCommentEnd, *pLineEnd;
-    char *logLevel;
-    char logstr[1024];
-    int         logstrlen;
+    if (!g.make)
+      fputs("DEBUG: USB make string is NULL\n", stderr);
+    if (!g.model)
+      fputs("DEBUG: USB model string is NULL\n", stderr);
 
-    if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
-       gErrorBufferPtr = gErrorBuffer;
-    if (len > sizeof(gErrorBuffer) - 1)
-       len = sizeof(gErrorBuffer) - 1;
+    return (CUPS_BACKEND_STOP);
+  }
 
-    memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
-    gErrorBufferPtr += len;
-    *(gErrorBufferPtr + 1) = '\0';
+  fputs("STATE: +connecting-to-device\n", stderr);
 
+  countdown = INITIAL_LOG_INTERVAL;
 
-    pLineEnd = (char *)nextLine((const char *)gErrorBuffer);
-    while (pLineEnd != NULL)
+  do
+  {
+    if (g.printer_obj)
     {
-       *pLineEnd++ = '\0';
+      IOObjectRelease(g.printer_obj);
+      unload_classdriver(&g.classdriver);
+      g.printer_obj = 0x0;
+      g.classdriver = 0x0;
+    }
 
-       pCommentBegin = strstr(gErrorBuffer,"%%[");
-       pCommentEnd = strstr(gErrorBuffer, "]%%");
-       if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL)
-       {
-           pCommentEnd += 3;            /* Skip past "]%%" */
-           *pCommentEnd = '\0';         /* There's always room for the nul */
-
-           if (strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
-               logLevel = "DEBUG";
-           else if (strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
-               logLevel = "DEBUG";
-           else
-               logLevel = "INFO";
-           
-           if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr))
-           {
-               /* If the string was trucnated make sure it has a linefeed before the nul */
-               logstrlen = sizeof(logstr) - 1;
-               logstr[logstrlen - 1] = '\n';
-           }
-           write(STDERR_FILENO, logstr, logstrlen);
-       }
+    fprintf(stderr, "DEBUG: Looking for '%s %s'\n", hostname, resource);
 
-       /* move everything over... */
-       strcpy(gErrorBuffer, pLineEnd);
-       gErrorBufferPtr = gErrorBuffer;
-       pLineEnd = (char *)nextLine((const char *)gErrorBuffer);
-    }
-}
-#endif /* PARSE_PS_ERRORS */
+    iterate_printers(find_device_cb, NULL);
 
-void *
-readthread( void *reference )
-{
+    fputs("DEBUG: Opening connection\n", stderr);
+
+    driverBundlePath = NULL;
+
+    status = registry_open(&driverBundlePath);
+
+#if defined(__i386__) || defined(__x86_64__)
     /*
-    ** post a read to the device and write results to stdout
-    ** the final pending read will be Aborted in the main thread
-    */
-    UInt8                  readbuffer[512];
-    UInt32                 rbytes;
-    kern_return_t          readstatus;
-    USBPrinterClassContext  **classdriver = (USBPrinterClassContext **) reference;
+     * If we were unable to load the class drivers for this printer it's
+     * probably because they're ppc or i386. In this case try to run this
+     * backend as i386 or ppc executables so we can use them...
+     */
+    if (status == -2)
+    {
+      run_legacy_backend(argc, argv, print_fd);
+      /* Never returns here */
+    }
+#endif /* __i386__ || __x86_64__ */
 
-    
-    do
+    if (status ==  -2)
     {
-       rbytes = sizeof(readbuffer) - 1;
-       readstatus = (*classdriver)->ReadPipe( classdriver, readbuffer, &rbytes );
-       if ( kIOReturnSuccess == readstatus && rbytes > 0 )
-       {
-           write( STDOUT_FILENO, readbuffer, rbytes );
-           /* cntrl-d is echoed by the printer.
-           * NOTES: 
-           *   Xerox Phaser 6250D doesn't echo the cntrl-d.
-           *   Xerox Phaser 6250D doesn't always send the product query.
-           */
-           if (gWaitEOF && readbuffer[rbytes-1] == 0x4)
-               break;
-#ifdef PARSE_PS_ERRORS
-           parsePSError(readbuffer, rbytes);
-#endif
-       }
-    } while ( gWaitEOF || !done );  /* Abort from main thread tests error here */
+     /*
+      * If we still were unable to load the class drivers for this printer log
+      * the error and stop the queue...
+      */
 
-    /* Let the other thread (main thread) know that we have
-    * completed the read thread...
-    */
-    pthread_mutex_lock(gReadMutexPtr);
-    pthread_cond_signal(gReadCompleteConditionPtr);
-    pthread_mutex_unlock(gReadMutexPtr);
+      if (driverBundlePath == NULL || !CFStringGetCString(driverBundlePath, print_buffer, sizeof(print_buffer), kCFStringEncodingUTF8))
+        strlcpy(print_buffer, "USB class driver", sizeof(print_buffer));
 
-    return NULL;
-}
+      fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr);
+      _cupsLangPrintFilter(stderr, "ERROR",
+                          _("There was an unrecoverable USB error."));
+      fprintf(stderr, "DEBUG: Could not load %s\n", print_buffer);
 
-/*
-* 'print_device()' - Send a file to the specified USB port.
-*/
+      if (driverBundlePath)
+       CFRelease(driverBundlePath);
 
-int print_device(const char *uri, const char *hostname, const char *resource, const char *options, int fd, int copies)
-{
-    UInt32     wbytes,         /* Number of bytes written */
-               buffersize = 2048;
-    size_t     nbytes;         /* Number of bytes read */
-    off_t      tbytes;         /* Total number of bytes written */
-    char       *buffer,        /* Output buffer */
-               *bufptr;        /* Pointer into buffer */
+      return (CUPS_BACKEND_STOP);
+    }
 
-   pthread_cond_t   readCompleteCondition;
-   pthread_mutex_t  readMutex;
-   pthread_t       thr;
-   int             thread_created = 0;
+    if (driverBundlePath)
+      CFRelease(driverBundlePath);
 
-    USBPrinterInfo     *targetPrinter = NULL;
-    CFMutableArrayRef  usbPrinters;
-    char               manufacturer_buf[USB_MAX_STR_SIZE],
-                       product_buf[USB_MAX_STR_SIZE],
-                       serial_buf[USB_MAX_STR_SIZE];
-    CFStringRef                manufacturer;
-    CFStringRef                product;
-    CFStringRef                serial;
+    if (status != noErr)
+    {
+      sleep(PRINTER_POLLING_INTERVAL);
+      countdown -= PRINTER_POLLING_INTERVAL;
+      if (countdown <= 0)
+      {
+       _cupsLangPrintFilter(stderr, "INFO",
+                            _("Waiting for printer to become available."));
+       fprintf(stderr, "DEBUG: USB printer status: 0x%08x\n", (int)status);
+       countdown = SUBSEQUENT_LOG_INTERVAL;    /* subsequent log entries, every 15 seconds */
+      }
+    }
+  } while (status != noErr);
 
-    OSStatus           status = noErr;
+  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 defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
-       struct sigaction action;    /* Actions for POSIX signals */
-    #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+  if (!print_fd)
+  {
+    memset(&action, 0, sizeof(action));
 
-    fprintf(stderr, "INFO: Opening the print file and connection...\n");
+    sigemptyset(&action.sa_mask);
+    action.sa_handler = SIG_IGN;
+    sigaction(SIGTERM, &action, NULL);
+  }
 
-    parseOptions(options, serial_buf);
+ /*
+  * Start the side channel thread if the descriptor is valid...
+  */
 
-    if (resource[0] == '/')
-      resource++;
+  pthread_mutex_init(&g.readwrite_lock_mutex, NULL);
+  pthread_cond_init(&g.readwrite_lock_cond, NULL);
+  g.readwrite_lock = 1;
 
-    removePercentEscapes(hostname,     manufacturer_buf,   sizeof(manufacturer_buf));
-    removePercentEscapes(resource,     product_buf,        sizeof(product_buf));
+  if (have_sidechannel)
+  {
+    g.sidechannel_thread_stop = 0;
+    g.sidechannel_thread_done = 0;
 
-    manufacturer = CFStringCreateWithCString(NULL, manufacturer_buf, kCFStringEncodingUTF8);
-    product     = CFStringCreateWithCString(NULL, product_buf,      kCFStringEncodingUTF8);
-    serial      = CFStringCreateWithCString(NULL, serial_buf,       kCFStringEncodingUTF8);
+    pthread_cond_init(&g.sidechannel_thread_cond, NULL);
+    pthread_mutex_init(&g.sidechannel_thread_mutex, NULL);
 
-    USBPrinterInfo         *activePrinter = NULL;
-    USBPrinterClassContext  **classdriver;
-    int                            countdown = INITIAL_LOG_INTERVAL;
-    do
+    if (pthread_create(&sidechannel_thread_id, NULL, sidechannel_thread, NULL))
     {
-       /* */
-       /*  given a manufacturer and product, bind to a specific printer on the bus */
-       /* */
-       usbPrinters = UsbGetAllPrinters();
-       /* */
-       /*  if we have at least one element of the URI, find a printer module that matches */
-       /* */
-       if ( NULL != usbPrinters && (manufacturer || product ) )
-       {
-           int i,
-               numPrinters =  CFArrayGetCount(usbPrinters);
-           for ( i = 0; i < numPrinters; ++i ) 
-           {
-               int             match = FALSE;
-               USBPrinterInfo  *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbPrinters, i );
-               if ( printer )
-               {
-                   match = printer->address.manufacturer && manufacturer? CFEqual(printer->address.manufacturer, manufacturer ): FALSE;
-                   if ( match )
-                   {
-                       match = printer->address.product && product? CFEqual(printer->address.product, product ): FALSE;
-                   }
-                   if ( match && serial )  
-                   {
-                       /* Note with old queues (pre Panther) the CUPS uri may have no serial number (serial==NULL). */
-                       /*  In this case, we will ignore serial number (as before), and we'll match to the first */
-                       /*  printer that agrees with manufacturer and product. */
-                       /* If the CUPS uri does include a serial number, we'll enter this clause */
-                       /*  which requires the printer's serial number to match the CUPS serial number. */
-                       /* The net effect is that for printers with a serial number, */
-                       /*  new queues must match the serial number, while old queues match any printer  */
-                       /*  that satisfies the manufacturer/product match. */
-                       /* */
-                       match = printer->address.serial? CFEqual(printer->address.serial, serial ): FALSE;
-                   }
-                   if ( match )
-                   {
-                       targetPrinter = UsbCopyPrinter( printer );
-                       break;  /* for, compare partial address to address for each printer on usb bus */
-                   }
-               }
-           }
-       }
-       UsbReleaseAllPrinters( usbPrinters );
-       if ( NULL != targetPrinter )
-           status = UsbRegistryOpen( &targetPrinter->address, &activePrinter );
+      fprintf(stderr, "DEBUG: Fatal USB error.\n");
+      _cupsLangPrintFilter(stderr, "ERROR",
+                          _("There was an unrecoverable USB error."));
+      fputs("DEBUG: Couldn't create side-channel thread\n", stderr);
+      registry_close();
+      return (CUPS_BACKEND_STOP);
+    }
+  }
 
-       if ( NULL == activePrinter )
-       {
-           sleep( PRINTER_POLLING_INTERVAL );
-           countdown -= PRINTER_POLLING_INTERVAL;
-           if ( !countdown )
-           {
-               /* periodically, write to the log so someone knows we're waiting */
-               if (NULL == targetPrinter)
-                   fprintf( stderr, "WARNING: Printer not responding\n" );
-               else
-                   fprintf( stderr, "INFO: Printer busy\n" );
-               countdown = SUBSEQUENT_LOG_INTERVAL;    /* subsequent log entries, every 30 minutes */
-           }
-       }
-    } while ( NULL == activePrinter );
+ /*
+  * Get the read thread going...
+  */
 
-    classdriver = activePrinter->classdriver;
-    if ( NULL == classdriver )
-    {
-       perror("ERROR: Unable to open USB Printing Class port");
-       return (status);
-    }
-    
-    /*
-    * 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 */
-    }
+  g.read_thread_stop = 0;
+  g.read_thread_done = 0;
 
-    buffer = malloc( buffersize );
-    if ( !buffer ) {
-       fprintf( stderr, "ERROR: Couldn't allocate internal buffer\n" );
-       status = -1;
-    }
-    else
-    {
-       fprintf(stderr, "INFO: Sending the print file...\n");
-       if (pthread_cond_init(&readCompleteCondition, NULL) == 0)
-       {
-           gReadCompleteConditionPtr = &readCompleteCondition;
-           
-           if (pthread_mutex_init(&readMutex, NULL) == 0)
-           {
-               gReadMutexPtr = &readMutex;
+  pthread_cond_init(&g.read_thread_cond, NULL);
+  pthread_mutex_init(&g.read_thread_mutex, NULL);
 
-               if (pthread_create(&thr, NULL, readthread, classdriver ) > 0)
-                   fprintf(stderr, "WARNING: Couldn't create read channel\n");
-               else
-                   thread_created = 1;
-           }
-       }
-    }
-    /*
-    * the main thread sends the print file...
-    */
-    while (noErr == status && copies > 0)
-    {
-       copies --;
-       if (STDIN_FILENO != fd)
-       {
-           fputs("PAGE: 1 1", stderr);
-           lseek( fd, 0, SEEK_SET );   /* rewind */
-       }
+  if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
+  {
+    fprintf(stderr, "DEBUG: Fatal USB error.\n");
+    _cupsLangPrintFilter(stderr, "ERROR",
+                         _("There was an unrecoverable USB error."));
+    fputs("DEBUG: Couldn't create read thread\n", stderr);
+    registry_close();
+    return (CUPS_BACKEND_STOP);
+  }
 
-       tbytes = 0;
-       while (noErr == status && (nbytes = read(fd, buffer, buffersize)) > 0)
-       {
-           /*
-           * Write the print data to the printer...
-           */
+ /*
+  * The main thread sends the print file...
+  */
 
-           tbytes += nbytes;
-           bufptr = buffer;
+  g.drain_output = 0;
+  g.print_bytes         = 0;
+  total_bytes   = 0;
+  print_ptr     = print_buffer;
 
-           while (nbytes > 0 && noErr == status )
-           {
-               wbytes = nbytes;
-               status = (*classdriver)->WritePipe( classdriver, (UInt8*)bufptr, &wbytes, 0 /*nbytes > wbytes? 0: feof(fp)*/ );
-               if (wbytes < 0 || noErr != status)
-               {
-                   OSStatus err;
-                   err = (*classdriver)->Abort( classdriver );
-                   fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status, err );
-                   break;
-               }
-
-               nbytes -= wbytes;
-               bufptr += wbytes;
-           }
+  while (status == noErr && copies-- > 0)
+  {
+    _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer."));
 
-           if (fd != 0 && noErr == status)
-               fprintf(stderr, "INFO: Sending print file, %qd bytes...\n", (off_t)tbytes);
-       }
+    if (print_fd != STDIN_FILENO)
+    {
+      fputs("PAGE: 1 1\n", stderr);
+      lseek(print_fd, 0, SEEK_SET);
     }
-    done = 1;  /* stop scheduling reads */
 
-    if ( thread_created )
+    while (status == noErr)
     {
-       /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
-       * we are not signaled in that time then force the thread to exit by setting
-       * the waiteof to be false. Plese note that this relies on us using the timeout
-       * class driver.
-       */
-       struct timespec sleepUntil = { time(NULL) + WAITEOF_DELAY, 0 };
-       pthread_mutex_lock(&readMutex);
-       if (pthread_cond_timedwait(&readCompleteCondition, &readMutex, (const struct timespec *)&sleepUntil) != 0)
-           gWaitEOF = false;
-       pthread_mutex_unlock(&readMutex);
-       pthread_join( thr,NULL);                /* wait for the child thread to return */
-    }
+      FD_ZERO(&input_set);
+
+      if (!g.print_bytes)
+       FD_SET(print_fd, &input_set);
+
+     /*
+      * Calculate select timeout...
+      *   If we have data waiting to send timeout is 100ms.
+      *   else if we're draining print_fd timeout is 0.
+      *   else we're waiting forever...
+      */
+
+      if (g.print_bytes)
+      {
+       tv.tv_sec  = 0;
+       tv.tv_usec = 100000;            /* 100ms */
+       timeout = &tv;
+      }
+      else if (g.drain_output)
+      {
+       tv.tv_sec  = 0;
+       tv.tv_usec = 0;
+       timeout = &tv;
+      }
+      else
+       timeout = NULL;
+
+     /*
+      * I/O is unlocked around select...
+      */
+
+      pthread_mutex_lock(&g.readwrite_lock_mutex);
+      g.readwrite_lock = 0;
+      pthread_cond_signal(&g.readwrite_lock_cond);
+      pthread_mutex_unlock(&g.readwrite_lock_mutex);
+
+      nfds = select(print_fd + 1, &input_set, NULL, NULL, timeout);
+
+     /*
+      * Reacquire the lock...
+      */
+
+      pthread_mutex_lock(&g.readwrite_lock_mutex);
+      while (g.readwrite_lock)
+       pthread_cond_wait(&g.readwrite_lock_cond, &g.readwrite_lock_mutex);
+      g.readwrite_lock = 1;
+      pthread_mutex_unlock(&g.readwrite_lock_mutex);
+
+      if (nfds < 0)
+      {
+       if (errno == EINTR && total_bytes == 0)
+       {
+         fputs("DEBUG: Received an interrupt before any bytes were "
+               "written, aborting\n", stderr);
+          registry_close();
+          return (CUPS_BACKEND_OK);
+       }
+       else if (errno != EAGAIN && errno != EINTR)
+       {
+         _cupsLangPrintFilter(stderr, "ERROR",
+                              _("Unable to read print data."));
+         perror("DEBUG: select");
+         registry_close();
+          return (CUPS_BACKEND_FAILED);
+       }
+      }
 
-    (*classdriver)->Close( classdriver );   /* forces the read to stop incase we are doing a blocking read */
-    UsbUnloadClassDriver( activePrinter );
-    /*
-    * Close the socket connection and input file and return...
-    */
-    free( buffer );
+     /*
+      * If drain output has finished send a response...
+      */
 
-    if (STDIN_FILENO != fd)
-       close(fd);
+      if (g.drain_output && !nfds && !g.print_bytes)
+      {
+       /* Send a response... */
+       cupsSideChannelWrite(CUPS_SC_CMD_DRAIN_OUTPUT, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
+       g.drain_output = 0;
+      }
 
-    if (gReadCompleteConditionPtr != NULL)
-       pthread_cond_destroy(gReadCompleteConditionPtr);
-    if (gReadMutexPtr != NULL)
-       pthread_mutex_destroy(gReadMutexPtr);
+     /*
+      * Check if we have print data ready...
+      */
 
-    return status == kIOReturnSuccess? 0: status;
-}
+      if (FD_ISSET(print_fd, &input_set))
+      {
+#if DEBUG_WRITES
+       g.debug_bytes += 512;
+        if (g.debug_bytes > sizeof(print_buffer))
+         g.debug_bytes = 512;
 
-static Boolean
-encodecfstr( CFStringRef cfsrc, char *dst, long len )
-{
-    return CFStringGetCString(cfsrc, dst, len, kCFStringEncodingUTF8 );
-}
+       g.print_bytes = read(print_fd, print_buffer, g.debug_bytes);
 
-/*
-* 'list_devices()' - List all USB devices.
-*/
-void list_devices(void)
-{
-    char               encodedManufacturer[1024];
-    char               encodedProduct[1024];
-    char               uri[1024];
-    CFMutableArrayRef  usbBusPrinters = UsbGetAllPrinters();
-    CFIndex            i, numPrinters = NULL != usbBusPrinters? CFArrayGetCount( usbBusPrinters ): 0;
-    
-    puts("direct usb \"Unknown\" \"USB Printer (usb)\"");
-    for ( i = 0;  i < numPrinters; ++i )
-    {
-       USBPrinterInfo      *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbBusPrinters, i );
+#else
+       g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer));
+#endif /* DEBUG_WRITES */
 
-       if ( printer ) 
+       if (g.print_bytes < 0)
        {
-           CFStringRef addressRef = UsbMakeFullUriAddress( printer );
-           if ( addressRef )
-           {
-               if ( CFStringGetCString(addressRef, uri, sizeof(uri), kCFStringEncodingUTF8) ) {
-           
-                   encodecfstr( printer->address.manufacturer, encodedManufacturer, sizeof(encodedManufacturer) );
-                   encodecfstr( printer->address.product, encodedProduct, sizeof(encodedProduct) );
-                   printf("direct %s \"%s %s\" \"%s\"\n", uri, encodedManufacturer, encodedProduct, encodedProduct);
-               }
-           }
+        /*
+         * Read error - bail if we don't see EAGAIN or EINTR...
+         */
+
+         if (errno != EAGAIN && errno != EINTR)
+         {
+           _cupsLangPrintFilter(stderr, "ERROR",
+                                _("Unable to read print data."));
+           perror("DEBUG: read");
+           registry_close();
+           return (CUPS_BACKEND_FAILED);
+         }
+
+         g.print_bytes = 0;
        }
-    }
-    UsbReleaseAllPrinters( usbBusPrinters );
-    fflush(NULL);
-}
-
+       else if (g.print_bytes == 0)
+       {
+        /*
+         * End of file, break out of the loop...
+         */
 
-static void parseOptions(const char *options, char *serial)
-{
-    char    *serialnumber;  /* ?serial=<serial> or ?location=<location> */
-    char    optionName[255],   /* Name of option */
-           value[255],         /* Value of option */
-           *ptr;               /* Pointer into name or value */
+         break;
+       }
 
-    if (serial)
-       *serial = '\0';
+       print_ptr = print_buffer;
 
-    if (!options)
-       return;
+       fprintf(stderr, "DEBUG: Read %d bytes of print data...\n",
+               (int)g.print_bytes);
+      }
 
-    serialnumber = NULL;
+      if (g.print_bytes)
+      {
+       bytes    = (UInt32)g.print_bytes;
+       iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
 
-    while (*options != '\0')
-    {
-       /*
-       * Get the name...
+       /*
+       * Ignore timeout errors, but retain the number of bytes written to
+       * avoid sending duplicate data...
        */
-       for (ptr = optionName; *options && *options != '=' && *options != '+' && *options != '&'; )
-           *ptr++ = *options++;
-
-       *ptr = '\0';
-       value[0] = '\0';
 
-       if (*options == '=')
+       if (iostatus == kIOUSBTransactionTimeout)
        {
-           /*
-           * Get the value...
-           */
-           
-           options ++;
-           
-           for (ptr = value; *options && *options != '+' && *options != '&';)
-               *ptr++ = *options++;
-
-           *ptr = '\0';
-           
-           if (*options == '+' || *options == '&')
-               options ++;
-       }
-       else if (*options == '+' || *options == '&')
-       {
-           options ++;
+         fputs("DEBUG: Got USB transaction timeout during write\n", stderr);
+         iostatus = 0;
        }
 
-       /*
-       * Process the option...
+       /*
+        * If we've stalled, retry the write...
        */
-       if (strcasecmp(optionName, "waiteof") == 0)
+
+       else if (iostatus == kIOUSBPipeStalled)
        {
-           if (strcasecmp(value, "on") == 0 ||
-               strcasecmp(value, "yes") == 0 ||
-               strcasecmp(value, "true") == 0)
-           {
-               gWaitEOF = true;
-           }
-           else if (strcasecmp(value, "off") == 0 ||
-                   strcasecmp(value, "no") == 0 ||
-                   strcasecmp(value, "false") == 0)
-           {
-               gWaitEOF = false;
-           }
-           else
-           {
-               fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value);
-           }
+         fputs("DEBUG: Got USB pipe stalled during write\n", stderr);
+
+         bytes    = (UInt32)g.print_bytes;
+         iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
        }
-       else if (strcasecmp(optionName, "serial") == 0 ||
-                strcasecmp(optionName, "location") == 0 )
+
+       /*
+       * Retry a write after an aborted write since we probably just got
+       * SIGTERM...
+       */
+
+       else if (iostatus == kIOReturnAborted)
        {
-           strcpy(serial, value);
-           serialnumber = serial;
-       }
-    }
+         fputs("DEBUG: Got USB return aborted during write\n", stderr);
 
-    return;
-}
+         IOReturn err = (*g.classdriver)->Abort(g.classdriver);
+         fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n", err);
 
+#if DEBUG_WRITES
+          sleep(5);
+#endif /* DEBUG_WRITES */
 
-/*!
- * @function  addPercentEscapes
- * @abstract  Encode a string with percent escapes
- *
- * @param  src     The source C string
- * @param  dst     Desination buffer
- * @param  dstMax   Size of desination buffer
- *
- * @result    A non-zero return value for errors
- */
-static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax)
-{
-  unsigned char c;
-  char     *dstEnd = dst + dstMax - 1; /* -1 to leave room for the NUL */
+         bytes    = (UInt32)g.print_bytes;
+         iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+        }
 
-  while (*src)
-  {
-    c = *src++;
+       if (iostatus)
+       {
+        /*
+         * Write error - bail if we don't see an error we can retry...
+         */
 
-    if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 
-       (c >= '0' && c <= '9') || (c == '.' || c == '-'  || c == '*' || c == '_'))
-    {
-      if (dst >= dstEnd)
-       return -1;
+         _cupsLangPrintFilter(stderr, "ERROR",
+                              _("Unable to send data to printer."));
+         fprintf(stderr, "DEBUG: USB class driver WritePipe returned %x\n",
+                 iostatus);
 
-      *dst++ = c;
-    }
-    else
-    {
-      if (dst >= dstEnd - 2)
-       return -1;
+         IOReturn err = (*g.classdriver)->Abort(g.classdriver);
+         fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n",
+                 err);
+
+         status = CUPS_BACKEND_FAILED;
+         break;
+       }
+       else if (bytes > 0)
+       {
+         fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
+
+         g.print_bytes -= bytes;
+         print_ptr   += bytes;
+         total_bytes += bytes;
+       }
+      }
 
-      snprintf(dst, dstEnd - dst, "%%%02x", c);
-      dst += 3;
+      if (print_fd != 0 && status == noErr)
+       fprintf(stderr, "DEBUG: Sending print file, %lld bytes...\n",
+               (off_t)total_bytes);
     }
   }
 
-  *dst = '\0';
-  return 0;
-}
+  fprintf(stderr, "DEBUG: Sent %lld bytes...\n", (off_t)total_bytes);
+  fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
 
+ /*
+  * Signal the side channel thread to exit...
+  */
 
-/*!
- * @function   removePercentEscapes
- * @abstract   Returns a string with any percent escape sequences replaced with their equivalent character
- *
- * @param      src     Source buffer
- * @param      srclen  Number of bytes in source buffer
- * @param      dst     Desination buffer
- * @param      dstMax  Size of desination buffer
- *
- * @result     A non-zero return value for errors
- */
-static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax)
-{
-    int c;
-    const unsigned char *dstEnd = dst + dstMax;
+  if (have_sidechannel)
+  {
+    close(CUPS_SC_FD);
+    pthread_mutex_lock(&g.readwrite_lock_mutex);
+    g.readwrite_lock = 0;
+    pthread_cond_signal(&g.readwrite_lock_cond);
+    pthread_mutex_unlock(&g.readwrite_lock_mutex);
 
-    while (*src && dst < dstEnd)
-    {
-       c = *src++;
+    g.sidechannel_thread_stop = 1;
+    pthread_mutex_lock(&g.sidechannel_thread_mutex);
 
-       if (c == '%')
-       {
-           sscanf(src, "%02x", &c);
-           src += 2;
-       }
-       *dst++ = (char)c;
+    if (!g.sidechannel_thread_done)
+    {
+      gettimeofday(&tv, NULL);
+      cond_timeout.tv_sec  = tv.tv_sec + WAIT_SIDE_DELAY;
+      cond_timeout.tv_nsec = tv.tv_usec * 1000;
+
+      while (!g.sidechannel_thread_done)
+      {
+       if (pthread_cond_timedwait(&g.sidechannel_thread_cond,
+                                  &g.sidechannel_thread_mutex,
+                                  &cond_timeout) != 0)
+         break;
+      }
     }
 
-    if (dst >= dstEnd)
-       return -1;
-
-    *dst = '\0';
-    return 0;
-}
+    pthread_mutex_unlock(&g.sidechannel_thread_mutex);
+  }
 
-/*-----------------------------------------------------------------------------*
+ /*
+  * Signal the read thread to exit then wait 7 seconds for it to complete...
+  */
 
-       DelimitSubstring
+  g.read_thread_stop = 1;
 
-       Desc:   Search a string from a starting location, looking for a given
-               delimiter. Return the range from the start of the search to the
-               delimiter, or end of string (whichever is shorter).
+  pthread_mutex_lock(&g.read_thread_mutex);
 
-       In:     stringToSearch      string which contains a substring that we search
-               delim               string which marks the end of the string 
-               bounds              start and length of substring of stringToSearch
-               options             case sensitive, anchored, etc.
+  if (!g.read_thread_done)
+  {
+    fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
 
-       Out:    Range up to the delimiter.
+    gettimeofday(&tv, NULL);
+    cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
+    cond_timeout.tv_nsec = tv.tv_usec * 1000;
 
-*-----------------------------------------------------------------------------*/
-static CFRange
-DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options )
-{
-    CFRange    where_delim,    /* where the delimiter was found */
-               value;
-    /* */
-    /* trim leading space by changing bounds */
-    /* */
-    while ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, CFSTR(" "), bounds, kCFCompareAnchored, &where_delim ) )
-    {
-       ++bounds.location;  /* drop a leading ' ' */
-       --bounds.length;
-    }
-    value = bounds;        /* assume match to the end of string, may be NULL */
-    /* */
-    /* find the delimiter in the remaining string */
-    /* */
-    if (  bounds.length > 0 && CFStringFindWithOptions( stringToSearch, delim, bounds, options, &where_delim ) )
+    while (!g.read_thread_done)
     {
-       /* */
-       /* match to the delimiter */
-       /* */
-       value.length = where_delim.location /* delim */ - bounds.location /* start of search */;
+      if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
+                                &cond_timeout) != 0)
+       break;
     }
-    DEBUG_CFString( "\tFind target", stringToSearch );
-    DEBUG_CFString( "\tFind pattern", delim );
-    DEBUG_ERR( (int) value.location, "\t\tFound %d\n" );
-    DEBUG_ERR( (int) value.length, " length %d"         );
-
-    return value;
-}
-
 
-/*-----------------------------------------------------------------------------*
+   /*
+    * If it didn't exit abort the pending read and wait an additional second...
+    */
 
-       DeviceIDCreateValueList
+    if (!g.read_thread_done)
+    {
+      fputs("DEBUG: Read thread still active, aborting the pending read...\n",
+           stderr);
 
-       Desc:   Create a new string for the value list of the specified key.
-               The key may be specified as two strings (an abbreviated form
-               and a standard form). NULL can be passed for either form of 
-               the key.
-               
-               (Although passing NULL for both forms of the key is considered
-                bad form[!] it is handled correctly.)
+      g.wait_eof = 0;
 
-       In:     deviceID    the device's IEEE-1284 DeviceID key-value list
-               abbrevKey   the key we're interested in (NULL allowed)
-               key         
+      (*g.classdriver)->Abort(g.classdriver);
 
-       Out:    CFString    the value list 
-               or NULL     key wasn't found in deviceID
+      gettimeofday(&tv, NULL);
+      cond_timeout.tv_sec  = tv.tv_sec + 1;
+      cond_timeout.tv_nsec = tv.tv_usec * 1000;
 
-*-----------------------------------------------------------------------------*/
-CFStringRef
-DeviceIDCreateValueList( const CFStringRef deviceID, const CFStringRef abbrevKey, const CFStringRef key )
-{
-    CFRange    found = CFRangeMake( -1,0);   /* note CFStringFind sets length 0 if string not found */
-    CFStringRef valueList = NULL;
-
-    DEBUG_CFString( "---------DeviceIDCreateValueList DeviceID:", deviceID );
-    DEBUG_CFString( "---------DeviceIDCreateValueList key:", key );
-    DEBUG_CFString( "---------DeviceIDCreateValueList abbrevkey:", abbrevKey );
-   if ( NULL != deviceID && NULL != abbrevKey )
-       found = CFStringFind( deviceID, abbrevKey, kCFCompareCaseInsensitive );
-    if (  NULL != deviceID && NULL != key && found.length <= 0 )
-       found = CFStringFind( deviceID, key, kCFCompareCaseInsensitive );
-    if ( found.length > 0 )
-    {
-       /* the key is at found */
-       /* the value follows the key until we reach the semi-colon, or end of string */
-       /* */
-       CFRange search = CFRangeMake( found.location + found.length,
-                                 CFStringGetLength( deviceID ) - (found.location + found.length) );
-       /* */
-       /* finally extract the string */
-       /* */
-       valueList = CFStringCreateWithSubstring ( kCFAllocatorDefault, deviceID,
-                                                 DelimitSubstring( deviceID, kDeviceIDKeyValuePairDelimiter, search, kCFCompareCaseInsensitive ) );
-    DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList );
+      while (!g.read_thread_done)
+      {
+       if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
+                                  &cond_timeout) != 0)
+         break;
+      }
     }
-    return valueList;
+  }
 
-}
+  pthread_mutex_unlock(&g.read_thread_mutex);
 
+ /*
+  * Close the connection and input file and general clean up...
+  */
 
+  registry_close();
 
-/******************************************************************************/
+  if (print_fd != STDIN_FILENO)
+    close(print_fd);
 
-/*-----------------------------------------------------------------------------*
+  if (g.make != NULL)
+    CFRelease(g.make);
 
-CompareSameString
+  if (g.model != NULL)
+    CFRelease(g.model);
 
-Desc:  Return the CFCompare result for two strings, either or both of which
-       can be NULL.
+  if (g.serial != NULL)
+    CFRelease(g.serial);
 
-In:
-       a       current value
-       b       last value
+  if (g.printer_obj != 0x0)
+    IOObjectRelease(g.printer_obj);
 
-Out:
-       0       if the strings match
-       non-zero    if the strings don't match
+  return status;
+}
 
-*-----------------------------------------------------------------------------*/
-static int
-CompareSameString( const CFStringRef a, const CFStringRef b )
+
+/*
+ * 'read_thread()' - Thread to read the backchannel data on.
+ */
+
+static void *read_thread(void *reference)
 {
-    if ( NULL == a && NULL == b )
-       return 0;
-    else if ( NULL != a && NULL != b )
-       return CFStringCompare( a, b, kCFCompareAnchored );
-    else
-       return 1;   /* one of a or b is NULL this time, but wasn't last time */
+  UInt8                                readbuffer[512];
+  UInt32                       rbytes;
+  kern_return_t                        readstatus;
+  struct mach_timebase_info    timeBaseInfo;
+  uint64_t                     start,
+                               delay;
+
+
+  (void)reference;
+
+  /* Calculate what 250 milliSeconds are in mach absolute time...
+   */
+  mach_timebase_info(&timeBaseInfo);
+  delay = ((uint64_t)250000000 * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer;
+
+  do
+  {
+   /*
+    * Remember when we started so we can throttle the loop after the read call...
+    */
+
+    start = mach_absolute_time();
+
+    rbytes = sizeof(readbuffer);
+    readstatus = (*g.classdriver)->ReadPipe(g.classdriver, readbuffer, &rbytes);
+    if (readstatus == kIOReturnSuccess && rbytes > 0)
+    {
+      fprintf(stderr, "DEBUG: Read %d bytes of back-channel data...\n",
+              (int)rbytes);
+      cupsBackChannelWrite((char*)readbuffer, rbytes, 1.0);
+
+      /* cntrl-d is echoed by the printer.
+       * NOTES:
+       *   Xerox Phaser 6250D doesn't echo the cntrl-d.
+       *   Xerox Phaser 6250D doesn't always send the product query.
+       */
+      if (g.wait_eof && readbuffer[rbytes-1] == 0x4)
+       break;
+
+#ifdef PARSE_PS_ERRORS
+      parse_pserror(readbuffer, rbytes);
+#endif
+    }
+    else if (readstatus == kIOUSBTransactionTimeout)
+      fputs("DEBUG: Got USB transaction timeout during read\n", stderr);
+    else if (readstatus == kIOUSBPipeStalled)
+      fputs("DEBUG: Got USB pipe stalled during read\n", stderr);
+    else if (readstatus == kIOReturnAborted)
+      fputs("DEBUG: Got USB return aborted during read\n", stderr);
+
+   /*
+    * Make sure this loop executes no more than once every 250 miliseconds...
+    */
+
+    if ((readstatus != kIOReturnSuccess || rbytes == 0) && (g.wait_eof || !g.read_thread_stop))
+      mach_wait_until(start + delay);
+
+  } while (g.wait_eof || !g.read_thread_stop); /* Abort from main thread tests error here */
+
+ /*
+  * Let the main thread know that we have completed the read thread...
+  */
+
+  pthread_mutex_lock(&g.read_thread_mutex);
+  g.read_thread_done = 1;
+  pthread_cond_signal(&g.read_thread_cond);
+  pthread_mutex_unlock(&g.read_thread_mutex);
+
+  return NULL;
 }
 
 
-/******************************************************************************/
-kern_return_t
-UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle )
+/*
+ * 'sidechannel_thread()' - Handle side-channel requests.
+ */
+
+static void*
+sidechannel_thread(void *reference)
 {
-    kern_return_t   kr = kUSBPrinterClassDeviceNotOpen;
-    if ( NULL != classDriverBundle )
-       printer->bundle = classDriverBundle;    /* vendor-specific class override */
-    else
-#ifdef TIMEOUT
-       classDriverBundle = kUSBGenericTOPrinterClassDriver;    /*  supply the generic TIMEOUT class driver */
-#else
-       classDriverBundle = kUSBGenericPrinterClassDriver;  /*  supply the generic  class driver */
-#endif
-    DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle );
-    if ( NULL != classDriverBundle )
+  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 */
+
+
+  (void)reference;
+
+  do
+  {
+    datalen = sizeof(data);
+
+    if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
     {
-       USBPrinterClassContext  **classdriver = NULL;
-       CFURLRef                classDriverURL = CFURLCreateWithFileSystemPath( NULL, classDriverBundle, kCFURLPOSIXPathStyle, TRUE );
-       CFPlugInRef             plugin = NULL == classDriverURL? NULL: CFPlugInCreate( NULL, classDriverURL );
-       if ( NULL != plugin)
-       {
-           /* See if this plug-in implements the Test type. */
-           CFArrayRef factories =  CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID, plugin );
+      if (status == CUPS_SC_STATUS_TIMEOUT)
+       continue;
+      else
+       break;
+    }
 
-           /* If there are factories for the requested type, attempt to */
-           /* get the IUnknown interface. */
-           DEBUG_ERR( 0, "UsbLoadClassDriver plugin %x\n" );
-           if (NULL != factories && CFArrayGetCount(factories) > 0) 
-           {
-               /* Get the factory ID for the first location in the array of IDs. */
-               CFUUIDRef factoryID = CFArrayGetValueAtIndex( factories, 0 );
-               /* Use the factory ID to get an IUnknown interface. */
-               /* Here the code for the PlugIn is loaded. */
-               IUnknownVTbl **iunknown = CFPlugInInstanceCreate( NULL, factoryID, kUSBPrinterClassTypeID );
-               /* If this is an IUnknown interface, query for the Test interface. */
-               DEBUG_ERR( 0, "UsbLoadClassDriver factories %x\n" );
-               if (NULL != iunknown)
-               {
-                   DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" );
-                   kr = (*iunknown)->QueryInterface( iunknown, CFUUIDGetUUIDBytes(interfaceID), (LPVOID *) &classdriver );
-
-                   (*iunknown)->Release( iunknown );
-                   if ( S_OK == kr && NULL != classdriver )
-                   {
-                       DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface %x\n" );
-                       printer->plugin = plugin;
-                       kr = (*classdriver)->Initialize( classdriver, printer->classdriver );
-                       
-                       kr = kIOReturnSuccess;
-                       printer->classdriver = classdriver;
-                   }
-                   else
-                   {
-                       DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface FAILED %x\n" );
-                   }
-               }
-               else
-               {
-                   DEBUG_ERR( kr, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" );
-               }
-           }
-           else
-           {
-               DEBUG_ERR( kr, "UsbLoadClassDriver factories FAILED %x\n" );
-           }
-       }
-       else
-       {
-           DEBUG_ERR( kr, "UsbLoadClassDriver plugin FAILED %x\n" );
-       }
-       if ( kr != kIOReturnSuccess || NULL == plugin || NULL == classdriver )
-       {
-           UsbUnloadClassDriver( printer );
-       }
+    switch (command)
+    {
+      case CUPS_SC_CMD_SOFT_RESET:     /* Do a soft reset */
+         fputs("DEBUG: CUPS_SC_CMD_SOFT_RESET received from driver...\n",
+               stderr);
+
+          if ((*g.classdriver)->SoftReset != NULL)
+         {
+           soft_reset();
+           cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
+           fputs("DEBUG: Returning status CUPS_STATUS_OK with no bytes...\n",
+                 stderr);
+         }
+         else
+         {
+           cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
+                                NULL, 0, 1.0);
+           fputs("DEBUG: Returning status CUPS_STATUS_NOT_IMPLEMENTED with "
+                 "no bytes...\n", stderr);
+         }
+         break;
+
+      case CUPS_SC_CMD_DRAIN_OUTPUT:   /* Drain all pending output */
+         fputs("DEBUG: CUPS_SC_CMD_DRAIN_OUTPUT received from driver...\n",
+               stderr);
+
+         g.drain_output = 1;
+         break;
+
+      case CUPS_SC_CMD_GET_BIDI:               /* Is the connection bidirectional? */
+         fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n",
+               stderr);
+
+         data[0] = (char)g.bidi_flag;
+         cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
+
+         fprintf(stderr,
+                 "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
+                 data[0]);
+         break;
+
+      case CUPS_SC_CMD_GET_DEVICE_ID:  /* Return IEEE-1284 device ID */
+         fputs("DEBUG: CUPS_SC_CMD_GET_DEVICE_ID received from driver...\n",
+               stderr);
+
+         datalen = sizeof(data);
+         get_device_id(&status, data, &datalen);
+         cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0);
+
+          if ((size_t)datalen < sizeof(data))
+           data[datalen] = '\0';
+         else
+           data[sizeof(data) - 1] = '\0';
+
+         fprintf(stderr,
+                 "DEBUG: Returning CUPS_SC_STATUS_OK with %d bytes (%s)...\n",
+                 datalen, data);
+         break;
+
+      case CUPS_SC_CMD_GET_STATE:              /* Return device state */
+         fputs("DEBUG: CUPS_SC_CMD_GET_STATE received from driver...\n",
+               stderr);
+
+         data[0] = CUPS_SC_STATE_ONLINE;
+         cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
+
+         fprintf(stderr,
+                 "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
+                 data[0]);
+         break;
+
+      default:
+         fprintf(stderr, "DEBUG: Unknown side-channel command (%d) received "
+                         "from driver...\n", command);
+
+         cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
+                              NULL, 0, 1.0);
+
+         fputs("DEBUG: Returned CUPS_SC_STATUS_NOT_IMPLEMENTED with no bytes...\n",
+               stderr);
+         break;
     }
-    
-    return kr;
+  }
+  while (!g.sidechannel_thread_stop);
+
+  pthread_mutex_lock(&g.sidechannel_thread_mutex);
+  g.sidechannel_thread_done = 1;
+  pthread_cond_signal(&g.sidechannel_thread_cond);
+  pthread_mutex_unlock(&g.sidechannel_thread_mutex);
+
+  return NULL;
 }
 
 
-kern_return_t
-UsbUnloadClassDriver( USBPrinterInfo *printer )
+#pragma mark -
+/*
+ * 'iterate_printers()' - Iterate over all the printers.
+ */
+
+static void iterate_printers(iterator_callback_t callBack,
+                            void *userdata)
 {
-    DEBUG_ERR( kIOReturnSuccess, "UsbUnloadClassDriver %x\n" );
-    if ( NULL != printer->classdriver )
-       (*printer->classdriver)->Release( printer->classdriver );
-    printer->classdriver = NULL;
-    
-    if ( NULL != printer->plugin )
-       CFRelease( printer->plugin );
-    printer->plugin = NULL;
-    
-    return kIOReturnSuccess;
-}
+  Iterating = 1;
+
+  mach_port_t  masterPort = 0x0;
+  kern_return_t kr = IOMasterPort (bootstrap_port, &masterPort);
+
+  if (kr == kIOReturnSuccess && masterPort != 0x0)
+  {
+    io_iterator_t addIterator = 0x0;
+
+    iterator_reference_t reference = { callBack, userdata, true };
+    IONotificationPortRef addNotification = IONotificationPortCreate(masterPort);
+
+    int klass = kUSBPrintingClass;
+    int subklass = kUSBPrintingSubclass;
 
+    CFNumberRef usb_klass = CFNumberCreate(NULL, kCFNumberIntType, &klass);
+    CFNumberRef usb_subklass = CFNumberCreate(NULL, kCFNumberIntType, &subklass);
+    CFMutableDictionaryRef usbPrinterMatchDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
 
-/*-----------------------------------------------------------------------------*
+    CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceClass"), usb_klass);
+    CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceSubClass"), usb_subklass);
 
-    UsbAddressDispose
+    CFRelease(usb_klass);
+    CFRelease(usb_subklass);
 
-    Desc:   deallocates anything used to create a persistent printer address
+    IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification, usbPrinterMatchDictionary, &device_added, &reference, &addIterator);
+    if (addIterator != 0x0)
+    {
+      device_added (&reference, addIterator);
+
+      if (reference.keepRunning)
+      {
+       CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode);
+       CFRunLoopRun();
+      }
+      IOObjectRelease(addIterator);
+    }
+    mach_port_deallocate(mach_task_self(), masterPort);
+  }
 
-    In: address            the printer address we've created
+  Iterating = 0;
+}
 
-    Out:    <none>
 
-*-----------------------------------------------------------------------------*/
-void
-UsbAddressDispose( USBPrinterAddress *address )
+/*
+ * 'device_added()' - Device added notifier.
+ */
+
+static void device_added(void *userdata,
+                        io_iterator_t iterator)
 {
-    if ( address->product != NULL ) CFRelease( address->product );
-    if ( address->manufacturer != NULL ) CFRelease( address->manufacturer );
-    if ( address->serial != NULL ) CFRelease( address->serial );
-    if ( address->command != NULL ) CFRelease( address->command );
+  iterator_reference_t *reference = userdata;
 
-    address->product =
-    address->manufacturer =
-    address->serial =
-    address->command = NULL;
+  io_service_t obj;
+  while (reference->keepRunning && (obj = IOIteratorNext(iterator)) != 0x0)
+  {
+    if (reference->callback != NULL)
+      reference->keepRunning = reference->callback(reference->userdata, obj);
+
+    IOObjectRelease(obj);
+  }
+
+  /* One last call to the call back now that we are not longer have printers left to iterate...
+   */
+  if (reference->keepRunning && reference->callback)
+    reference->keepRunning = reference->callback(reference->userdata, 0x0);
 
+  if (!reference->keepRunning)
+    CFRunLoopStop(CFRunLoopGetCurrent());
 }
 
-/*-----------------------------------------------------------------------------*
-
-    UsbGetPrinterAddress
-
-    Desc:   Given a printer we're enumerating, discover it's persistent
-    reference.
-
-    A "persistent reference" is one which enables us to identify
-    a printer regardless of where it resides on the USB topology,
-    and enumeration sequence.
-
-    To do this, we actually construct a reference from information
-    buried inside the printer. First we look at the USB device
-    descripton: an ideally defined device will support strings for
-    manufacturer and product id, and serial number. The serial number
-    will be unique for each printer.
-
-    Our prefered identification fetches the IEEE-1284 device id string.
-    This transparently handled IEEE-1284 compatible printers which
-    connected over a USB-parallel cable. Only if we can't get all the
-    information to uniquely identify the printer do we try the strings
-    referenced in the printer's USB device descriptor. (These strings
-    are typically absent in a USB-parallel cable.)
-
-    If a device doesn't support serial numbers we have a problem:
-    we can't distinguish between two identical printers. Unique serial
-    numbers allow us to distinguish between two same-model, same-manufacturer
-    USB printers.
-
-    In:
-       thePrinter      iterator required for fetching device descriptor
-       devRefNum       required to configure the interface
-
-    Out:
-       address->manufacturer
-       address->product
-       address->serial
-               Any (and all) of these may be NULL if we can't retrieve
-               information for IEEE1284 DeviceID or the USB device
-               descriptor. Caller should be prepared to handle such a case.
-       address->command
-               May be updated.
-
-*-----------------------------------------------------------------------------*/
-OSStatus
-UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout )
+
+/*
+ * 'list_device_cb()' - list_device iterator callback.
+ */
+
+static Boolean list_device_cb(void *refcon,
+                             io_service_t obj)
 {
+  Boolean keepRunning = (obj != 0x0);
+
+
+  (void)refcon;
 
-    /* */
-    /* start by assuming the device is not IEEE-1284 compliant */
-    /* and that we can't read in the required strings. */
-    /* */
-    OSStatus               err;
-    CFStringRef                    deviceId = NULL;
-    USBPrinterClassContext  **printer = NULL == thePrinter? NULL: thePrinter->classdriver;
-    
-    address->manufacturer =
-    address->product =
-    address->compatible =
-    address->serial =
-    address->command = NULL;
-
-    DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter, sizeof(USBPrinterInfo) );
-
-    err = (*printer)->GetDeviceID( printer, &deviceId, timeout );
-    if ( noErr == err && NULL != deviceId )
+  if (keepRunning)
+  {
+    CFStringRef deviceIDString = NULL;
+    UInt32 deviceLocation = 0;
+    UInt8      interfaceNum = 0;
+
+    copy_devicestring(obj, &deviceIDString, &deviceLocation, &interfaceNum);
+    if (deviceIDString != NULL)
     {
-       /* the strings embedded here are defined in the IEEE1284 spec */
-       /* */
-       /*  use the MFG/MANUFACTURER for the manufacturer */
-       /*  and the MDL/MODEL for the product */
-       /*  there is no serial number defined in IEEE1284 */
-       /*      but it's been observed in recent HP printers */
-       /* */
-       address->command = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCommandAbbrev, kDeviceIDKeyCommand );
-
-       address->product = DeviceIDCreateValueList( deviceId, kDeviceIDKeyModelAbbrev, kDeviceIDKeyModel );
-       address->compatible = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCompatibleAbbrev, kDeviceIDKeyCompatible );
-
-       address->manufacturer = DeviceIDCreateValueList( deviceId, kDeviceIDKeyManufacturerAbbrev, kDeviceIDKeyManufacturer );
-
-       address->serial = DeviceIDCreateValueList( deviceId, kDeviceIDKeySerialAbbrev, kDeviceIDKeySerial );
-       CFRelease( deviceId );
+      CFStringRef make = NULL,  model = NULL, serial = NULL;
+      char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024];
+      char optionsstr[1024], idstr[1024], make_modelstr[1024];
+
+      copy_deviceinfo(deviceIDString, &make, &model, &serial);
+      CFStringGetCString(deviceIDString, idstr, sizeof(idstr),
+                         kCFStringEncodingUTF8);
+      backendGetMakeModel(idstr, make_modelstr, sizeof(make_modelstr));
+
+      modelstr[0] = '/';
+
+      if (!make ||
+          !CFStringGetCString(make, makestr, sizeof(makestr),
+                             kCFStringEncodingUTF8))
+        strlcpy(makestr, "Unknown", sizeof(makestr));
+
+      if (!model ||
+          !CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
+                             kCFStringEncodingUTF8))
+        strlcpy(modelstr + 1, "Printer", sizeof(modelstr) - 1);
+
+      optionsstr[0] = '\0';
+      if (serial != NULL)
+      {
+        CFStringGetCString(serial, serialstr, sizeof(serialstr), kCFStringEncodingUTF8);
+       snprintf(optionsstr, sizeof(optionsstr), "?serial=%s", serialstr);
+      }
+      else if (deviceLocation != 0)
+       snprintf(optionsstr, sizeof(optionsstr), "?location=%x", (unsigned)deviceLocation);
+
+      httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
+      strlcat(uristr, optionsstr, sizeof(uristr));
+
+      cupsBackendReport("direct", uristr, make_modelstr, make_modelstr, idstr,
+                        NULL);
+
+      release_deviceinfo(&make, &model, &serial);
+      CFRelease(deviceIDString);
     }
-    DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->product", address->product );
-    DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->compatible", address->compatible );
-    DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->manufacturer", address->manufacturer );
-    DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->serial", address->serial );
+  }
 
-    if ( NULL == address->product || NULL == address->manufacturer || NULL == address->serial )
-    {
-       /* */
-       /*  if the manufacturer or the product or serial number were not specified in DeviceID */
-       /*      try to construct the address using USB English string descriptors */
-       /* */
-       IOUSBDeviceDescriptor   desc;
-       USBIODeviceRequest      request;
-                               
-       request.requestType = USBmakebmRequestType( kUSBIn,  kUSBStandard, kUSBDevice );
-       request.request = kUSBRqGetDescriptor;
-       request.value = (kUSBDeviceDesc << 8) | 0;
-       request.index = 0;  /* not kUSBLanguageEnglish*/
-       request.length = sizeof(desc);
-       request.buffer = &desc;
-       err = (*printer)->DeviceRequest( printer, &request, timeout );
-       DEBUG_ERR( (kern_return_t) err, "UsbGetPrinterAddress: GetDescriptor %x" );
-       if ( kIOReturnSuccess == err )
-       {
-           /* once we've retrieved the device descriptor */
-           /*  try to fill in missing pieces of information */
-           /* */
-           /*  Don't override any information already retrieved from DeviceID. */
+  return keepRunning;
+}
 
-           if ( NULL == address->product)
-           {
-               err = (*printer)->GetString( printer, desc.iProduct, kUSBLanguageEnglish, timeout, &address->product );
-               if ( kIOReturnSuccess != err || address->product == NULL) {
-                   address->product = CFSTR("Unknown");
-               }                
-           }
-           DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address->product );
 
-           if ( NULL == address->manufacturer )
-           {
-               err = (*printer)->GetString( printer, desc.iManufacturer, kUSBLanguageEnglish, timeout, &address->manufacturer );
-               if (kIOReturnSuccess != err || address->manufacturer == NULL) {
-                   address->manufacturer = CFSTR("Unknown");
-               }
-           }
-           DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address->manufacturer );
+/*
+ * 'find_device_cb()' - print_device iterator callback.
+ */
+
+static Boolean find_device_cb(void *refcon,
+                             io_service_t obj)
+{
+  Boolean keepLooking = true;
+
+  if (obj != 0x0)
+  {
+    CFStringRef idString = NULL;
+    UInt32 location = ~0U;
+    UInt8      interfaceNum = 0;
 
-           if ( NULL == address->serial )
+    copy_devicestring(obj, &idString, &location, &interfaceNum);
+    if (idString != NULL)
+    {
+      CFStringRef make = NULL,  model = NULL, serial = NULL;
+
+      copy_deviceinfo(idString, &make, &model, &serial);
+      if (make && CFStringCompare(make, g.make, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+      {
+       if (model && CFStringCompare(model, g.model, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+       {
+         if (g.serial != NULL && CFStringGetLength(g.serial) > 0)
+         {
+           if (serial != NULL && CFStringCompare(serial, g.serial, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
            {
-               /* if the printer doesn't have a serial number, use locationId */
-               if ( 0 == desc.iSerialNumber )
-               {
-                   address->serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), (*printer)->location );
-               }
-               else
-               {
-                   err = (*printer)->GetString( printer, desc.iSerialNumber, kUSBLanguageEnglish, timeout, &address->serial );
-                   /* trailing NULs aren't handled correctly in URI */
-                   if ( address->serial )
-                   {
-                       UniChar     nulbyte = { 0 };
-                       CFStringRef trim = CFStringCreateWithCharacters(NULL, &nulbyte, 1);
-                       CFMutableStringRef newserial = CFStringCreateMutableCopy(NULL, 0, address->serial);
-
-                       CFStringTrim( newserial, trim );
-
-                       CFRelease(trim);
-                       CFRelease( address->serial );
-
-                       address->serial = newserial;
-                   }
-               }
+             IOObjectRetain(obj);
+             g.printer_obj = obj;
+             keepLooking = false;
            }
-           DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address->serial );
+         }
+         else
+         {
+           if (g.printer_obj != 0)
+             IOObjectRelease(g.printer_obj);
+
+           g.printer_obj = obj;
+           IOObjectRetain(obj);
+
+           if (g.location == 0 || g.location == location)
+             keepLooking = false;
+         }
+         if ( !keepLooking )
+               g.interfaceNum = interfaceNum;
        }
+      }
+
+      release_deviceinfo(&make, &model, &serial);
+      CFRelease(idString);
     }
-    if ( NULL != address->product)
-       CFRetain(address->product);         /* UsbGetString is really a UsbCopyString. */
-    if ( NULL != address->manufacturer )
-       CFRetain( address->manufacturer );
-    if ( NULL != address->serial )
-       CFRetain( address->serial );
-    return err;
+  }
+  else
+  {
+    keepLooking = (g.printer_obj == 0);
+    if (obj == 0x0 && keepLooking)
+    {
+      CFRunLoopTimerContext context = { 0, refcon, NULL, NULL, NULL };
+      CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 10, 0x0, 0x0, status_timer_cb, &context);
+      if (timer != NULL)
+      {
+       CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+       g.status_timer = timer;
+      }
+    }
+  }
+
+  if (!keepLooking && g.status_timer != NULL)
+  {
+    fputs("STATE: -offline-report\n", stderr);
+    _cupsLangPrintFilter(stderr, "INFO", _("The printer is now online."));
+    CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), g.status_timer, kCFRunLoopDefaultMode);
+    CFRelease(g.status_timer);
+    g.status_timer = NULL;
+  }
+
+  return keepLooking;
 }
 
 
-/*-----------------------------------------------------------------------------*
+/*
+ * 'status_timer_cb()' - Status timer callback.
+ */
+
+static void status_timer_cb(CFRunLoopTimerRef timer,
+                           void *info)
+{
+  (void)timer;
+  (void)info;
+
+  fputs("STATE: +offline-report\n", stderr);
+  _cupsLangPrintFilter(stderr, "INFO", _("The printer is offline."));
+
+  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.
+    *
+    * Sleep 5 seconds to keep the job from requeuing too rapidly...
+    */
 
-UsbSamePrinter
+    sleep(5);
 
-       Desc:   match two Usb printer address; return TRUE if they are the same.
+    exit(CUPS_BACKEND_FAILED);
+  }
+}
 
-       In:     a   the persistent address found last time
-               b   the persistent address found this time
 
-       Out:    non-zero iff the addresses are the same
+#pragma mark -
+/*
+ * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
+ */
 
-*-----------------------------------------------------------------------------*/
-int
-UsbSamePrinter( const USBPrinterAddress *a, const USBPrinterAddress *b )
+static void copy_deviceinfo(CFStringRef deviceIDString,
+                           CFStringRef *make,
+                           CFStringRef *model,
+                           CFStringRef *serial)
 {
-    int result = 0;
-    DEBUG_CFCompareString( "UsbSamePrinter serial", a->serial, b->serial );
-    DEBUG_CFCompareString( "UsbSamePrinter product", a->product, b->product );
-    DEBUG_CFCompareString( "UsbSamePrinter manufacturer", a->manufacturer, b->manufacturer );
+  CFStringRef modelKeys[]  = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL };
+  CFStringRef makeKeys[]   = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL };
+  CFStringRef serialKeys[] = { CFSTR("SN:"),  CFSTR("SERN:"), NULL };
+
+  if (make != NULL)
+    *make = copy_value_for_key(deviceIDString, makeKeys);
+
+  if (model != NULL)
+    *model = copy_value_for_key(deviceIDString, modelKeys);
+
+  if (serial != NULL)
+    *serial = copy_value_for_key(deviceIDString, serialKeys);
+}
+
+
+/*
+ * 'release_deviceinfo()' - Release deviceinfo strings.
+ */
+
+static void release_deviceinfo(CFStringRef *make,
+                              CFStringRef *model,
+                              CFStringRef *serial)
+{
+  if (make != NULL && *make != NULL)
+  {
+    CFRelease(*make);
+    *make = NULL;
+  }
+
+  if (model != NULL && *model != NULL)
+  {
+    CFRelease(*model);
+    *model = NULL;
+  }
+
+  if (serial != NULL && *serial != NULL)
+  {
+    CFRelease(*serial);
+    *serial = NULL;
+  }
+}
+
+
+#pragma mark -
+/*
+ * 'load_classdriver()' - Load a classdriver.
+ */
+
+static kern_return_t load_classdriver(CFStringRef          driverPath,
+                                     printer_interface_t   interface,
+                                     classdriver_t         ***printerDriver)
+{
+  kern_return_t        kr = kUSBPrinterClassDeviceNotOpen;
+  classdriver_t        **driver = NULL;
+  CFStringRef  bundle = driverPath ? driverPath : kUSBGenericTOPrinterClassDriver;
+  char                 bundlestr[1024];        /* Bundle path */
+  CFURLRef     url;                    /* URL for driver */
+  CFPlugInRef  plugin = NULL;          /* Plug-in address */
+
+
+  CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
+
+ /*
+  * Validate permissions for the class driver...
+  */
+
+  _cups_fc_result_t result = _cupsFileCheck(bundlestr,
+                                            _CUPS_FILE_CHECK_DIRECTORY, 1,
+                                            Iterating ? NULL : _cupsFileCheckFilter, NULL);
+
+  if (result && driverPath)
+    return (load_classdriver(NULL, interface, printerDriver));
+  else if (result)
+    return (kr);
+
+ /*
+  * Try loading the class driver...
+  */
+
+  url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
+
+  if (url)
+  {
+    plugin = CFPlugInCreate(NULL, url);
+    CFRelease(url);
+  }
+  else
+    plugin = NULL;
+
+  if (plugin)
+  {
+    CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
+    if (factories != NULL && CFArrayGetCount(factories) > 0)
+    {
+      CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
+      IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
+      if (iunknown != NULL)
+      {
+       kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
+       if (kr == kIOReturnSuccess && driver != NULL)
+       {
+         classdriver_t **genericDriver = NULL;
+         if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
+           kr = load_classdriver(NULL, interface, &genericDriver);
+
+         if (kr == kIOReturnSuccess)
+         {
+           (*driver)->interface = interface;
+           (*driver)->Initialize(driver, genericDriver);
+
+           (*driver)->plugin = plugin;
+           (*driver)->interface = interface;
+           *printerDriver = driver;
+         }
+       }
+       (*iunknown)->Release(iunknown);
+      }
+      CFRelease(factories);
+    }
+  }
 
-    result = !CompareSameString( a->serial, b->serial );
-    if ( result )  result = !CompareSameString( a->product, b->product );
-    if ( result ) result = !CompareSameString( a->manufacturer, b->manufacturer );
+  fprintf(stderr, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
 
-    return result;
+  return (kr);
 }
 
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Method:    UsbMakeFullUriAddress
+/*
+ * 'unload_classdriver()' - Unload a classdriver.
+ */
+
+static kern_return_t unload_classdriver(classdriver_t ***classdriver)
+{
+  if (*classdriver != NULL)
+  {
+    (**classdriver)->Release(*classdriver);
+    *classdriver = NULL;
+  }
 
-    Input Parameters:
+  return kIOReturnSuccess;
+}
 
-    Output Parameters:
 
-    Description:
-       Fill in missing address information
+/*
+ * 'load_printerdriver()' - Load vendor's classdriver.
+ *
+ * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
+ */
 
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-CFStringRef
-UsbMakeFullUriAddress( USBPrinterInfo *printer )
+static kern_return_t load_printerdriver(CFStringRef *driverBundlePath)
 {
-    /* */
-    /* fill in missing address information. */
-    /* */
-    CFMutableStringRef printerUri = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("usb://") );
-    if ( NULL != printerUri )
+  IOCFPlugInInterface  **iodev = NULL;
+  SInt32               score;
+  kern_return_t                kr;
+  printer_interface_t  interface;
+  HRESULT              res;
+
+  kr = IOCreatePlugInInterfaceForService(g.printer_obj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
+  if (kr == kIOReturnSuccess)
+  {
+    if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &interface)) == noErr)
     {
-       CFStringRef serial = printer->address.serial;
+      *driverBundlePath = IORegistryEntryCreateCFProperty(g.printer_obj, kUSBClassDriverProperty, NULL, kNilOptions);
 
-       CFStringAppend(printerUri, printer->address.manufacturer? CreateEncodedCFString( printer->address.manufacturer ): CFSTR("Unknown") );
-       CFStringAppend(printerUri, CFSTR("/") );
+      kr = load_classdriver(*driverBundlePath, interface, &g.classdriver);
 
-       CFStringAppend(printerUri, printer->address.product? CreateEncodedCFString( printer->address.product ): CFSTR("Unknown") );
+      if (kr != kIOReturnSuccess)
+       (*interface)->Release(interface);
+    }
+    IODestroyPlugInInterface(iodev);
+  }
+  return kr;
+}
 
-       /*Handle the common case where there is no serial number (S450?) */
-       CFStringAppend(printerUri, serial == NULL? CFSTR("?location="): CFSTR("?serial=") );
-       if ( serial == NULL)
-           serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), printer->location );
 
-        CFStringAppend(printerUri,  serial? CreateEncodedCFString( serial ): CFSTR("Unknown") );
+/*
+ * 'registry_open()' - Open a connection to the printer.
+ */
+
+static kern_return_t registry_open(CFStringRef *driverBundlePath)
+{
+  g.bidi_flag = 0;     /* 0=unidirectional */
+
+  kern_return_t kr = load_printerdriver(driverBundlePath);
+  if (kr != kIOReturnSuccess)
+    kr = -2;
+
+  if (g.classdriver != NULL)
+  {
+       (*g.classdriver)->interfaceNumber = g.interfaceNum;
+    kr = (*g.classdriver)->Open(g.classdriver, g.location, kUSBPrintingProtocolBidirectional);
+    if (kr != kIOReturnSuccess || (*g.classdriver)->interface == NULL)
+    {
+      kr = (*g.classdriver)->Open(g.classdriver, g.location, kUSBPrintingProtocolUnidirectional);
+      if (kr == kIOReturnSuccess)
+      {
+       if ((*g.classdriver)->interface == NULL)
+       {
+         (*g.classdriver)->Close(g.classdriver);
+         kr = -1;
+       }
+      }
     }
-    
-    return printerUri;
+    else
+      g.bidi_flag = 1; /* 1=bidirectional */
+  }
+
+  if (kr != kIOReturnSuccess)
+    unload_classdriver(&g.classdriver);
+
+  return kr;
 }
 
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Method:    UsbGetAllPrinters
+/*
+ * 'registry_close()' - Close the connection to the printer.
+ */
 
-    Input Parameters:
+static kern_return_t registry_close(void)
+{
+  if (g.classdriver != NULL)
+    (*g.classdriver)->Close(g.classdriver);
+
+  unload_classdriver(&g.classdriver);
+  return kIOReturnSuccess;
+}
 
-    Output Parameters:
-       array of all USB printers on the system
 
-    Description:
-       Build a list of USB printers by iterating IOKit USB objects
+/*
+ * 'copy_deviceid()' - Copy the 1284 device id string.
+ */
 
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-CFMutableArrayRef
-UsbGetAllPrinters( void )
+static OSStatus copy_deviceid(classdriver_t **classdriver,
+                             CFStringRef *deviceID)
 {
-    kern_return_t      kr;     /* kernel errors */
-    mach_port_t                master_device_port = 0;
-    io_service_t       usbInterface = 0;
-    io_iterator_t      iter = 0;
-    CFMutableArrayRef  printers = CFArrayCreateMutable( NULL, 0, NULL );   /* all printers */
+  CFStringRef devID = NULL;
+  CFStringRef deviceMake = NULL;
+  CFStringRef deviceModel = NULL;
+  CFStringRef deviceSerial = NULL;
+
+  *deviceID = NULL;
+
+  OSStatus err = (*classdriver)->GetDeviceID(classdriver, &devID, DEFAULT_TIMEOUT);
 
-    do
+  copy_deviceinfo(devID, &deviceMake, &deviceModel, &deviceSerial);
+
+  if (deviceMake == NULL || deviceModel == NULL || deviceSerial == NULL)
+  {
+    IOUSBDeviceDescriptor      desc;
+    iodevice_request_t         request;
+
+    request.requestType = USBmakebmRequestType(kUSBIn,  kUSBStandard, kUSBDevice);
+    request.request = kUSBRqGetDescriptor;
+    request.value = (kUSBDeviceDesc << 8) | 0;
+    request.index = 0;
+    request.length = sizeof(desc);
+    request.buffer = &desc;
+    err = (*classdriver)->DeviceRequest(classdriver, &request, DEFAULT_TIMEOUT);
+    if (err == kIOReturnSuccess)
     {
+      CFMutableStringRef newDevID = CFStringCreateMutable(NULL, 0);
 
-       kr = IOMasterPort( bootstrap_port, &master_device_port );
-       DEBUG_ERR( kr, "UsbGetAllPrinters IOMasterPort %x\n" );
-       if(kIOReturnSuccess != kr)  break;
+      if (deviceMake == NULL)
+      {
+       CFStringRef data = NULL;
+       err = (*classdriver)->GetString(classdriver, desc.iManufacturer, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
+       if (data != NULL)
+       {
+         CFStringAppendFormat(newDevID, NULL, CFSTR("MFG:%@;"), data);
+         CFRelease(data);
+       }
+      }
 
+      if (deviceModel == NULL)
+      {
+       CFStringRef data = NULL;
+       err = (*classdriver)->GetString(classdriver, desc.iProduct, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
+       if (data != NULL)
        {
-           CFDictionaryRef     usbMatch = NULL;
-           
-           /* iterate over all interfaces.  */
-           usbMatch = IOServiceMatching(kIOUSBInterfaceClassName);
-           if ( !usbMatch ) break;
-           DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceMatching %x\n" );
-       
-           /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */
-           kr = IOServiceGetMatchingServices(master_device_port, usbMatch, &iter);
-           usbMatch = NULL;
-           
-           DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" );
-           if(kIOReturnSuccess != kr || iter == NULL)  break;
+         CFStringAppendFormat(newDevID, NULL, CFSTR("MDL:%@;"), data);
+         CFRelease(data);
        }
-       
-       while (  NULL != (usbInterface = IOIteratorNext(iter))  )
+      }
+
+      if (deviceSerial == NULL && desc.iSerialNumber != 0)
+      {
+       err = (*classdriver)->GetString(classdriver, desc.iSerialNumber, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &deviceSerial);
+       if (deviceSerial != NULL)
        {
-           IOCFPlugInInterface     **iodev;
-           USBPrinterInterface     intf;
-           HRESULT                 res;
-           SInt32                  score;
-           CFMutableDictionaryRef  properties;
-           CFStringRef             classDriver = NULL;
-
-           kr = IORegistryEntryCreateCFProperties( usbInterface, &properties, kCFAllocatorDefault, kNilOptions);
-           if ( kIOReturnSuccess == kr && NULL != properties)
-           {
-               classDriver = (CFStringRef) CFDictionaryGetValue( properties, kUSBClassDriverProperty );
-               if ( NULL != classDriver )
-                   CFRetain( classDriver );
-               CFRelease( properties );
-           }    
-
-           kr = IOCreatePlugInInterfaceForService( usbInterface,
-                                                       kIOUSBInterfaceUserClientTypeID, 
-                                                       kIOCFPlugInInterfaceID,
-                                                       &iodev,
-                                                       &score);
-               
-           DEBUG_ERR( kr, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" );
-           if ( kIOReturnSuccess == kr )
-           {
-               UInt8               intfClass = 0;
-               UInt8               intfSubClass = 0;
-               res = (*iodev)->QueryInterface( iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
-               DEBUG_ERR( (kern_return_t) res, "UsbGetAllPrinters QueryInterface %x\n" );
-
-              (*iodev)->Release(iodev);
-               if ( noErr != res ) break;
-               kr = (*intf)->GetInterfaceClass(intf, &intfClass);
-               DEBUG_ERR(kr, "UsbGetAllPrinters GetInterfaceClass %x\n");
-               if ( kIOReturnSuccess == kr )
-                   kr = (*intf)->GetInterfaceSubClass(intf, &intfSubClass);
-               DEBUG_ERR(kr, "UsbGetAllPrinters GetInterfaceSubClass %x\n");
-               
-               if ( kIOReturnSuccess == kr &&
-                       kUSBPrintingClass == intfClass &&
-                       kUSBPrintingSubclass == intfSubClass )
-               {
-
-                   USBPrinterInfo          printer,
-                                           *printerInfo;
-                   /*
-                   For each type of printer specified in the lookup spec array, find
-                   all of that type of printer and add the results to the list of found
-                   printers.
-                   */
-                   /* create this printer's persistent address */
-                   memset( &printer, 0, sizeof(USBPrinterInfo) );
-                   kr = (*intf)->GetLocationID(intf, &printer.location);
-                   DEBUG_ERR(kr, "UsbGetAllPrinters GetLocationID %x\n");
-                   if ( kIOReturnSuccess == kr )
-                   {
-                       kr = UsbLoadClassDriver( &printer, kUSBPrinterClassInterfaceID, classDriver );
-                       DEBUG_ERR(kr, "UsbGetAllPrinters UsbLoadClassDriver %x\n");
-                       if ( kIOReturnSuccess == kr && printer.classdriver )
-                       {
-                           (*(printer.classdriver))->interface = intf;
-                           kr = UsbGetPrinterAddress( &printer, &printer.address, 60000L );
-                           { 
-                               /* always unload the driver */
-                               /*  but don't mask last error */
-                               kern_return_t unload_err = UsbUnloadClassDriver( &printer );
-                               if ( kIOReturnSuccess == kr )
-                                   kr = unload_err;
-                           }
-                       }
-                   }
-                   
-                   printerInfo = UsbCopyPrinter( &printer );
-                   if ( NULL != printerInfo )
-                       CFArrayAppendValue( printers, (const void *) printerInfo );     /* keep track of it */
-
-                } /* if there's a printer */
-               kr = (*intf)->Release(intf);
-           } /* if IOCreatePlugInInterfaceForService */
-           
-           IOObjectRelease(usbInterface);
-           usbInterface = NULL;
-           
-       } /* while there's an interface */
-    } while ( 0 );
-
-    if (iter) 
+         CFStringAppendFormat(newDevID, NULL, CFSTR("SERN:%@;"), deviceSerial);
+       }
+      }
+
+      if (devID != NULL)
+      {
+       CFStringAppend(newDevID, devID);
+       CFRelease(devID);
+      }
+
+      *deviceID = newDevID;
+    }
+  }
+  else
+  {
+    *deviceID = devID;
+  }
+
+  if (*deviceID == NULL)
+      return err;
+
+  /* Remove special characters from the serial number */
+  CFRange range = (deviceSerial != NULL ? CFStringFind(deviceSerial, CFSTR("+"), 0) : CFRangeMake(0, 0));
+  if (range.length == 1) {
+      range = CFStringFind(*deviceID, deviceSerial, 0);
+
+      CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(NULL, 0, *deviceID);
+      CFStringFindAndReplace(deviceIDString, CFSTR("+"), CFSTR(""), range, 0);
+      CFRelease(*deviceID);
+      *deviceID = deviceIDString;
+  }
+
+  release_deviceinfo(&deviceMake, &deviceModel, &deviceSerial);
+
+  return err;
+}
+
+
+/*
+ * 'copy_devicestring()' - Copy the 1284 device id string.
+ */
+
+static void copy_devicestring(io_service_t usbInterface,
+                             CFStringRef *deviceID,
+                             UInt32 *deviceLocation,
+                             UInt8     *interfaceNumber )
+{
+  IOCFPlugInInterface  **iodev = NULL;
+  SInt32               score;
+  kern_return_t                kr;
+  printer_interface_t  interface;
+  HRESULT              res;
+  classdriver_t        **klassDriver = NULL;
+  CFStringRef          driverBundlePath;
+
+  if ((kr = IOCreatePlugInInterfaceForService(usbInterface,
+                                        kIOUSBInterfaceUserClientTypeID,
+                                        kIOCFPlugInInterfaceID,
+                                        &iodev, &score)) == kIOReturnSuccess)
+  {
+    if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *)
+                                       &interface)) == noErr)
     {
-       IOObjectRelease(iter);
-       iter = 0;
+      (*interface)->GetLocationID(interface, deviceLocation);
+      (*interface)->GetInterfaceNumber(interface, interfaceNumber);
+
+      driverBundlePath = IORegistryEntryCreateCFProperty(usbInterface,
+                                                        kUSBClassDriverProperty,
+                                                        NULL, kNilOptions);
+
+      kr = load_classdriver(driverBundlePath, interface, &klassDriver);
+
+      if (kr != kIOReturnSuccess && driverBundlePath != NULL)
+       kr = load_classdriver(NULL, interface, &klassDriver);
+
+      if (kr == kIOReturnSuccess && klassDriver != NULL)
+         copy_deviceid(klassDriver, deviceID);
+
+      unload_classdriver(&klassDriver);
+
+      if (driverBundlePath != NULL)
+       CFRelease(driverBundlePath);
+
+      /* (*interface)->Release(interface); */
     }
+    IODestroyPlugInInterface(iodev);
+  }
+}
 
-    if (master_device_port) 
+
+#pragma mark -
+/*
+ * 'copy_value_for_key()' - Copy value string associated with a key.
+ */
+
+static CFStringRef copy_value_for_key(CFStringRef deviceID,
+                                     CFStringRef *keys)
+{
+  CFStringRef  value = NULL;
+  CFArrayRef   kvPairs = deviceID != NULL ? CFStringCreateArrayBySeparatingStrings(NULL, deviceID, CFSTR(";")) : NULL;
+  CFIndex      max = kvPairs != NULL ? CFArrayGetCount(kvPairs) : 0;
+  CFIndex      idx = 0;
+
+  while (idx < max && value == NULL)
+  {
+    CFStringRef kvpair = CFArrayGetValueAtIndex(kvPairs, idx);
+    CFIndex idxx = 0;
+    while (keys[idxx] != NULL && value == NULL)
     {
-       mach_port_deallocate(mach_task_self(), master_device_port);
-       master_device_port = 0;
+      CFRange range = CFStringFind(kvpair, keys[idxx], kCFCompareCaseInsensitive);
+      if (range.length != -1)
+      {
+       if (range.location != 0)
+       {
+         CFMutableStringRef theString = CFStringCreateMutableCopy(NULL, 0, kvpair);
+         CFStringTrimWhitespace(theString);
+         range = CFStringFind(theString, keys[idxx], kCFCompareCaseInsensitive);
+         if (range.location == 0)
+           value = CFStringCreateWithSubstring(NULL, theString, CFRangeMake(range.length, CFStringGetLength(theString) - range.length));
+
+         CFRelease(theString);
+       }
+       else
+       {
+         CFStringRef theString = CFStringCreateWithSubstring(NULL, kvpair, CFRangeMake(range.length, CFStringGetLength(kvpair) - range.length));
+         CFMutableStringRef theString2 = CFStringCreateMutableCopy(NULL, 0, theString);
+         CFRelease(theString);
+
+         CFStringTrimWhitespace(theString2);
+         value = theString2;
+       }
+      }
+      idxx++;
     }
-    return printers;
+    idx++;
+  }
+
+  if (kvPairs != NULL)
+    CFRelease(kvPairs);
+  return value;
+}
 
-} /* UsbGetAllPrinters */
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Method:    UsbReleasePrinter
+/*
+ * 'cfstr_create_trim()' - Create CFString and trim whitespace characters.
+ */
 
-    Input Parameters:
+CFStringRef cfstr_create_trim(const char *cstr)
+{
+  CFStringRef          cfstr;
+  CFMutableStringRef   cfmutablestr = NULL;
 
-    Output Parameters:
+  if ((cfstr = CFStringCreateWithCString(NULL, cstr, kCFStringEncodingUTF8)) != NULL)
+  {
+    if ((cfmutablestr = CFStringCreateMutableCopy(NULL, 1024, cfstr)) != NULL)
+      CFStringTrimWhitespace(cfmutablestr);
 
-    Description:
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-void
-UsbReleasePrinter( USBPrinterInfo *printer )
+    CFRelease(cfstr);
+  }
+  return (CFStringRef) cfmutablestr;
+}
+
+
+#pragma mark -
+/*
+ * 'parse_options()' - Parse URI options.
+ */
+
+static void parse_options(char *options,
+                         char *serial,
+                         int serial_size,
+                         UInt32 *location,
+                         Boolean *wait_eof)
 {
-    if ( printer )
+  char sep,                            /* Separator character */
+       *name,                          /* Name of option */
+       *value;                         /* Value of option */
+
+
+  if (serial)
+    *serial = '\0';
+  if (location)
+    *location = 0;
+
+  if (!options)
+    return;
+
+  while (*options)
+  {
+   /*
+    * Get the name...
+    */
+
+    name = options;
+
+    while (*options && *options != '=' && *options != '+' && *options != '&')
+      options ++;
+
+    if ((sep = *options) != '\0')
+      *options++ = '\0';
+
+    if (sep == '=')
     {
-       UsbUnloadClassDriver( printer );
-       if ( NULL != printer->address.manufacturer )
-           CFRelease( printer->address.manufacturer );
-       if ( NULL != printer->address.product )
-           CFRelease( printer->address.product );
-       if ( NULL != printer->address.serial )
-           CFRelease( printer->address.serial );
-       if ( NULL != printer->address.command )
-           CFRelease( printer->address.command );
-       if ( NULL != printer->bundle )
-           CFRelease( printer->bundle );
-       free( printer );
-   }
+     /*
+      * Get the value...
+      */
+
+      value = options;
+
+      while (*options && *options != '+' && *options != '&')
+       options ++;
+
+      if (*options)
+       *options++ = '\0';
+    }
+    else
+      value = (char *)"";
+
+   /*
+    * Process the option...
+    */
+
+    if (!_cups_strcasecmp(name, "waiteof"))
+    {
+      if (!_cups_strcasecmp(value, "on") ||
+         !_cups_strcasecmp(value, "yes") ||
+         !_cups_strcasecmp(value, "true"))
+       *wait_eof = true;
+      else if (!_cups_strcasecmp(value, "off") ||
+              !_cups_strcasecmp(value, "no") ||
+              !_cups_strcasecmp(value, "false"))
+       *wait_eof = false;
+      else
+       _cupsLangPrintFilter(stderr, "WARNING",
+                            _("Boolean expected for waiteof option \"%s\"."),
+                            value);
+    }
+    else if (!_cups_strcasecmp(name, "serial"))
+      strlcpy(serial, value, serial_size);
+    else if (!_cups_strcasecmp(name, "location") && location)
+      *location = (UInt32)strtoul(value, NULL, 16);
+  }
 }
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    Method:    UsbReleaseAllPrinters
 
-    Input Parameters:
+/*!
+ * @function   setup_cfLanguage
+ * @abstract   Convert the contents of the CUPS 'APPLE_LANGUAGE' environment
+ *             variable into a one element CF array of languages.
+ *
+ * @discussion Each submitted job comes with a natural language. CUPS passes
+ *             that language in an environment variable. We take that language
+ *             and jam it into the AppleLanguages array so that CF will use
+ *             it when reading localized resources. We need to do this before
+ *             any CF code reads and caches the languages array, so this function
+ *             should be called early in main()
+ */
+static void setup_cfLanguage(void)
+{
+  CFStringRef  lang[1] = {NULL};
+  CFArrayRef   langArray = NULL;
+  const char   *requestedLang = NULL;
+
+  if ((requestedLang = getenv("APPLE_LANGUAGE")) == NULL)
+    requestedLang = getenv("LANG");
 
-    Output Parameters:
+  if (requestedLang != NULL)
+  {
+    lang[0] = CFStringCreateWithCString(kCFAllocatorDefault, requestedLang, kCFStringEncodingUTF8);
+    langArray = CFArrayCreate(kCFAllocatorDefault, (const void **)lang, sizeof(lang) / sizeof(lang[0]), &kCFTypeArrayCallBacks);
 
-    Description:
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-void
-UsbReleaseAllPrinters( CFMutableArrayRef printers )
+    CFPreferencesSetValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
+    fprintf(stderr, "DEBUG: usb: AppleLanguages=\"%s\"\n", requestedLang);
+
+    CFRelease(lang[0]);
+    CFRelease(langArray);
+  }
+  else
+    fputs("DEBUG: usb: LANG and APPLE_LANGUAGE environment variables missing.\n", stderr);
+}
+
+#pragma mark -
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * @function   run_legacy_backend
+ *
+ * @abstract   Starts child backend process running as a ppc or i386 executable.
+ *
+ * @result     Never returns; always calls exit().
+ *
+ * @discussion
+ */
+static void run_legacy_backend(int argc,
+                              char *argv[],
+                              int fd)
 {
-    if ( NULL != printers )
+  int  i;
+  int  exitstatus = 0;
+  int  childstatus;
+  pid_t        waitpid_status;
+  char *my_argv[32];
+  char *usb_legacy_status;
+
+
+ /*
+  * If we're running as x86_64 or i386 and couldn't load the class driver
+  * (because it's ppc or i386), then try to re-exec ourselves in ppc or i386
+  * mode to try again. If we don't have a ppc or i386 architecture we may be
+  * running with the same architecture again so guard against this by setting
+  * and testing an environment variable...
+  */
+
+#  ifdef __x86_64__
+  usb_legacy_status = getenv("USB_I386_STATUS");
+#  else
+  usb_legacy_status = getenv("USB_PPC_STATUS");
+#  endif /* __x86_64__ */
+
+  if (!usb_legacy_status)
+  {
+   /*
+    * Setup a SIGTERM handler then block it before forking...
+    */
+
+    int                        err;            /* posix_spawn result */
+    struct sigaction   action;         /* POSIX signal action */
+    sigset_t           newmask,        /* New signal mask */
+                       oldmask;        /* Old signal mask */
+    char               usbpath[1024];  /* Path to USB backend */
+    const char         *cups_serverbin;/* Path to CUPS binaries */
+
+
+    memset(&action, 0, sizeof(action));
+    sigaddset(&action.sa_mask, SIGTERM);
+    action.sa_handler = sigterm_handler;
+    sigaction(SIGTERM, &action, NULL);
+
+    sigemptyset(&newmask);
+    sigaddset(&newmask, SIGTERM);
+    sigprocmask(SIG_BLOCK, &newmask, &oldmask);
+
+   /*
+    * Set the environment variable...
+    */
+
+#  ifdef __x86_64__
+    setenv("USB_I386_STATUS", "1", false);
+#  else
+    setenv("USB_PPC_STATUS", "1", false);
+#  endif /* __x86_64__ */
+
+   /*
+    * Tell the kernel to use the specified CPU architecture...
+    */
+
+#  ifdef __x86_64__
+    cpu_type_t cpu = CPU_TYPE_I386;
+#  else
+    cpu_type_t cpu = CPU_TYPE_POWERPC;
+#  endif /* __x86_64__ */
+    size_t ocount = 1;
+    posix_spawnattr_t attrs;
+
+    if (!posix_spawnattr_init(&attrs))
+    {
+      posix_spawnattr_setsigdefault(&attrs, &oldmask);
+      if (posix_spawnattr_setbinpref_np(&attrs, 1, &cpu, &ocount) || ocount != 1)
+      {
+#  ifdef __x86_64__
+       perror("DEBUG: Unable to set binary preference to i386");
+#  else
+       perror("DEBUG: Unable to set binary preference to ppc");
+#  endif /* __x86_64__ */
+       _cupsLangPrintFilter(stderr, "ERROR",
+                            _("Unable to use legacy USB class driver."));
+       exit(CUPS_BACKEND_STOP);
+      }
+    }
+
+   /*
+    * Set up the arguments and call posix_spawn...
+    */
+
+    if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+      cups_serverbin = CUPS_SERVERBIN;
+    snprintf(usbpath, sizeof(usbpath), "%s/backend/usb", cups_serverbin);
+
+    for (i = 0; i < argc && i < (int)(sizeof(my_argv) / sizeof(my_argv[0])) - 1; i ++)
+      my_argv[i] = argv[i];
+
+    my_argv[i] = NULL;
+
+    if ((err = posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv,
+                           environ)) != 0)
     {
-       CFIndex i,
-               numPrinters = CFArrayGetCount(printers);
-       for ( i = 0; i < numPrinters; ++i ) 
-           UsbReleasePrinter( (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i ) );
-       CFRelease( printers );          
+      fprintf(stderr, "DEBUG: Unable to exec %s: %s\n", usbpath,
+              strerror(err));
+      _cupsLangPrintFilter(stderr, "ERROR",
+                           _("Unable to use legacy USB class driver."));
+      exit(CUPS_BACKEND_STOP);
     }
+
+   /*
+    * Unblock signals...
+    */
+
+    sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+   /*
+    * Close the fds we won't be using then wait for the child backend to exit.
+    */
+
+    close(fd);
+    close(1);
+
+    fprintf(stderr, "DEBUG: Started usb(legacy) backend (PID %d)\n",
+            (int)child_pid);
+
+    while ((waitpid_status = waitpid(child_pid, &childstatus, 0)) == (pid_t)-1 && errno == EINTR)
+      usleep(1000);
+
+    if (WIFSIGNALED(childstatus))
+    {
+      exitstatus = CUPS_BACKEND_STOP;
+      fprintf(stderr, "DEBUG: usb(legacy) backend %d crashed on signal %d\n",
+              child_pid, WTERMSIG(childstatus));
+    }
+    else
+    {
+      if ((exitstatus = WEXITSTATUS(childstatus)) != 0)
+       fprintf(stderr,
+               "DEBUG: usb(legacy) backend %d stopped with status %d\n",
+               child_pid, exitstatus);
+      else
+       fprintf(stderr, "DEBUG: usb(legacy) backend %d exited with no errors\n",
+               child_pid);
+    }
+  }
+  else
+  {
+    fputs("DEBUG: usb(legacy) backend running native again\n", stderr);
+    exitstatus = CUPS_BACKEND_STOP;
+  }
+
+  exit(exitstatus);
 }
+#endif /* __i386__ || __x86_64__ */
+
 
-USBPrinterInfo *
-UsbCopyPrinter( USBPrinterInfo *aPrinter )
+/*
+ * 'sigterm_handler()' - SIGTERM handler.
+ */
+
+static void
+sigterm_handler(int sig)               /* I - Signal */
 {
-    /* */
-    /* note this does not copy interface information, just address information */
-    /* */
-    USBPrinterInfo *printerInfo = (USBPrinterInfo *) calloc( 1, sizeof(USBPrinterInfo));
-    if ( NULL != printerInfo && NULL != aPrinter )
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+  * If we started a child process pass the signal on to it...
+  */
+
+  if (child_pid)
+  {
+   /*
+    * If we started a child process pass the signal on to it...
+    */
+
+    int        status;
+
+    kill(child_pid, sig);
+    while (waitpid(child_pid, &status, 0) < 0 && errno == EINTR);
+
+    if (WIFEXITED(status))
+      exit(WEXITSTATUS(status));
+    else if (status == SIGTERM || status == SIGKILL)
+      exit(0);
+    else
     {
-       printerInfo->location = aPrinter->location;
-       if ( NULL != (printerInfo->address.manufacturer = aPrinter->address.manufacturer) )
-           CFRetain( printerInfo->address.manufacturer );
-       if ( NULL != (printerInfo->address.product = aPrinter->address.product) )
-           CFRetain( printerInfo->address.product );
-       if ( NULL != (printerInfo->address.serial = aPrinter->address.serial) )
-           CFRetain( printerInfo->address.serial );
-       if ( NULL != (printerInfo->address.command = aPrinter->address.command) )
-           CFRetain( printerInfo->address.command );
-       if ( NULL != (printerInfo->bundle = aPrinter->bundle) )
-           CFRetain( printerInfo->bundle );
+      fprintf(stderr, "DEBUG: Child crashed on signal %d\n", status);
+      exit(CUPS_BACKEND_STOP);
     }
-    
-    return printerInfo;
+  }
+#endif /* __i386__ || __x86_64__ */
+}
+
+
+/*
+ * 'sigquit_handler()' - SIGQUIT handler.
+ */
+
+static void sigquit_handler(int sig, siginfo_t *si, void *unused)
+{
+  char  *path;
+  char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
+  static char msgbuf[256] = "";
+
+
+  (void)sig;
+  (void)unused;
+
+  if (proc_pidpath(si->si_pid, pathbuf, sizeof(pathbuf)) > 0 &&
+      (path = basename(pathbuf)) != NULL)
+    snprintf(msgbuf, sizeof(msgbuf), "SIGQUIT sent by %s(%d)", path, (int)si->si_pid);
+  else
+    snprintf(msgbuf, sizeof(msgbuf), "SIGQUIT sent by PID %d", (int)si->si_pid);
+
+  CRSetCrashLogMessage(msgbuf);
+
+  abort();
 }
 
-/*-----------------------------------------------------------------------------*
 
-       UsbRegistryOpen
+#ifdef PARSE_PS_ERRORS
+/*
+ * 'next_line()' - Find the next line in a buffer.
+ */
+
+static const char *next_line (const char *buffer)
+{
+  const char *cptr, *lptr = NULL;
+
+  for (cptr = buffer; *cptr && lptr == NULL; cptr++)
+    if (*cptr == '\n' || *cptr == '\r')
+      lptr = cptr;
+  return lptr;
+}
 
-       Desc:   opens the USB printer which matches the supplied printerAddress
 
-       In:     myContext->printerAddress   persistent name which identifies the printer
+/*
+ * 'parse_pserror()' - Scan the backchannel data for postscript errors.
+ */
 
-       Out:    myContext->usbDeviceRef     current IOKit address of this printer
-*-----------------------------------------------------------------------------*/
-kern_return_t
-UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result )
+static void parse_pserror(char *sockBuffer,
+                         int len)
 {
-    kern_return_t      kr = -1;    /* indeterminate failure */
-    CFMutableArrayRef  printers = UsbGetAllPrinters();
-    CFIndex            numPrinters = NULL != printers? CFArrayGetCount( printers): 0;
-    CFIndex            i;
+  static char  gErrorBuffer[1024] = "";
+  static char *gErrorBufferPtr = gErrorBuffer;
+  static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
+
+  char *pCommentBegin, *pCommentEnd, *pLineEnd;
+  char *logLevel;
+  char logstr[1024];
+  int  logstrlen;
+
+  if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
+    gErrorBufferPtr = gErrorBuffer;
+  if (len > sizeof(gErrorBuffer) - 1)
+    len = sizeof(gErrorBuffer) - 1;
+
+  memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
+  gErrorBufferPtr += len;
+  *(gErrorBufferPtr + 1) = '\0';
+
+  pLineEnd = (char *)next_line((const char *)gErrorBuffer);
+  while (pLineEnd != NULL)
+  {
+    *pLineEnd++ = '\0';
 
-    *result = NULL; /* nothing matched */
-    for ( i = 0; i < numPrinters; ++i )
+    pCommentBegin = strstr(gErrorBuffer,"%%[");
+    pCommentEnd = strstr(gErrorBuffer, "]%%");
+    if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL)
     {
-       USBPrinterInfo  *thisPrinter = (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i );
-       if (  NULL != thisPrinter && UsbSamePrinter( usbAddress, &thisPrinter->address ) ) 
-       {
-           *result = UsbCopyPrinter( thisPrinter );    /* retains reference */
-           if ( NULL != *result )
-           {
-               /* */
-               /*  if we can't find a bi-di interface, settle for a known uni-directional interface */
-               /* */
-               USBPrinterClassContext **printer = NULL;
-               /* */
-               /*  setup the default class driver */
-               /*  If one is specified, allow the vendor driver to override our default implementation */
-               /* */
-               kr = UsbLoadClassDriver( *result, kUSBPrinterClassInterfaceID, NULL );
-               if ( kIOReturnSuccess == kr && (*result)->bundle )
-                   kr = UsbLoadClassDriver( *result, kUSBPrinterClassInterfaceID, (*result)->bundle );
-               if ( kIOReturnSuccess == kr && NULL != (*result)->classdriver )
-               {
-                   printer = (*result)->classdriver;
-                   kr = (*printer)->Open( printer, (*result)->location, kUSBPrintingProtocolBidirectional );
-                   if ( kIOReturnSuccess != kr || NULL == (*printer)->interface )
-                       kr = (*printer)->Open( printer, (*result)->location, kUSBPrintingProtocolUnidirectional );
-                   /*  it's possible kIOReturnSuccess == kr && NULL == (*printer)->interface */
-                   /*      in the event that we can't open either Bidirectional or Unidirectional interface */
-                   if ( kIOReturnSuccess == kr )
-                   {
-                       if ( NULL == (*printer)->interface )
-                       {
-                           (*printer)->Close( printer );
-                           UsbReleasePrinter( *result );
-                           *result = NULL;
-                       }
-                   }
-               }
-           }
-           break;
-       }
+      pCommentEnd += 3;            /* Skip past "]%%" */
+      *pCommentEnd = '\0';         /* There's always room for the nul */
+
+      if (_cups_strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
+       logLevel = "DEBUG";
+      else if (_cups_strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
+       logLevel = "DEBUG";
+      else
+       logLevel = "INFO";
+
+      if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr))
+      {
+       /* If the string was trucnated make sure it has a linefeed before the nul */
+       logstrlen = sizeof(logstr) - 1;
+       logstr[logstrlen - 1] = '\n';
+      }
+      write(STDERR_FILENO, logstr, logstrlen);
     }
-    UsbReleaseAllPrinters( printers ); /* but, copied printer is retained */
-    DEBUG_ERR( kr, "UsbRegistryOpen return %x\n" );
 
-    return kr;
+    /* move everything over... */
+    strlcpy(gErrorBuffer, pLineEnd, sizeof(gErrorBuffer));
+    gErrorBufferPtr = gErrorBuffer;
+    pLineEnd = (char *)next_line((const char *)gErrorBuffer);
+  }
 }
+#endif /* PARSE_PS_ERRORS */
 
-/*!
- * @function   CreateEncodedCFString
- *
- * @abstract   Create an encoded version of the string parameter 
- *             so that it can be included in a URI.
- *
- * @param   string  A CFStringRef of the string to be encoded.
- * @result  An encoded CFString.
- *
- * @discussion This function will change all characters in string into URL acceptable format
- *             by encoding the text using the US-ASCII coded character set.  The following
- *             are invalid characters: the octets 00-1F, 7F, and 80-FF hex.  Also called out
- *             are the chars "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "[", "]", "`".
- *             The reserved characters for URL syntax are also to be encoded: (so don't pass
- *             in a full URL here!) ";", "/", "?", ":", "@", "=", "%", and "&".
+
+/*
+ * 'soft_reset()' - Send a soft reset to the device.
  */
-static CFStringRef CreateEncodedCFString(CFStringRef string)
+
+static void soft_reset(void)
 {
-    CFStringRef result = NULL;
-    char *bufferUTF8 = NULL;
-    char *bufferEncoded = NULL;
+  fd_set         input_set;            /* Input set for select() */
+  struct timeval  tv;                  /* Time value */
+  char           buffer[2048];         /* Buffer */
+  struct timespec cond_timeout;                /* pthread condition timeout */
 
-    if (string != NULL)
+ /*
+  * Send an abort once a second until the I/O lock is released by the main thread...
+  */
+
+  pthread_mutex_lock(&g.readwrite_lock_mutex);
+  while (g.readwrite_lock)
+  {
+    (*g.classdriver)->Abort(g.classdriver);
+
+    gettimeofday(&tv, NULL);
+    cond_timeout.tv_sec  = tv.tv_sec + 1;
+    cond_timeout.tv_nsec = tv.tv_usec * 1000;
+
+    while (g.readwrite_lock)
     {
-       CFIndex bufferSizeUTF8 = (3 * CFStringGetLength(string));
-       if ((bufferUTF8 = (char*)malloc(bufferSizeUTF8)) != NULL)
-       {
-           CFStringGetCString(string, bufferUTF8, bufferSizeUTF8, kCFStringEncodingUTF8);
-           {
-               UInt16 bufferSizeEncoded = (3 * strlen(bufferUTF8)) + 1;
-               if ((bufferEncoded = (char*)malloc(bufferSizeEncoded)) != NULL)
-               {
-                   addPercentEscapes(bufferUTF8, bufferEncoded, bufferSizeEncoded);
-                   result = CFStringCreateWithCString(kCFAllocatorDefault, bufferEncoded, kCFStringEncodingUTF8);
-               }
-           }
-       }
+      if (pthread_cond_timedwait(&g.readwrite_lock_cond,
+                                &g.readwrite_lock_mutex,
+                                &cond_timeout) != 0)
+       break;
     }
+  }
+
+  g.readwrite_lock = 1;
+  pthread_mutex_unlock(&g.readwrite_lock_mutex);
+
+ /*
+  * Flush bytes waiting on print_fd...
+  */
+
+  g.print_bytes = 0;
+
+  FD_ZERO(&input_set);
+  FD_SET(g.print_fd, &input_set);
+
+  tv.tv_sec  = 0;
+  tv.tv_usec = 0;
+
+  while (select(g.print_fd+1, &input_set, NULL, NULL, &tv) > 0)
+    if (read(g.print_fd, buffer, sizeof(buffer)) <= 0)
+      break;
+
+ /*
+  * Send the reset...
+  */
+
+  (*g.classdriver)->SoftReset(g.classdriver, DEFAULT_TIMEOUT);
 
-    if (bufferUTF8)    free(bufferUTF8);
-    if (bufferEncoded) free(bufferEncoded);
+ /*
+  * Release the I/O lock...
+  */
 
-    return result;
+  pthread_mutex_lock(&g.readwrite_lock_mutex);
+  g.readwrite_lock = 0;
+  pthread_cond_signal(&g.readwrite_lock_cond);
+  pthread_mutex_unlock(&g.readwrite_lock_mutex);
 }
 
+
+/*
+ * 'get_device_id()' - Return IEEE-1284 device ID.
+ */
+
+static void get_device_id(cups_sc_status_t *status,
+                         char *data,
+                         int *datalen)
+{
+  CFStringRef deviceIDString = NULL;
+
+  /* GetDeviceID */
+  copy_deviceid(g.classdriver, &deviceIDString);
+
+  if (deviceIDString)
+  {
+    CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);
+    *datalen = (int)strlen(data);
+    CFRelease(deviceIDString);
+  }
+  *status  = CUPS_SC_STATUS_OK;
+}
+
+
 /*
- * End of "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $".
+ * End of "$Id$".
  */