/*
* "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $"
*
-* Copyright 2005-2009 Apple Inc. All rights reserved.
+* Copyright 2005-2010 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
*
* Contents:
*
-* list_devices() - List all USB devices.
-* print_device() - Print a file to a USB device.
-* sidechannel_thread() - Thread to handle side-channel requests.
-* read_thread() - Thread to read the backchannel data on.
-* list_device_cb() - list_device iterator callback.
-* find_device_cb() - print_device iterator callback.
-* status_timer_cb() - Status timer callback.
-* iterate_printers() - Iterate over all the printers.
-* device_added() - Device added notifier.
-* copy_deviceinfo() - Copy strings from the 1284 device ID.
-* release_deviceinfo() - Release deviceinfo strings.
-* load_classdriver() - Load a classdriver.
-* unload_classdriver() - Unload a classdriver.
-* load_printerdriver() - Load vendor's classdriver.
-* registry_open() - Open a connection to the printer.
-* registry_close() - Close the connection to the printer.
-* copy_deviceid() - Copy the 1284 device id string.
-* copy_devicestring() - Copy the 1284 device id string.
-* copy_value_for_key() - Copy value string associated with a key.
-* cfstr_create_trim() - Create CFString and trim whitespace characters.
-* parse_options() - Parse uri options.
-* setup_cfLanguage() - Create AppleLanguages array from LANG environment var.
-* run_legacy_backend() - Re-exec backend as ppc or i386.
-* sigterm_handler() - SIGTERM handler.
-* next_line() - Find the next line in a buffer.
-* parse_pserror() - Scan the backchannel data for postscript errors.
-* get_device_id() - Return IEEE-1284 device ID.
+ * list_devices() - List all USB devices.
+ * print_device() - Print a file to a USB device.
+ * read_thread() - Thread to read the backchannel data on.
+ * sidechannel_thread() - Handle side-channel requests.
+ * iterate_printers() - Iterate over all the printers.
+ * device_added() - Device added notifier.
+ * list_device_cb() - list_device iterator callback.
+ * find_device_cb() - print_device iterator callback.
+ * status_timer_cb() - Status timer callback.
+ * copy_deviceinfo() - Copy strings from the 1284 device ID.
+ * release_deviceinfo() - Release deviceinfo strings.
+ * load_classdriver() - Load a classdriver.
+ * unload_classdriver() - Unload a classdriver.
+ * load_printerdriver() - Load vendor's classdriver.
+ * registry_open() - Open a connection to the printer.
+ * registry_close() - Close the connection to the printer.
+ * copy_deviceid() - Copy the 1284 device id string.
+ * copy_devicestring() - Copy the 1284 device id string.
+ * copy_value_for_key() - Copy value string associated with a key.
+ * cfstr_create_trim() - Create CFString and trim whitespace characters.
+ * parse_options() - Parse URI options.
+ * sigterm_handler() - SIGTERM handler.
+ * next_line() - Find the next line in a buffer.
+ * parse_pserror() - Scan the backchannel data for postscript errors.
+ * soft_reset() - Send a soft reset to the device.
+ * get_device_id() - Return IEEE-1284 device ID.
*/
/*
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
-#include <cups/debug.h>
+#include <cups/debug-private.h>
#include <cups/sidechannel.h>
-#include <cups/i18n.h>
+#include <cups/language-private.h>
#include "backend-private.h"
#include <CoreFoundation/CoreFoundation.h>
extern char **environ;
+/*
+ * 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 DEBUG_WRITES 0
+
/*
* WAIT_EOF_DELAY is number of seconds we'll wait for responses from
* the printer after we've finished sending all the data
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 wait_eof;
int drain_output; /* Drain all pending output */
static pid_t child_pid; /* Child PID */
static void run_legacy_backend(int argc, char *argv[], int fd); /* Starts child backend process running as a ppc executable */
#endif /* __i386__ || __x86_64__ */
-static int job_canceled = 0; /* Was the job canceled? */
static void sigterm_handler(int sig); /* SIGTERM handler */
#ifdef PARSE_PS_ERRORS
{
char serial[1024]; /* Serial number buffer */
OSStatus status; /* Function results */
- IOReturn iostatus, /* Current IO status */
- prev_iostatus = 0; /* Previous IO status */
+ 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? */
if (!g.make || !g.model)
{
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
+ fprintf(stderr, "DEBUG: Fatal USB error.\n");
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("There was an unrecoverable USB error."));
if (!g.make)
- fputs("DEBUG: USB make string is NULL!\n", stderr);
+ fputs("DEBUG: USB make string is NULL\n", stderr);
if (!g.model)
- fputs("DEBUG: USB model string is NULL!\n", stderr);
+ fputs("DEBUG: USB model string is NULL\n", stderr);
return (CUPS_BACKEND_STOP);
}
strlcpy(print_buffer, "USB class driver", sizeof(print_buffer));
fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr);
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("There was an unrecoverable USB error."));
fprintf(stderr, "DEBUG: Could not load %s\n", print_buffer);
if (driverBundlePath)
countdown -= PRINTER_POLLING_INTERVAL;
if (countdown <= 0)
{
- _cupsLangPuts(stderr,
- _("INFO: Waiting for printer to become available...\n"));
+ _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 */
}
fputs("STATE: -connecting-to-device\n", stderr);
/*
- * Now that we are "connected" to the port, catch SIGTERM so that we
+ * 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 catch SIGTERM if we are printing data from
+ * current page... Only ignore SIGTERM if we are printing data from
* stdin (otherwise you can't cancel raw jobs...)
*/
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
- action.sa_handler = sigterm_handler;
+ action.sa_handler = SIG_IGN;
sigaction(SIGTERM, &action, NULL);
}
if (pthread_create(&sidechannel_thread_id, NULL, sidechannel_thread, NULL))
{
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
- fputs("DEBUG: Couldn't create side-channel thread!\n", stderr);
+ 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 (pthread_create(&read_thread_id, NULL, read_thread, NULL))
{
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
- fputs("DEBUG: Couldn't create read thread!\n", stderr);
+ 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);
}
while (status == noErr && copies-- > 0)
{
- _cupsLangPuts(stderr, _("INFO: Sending print data...\n"));
+ _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer."));
if (print_fd != STDIN_FILENO)
{
if (errno == EINTR && total_bytes == 0)
{
fputs("DEBUG: Received an interrupt before any bytes were "
- "written, aborting!\n", stderr);
+ "written, aborting\n", stderr);
+ registry_close();
return (CUPS_BACKEND_OK);
}
else if (errno != EAGAIN && errno != EINTR)
{
- _cupsLangPuts(stderr, _("ERROR: Unable to read print data!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to read print data."));
perror("DEBUG: select");
- return (CUPS_BACKEND_FAILED);
+ registry_close();
+ return (CUPS_BACKEND_FAILED);
}
}
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;
+
+ g.print_bytes = read(print_fd, print_buffer, g.debug_bytes);
+
+#else
g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer));
+#endif /* DEBUG_WRITES */
if (g.print_bytes < 0)
{
if (errno != EAGAIN && errno != EINTR)
{
- _cupsLangPuts(stderr, _("ERROR: Unable to read print data!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to read print data."));
perror("DEBUG: read");
+ registry_close();
return (CUPS_BACKEND_FAILED);
}
if (g.print_bytes)
{
- bytes = g.print_bytes;
-
+ bytes = g.print_bytes;
iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
/*
*/
if (iostatus == kIOUSBTransactionTimeout)
+ {
+ fputs("DEBUG: Got USB transaction timeout during write\n", stderr);
iostatus = 0;
+ }
/*
- * Ignore stall errors, since we clear any stalls in the class driver...
+ * If we've stalled, retry the write...
*/
- if (iostatus == kIOUSBPipeStalled)
- iostatus = 0;
+ else if (iostatus == kIOUSBPipeStalled)
+ {
+ fputs("DEBUG: Got USB pipe stalled during write\n", stderr);
+
+ bytes = g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+ }
/*
- * Ignore the first "aborted" status we get, since we might have
- * received a signal (<rdar://problem/6860126>)...
+ * Retry a write after an aborted write since we probably just got
+ * SIGTERM (<rdar://problem/6860126>)...
*/
- if (iostatus == kIOReturnAborted && prev_iostatus != kIOReturnAborted)
+ else if (iostatus == kIOReturnAborted)
{
- prev_iostatus = iostatus;
- iostatus = 0;
- }
- else
- prev_iostatus = iostatus;
+ fputs("DEBUG: Got USB return aborted during write\n", stderr);
+
+ IOReturn err = (*g.classdriver)->Abort(g.classdriver);
+ fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n", err);
- if (iostatus || bytes < 0)
+#if DEBUG_WRITES
+ sleep(5);
+#endif /* DEBUG_WRITES */
+
+ bytes = g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+ }
+
+ if (iostatus)
{
/*
* Write error - bail if we don't see an error we can retry...
*/
-
- IOReturn err = (*g.classdriver)->Abort(g.classdriver);
- _cupsLangPuts(stderr, _("ERROR: Unable to send print data!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to send data to printer."));
fprintf(stderr, "DEBUG: USB class driver WritePipe returned %x\n",
iostatus);
+
+ IOReturn err = (*g.classdriver)->Abort(g.classdriver);
fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n",
err);
- status = job_canceled ? CUPS_BACKEND_FAILED : CUPS_BACKEND_STOP;
+
+ status = CUPS_BACKEND_FAILED;
break;
}
+ else if (bytes > 0)
+ {
+ fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
- fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
-
- g.print_bytes -= bytes;
- print_ptr += bytes;
- total_bytes += bytes;
+ g.print_bytes -= bytes;
+ print_ptr += bytes;
+ total_bytes += bytes;
+ }
}
if (print_fd != 0 && status == noErr)
fprintf(stderr, "DEBUG: Sent %lld bytes...\n", (off_t)total_bytes);
+ if (!print_fd)
+ {
+ /*
+ * Re-enable the SIGTERM handler so pthread_kill() will work...
+ */
+
+ struct sigaction action; /* POSIX signal action */
+
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGTERM);
+ action.sa_handler = sigterm_handler;
+ sigaction(SIGTERM, &action, NULL);
+ }
+
/*
* Wait for the side channel thread to exit...
*/
* Force the side-channel thread to exit...
*/
+ fputs("DEBUG: Force the side-channel thread to exit...\n", stderr);
pthread_kill(sidechannel_thread_id, SIGTERM);
}
}
*/
g.wait_eof = 0;
+ fputs("DEBUG: Force the read thread to exit...\n", stderr);
pthread_kill(read_thread_id, SIGTERM);
}
}
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.
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...
datalen = sizeof(data);
if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
- continue;
+ {
+ if (status == CUPS_SC_STATUS_TIMEOUT)
+ continue;
+ else
+ break;
+ }
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] = 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 (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;
}
}
snprintf(optionsstr, sizeof(optionsstr), "?location=%x", (unsigned)deviceLocation);
httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
- strncat(uristr, optionsstr, sizeof(uristr));
+ strlcat(uristr, optionsstr, sizeof(uristr));
cupsBackendReport("direct", uristr, make_modelstr, make_modelstr, idstr,
NULL);
if (!keepLooking && g.status_timer != NULL)
{
fputs("STATE: -offline-report\n", stderr);
- _cupsLangPuts(stderr, _("INFO: Printer is now online.\n"));
+ _cupsLangPrintFilter(stderr, "INFO", _("Printer is now online."));
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), g.status_timer, kCFRunLoopDefaultMode);
CFRelease(g.status_timer);
g.status_timer = NULL;
void *info)
{
fputs("STATE: +offline-report\n", stderr);
- _cupsLangPuts(stderr, _("INFO: Printer is offline.\n"));
+ _cupsLangPrintFilter(stderr, "INFO", _("Printer is offline."));
if (getenv("CLASS") != NULL)
{
if (stat(bundlestr, &bundleinfo))
{
- fprintf(stderr, "Unable to load class driver \"%s\": %s", bundlestr,
- strerror(errno));
- return (kr);
+ fprintf(stderr, "DEBUG: Class driver \"%s\" not available: %s\n",
+ bundlestr, strerror(errno));
+ fputs("STATE: +cups-missing-filter-warning\n", stderr);
+
+ if (errno == ENOENT && driverPath)
+ return (load_classdriver(NULL, intf, printerDriver));
+ else
+ return (kr);
}
- else if (bundleinfo.st_mode & S_IWOTH)
+ else if (bundleinfo.st_uid ||
+ (bundleinfo.st_gid && (bundleinfo.st_mode & S_IWGRP)) ||
+ (bundleinfo.st_mode & S_IWOTH))
{
- fprintf(stderr, "Unable to load class driver \"%s\": insecure file "
- "permissions (0%o)", bundlestr, bundleinfo.st_mode);
- return (kr);
+ fprintf(stderr, "DEBUG: Class driver \"%s\" has insecure file "
+ "permissions (0%o/uid=%d/gid=%d).\n", bundlestr,
+ bundleinfo.st_mode, (int)bundleinfo.st_uid,
+ (int)bundleinfo.st_gid);
+ fputs("STATE: +cups-insecure-filter-warning\n", stderr);
+
+ if (bundleinfo.st_uid || (bundleinfo.st_mode & S_IWOTH))
+ {
+ if (driverPath)
+ return (load_classdriver(NULL, intf, printerDriver));
+ else
+ return (kr);
+ }
}
/*
!strcasecmp(value, "false"))
*wait_eof = false;
else
- _cupsLangPrintf(stderr,
- _("WARNING: Boolean expected for waiteof option "
- "\"%s\"\n"), value);
+ _cupsLangPrintFilter(stderr, "WARNING",
+ _("Boolean expected for waiteof option \"%s\"."),
+ value);
}
else if (!strcasecmp(name, "serial"))
strlcpy(serial, value, serial_size);
* 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 */
# else
perror("DEBUG: Unable to set binary preference to ppc");
# endif /* __x86_64__ */
- _cupsLangPrintf(stderr, _("Unable to use legacy USB class driver!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to use legacy USB class driver."));
exit(CUPS_BACKEND_STOP);
}
}
my_argv[i] = NULL;
- if (posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv, environ))
+ if ((err = posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv,
+ environ)) != 0)
{
fprintf(stderr, "DEBUG: Unable to exec %s: %s\n", usbpath,
- strerror(errno));
- _cupsLangPrintf(stderr, _("Unable to use legacy USB class driver!\n"));
+ strerror(err));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to use legacy USB class driver."));
exit(CUPS_BACKEND_STOP);
}
if (WIFSIGNALED(childstatus))
{
exitstatus = CUPS_BACKEND_STOP;
- fprintf(stderr, "DEBUG: usb(legacy) backend %d crashed on signal %d!\n",
+ 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",
+ "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",
sigterm_handler(int sig) /* I - Signal */
{
#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * If we started a child process pass the signal on to it...
+ */
+
if (child_pid)
{
/*
exit(0);
else
{
- fprintf(stderr, "DEBUG: Child crashed on signal %d!\n", status);
+ fprintf(stderr, "DEBUG: Child crashed on signal %d\n", status);
exit(CUPS_BACKEND_STOP);
}
}
#endif /* __i386__ || __x86_64__ */
-
- /*
- * Otherwise just flag that the job has been canceled...
- */
-
- job_canceled = 1;
}
char *data,
int *datalen)
{
- UInt32 deviceLocation = 0;
- UInt8 interfaceNum = 0;
CFStringRef deviceIDString = NULL;
/* GetDeviceID */
- copy_devicestring(g.printer_obj, &deviceIDString, &deviceLocation, &interfaceNum);
+ copy_deviceid(g.classdriver, &deviceIDString);
+
if (deviceIDString)
{
CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);