]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/usb-darwin.c
Fix the signal handlers in the dnssd and usb backends to only use async-safe
[thirdparty/cups.git] / backend / usb-darwin.c
index 302ae98168c6a60e0f0325fd208469fd36347bda..2bf9615c8df229af3f1acc2858d8930ce9506918 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * "$Id$"
  *
- * Copyright 2005-2014 Apple Inc. All rights reserved.
+ * Copyright 2005-2015 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
 #include <IOKit/usb/IOUSBLib.h>
 #include <IOKit/IOCFPlugIn.h>
 #include <libproc.h>
-
+#include <asl.h>
 #include <spawn.h>
 #include <pthread.h>
 
+/*
+ * Include necessary headers.
+ */
+
 extern char **environ;
 
 
@@ -91,7 +95,7 @@ extern char **environ;
 #define WAIT_SIDE_DELAY                        3
 #define DEFAULT_TIMEOUT                        5000L
 
-#define        USB_INTERFACE_KIND              CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
+#define        USB_INTERFACE_KIND              CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID245)
 #define kUSBLanguageEnglish            0x409
 
 #define PRINTER_POLLING_INTERVAL       5                       /* seconds */
@@ -134,8 +138,9 @@ struct crashreporter_annotations_t gCRAnnotations
 #define kUSBPrintingProtocolNoOpen             0
 #define kUSBPrintingProtocolUnidirectional     1
 #define kUSBPrintingProtocolBidirectional      2
+#define kUSBPrintingProtocolIPP                        4
 
-typedef IOUSBInterfaceInterface190     **printer_interface_t;
+typedef IOUSBInterfaceInterface245     **printer_interface_t;
 
 typedef struct iodevice_request_s      /**** Device request ****/
 {
@@ -198,7 +203,7 @@ typedef struct classdriver_s                /**** g.classdriver context ****/
 
 } classdriver_t;
 
-typedef Boolean (*iterator_callback_t)(void *refcon, io_service_t obj);
+typedef Boolean (*iterator_callback_t)(void *refcon, io_service_t obj, CFStringRef deviceIDString, UInt32 deviceLocation, UInt8 interfaceNum, UInt8 alternateSetting);
 
 typedef struct iterator_reference_s    /**** Iterator reference data */
 {
@@ -226,6 +231,7 @@ typedef struct globals_s
   CFStringRef          serial;
   UInt32               location;
   UInt8                        interfaceNum;
+  UInt8                        alternateSetting;
 
   CFRunLoopTimerRef    status_timer;
 
@@ -235,6 +241,7 @@ typedef struct globals_s
   ssize_t              debug_bytes;    /* Current bytes to read */
 #endif /* DEBUG_WRITES */
 
+  Boolean              use_generic_class_driver;
   Boolean              wait_eof;
   int                  drain_output;   /* Drain all pending output */
   int                  bidi_flag;      /* 0=unidirectional, 1=bidirectional */
@@ -258,8 +265,9 @@ int Iterating = 0;                  /* Are we iterating the bus? */
  * Local functions...
  */
 
-static Boolean find_device_cb(void *refcon, io_service_t obj);
-static Boolean list_device_cb(void *refcon, io_service_t obj);
+static Boolean find_device_cb(void *refcon, io_service_t obj, CFStringRef deviceIDString, UInt32 deviceLocation, UInt8 interfaceNum, UInt8 alternateSetting);
+static Boolean list_device_cb(void *refcon, io_service_t obj, CFStringRef deviceIDString, UInt32 deviceLocation, UInt8 interfaceNum, UInt8 alternateSetting);
+
 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);
@@ -267,19 +275,19 @@ 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);
+static void log_usb_class_driver(int is_64bit);
+#define IS_64BIT 1
+#define IS_NOT_64BIT 0
 
 #if defined(__i386__) || defined(__x86_64__)
 static pid_t   child_pid;              /* Child PID */
@@ -293,6 +301,14 @@ static const char *next_line (const char *buffer);
 static void parse_pserror (char *sockBuffer, int len);
 #endif /* PARSE_PS_ERRORS */
 
+static printer_interface_t usb_printer_interface_interface(io_service_t usbClass);
+static IOUSBDeviceInterface **usb_device_interface_for_device(io_service_t usbDevice);
+static CFStringRef copy_printer_interface_deviceid(printer_interface_t printer, UInt8 alternateSetting);
+static CFStringRef copy_printer_interface_indexed_description(printer_interface_t  printer, UInt8 index, UInt16 language);
+static CFStringRef deviceIDCopyManufacturer(CFStringRef deviceID);
+static CFStringRef deviceIDCopyModel(CFStringRef deviceID);
+static CFStringRef deviceIDCopySerialNumber(CFStringRef deviceID);
+
 #pragma mark -
 
 /*
@@ -448,6 +464,11 @@ print_device(const char *uri,              /* I - Device URI */
       return (CUPS_BACKEND_STOP);
     }
 
+#ifdef __x86_64__
+    if (status == noErr && driverBundlePath != NULL && CFStringCompare(driverBundlePath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
+      log_usb_class_driver(IS_64BIT);
+#endif /* __x86_64__ */
+
     if (driverBundlePath)
       CFRelease(driverBundlePath);
 
@@ -937,6 +958,27 @@ static void *read_thread(void *reference)
 
   } while (g.wait_eof || !g.read_thread_stop); /* Abort from main thread tests error here */
 
+  /* Workaround for usb race condition. <rdar://problem/21882551> */
+  if (!g.wait_eof && g.use_generic_class_driver)
+  {
+     const char *pdl = getenv("FINAL_CONTENT_TYPE");
+     if (pdl && strcmp(pdl, "application/vnd.cups-postscript") == 0)
+     {
+       while (readstatus == kIOReturnSuccess && ((rbytes > 0 && readbuffer[rbytes-1] != 0x4) || rbytes == 0))
+       {
+         start = mach_absolute_time();
+
+         rbytes = sizeof(readbuffer);
+         readstatus = (*g.classdriver)->ReadPipe(g.classdriver, readbuffer, &rbytes);
+         if (readstatus == kIOReturnSuccess && rbytes > 0 && readbuffer[rbytes-1] == 0x4)
+           break;
+
+         /* Make sure this loop executes no more than once every 250 miliseconds... */
+         mach_wait_until(start + delay);
+       }
+     }
+  }
+
  /*
   * Let the main thread know that we have completed the read thread...
   */
@@ -1076,228 +1118,276 @@ sidechannel_thread(void *reference)
  * 'iterate_printers()' - Iterate over all the printers.
  */
 
-static void iterate_printers(iterator_callback_t callBack,
-                            void *userdata)
+static void iterate_printers(iterator_callback_t callBack, void *userdata)
 {
-  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);
+    Iterating = 1;
 
-    int klass = kUSBPrintingClass;
-    int subklass = kUSBPrintingSubclass;
+    mach_port_t        masterPort = 0x0;
+    kern_return_t kr = IOMasterPort (bootstrap_port, &masterPort);
 
-    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);
-
-    CFRelease(usb_klass);
-    CFRelease(usb_subklass);
-
-    IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification, usbPrinterMatchDictionary, &device_added, &reference, &addIterator);
-    if (addIterator != 0x0)
+    if (kr == kIOReturnSuccess && masterPort != 0x0)
     {
-      device_added (&reference, addIterator);
-
-      if (reference.keepRunning)
-      {
-       CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode);
-       CFRunLoopRun();
-      }
-      IOObjectRelease(addIterator);
+        iterator_reference_t reference = { callBack, userdata, true };
+
+        IONotificationPortRef addNotification = IONotificationPortCreate(masterPort);
+        io_iterator_t addIterator = IO_OBJECT_NULL;
+
+        kr = IOServiceAddMatchingNotification(addNotification, kIOFirstMatchNotification, IOServiceMatching(kIOUSBDeviceClassName),
+                                              &device_added, &reference, &addIterator);
+        if (kr == kIOReturnSuccess && addIterator != IO_OBJECT_NULL)
+        {
+            device_added(&reference, addIterator);
+            if (reference.keepRunning)
+            {
+                CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode);
+                CFRunLoopRun();
+            }
+            IOObjectRelease(addIterator);
+        }
+        mach_port_deallocate(mach_task_self(), masterPort);
     }
-    mach_port_deallocate(mach_task_self(), masterPort);
-  }
 
-  Iterating = 0;
+    Iterating = 0;
 }
 
 
 /*
  * 'device_added()' - Device added notifier.
  */
+#define IsPrintingInterface(c,s,p) ((c) == kUSBPrintingInterfaceClass && (s) == kUSBPrintingSubclass && (p) != kUSBPrintingProtocolIPP)
 
-static void device_added(void *userdata,
-                        io_iterator_t iterator)
+static void device_added(void *userdata, io_iterator_t iterator)
 {
-  iterator_reference_t *reference = userdata;
+    iterator_reference_t *reference = userdata;
+    io_service_t device;
 
-  io_service_t obj;
-  while (reference->keepRunning && (obj = IOIteratorNext(iterator)) != 0x0)
-  {
-    if (reference->callback != NULL)
-      reference->keepRunning = reference->callback(reference->userdata, obj);
+    while (reference->keepRunning && (device = IOIteratorNext(iterator)) != 0x0)
+    {
+        UInt32 locationID = 0;
+        IOUSBDeviceInterface **devIntf = NULL;
+        io_iterator_t intfIterator = IO_OBJECT_NULL;
+        io_object_t intf = IO_OBJECT_NULL;
+
+        devIntf = usb_device_interface_for_device(device);
+        if (devIntf == NULL)
+            goto device_added_done;
+
+        UInt16 vendorUniqueID;
+        if ((*devIntf)->GetDeviceVendor(devIntf, &vendorUniqueID) != kIOReturnSuccess || vendorUniqueID == kAppleVendorID || vendorUniqueID == 0x0A5C)
+            goto device_added_done;
+
+        if ((*devIntf)->GetLocationID(devIntf, &locationID) != kIOReturnSuccess)
+            goto device_added_done;
+
+        IOUSBFindInterfaceRequest req = { kIOUSBFindInterfaceDontCare, kIOUSBFindInterfaceDontCare, kIOUSBFindInterfaceDontCare, kIOUSBFindInterfaceDontCare };
+        if ((*devIntf)->CreateInterfaceIterator(devIntf, &req, &intfIterator) != kIOReturnSuccess)
+            goto device_added_done;
+
+        while (reference->keepRunning && (intf = IOIteratorNext(intfIterator)))
+        {
+            printer_interface_t printerIntf = usb_printer_interface_interface(intf);
+            if (printerIntf != NULL)
+            {
+                UInt8 intfClass = 0, intfSubclass = 0, intfProtocol = 0, intfNumber = 0;
+
+                (*printerIntf)->GetInterfaceClass(printerIntf, &intfClass);
+                (*printerIntf)->GetInterfaceSubClass(printerIntf, &intfSubclass);
+                (*printerIntf)->GetInterfaceProtocol(printerIntf, &intfProtocol);
+                (*printerIntf)->GetInterfaceNumber(printerIntf, &intfNumber);
+
+                if (IsPrintingInterface(intfClass, intfSubclass, intfProtocol))
+                {
+                    CFStringRef deviceIDString = copy_printer_interface_deviceid(printerIntf, 0);
+                    if (deviceIDString != NULL)
+                    {
+                        reference->keepRunning = reference->callback(userdata, intf, deviceIDString, locationID, intfNumber, 0);
+                        CFRelease(deviceIDString);
+                    }
+                }
+
+                IOUSBInterfaceDescriptor *intfDesc = NULL;
+                while (reference->keepRunning && (intfDesc = (IOUSBInterfaceDescriptor *)(*printerIntf)->FindNextAssociatedDescriptor(printerIntf, intfDesc, kUSBInterfaceDesc)))
+                {
+                    intfClass = intfDesc->bInterfaceClass;
+                    intfSubclass = intfDesc->bInterfaceSubClass;
+                    intfProtocol = intfDesc->bInterfaceProtocol;
+
+                    if ((IsPrintingInterface(intfClass, intfSubclass, intfProtocol)))
+                    {
+                        CFStringRef deviceIDString = copy_printer_interface_deviceid(printerIntf, intfDesc->bAlternateSetting);
+                        if (deviceIDString != NULL)
+                        {
+                            reference->keepRunning = reference->callback(userdata, intf, deviceIDString, locationID, intfNumber, intfDesc->bAlternateSetting);
+                            CFRelease(deviceIDString);
+                        }
+                    }
+                }
+                (*printerIntf)->Release(printerIntf);
+            }
+            IOObjectRelease(intf);
+        }
 
-    IOObjectRelease(obj);
-  }
+        device_added_done:
+            if (devIntf != NULL) (*devIntf)->Release(devIntf);
+            if (intfIterator != IO_OBJECT_NULL) IOObjectRelease(intfIterator);
 
-  /* 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);
+        IOObjectRelease(device);
+    }
 
-  if (!reference->keepRunning)
-    CFRunLoopStop(CFRunLoopGetCurrent());
-}
+    /* 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, IO_OBJECT_NULL, NULL, 0, 0, 0);
 
+    if (!reference->keepRunning)
+        CFRunLoopStop(CFRunLoopGetCurrent());
+}
 
 /*
  * 'list_device_cb()' - list_device iterator callback.
  */
 
-static Boolean list_device_cb(void *refcon,
-                             io_service_t obj)
+static Boolean list_device_cb(void *refcon, io_service_t obj, CFStringRef deviceIDString, UInt32 deviceLocation, UInt8 interfaceNum, UInt8 alternateSetting)
 {
-  Boolean keepRunning = (obj != 0x0);
-
-
-  (void)refcon;
-
-  if (keepRunning)
-  {
-    CFStringRef deviceIDString = NULL;
-    UInt32 deviceLocation = 0;
-    UInt8      interfaceNum = 0;
+    (void)refcon;
+    (void)interfaceNum;
+    (void)alternateSetting;
 
-    copy_devicestring(obj, &deviceIDString, &deviceLocation, &interfaceNum);
-    if (deviceIDString != NULL)
+    if (obj != IO_OBJECT_NULL)
     {
-      CFStringRef make = NULL,  model = NULL, serial = NULL;
-      char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024];
-      char optionsstr[1024], idstr[1024], make_modelstr[1024];
+        CFStringRef make = deviceIDCopyManufacturer(deviceIDString);
+        CFStringRef model = deviceIDCopyModel(deviceIDString);
+        CFStringRef serial = deviceIDCopySerialNumber(deviceIDString);
 
-      copy_deviceinfo(deviceIDString, &make, &model, &serial);
-      CFStringGetCString(deviceIDString, idstr, sizeof(idstr),
-                         kCFStringEncodingUTF8);
-      backendGetMakeModel(idstr, make_modelstr, sizeof(make_modelstr));
+        char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024];
+        char optionsstr[1024], idstr[1024], make_modelstr[1024];
 
-      modelstr[0] = '/';
+        CFStringGetCString(deviceIDString, idstr, sizeof(idstr), kCFStringEncodingUTF8);
+        backendGetMakeModel(idstr, make_modelstr, sizeof(make_modelstr));
 
-      if (!make ||
-          !CFStringGetCString(make, makestr, sizeof(makestr),
-                             kCFStringEncodingUTF8))
-        strlcpy(makestr, "Unknown", sizeof(makestr));
+        modelstr[0] = '/';
 
-      if (!model ||
-          !CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
-                             kCFStringEncodingUTF8))
-        strlcpy(modelstr + 1, "Printer", sizeof(modelstr) - 1);
+        if (make  == NULL || !CFStringGetCString(make, makestr, sizeof(makestr), kCFStringEncodingUTF8))
+            strlcpy(makestr, "Unknown", sizeof(makestr));
 
-      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);
+        if (model == NULL || !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));
+        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);
+        cupsBackendReport("direct", uristr, make_modelstr, make_modelstr, idstr,
+                          NULL);
 
-      release_deviceinfo(&make, &model, &serial);
-      CFRelease(deviceIDString);
+        if (make != NULL) CFRelease(make);
+        if (model != NULL) CFRelease(model);
+        if (serial != NULL) CFRelease(serial);
     }
-  }
 
-  return keepRunning;
+    return obj != IO_OBJECT_NULL;
 }
 
-
 /*
  * 'find_device_cb()' - print_device iterator callback.
  */
-
-static Boolean find_device_cb(void *refcon,
-                             io_service_t obj)
+static Boolean find_device_cb(void *refcon, io_service_t obj, CFStringRef deviceIDString, UInt32 deviceLocation, UInt8 interfaceNum, UInt8 alternateSetting)
 {
-  Boolean keepLooking = true;
-
-  if (obj != 0x0)
-  {
-    CFStringRef idString = NULL;
-    UInt32 location = ~0U;
-    UInt8      interfaceNum = 0;
+    Boolean keepLooking = true;
 
-    copy_devicestring(obj, &idString, &location, &interfaceNum);
-    if (idString != NULL)
+    if (obj != IO_OBJECT_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)
-           {
-             IOObjectRetain(obj);
-             g.printer_obj = obj;
-             keepLooking = false;
-           }
-         }
-         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;
-       }
-      }
+        CFStringRef make = deviceIDCopyManufacturer(deviceIDString);
+        CFStringRef model = deviceIDCopyModel(deviceIDString);
+        CFStringRef serial = deviceIDCopySerialNumber(deviceIDString);
+
+        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)
+                    {
+                        IOObjectRetain(obj);
+                        g.printer_obj = obj;
+                        g.location = deviceLocation;
+                        g.alternateSetting = alternateSetting;
+                        keepLooking = false;
+                    }
+                }
+                else
+                {
+                    if (g.printer_obj != 0)
+                        IOObjectRelease(g.printer_obj);
+
+                    g.alternateSetting = alternateSetting;
+                    g.printer_obj = obj;
+                    IOObjectRetain(obj);
+
+                    if (g.location == 0 || g.location == deviceLocation)
+                        keepLooking = false;
+                }
+
+                if ( !keepLooking )
+                    g.interfaceNum = interfaceNum;
+            }
+        }
 
-      release_deviceinfo(&make, &model, &serial);
-      CFRelease(idString);
+        if (make) CFRelease(make);
+        if (model) CFRelease(model);
+        if (serial) CFRelease(serial);
     }
-  }
-  else
-  {
-    keepLooking = (g.printer_obj == 0);
-    if (obj == 0x0 && keepLooking)
+    else
     {
-      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;
-      }
+        keepLooking = (g.printer_obj == 0);
+        if (obj == IO_OBJECT_NULL && 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;
-  }
+    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;
+}
 
-  return keepLooking;
+static CFStringRef deviceIDCopySerialNumber(CFStringRef deviceID)
+{
+    CFStringRef serialKeys[] = { CFSTR("SN:"),  CFSTR("SERN:"), NULL };
+
+    return copy_value_for_key(deviceID, serialKeys);
+}
+
+static CFStringRef deviceIDCopyModel(CFStringRef deviceID)
+{
+    CFStringRef modelKeys[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL };
+    return copy_value_for_key(deviceID, modelKeys);
 }
 
+static CFStringRef deviceIDCopyManufacturer(CFStringRef deviceID)
+{
+    CFStringRef makeKeys[]   = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL };
+    return copy_value_for_key(deviceID, makeKeys);
+}
 
 /*
  * 'status_timer_cb()' - Status timer callback.
@@ -1330,59 +1420,6 @@ static void status_timer_cb(CFRunLoopTimerRef timer,
 }
 
 
-#pragma mark -
-/*
- * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
- */
-
-static void copy_deviceinfo(CFStringRef deviceIDString,
-                           CFStringRef *make,
-                           CFStringRef *model,
-                           CFStringRef *serial)
-{
-  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.
@@ -1504,6 +1541,7 @@ static kern_return_t load_printerdriver(CFStringRef *driverBundlePath)
     {
       *driverBundlePath = IORegistryEntryCreateCFProperty(g.printer_obj, kUSBClassDriverProperty, NULL, kNilOptions);
 
+      g.use_generic_class_driver = (*driverBundlePath == NULL || (CFStringCompare(*driverBundlePath, kUSBGenericTOPrinterClassDriver, 0x0) == kCFCompareEqualTo));
       kr = load_classdriver(*driverBundlePath, interface, &g.classdriver);
 
       if (kr != kIOReturnSuccess)
@@ -1514,6 +1552,301 @@ static kern_return_t load_printerdriver(CFStringRef *driverBundlePath)
   return kr;
 }
 
+static printer_interface_t usb_printer_interface_interface(io_service_t usbClass)
+{
+       printer_interface_t  intf = NULL;
+       IOCFPlugInInterface **plugin = NULL;
+       SInt32  score;
+       int kr = IOCreatePlugInInterfaceForService(usbClass, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score);
+       if (kr == kIOReturnSuccess)
+       {
+               (*plugin)->QueryInterface(plugin, USB_INTERFACE_KIND, (LPVOID *)&intf);
+               IODestroyPlugInInterface(plugin);
+       }
+
+       return intf;
+}
+
+static IOUSBDeviceInterface **usb_device_interface_for_device(io_service_t usbDevice)
+{
+       IOUSBDeviceInterface ** intf = NULL;
+       IOCFPlugInInterface **plugin = NULL;
+       SInt32  score;
+
+       int kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score);
+       if (kr == kIOReturnSuccess)
+       {
+               (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID *)&intf);
+               IODestroyPlugInInterface(plugin);
+       }
+
+       return intf;
+}
+
+
+static CFStringRef copy_printer_interface_deviceid(printer_interface_t printer, UInt8 alternateSetting)
+{
+       // I have tried to make this function as neat as I can, but the possibility of needing to resend
+       // a request to get the entire string makes it hideous...
+       //
+       // We package the job of sending a request up into the block (^sendRequest), which takes the size
+       // it should allocate for the message buffer. It frees the current buffer if one is set and
+       // allocates one of the specified size, then performs the request. We can then easily retry by
+       // calling the block again if we fail to get the whole string the first time around.
+
+       #define kUSBPrintClassGetDeviceID           0
+       #define kDefaultNoDataTimeout               5000L
+       #define pack_device_id_wIndex(intf, alt)  ((UInt16)((((UInt16)(intf)) << 8) | ((UInt8)(alt))))
+
+       if (printer == NULL)
+                       return NULL;
+
+
+       IOReturn err        = kIOReturnError;
+       UInt8    configurationIndex     = 0;
+       UInt8    interfaceNumber        = 0;
+       size_t   bufferLength           = 256;
+       CFStringRef ret             = NULL;
+
+       if ((*printer)->GetConfigurationValue( printer, &configurationIndex) == kIOReturnSuccess &&
+                       (*printer)->GetInterfaceNumber( printer, &interfaceNumber) == kIOReturnSuccess)
+       {
+               __block IOUSBDevRequestTO       request;
+               IOReturn (^sendRequest)(size_t) = ^ (size_t size)
+               {
+                       if (request.pData)
+                       {
+                               free(request.pData);
+                               request.wLength = 0;
+                               request.pData = NULL;
+                       }
+
+                       IOReturn berr = kIOReturnError;
+                       char *buffer = malloc(size);
+                       if (buffer == NULL)
+                               return kIOReturnNoMemory;
+
+                       request.wLength = HostToUSBWord(size);
+                       request.pData = buffer;
+                       berr = (*printer)->ControlRequestTO(printer, (UInt8)0, &request);
+                       return berr;
+               };
+
+               /* This request takes the 0 based configuration index. IOKit returns a 1 based configuration index */
+               configurationIndex -= 1;
+
+               bzero(&request, sizeof(request));
+
+               request.bmRequestType           = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBInterface);
+               request.bRequest                        = kUSBPrintClassGetDeviceID;
+               request.wValue                          = HostToUSBWord(configurationIndex);
+               request.wIndex                          = HostToUSBWord(pack_device_id_wIndex(interfaceNumber, alternateSetting));
+               request.noDataTimeout           = kDefaultNoDataTimeout;
+               request.completionTimeout       = 0; // Copying behavior from Generic Class Driver
+
+               err = sendRequest(bufferLength);
+
+               if (err == kIOReturnSuccess && request.wLenDone > 1)
+               {
+                       UInt16 actualLength = OSSwapBigToHostInt16(*((UInt16 *)request.pData));
+
+                       if (actualLength > 2 && actualLength <= bufferLength - 2)
+                       {
+                               ret = CFStringCreateWithBytes(NULL, (const UInt8 *) &request.pData[2], actualLength - 2, kCFStringEncodingUTF8, false);
+                       }
+                       else {
+                               err = sendRequest(actualLength);
+                               if (err == kIOReturnSuccess && request.wLenDone > 0)
+                               {
+                                       actualLength = OSSwapBigToHostInt16(*((UInt16 *)request.pData));
+                                       ret = CFStringCreateWithBytes(NULL, (const UInt8 *) &request.pData[2], actualLength - 2, kCFStringEncodingUTF8, false);
+                               }
+                       }
+               }
+
+               if (request.pData)
+                       free(request.pData);
+       }
+
+       CFStringRef manufacturer = deviceIDCopyManufacturer(ret);
+       CFStringRef model = deviceIDCopyModel(ret);
+       CFStringRef serial = deviceIDCopySerialNumber(ret);
+
+       if (manufacturer == NULL || serial == NULL || model == NULL)
+       {
+               IOUSBDevRequestTO               request;
+               IOUSBDeviceDescriptor   desc;
+
+               bzero(&request, sizeof(request));
+
+               request.bmRequestType = USBmakebmRequestType( kUSBIn,  kUSBStandard, kUSBDevice );
+               request.bRequest = kUSBRqGetDescriptor;
+               request.wValue = kUSBDeviceDesc << 8;
+               request.wIndex = 0;
+               request.wLength = sizeof(desc);
+               request.pData = &desc;
+               request.completionTimeout = 0;
+               request.noDataTimeout = 60L;
+
+               err = (*printer)->ControlRequestTO(printer, 0, &request);
+               if (err == kIOReturnSuccess)
+               {
+                       CFMutableStringRef extras = CFStringCreateMutable(NULL, 0);
+                       if (manufacturer == NULL)
+                       {
+                               manufacturer = copy_printer_interface_indexed_description(printer, desc.iManufacturer, kUSBLanguageEnglish);
+                               if (manufacturer && CFStringGetLength(manufacturer) > 0)
+                                       CFStringAppendFormat(extras, NULL, CFSTR("MFG:%@;"), manufacturer);
+                       }
+
+                       if (model == NULL)
+                       {
+                               model = copy_printer_interface_indexed_description(printer, desc.iProduct, kUSBLanguageEnglish);
+                               if (model && CFStringGetLength(model) > 0)
+                                       CFStringAppendFormat(extras, NULL, CFSTR("MDL:%@;"), model);
+                       }
+
+                       if (serial == NULL && desc.iSerialNumber != 0)
+                       {
+                               serial = copy_printer_interface_indexed_description(printer, desc.iSerialNumber, kUSBLanguageEnglish);
+                               if (serial && CFStringGetLength(serial) > 0)
+                                       CFStringAppendFormat(extras, NULL, CFSTR("SERN:%@;"), serial);
+                       }
+
+                       if (ret != NULL)
+                       {
+                               CFStringAppend(extras, ret);
+                               CFRelease(ret);
+
+                               ret = extras;
+                       }
+                       else
+                       {
+                               ret = extras;
+                       }
+               }
+       }
+
+       if (ret != NULL)
+       {
+       /* Remove special characters from the serial number */
+       CFRange range = (serial != NULL ? CFStringFind(serial, CFSTR("+"), 0) : CFRangeMake(0, 0));
+       if (range.length == 1)
+       {
+               range = CFStringFind(ret, serial, 0);
+
+               CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(NULL, 0, ret);
+               CFRelease(ret);
+
+               ret = deviceIDString;
+               CFStringFindAndReplace(deviceIDString, CFSTR("+"), CFSTR(""), range, 0);
+       }
+       }
+
+       if (manufacturer != NULL)
+               CFRelease(manufacturer);
+
+       if (model != NULL)
+               CFRelease(model);
+
+       if (serial != NULL)
+               CFRelease(serial);
+
+       if (ret != NULL && CFStringGetLength(ret) == 0)
+       {
+               CFRelease(ret);
+               return NULL;
+       }
+
+       return ret;
+}
+
+static CFStringRef copy_printer_interface_indexed_description(printer_interface_t  printer, UInt8 index, UInt16 language)
+{
+       IOReturn err;
+       UInt8 description[256]; // Max possible descriptor length
+       IOUSBDevRequestTO       request;
+
+       bzero(description, 2);
+
+       request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+       request.bRequest = kUSBRqGetDescriptor;
+       request.wValue = (kUSBStringDesc << 8) | index;
+       request.wIndex = language;
+       request.wLength = 2;
+       request.pData = &description;
+       request.completionTimeout = 0;
+       request.noDataTimeout = 60L;
+
+       err = (*printer)->ControlRequestTO(printer, 0, &request);
+       if (err != kIOReturnSuccess && err != kIOReturnOverrun)
+       {
+               bzero(description, request.wLength);
+
+               // Let's try again full length. Here's why:
+               //      On USB 2.0 controllers, we will not get an overrun error.  We just get a "babble" error
+               //      and no valid data.  So, if we ask for the max size, we will either get it, or we'll get an underrun.
+               //      It looks like we get it w/out an underrun
+
+               request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+               request.bRequest = kUSBRqGetDescriptor;
+               request.wValue = (kUSBStringDesc << 8) | index;
+               request.wIndex = language;
+               request.wLength = sizeof description;
+               request.pData = &description;
+               request.completionTimeout = 0;
+               request.noDataTimeout = 60L;
+
+               err = (*printer)->ControlRequestTO(printer, 0, &request);
+               if (err != kIOReturnSuccess && err != kIOReturnUnderrun)
+                       return NULL;
+       }
+
+       unsigned int length = description[0];
+       if (length == 0)
+               return CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+
+       if (description[1] != kUSBStringDesc)
+               return NULL;
+
+       request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+       request.bRequest = kUSBRqGetDescriptor;
+       request.wValue = (kUSBStringDesc << 8) | index;
+       request.wIndex = language;
+
+       bzero(description, length);
+       request.wLength = (UInt16)length;
+       request.pData = &description;
+       request.completionTimeout = 0;
+       request.noDataTimeout = 60L;
+
+       err = (*printer)->ControlRequestTO(printer, 0, &request);
+       if (err != kIOReturnSuccess)
+               return NULL;
+
+       if (description[1] != kUSBStringDesc)
+               return NULL;
+
+       if ((description[0] & 1) != 0)
+               description[0] &= 0xfe;
+
+       char buffer[258] = {};
+       unsigned int maxLength = sizeof buffer;
+       if (description[0] > 1)
+       {
+               length = (description[0]-2)/2;
+
+               if (length > maxLength - 1)
+                       length = maxLength -1;
+
+               for (unsigned i = 0; i < length; i++)
+                       buffer[i] = (char) description[2*i+2];
+
+               buffer[length] = 0;
+       }
+
+       return CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
+}
 
 /*
  * 'registry_open()' - Open a connection to the printer.
@@ -1567,158 +1900,6 @@ static kern_return_t registry_close(void)
   return kIOReturnSuccess;
 }
 
-
-/*
- * 'copy_deviceid()' - Copy the 1284 device id string.
- */
-
-static OSStatus copy_deviceid(classdriver_t **classdriver,
-                             CFStringRef *deviceID)
-{
-  CFStringRef devID = NULL;
-  CFStringRef deviceMake = NULL;
-  CFStringRef deviceModel = NULL;
-  CFStringRef deviceSerial = NULL;
-
-  *deviceID = NULL;
-
-  OSStatus err = (*classdriver)->GetDeviceID(classdriver, &devID, DEFAULT_TIMEOUT);
-
-  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);
-
-      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)
-       {
-         CFStringAppendFormat(newDevID, NULL, CFSTR("MDL:%@;"), data);
-         CFRelease(data);
-       }
-      }
-
-      if (deviceSerial == NULL && desc.iSerialNumber != 0)
-      {
-       err = (*classdriver)->GetString(classdriver, desc.iSerialNumber, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &deviceSerial);
-       if (deviceSerial != NULL)
-       {
-         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)
-    {
-      (*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);
-  }
-}
-
-
 #pragma mark -
 /*
  * 'copy_value_for_key()' - Copy value string associated with a key.
@@ -1949,6 +2130,8 @@ static void run_legacy_backend(int argc,
 
   if (!usb_legacy_status)
   {
+    log_usb_class_driver(IS_NOT_64BIT);
+
    /*
     * Setup a SIGTERM handler then block it before forking...
     */
@@ -2102,13 +2285,13 @@ sigterm_handler(int sig)                /* I - Signal */
     while (waitpid(child_pid, &status, 0) < 0 && errno == EINTR);
 
     if (WIFEXITED(status))
-      exit(WEXITSTATUS(status));
+      _exit(WEXITSTATUS(status));
     else if (status == SIGTERM || status == SIGKILL)
-      exit(0);
+      _exit(0);
     else
     {
-      fprintf(stderr, "DEBUG: Child crashed on signal %d\n", status);
-      exit(CUPS_BACKEND_STOP);
+      write(2, "DEBUG: Child crashed.\n", 22);
+      _exit(CUPS_BACKEND_STOP);
     }
   }
 #endif /* __i386__ || __x86_64__ */
@@ -2298,19 +2481,61 @@ static void get_device_id(cups_sc_status_t *status,
 {
   CFStringRef deviceIDString = NULL;
 
-  /* GetDeviceID */
-  copy_deviceid(g.classdriver, &deviceIDString);
+  if (g.printer_obj != IO_OBJECT_NULL)
+  {
+    printer_interface_t printerIntf = usb_printer_interface_interface(g.printer_obj);
+    if (printerIntf)
+    {
+      deviceIDString = copy_printer_interface_deviceid(printerIntf, g.alternateSetting);
+      (*printerIntf)->Release(printerIntf);
+    }
+  }
+
 
   if (deviceIDString)
   {
-    CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);
-    *datalen = (int)strlen(data);
+    if (CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8))
+      *datalen = (int)strlen(data);
+    else
+      *datalen = 0;
+
     CFRelease(deviceIDString);
   }
+  else
+  {
+    *datalen = 0;
+  }
+
   *status  = CUPS_SC_STATUS_OK;
 }
 
 
+static void
+log_usb_class_driver(int is_64bit)     /* I - Is the USB class driver 64-bit? */
+{
+ /*
+  * Report the usage of legacy USB class drivers to Apple if the user opts into providing
+  * feedback to Apple...
+  */
+
+  aslmsg aslm = asl_new(ASL_TYPE_MSG);
+  if (aslm)
+  {
+    ppd_file_t *ppd = ppdOpenFile(getenv("PPD"));
+    const char *make_model = ppd ? ppd->nickname : NULL;
+    ppd_attr_t *version = ppdFindAttr(ppd, "FileVersion", "");
+
+    asl_set(aslm, "com.apple.message.domain", "com.apple.printing.usb.64bit");
+    asl_set(aslm, "com.apple.message.result", is_64bit ? "yes" : "no");
+    asl_set(aslm, "com.apple.message.signature", make_model ? make_model : "Unknown");
+    asl_set(aslm, "com.apple.message.signature2", version ? version->value : "?.?");
+    asl_set(aslm, "com.apple.message.summarize", "YES");
+    asl_log(NULL, aslm, ASL_LEVEL_NOTICE, "");
+    asl_free(aslm);
+  }
+}
+
+
 /*
  * End of "$Id$".
  */