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