]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/usb-darwin.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / usb-darwin.c
index 093535054e474e4d9124be5d52458b0bd1e4efe0..4cde9129ca807f04da63bfb453571eae37b26196 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * "$Id$"
+ * "$Id: usb-darwin.c 6491 2007-04-30 18:21:52Z mike $"
  *
- * © Copyright 2005-2006 Apple Computer, Inc. All rights reserved.
+ * Copyright © 2005-2007 Apple Inc. All rights reserved.
  *
  * IMPORTANT:  This Apple software is supplied to you by Apple Computer,
  * Inc. ("Apple") in consideration of your agreement to the following
@@ -58,6 +58,8 @@
 #include <mach/mach_error.h>
 #include <mach/mach_time.h>
 #include <cups/debug.h>
+#include <cups/sidechannel.h>
+#include <cups/i18n.h>
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/usb/IOUSBLib.h>
@@ -65,6 +67,7 @@
 
 #include <pthread.h>
 
+
 /* 
  * WAITEOF_DELAY is number of seconds we'll wait for responses from
  * the printer after we've finished sending all the data 
@@ -86,6 +89,7 @@
 
 #define kUSBGenericTOPrinterClassDriver        CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
 #define kUSBPrinterClassDeviceNotOpen  -9664   /*kPMInvalidIOMContext*/
+#define kWriteBufferSize               2048
 
 
 #pragma mark -
@@ -182,7 +186,20 @@ typedef struct printer_data_s {                    /**** Printer context data ****/
 
   UInt32               location;
   Boolean              waitEOF;
-
+  
+  CFRunLoopTimerRef statusTimer;
+
+  pthread_cond_t       reqWaitCompCond;
+  pthread_mutex_t      reqWaitMutex;
+  pthread_mutex_t      waitCloseMutex;
+  pthread_mutex_t      writeCompMutex;
+  int                  writeDone;
+  int                  reqWaitDone;
+  int                  reqWqitFlag;
+  int                  directionalFlag;        /* 0=uni, 1=bidi */
+  ssize_t              dataSize;
+  ssize_t              dataOffset;
+  char                 dataBuffer[kWriteBufferSize];
 } printer_data_t;
 
 
@@ -192,21 +209,29 @@ typedef struct printer_data_s {                   /**** Printer context data ****/
 
 static Boolean list_device_callback(void *refcon, io_service_t obj);
 static Boolean find_device_callback(void *refcon, io_service_t obj);
+static void statusTimerCallback(CFRunLoopTimerRef timer, void *info);
 static void iterate_printers(iterator_callback_t callBack, void *userdata);
 static void device_added(void *userdata, io_iterator_t iterator);
 static void copy_deviceinfo(CFStringRef deviceIDString, CFStringRef *make, CFStringRef *model, CFStringRef *serial);
 static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial);
 static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t intf, classdriver_context_t ***driver);
 static kern_return_t unload_classdriver(classdriver_context_t ***classDriver);
-static kern_return_t load_printerdriver(printer_data_t *printer);
-static kern_return_t registry_open(printer_data_t *printer);
+static kern_return_t load_printerdriver(printer_data_t *printer, CFStringRef *driverBundlePath);
+static kern_return_t registry_open(printer_data_t *printer, CFStringRef *driverBundlePath);
 static kern_return_t registry_close(printer_data_t *printer);
 static OSStatus copy_deviceid(classdriver_context_t **printer, CFStringRef *deviceID);
 static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation);
 static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys);
+static CFStringRef cfstr_create_and_trim(const char *cstr);
 static void parse_options(const char *options, char *serial, UInt32 *location, Boolean *waitEOF);
 static void setup_cfLanguage(void);
 static void *read_thread(void *reference);
+static void *reqestWait_thread(void *reference);
+static void usbSoftReset(printer_data_t *userData, cups_sc_status_t *status);
+static void usbDrainOutput(printer_data_t *userData, cups_sc_status_t *status);
+static void usbGetBidirectional(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen);
+static void usbGetDeviceID(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen);
+static void usbGetDevState(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen);
 
 
 #if defined(__i386__)
@@ -255,6 +280,13 @@ print_device(const char *uri,              /* I - Device URI */
   int            countdown = INITIAL_LOG_INTERVAL;     /* Logging interval */
   pthread_cond_t  *readCompleteConditionPtr = NULL;    /* Read complete condition */
   pthread_mutex_t *readMutexPtr = NULL;                        /* Read mutex */
+  CFStringRef    driverBundlePath;                     /* Class driver path */
+  int             reqWait_create = 0;                  /* RequestWait thread created? */
+  pthread_t       reqWaitThread;                       /* RequestWait thread */
+  pthread_cond_t  *reqWaitCompCondPtr = NULL;          /* RequestWait complete condition */
+  pthread_mutex_t *reqWaitMutexPtr = NULL;             /* RequestWait mutex */
+  pthread_mutex_t *waitCloseMutexPtr = NULL;           /* wait close mutex */
+  pthread_mutex_t *writeCompMutexPtr = NULL;           /* write complete mutex */
 
   setup_cfLanguage();
   parse_options(options, serial, &printer_data.location, &printer_data.waitEOF);
@@ -263,9 +295,10 @@ print_device(const char *uri,              /* I - Device URI */
     resource++;
 
   printer_data.uri = uri;
-  printer_data.make   = CFStringCreateWithCString(NULL, hostname, kCFStringEncodingUTF8);
-  printer_data.model  = CFStringCreateWithCString(NULL, resource, kCFStringEncodingUTF8);
-  printer_data.serial = CFStringCreateWithCString(NULL, serial, kCFStringEncodingUTF8);
+  
+  printer_data.make   = cfstr_create_and_trim(hostname);
+  printer_data.model  = cfstr_create_and_trim(resource);
+  printer_data.serial = cfstr_create_and_trim(serial);
 
   fputs("STATE: +connecting-to-device\n", stderr);
 
@@ -277,11 +310,13 @@ print_device(const char *uri,             /* I - Device URI */
       printer_data.printerDriver = 0x0;
     }
 
-    fprintf(stderr, "INFO: Looking for '%s %s'\n", hostname, resource);
+    fprintf(stderr, "DEBUG: Looking for '%s %s'\n", hostname, resource);
     iterate_printers(find_device_callback, &printer_data);             
 
-    fprintf(stderr, "INFO: Opening Connection\n");
-    status = registry_open(&printer_data);
+    fputs("DEBUG: Opening connection\n", stderr);
+
+    driverBundlePath = NULL;
+    status = registry_open(&printer_data, &driverBundlePath);
 #if defined(__i386__)
     /*
      * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
@@ -292,12 +327,32 @@ print_device(const char *uri,             /* I - Device URI */
       /* Never returns here */
     }
 #endif /* __i386__ */
+    if (status ==  -2) {
+     /*
+      * If we still were unable to load the class drivers for this printer log
+      * the error and stop the queue...
+      */
+
+      if (driverBundlePath == NULL || !CFStringGetCString(driverBundlePath, buffer, sizeof(buffer), kCFStringEncodingUTF8))
+        strlcpy(buffer, "USB class driver", sizeof(buffer));
+
+      fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr);
+      fprintf(stderr, _("FATAL: Could not load %s\n"), buffer);
+
+      if (driverBundlePath)
+       CFRelease(driverBundlePath);
+
+      return CUPS_BACKEND_STOP;
+    }
+
+    if (driverBundlePath)
+      CFRelease(driverBundlePath);
 
     if (status != noErr) {
       sleep( PRINTER_POLLING_INTERVAL );
       countdown -= PRINTER_POLLING_INTERVAL;
       if ( countdown <= 0 ) {
-       fprintf(stderr, "INFO: Printer busy (status:0x%08x)\n", (int)status);
+       fprintf(stderr, _("INFO: Printer busy (status:0x%08x)\n"), (int)status);
        countdown = SUBSEQUENT_LOG_INTERVAL;    /* subsequent log entries, every 15 seconds */
       }
     }
@@ -333,23 +388,51 @@ print_device(const char *uri,             /* I - Device URI */
     if (pthread_mutex_init(&printer_data.readMutex, NULL) == 0)
       readMutexPtr = &printer_data.readMutex;
 
+    printer_data.done = 0;
+
     if (pthread_create(&thr, NULL, read_thread, &printer_data) == 0)
       thread_created = 1;
 
     if (thread_created == 0) 
-      fprintf(stderr, "WARNING: Couldn't create read channel\n");
+      fputs(_("WARNING: Couldn't create read channel\n"), stderr);
+
+    if (pthread_cond_init(&printer_data.reqWaitCompCond, NULL) == 0)   
+      reqWaitCompCondPtr = &printer_data.reqWaitCompCond;
+
+    if (pthread_mutex_init(&printer_data.reqWaitMutex, NULL) == 0)
+      reqWaitMutexPtr = &printer_data.reqWaitMutex;
+
+    printer_data.reqWaitDone = 0;
+    printer_data.reqWqitFlag = 0;
+
+    if (pthread_create(&reqWaitThread, NULL, reqestWait_thread, &printer_data) == 0)
+      reqWait_create = 1;
+
+    if (reqWait_create == 0) 
+      fputs(_("WARNING: Couldn't create sidechannel thread!\n"), stderr);
+
+    if (pthread_mutex_init(&printer_data.waitCloseMutex, NULL) == 0)
+      waitCloseMutexPtr = &printer_data.waitCloseMutex;
+
+    if (pthread_mutex_init(&printer_data.writeCompMutex, NULL) == 0)
+      writeCompMutexPtr = &printer_data.writeCompMutex;
   }
 
   /*
    * The main thread sends the print file...
    */
 
+  printer_data.writeDone = 0;
+  printer_data.dataSize = 0;
+  printer_data.dataOffset = 0;
+  pthread_mutex_lock(writeCompMutexPtr);
+
   while (status == noErr && copies-- > 0) {
     UInt32             wbytes;                 /* Number of bytes written */
     ssize_t            nbytes;                 /* Number of bytes read */
     off_t              tbytes = 0;             /* Total number of bytes written */
 
-    fprintf(stderr, "INFO: Sending data\n");
+    fputs(_("INFO: Sending data\n"), stderr);
 
     if (STDIN_FILENO != fd) {
       fputs("PAGE: 1 1", stderr);
@@ -361,11 +444,20 @@ print_device(const char *uri,             /* I - Device URI */
       tbytes += nbytes;
 
       while (nbytes > 0 && status == noErr) {
+       if (printer_data.writeDone) {
+         printer_data.dataSize = nbytes;
+         printer_data.dataOffset = bufptr - buffer;
+         memcpy(printer_data.dataBuffer, buffer, nbytes);
+
+         status = -1;
+         break;
+       }
+
        wbytes = nbytes;
        status = (*(printer_data.printerDriver))->WritePipe( printer_data.printerDriver, (UInt8*)bufptr, &wbytes, 0 /* nbytes > wbytes? 0: feof(fp) */ );
        if (wbytes < 0 || noErr != status) {
          OSStatus err = (*(printer_data.printerDriver))->Abort(printer_data.printerDriver);
-         fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n", status, err);
+         fprintf(stderr, _("ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n"), status, err);
          break;
        }
 
@@ -374,10 +466,13 @@ print_device(const char *uri,             /* I - Device URI */
       }
 
       if (fd != 0 && status == noErr)
-       fprintf(stderr, "DEBUG: Sending print file, %qd bytes...\n", (off_t)tbytes);
+       fprintf(stderr, "DEBUG: Sending print file, %lld bytes...\n", (off_t)tbytes);
     }
   }
 
+  printer_data.writeDone = 1;
+  pthread_mutex_unlock(writeCompMutexPtr);
+
   if (thread_created) {
     /* Signal the read thread that we are done... */
     printer_data.done = 1;
@@ -395,6 +490,35 @@ print_device(const char *uri,              /* I - Device URI */
     pthread_join( thr,NULL);                           /* wait for the child thread to return */
   }
 
+  if (reqWait_create) {
+    /* Signal the cupsSideChannelDoRequest wait thread that we are done... */
+    printer_data.reqWaitDone = 1;
+
+    /* 
+     * Give the cupsSideChannelDoRequest wait 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 reqWaitSleepUntil = { time(NULL) + WAITEOF_DELAY, 0 };
+    pthread_mutex_lock(&printer_data.reqWaitMutex);
+
+    while (!printer_data.reqWqitFlag) {
+      if (pthread_cond_timedwait(&printer_data.reqWaitCompCond,
+                                 &printer_data.reqWaitMutex,
+                                (const struct timespec *)&reqWaitSleepUntil) != 0) {
+       printer_data.waitEOF = false;
+       printer_data.reqWqitFlag = 1;
+      }
+    }
+    pthread_mutex_unlock(&printer_data.reqWaitMutex);
+    pthread_join(reqWaitThread,NULL);                  /* wait for the child thread to return */
+  }
+
+  /* interface close wait mutex(for softreset) */
+  pthread_mutex_lock(waitCloseMutexPtr);
+  pthread_mutex_unlock(waitCloseMutexPtr);
+
   /*
    * Close the connection and input file and general clean up...
    */
@@ -409,6 +533,18 @@ print_device(const char *uri,              /* I - Device URI */
   if (readMutexPtr != NULL)
     pthread_mutex_destroy(&printer_data.readMutex);
 
+  if (waitCloseMutexPtr != NULL)
+    pthread_mutex_destroy(&printer_data.waitCloseMutex);
+
+  if (writeCompMutexPtr != NULL)
+    pthread_mutex_destroy(&printer_data.writeCompMutex);
+
+  if (reqWaitCompCondPtr != NULL)
+    pthread_cond_destroy(&printer_data.reqWaitCompCond);
+
+  if (reqWaitMutexPtr != NULL)
+    pthread_mutex_destroy(&printer_data.reqWaitMutex);
+
   if (printer_data.make != NULL)
     CFRelease(printer_data.make);
 
@@ -440,26 +576,27 @@ static Boolean list_device_callback(void *refcon, io_service_t obj)
     copy_devicestring(obj, &deviceIDString, &deviceLocation);
     if (deviceIDString != NULL) {
       CFStringRef make = NULL,  model = NULL, serial = NULL;
-      char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024], optionsstr[1024];
-      char idstr[1024];
+      char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024];
+      char optionsstr[1024], idstr[1024];
 
       copy_deviceinfo(deviceIDString, &make, &model, &serial);
 
       modelstr[0] = '/';
 
-      CFStringGetCString(deviceIDString, idstr, sizeof(idstr),    kCFStringEncodingUTF8);
-      CFStringGetCString(make, makestr, sizeof(makestr),    kCFStringEncodingUTF8);
-      CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1, kCFStringEncodingUTF8);
-
-     /*
-      * Fix common HP 1284 bug...
-      */
+      CFStringGetCString(deviceIDString, idstr, sizeof(idstr),
+                         kCFStringEncodingUTF8);
 
-      if (!strcasecmp(makestr, "Hewlett-Packard"))
-        strcpy(makestr, "HP");
+      if (make)
+        CFStringGetCString(make, makestr, sizeof(makestr),
+                          kCFStringEncodingUTF8);
+      else
+        strcpy(makestr, "Unknown");
 
-      if (!strncasecmp(modelstr + 1, "hp ", 3))
-        _cups_strcpy(modelstr + 1, modelstr + 4);
+      if (model)
+       CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
+                          kCFStringEncodingUTF8);
+      else
+        strcpy(modelstr + 1, "Printer");
 
       optionsstr[0] = '\0';
       if (serial != NULL)
@@ -475,6 +612,16 @@ static Boolean list_device_callback(void *refcon, io_service_t obj)
       httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
       strncat(uristr, optionsstr, sizeof(uristr));
 
+     /*
+      * Fix common HP 1284 bug...
+      */
+
+      if (!strcasecmp(makestr, "Hewlett-Packard"))
+        strcpy(makestr, "HP");
+
+      if (!strncasecmp(modelstr + 1, "hp ", 3))
+        _cups_strcpy(modelstr + 1, modelstr + 4);
+
       printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr, makestr,
              &modelstr[1], makestr, &modelstr[1], idstr);
 
@@ -494,11 +641,11 @@ static Boolean list_device_callback(void *refcon, io_service_t obj)
 static Boolean find_device_callback(void *refcon, io_service_t obj)
 {
   Boolean keepLooking = true;
+  printer_data_t *userData = (printer_data_t *)refcon;
 
-  if (obj != 0x0 && refcon != NULL) {
+  if (obj != 0x0) {
     CFStringRef idString = NULL;
     UInt32 location = -1;
-    printer_data_t *userData = (printer_data_t *)refcon;
 
     copy_devicestring(obj, &idString, &location);
     if (idString != NULL) {
@@ -507,8 +654,8 @@ static Boolean find_device_callback(void *refcon, io_service_t obj)
       copy_deviceinfo(idString, &make, &model, &serial);
       if (CFStringCompare(make, userData->make, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
        if (CFStringCompare(model, userData->model, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
-         if (userData->serial != NULL) {
-           if (serial != NULL && CFStringCompare(model, userData->model, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+         if (userData->serial != NULL && CFStringGetLength(userData->serial) > 0 ) {
+           if (serial != NULL && CFStringCompare(serial, userData->serial, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
              IOObjectRetain(obj);
              userData->printerObj = obj;
              keepLooking = false;
@@ -533,12 +680,33 @@ static Boolean find_device_callback(void *refcon, io_service_t obj)
     }
   }
   else {               
-    keepLooking = (refcon != NULL && ((printer_data_t *)refcon)->printerObj == 0);
+    keepLooking = (userData->printerObj == 0);
+    if (obj == 0x0 && keepLooking) {
+      CFRunLoopTimerContext context = { 0, userData, NULL, NULL, NULL };
+      CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 10, 0x0, 0x0, statusTimerCallback, &context);
+      if (timer != NULL) {
+       CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+       userData->statusTimer = timer;
+      }
+    }
+  }
+  
+  if (!keepLooking && userData->statusTimer != NULL) {
+    fputs("STATE: -offline-error\n", stderr);
+    fputs(_("INFO: Printer is now on-line.\n"), stderr);
+    CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), userData->statusTimer, kCFRunLoopDefaultMode);
+    CFRelease(userData->statusTimer);
+    userData->statusTimer = NULL;
   }
 
   return keepLooking;
 }
 
+static void statusTimerCallback (CFRunLoopTimerRef timer, void *info)
+{
+  fputs("STATE: +offline-error\n", stderr);
+  fputs(_("INFO: Printer is currently off-line.\n"), stderr);
+}
 
 #pragma mark -
 /*
@@ -704,7 +872,7 @@ static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_
 #ifdef DEBUG
   char bundlestr[1024];
   CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
-  fprintf(stderr, "DEBUG:load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
+  fprintf(stderr, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
 #endif /* DEBUG */
 
   return kr;
@@ -728,28 +896,26 @@ static kern_return_t unload_classdriver(classdriver_context_t ***classDriver)
 
 /*
  * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
+ *
+ * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
  */
 
-static kern_return_t load_printerdriver(printer_data_t *printer)
+static kern_return_t load_printerdriver(printer_data_t *printer, CFStringRef *driverBundlePath)
 {
-  IOCFPlugInInterface **iodev = NULL;
-  SInt32 score;
+  IOCFPlugInInterface  **iodev = NULL;
+  SInt32               score;
+  kern_return_t                kr;
+  printer_interface_t  intf;
+  HRESULT              res;
 
-  kern_return_t kr = IOCreatePlugInInterfaceForService(printer->printerObj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
-  if (kr == kIOReturnSuccess) {
-    printer_interface_t intf;
-    HRESULT res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
-    if (res == noErr) {
-      CFMutableDictionaryRef properties = NULL;
+  kr = IOCreatePlugInInterfaceForService(printer->printerObj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
+  if (kr == kIOReturnSuccess)
+  {
+    if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf)) == noErr)
+    {
+      *driverBundlePath = IORegistryEntryCreateCFProperty(printer->printerObj, kUSBClassDriverProperty, NULL, kNilOptions);
 
-      kr = IORegistryEntryCreateCFProperties(printer->printerObj, &properties, NULL, kNilOptions);
-      if (kr == kIOReturnSuccess) {
-       CFStringRef driverBundlePath = NULL;
-       if (properties != NULL) {
-         driverBundlePath = (CFStringRef) CFDictionaryGetValue(properties, kUSBClassDriverProperty);
-       }
-       kr = load_classdriver(driverBundlePath, intf, &printer->printerDriver);
-      }
+      kr = load_classdriver(*driverBundlePath, intf, &printer->printerDriver);
 
       if (kr != kIOReturnSuccess)
        (*intf)->Release(intf);
@@ -764,9 +930,11 @@ static kern_return_t load_printerdriver(printer_data_t *printer)
  * 'registry_open()' - Open a connection to the printer.
  */
 
-static kern_return_t registry_open(printer_data_t *printer)
+static kern_return_t registry_open(printer_data_t *printer, CFStringRef *driverBundlePath)
 {
-  kern_return_t kr = load_printerdriver(printer);
+  printer->directionalFlag = 0;
+
+  kern_return_t kr = load_printerdriver(printer, driverBundlePath);
   if (kr != kIOReturnSuccess) {
     kr = -2;
   }
@@ -781,6 +949,8 @@ static kern_return_t registry_open(printer_data_t *printer)
          kr = -1;
        }
       }
+    } else {
+      printer->directionalFlag = 1;
     }
   }
 
@@ -886,40 +1056,37 @@ static OSStatus copy_deviceid(classdriver_context_t **printer, CFStringRef *devi
 
 static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation)
 {
-  IOCFPlugInInterface **iodev = NULL;
-  SInt32 score;
-
-  kern_return_t kr = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID, 
+  IOCFPlugInInterface  **iodev = NULL;
+  SInt32               score;
+  kern_return_t                kr;
+  printer_interface_t  intf;
+  HRESULT              res;
+  classdriver_context_t        **klassDriver = NULL;
+  CFStringRef          driverBundlePath;
+
+  kr = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID, 
                                                       kIOCFPlugInInterfaceID, &iodev, &score);
-  if (kr == kIOReturnSuccess) {
-    printer_interface_t intf;
-
-    HRESULT res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
-    if (res == noErr) {
+  if (kr == kIOReturnSuccess)
+  {
+    if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf)) == noErr)
+    {
       /* ignore the result for location id... */
       (void)(*intf)->GetLocationID(intf, deviceLocation);
 
-      CFMutableDictionaryRef properties = NULL;
-      kr = IORegistryEntryCreateCFProperties(usbInterface, &properties, NULL, kNilOptions);
-      if (kIOReturnSuccess == kr) {
-       classdriver_context_t **klassDriver = NULL;
-       CFStringRef driverBundlePath = NULL;
+      driverBundlePath = IORegistryEntryCreateCFProperty( usbInterface, kUSBClassDriverProperty, NULL, kNilOptions );
 
-       if (properties != NULL) {
-         driverBundlePath = (CFStringRef) CFDictionaryGetValue(properties, kUSBClassDriverProperty);
-       }
+      kr = load_classdriver(driverBundlePath, intf, &klassDriver);
+
+      if (kr != kIOReturnSuccess && driverBundlePath != NULL)
+       kr = load_classdriver(NULL, intf, &klassDriver);
 
-       kr = load_classdriver(driverBundlePath, intf, &klassDriver);
-       if (kr != kIOReturnSuccess && driverBundlePath != NULL)
-         kr = load_classdriver(NULL, intf, &klassDriver);
-       if (kr == kIOReturnSuccess && klassDriver != NULL) {                            
+      if (kr == kIOReturnSuccess && klassDriver != NULL)                       
          kr = copy_deviceid(klassDriver, deviceID);                                            
-       }
-       unload_classdriver(&klassDriver);
 
-       if (properties != NULL)
-         CFRelease(properties);
-      }
+      unload_classdriver(&klassDriver);
+
+      if (driverBundlePath != NULL)
+       CFRelease(driverBundlePath);
 
       /* (*intf)->Release(intf); */
     }          
@@ -975,6 +1142,27 @@ static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys)
 }
 
 
+/*
+ * 'cfstr_create_and_trim()' - Create a CFString from a c-string and 
+ *                            trim it's whitespace characters.
+ */
+
+CFStringRef cfstr_create_and_trim(const char *cstr)
+{
+  CFStringRef          cfstr;
+  CFMutableStringRef   cfmutablestr = NULL;
+  
+  if ((cfstr = CFStringCreateWithCString(NULL, cstr, kCFStringEncodingUTF8)) != NULL)
+  {
+    if ((cfmutablestr = CFStringCreateMutableCopy(NULL, 1024, cfstr)) != NULL)
+      CFStringTrimWhitespace(cfmutablestr);
+
+    CFRelease(cfstr);
+  }
+  return (CFStringRef) cfmutablestr;
+}
+
+
 #pragma mark -
 /*
  * 'parse_options()' - Parse uri options.
@@ -1036,7 +1224,7 @@ static void parse_options(const char *options, char *serial, UInt32 *location, B
        *waitEOF = false;
       }
       else {
-       fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value);
+       fprintf(stderr, _("WARNING: Boolean expected for waiteof option \"%s\"\n"), value);
       }
     }
     else if (strcasecmp(optionName, "serial") == 0) {
@@ -1081,7 +1269,7 @@ static void setup_cfLanguage(void)
     CFRelease(lang[0]);
     CFRelease(langArray);
   } else {
-    fprintf(stderr, "DEBUG: usb: LANG environment variable missing.\n");
+    fputs("DEBUG: usb: LANG environment variable missing.\n", stderr);
   }
 }
 
@@ -1183,7 +1371,7 @@ static void run_ppc_backend(int argc, char *argv[], int fd)
     }
   }
   else {
-    fprintf(stderr, "DEBUG: usb child running i386 again\n");
+    fputs("DEBUG: usb child running i386 again\n", stderr);
     exitstatus = ENOENT;
   }
 
@@ -1344,7 +1532,202 @@ static void *read_thread(void *reference)
   return NULL;
 }
 
+/*
+ * 'reqestWait_thread()' - A thread cupsSideChannelDoRequest wait.
+ */
+static void *reqestWait_thread(void *reference) {
+  printer_data_t *userData = (printer_data_t *)reference;
+  int datalen;
+  cups_sc_command_t command;
+  cups_sc_status_t status;
+  uint64_t start, delay;
+  struct mach_timebase_info timeBaseInfo;
+  char data[2048];
+
+  /*
+   * Calculate what 100 milliSeconds are in mach absolute time...
+   */
+  mach_timebase_info(&timeBaseInfo);
+  delay = ((uint64_t)100000000 * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer;
+
+  /* interface close wait mutex lock. */
+  pthread_mutex_lock(&(userData->waitCloseMutex));
+
+  do {
+    /* 
+     * Remember when we started so we can throttle the loop after the cupsSideChannelDoRequest call...
+     */
+    start = mach_absolute_time();
+
+    /* Poll for a command... */
+    command=0;
+    datalen = sizeof(data);
+    bzero(data, sizeof(data));
+
+    if (!cupsSideChannelRead(&command, &status, data, &datalen, 0.0)) {
+      datalen = sizeof(data);
+
+      switch (command) {
+       case CUPS_SC_CMD_SOFT_RESET:
+           /* do a soft reset */
+           usbSoftReset(userData, &status);
+           datalen = 0;
+           userData->reqWaitDone = 1;
+           break;
+       case CUPS_SC_CMD_DRAIN_OUTPUT:
+           /* drain all pending output */
+           usbDrainOutput(userData, &status);
+           datalen = 0;
+           break;
+       case CUPS_SC_CMD_GET_BIDI:
+           /* return whether the connection is bidirectional */
+           usbGetBidirectional(userData, &status, data, &datalen);
+           break;
+       case CUPS_SC_CMD_GET_DEVICE_ID:
+           /* return the IEEE-1284 device ID */
+           usbGetDeviceID(userData, &status, data, &datalen);
+           break;
+       case CUPS_SC_CMD_GET_STATE:
+           /* return the device state */
+           usbGetDevState(userData, &status, data, &datalen);
+           break;
+       default:
+           status  = CUPS_SC_STATUS_NOT_IMPLEMENTED;
+           datalen = 0;
+           break;
+      }
+
+      if (userData->writeDone) {
+        status = CUPS_SC_STATUS_NONE;
+      }
+
+      /* Send a response... */
+      cupsSideChannelWrite(command, status, data, datalen, 1.0);
+    }
+
+    /*
+     * Make sure this loop executes no more than once every 500 miliseconds...
+     */
+    if ((userData->waitEOF) || (!userData->reqWaitDone)) {
+      mach_wait_until(start + delay);
+    }
+  } while(!userData->reqWaitDone);
+
+  sleep(1);
+  pthread_mutex_lock(&userData->reqWaitMutex);
+  userData->reqWqitFlag = 1;
+  pthread_cond_signal(&userData->reqWaitCompCond);
+  pthread_mutex_unlock(&userData->reqWaitMutex);
+
+  /* interface close wait mutex unlock. */
+  pthread_mutex_unlock(&(userData->waitCloseMutex));
+
+  return NULL;
+}
+
+#pragma mark -
+/*
+ * 'usbSoftReset'
+ */
+static void usbSoftReset(printer_data_t *userData, cups_sc_status_t *status) {
+  OSStatus err;
+
+  /* write stop. */
+  userData->writeDone = 1;
+
+  /* Abort (print_device()-WritePipe kIOReturnAborted return) */
+  if (userData->printerDriver != NULL)
+    err = (*(userData->printerDriver))->Abort(userData->printerDriver);
+
+  /* print_device() WritePipe_Loop break wait. */
+  pthread_mutex_lock(&(userData->writeCompMutex));
+  pthread_mutex_unlock(&(userData->writeCompMutex));
+
+  /* SoftReset */
+  if (userData->printerDriver != NULL)
+    (*(userData->printerDriver))->SoftReset(userData->printerDriver, 0);
+
+  if (status != NULL)
+    *status  = CUPS_SC_STATUS_OK;
+}
+
+/*
+ * 'usbDrainOutput'
+ */
+static void usbDrainOutput(printer_data_t *userData, cups_sc_status_t *status) {
+  OSStatus osSts = noErr;      /* Function results */
+  OSStatus err = noErr;
+  UInt32  wbytes;                      /* Number of bytes written */
+  ssize_t nbytes;                      /* Number of bytes read */
+  char *bufptr;
+
+  bufptr = userData->dataBuffer+userData->dataOffset;
+  nbytes = userData->dataSize;
+
+  while((nbytes > 0) && (osSts == noErr)) {
+    wbytes = nbytes;
+    osSts = (*(userData->printerDriver))->WritePipe(userData->printerDriver, (UInt8*)bufptr, &wbytes, 0);
+
+    if (wbytes < 0 || noErr != osSts) {
+      if (osSts != kIOReturnAborted) {
+       err = (*(userData->printerDriver))->Abort(userData->printerDriver);
+       break;
+      }
+    }
+
+    nbytes -= wbytes;
+    bufptr += wbytes;
+  }
+
+  if (status != NULL) {
+    if ((osSts != noErr) || (err != noErr)) {
+      *status  = CUPS_SC_STATUS_IO_ERROR;
+    } else {
+      *status  = CUPS_SC_STATUS_OK;
+    }
+  }
+}
+
+/*
+ * 'usbGetBidirectional'
+ */
+static void usbGetBidirectional(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen) {
+  *data = userData->directionalFlag;
+  *datalen = 1;
+
+  if (status != NULL)
+    *status = CUPS_SC_STATUS_OK;
+}
+
+/*
+ * 'usbGetDeviceID'
+ */
+static void usbGetDeviceID(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen) {
+  UInt32 deviceLocation = 0;
+  CFStringRef deviceIDString = NULL;
+
+  /* GetDeviceID */
+  copy_devicestring(userData->printerObj, &deviceIDString, &deviceLocation);
+  CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);
+  *datalen = strlen(data);
+
+  if (status != NULL) {
+    *status  = CUPS_SC_STATUS_OK;
+  }
+}
+
+/*
+ * 'usbGetDevState'
+ */
+static void usbGetDevState(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen) {
+  *data = CUPS_SC_STATE_ONLINE;
+  *datalen = 1;
+
+  if (status != NULL) {
+    *status = CUPS_SC_STATUS_OK;
+  }
+}
 
 /*
- * End of "$Id$".
+ * End of "$Id: usb-darwin.c 6491 2007-04-30 18:21:52Z mike $".
  */