2 * "$Id: usb-darwin.c 181 2006-06-22 20:01:18Z jlovell $"
4 * © Copyright 2005-2006 Apple Computer, Inc. All rights reserved.
6 * IMPORTANT: This Apple software is supplied to you by Apple Computer,
7 * Inc. ("Apple") in consideration of your agreement to the following
8 * terms, and your use, installation, modification or redistribution of
9 * this Apple software constitutes acceptance of these terms. If you do
10 * not agree with these terms, please do not use, install, modify or
11 * redistribute this Apple software.
13 * In consideration of your agreement to abide by the following terms, and
14 * subject to these terms, Apple grants you a personal, non-exclusive
15 * license, under Apple's copyrights in this original Apple software (the
16 * "Apple Software"), to use, reproduce, modify and redistribute the Apple
17 * Software, with or without modifications, in source and/or binary forms;
18 * provided that if you redistribute the Apple Software in its entirety and
19 * without modifications, you must retain this notice and the following
20 * text and disclaimers in all such redistributions of the Apple Software.
21 * Neither the name, trademarks, service marks or logos of Apple Computer,
22 * Inc. may be used to endorse or promote products derived from the Apple
23 * Software without specific prior written permission from Apple. Except
24 * as expressly stated in this notice, no other rights or licenses, express
25 * or implied, are granted by Apple herein, including but not limited to
26 * any patent rights that may be infringed by your derivative works or by
27 * other works in which the Apple Software may be incorporated.
29 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30 * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31 * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33 * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
35 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39 * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40 * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41 * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
46 * USB port on Darwin backend for the Common UNIX Printing System (CUPS).
55 #include <sys/sysctl.h>
57 #include <mach/mach.h>
58 #include <mach/mach_error.h>
59 #include <mach/mach_time.h>
60 #include <cups/debug.h>
62 #include <CoreFoundation/CoreFoundation.h>
63 #include <IOKit/usb/IOUSBLib.h>
64 #include <IOKit/IOCFPlugIn.h>
69 * WAITEOF_DELAY is number of seconds we'll wait for responses from
70 * the printer after we've finished sending all the data
72 #define WAITEOF_DELAY 7
73 #define DEFAULT_TIMEOUT 60L
75 #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
76 #define kUSBLanguageEnglish 0x409
78 #define PRINTER_POLLING_INTERVAL 5 /* seconds */
79 #define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
80 #define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
82 #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
83 #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
85 #define kUSBClassDriverProperty CFSTR("USB Printing Class")
87 #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
88 #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
93 * Section 5.3 USB Printing Class spec
95 #define kUSBPrintingSubclass 1
96 #define kUSBPrintingProtocolNoOpen 0
97 #define kUSBPrintingProtocolUnidirectional 1
98 #define kUSBPrintingProtocolBidirectional 2
100 typedef IOUSBInterfaceInterface190
**printer_interface_t
;
102 typedef struct iodevice_request_s
/**** Device request ****/
110 } iodevice_request_t
;
112 typedef union { /**** Centronics status byte ****/
115 unsigned reserved0
:2;
116 unsigned paperError
:1;
119 unsigned reserved1
:3;
121 } centronics_status_t
;
123 typedef struct classdriver_context_s
/**** Classdriver context ****/
126 CFPlugInRef plugin
; /* release plugin */
127 IUnknownVTbl
**factory
; /* Factory */
128 void *vendorReference
; /* vendor class specific usage */
129 UInt32 location
; /* unique location in bus topology */
130 UInt8 interfaceNumber
; /* Interface number */
131 UInt16 vendorID
; /* Vendor id */
132 UInt16 productID
; /* Product id */
133 printer_interface_t interface
; /* identify the device to IOKit */
134 UInt8 outpipe
; /* mandatory bulkOut pipe */
135 UInt8 inpipe
; /* optional bulkIn pipe */
137 /* general class requests */
138 kern_return_t (*DeviceRequest
)( struct classdriver_context_s
**printer
, iodevice_request_t
*iorequest
, UInt16 timeout
);
139 kern_return_t (*GetString
)( struct classdriver_context_s
**printer
, UInt8 whichString
, UInt16 language
, UInt16 timeout
, CFStringRef
*result
);
141 /* standard printer class requests */
142 kern_return_t (*SoftReset
)( struct classdriver_context_s
**printer
, UInt16 timeout
);
143 kern_return_t (*GetCentronicsStatus
)( struct classdriver_context_s
**printer
, centronics_status_t
*result
, UInt16 timeout
);
144 kern_return_t (*GetDeviceID
)( struct classdriver_context_s
**printer
, CFStringRef
*devid
, UInt16 timeout
);
146 /* standard bulk device requests */
147 kern_return_t (*ReadPipe
)( struct classdriver_context_s
**printer
, UInt8
*buffer
, UInt32
*count
);
148 kern_return_t (*WritePipe
)( struct classdriver_context_s
**printer
, UInt8
*buffer
, UInt32
*count
, Boolean eoj
);
150 /* interface requests */
151 kern_return_t (*Open
)( struct classdriver_context_s
**printer
, UInt32 location
, UInt8 protocol
);
152 kern_return_t (*Abort
)( struct classdriver_context_s
**printer
);
153 kern_return_t (*Close
)( struct classdriver_context_s
**printer
);
155 /* initialize and terminate */
156 kern_return_t (*Initialize
)( struct classdriver_context_s
**printer
, struct classdriver_context_s
**baseclass
);
157 kern_return_t (*Terminate
)( struct classdriver_context_s
**printer
);
159 } classdriver_context_t
;
162 typedef Boolean (*iterator_callback_t
)(void *refcon
, io_service_t obj
);
164 typedef struct iterator_reference_s
{ /**** Iterator reference data */
165 iterator_callback_t callback
;
168 } iterator_reference_t
;
170 typedef struct printer_data_s
{ /**** Printer context data ****/
171 io_service_t printerObj
;
172 classdriver_context_t
**printerDriver
;
174 pthread_cond_t readCompleteCondition
;
175 pthread_mutex_t readMutex
;
193 static Boolean
list_device_callback(void *refcon
, io_service_t obj
);
194 static Boolean
find_device_callback(void *refcon
, io_service_t obj
);
195 static void iterate_printers(iterator_callback_t callBack
, void *userdata
);
196 static void device_added(void *userdata
, io_iterator_t iterator
);
197 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
198 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
199 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***driver
);
200 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
);
201 static kern_return_t
load_printerdriver(printer_data_t
*printer
);
202 static kern_return_t
registry_open(printer_data_t
*printer
);
203 static kern_return_t
registry_close(printer_data_t
*printer
);
204 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
);
205 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
);
206 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
);
207 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
);
208 static void setup_cfLanguage(void);
209 static void *read_thread(void *reference
);
212 #if defined(__i386__)
213 static pid_t child_pid
; /* Child PID */
214 static void run_ppc_backend(int argc
, char *argv
[], int fd
); /* Starts child backend process running as a ppc executable */
215 static void sigterm_handler(int sig
); /* SIGTERM handler */
216 #endif /* __i386__ */
218 #ifdef PARSE_PS_ERRORS
219 static const char *next_line (const char *buffer
);
220 static void parse_pserror (char *sockBuffer
, int len
);
221 #endif /* PARSE_PS_ERRORS */
226 * 'list_devices()' - List all USB devices.
231 iterate_printers(list_device_callback
, NULL
);
236 * 'print_device()' - Print a file to a USB device.
239 int /* O - Exit status */
240 print_device(const char *uri
, /* I - Device URI */
241 const char *hostname
, /* I - Hostname/manufacturer */
242 const char *resource
, /* I - Resource/modelname */
243 const char *options
, /* I - Device options/serial number */
244 int fd
, /* I - File descriptor to print */
245 int copies
, /* I - Copies to print */
246 int argc
, /* I - Number of command-line arguments (6 or 7) */
247 char *argv
[]) /* I - Command-line arguments */
249 printer_data_t printer_data
= { 0x0 }; /* Printer context */
250 char serial
[1024]; /* Serial number buffer */
251 OSStatus status
= noErr
; /* Function results */
252 pthread_t thr
; /* Read thread */
253 char buffer
[2048]; /* Write buffer */
254 int thread_created
= 0; /* Thread created? */
255 int countdown
= INITIAL_LOG_INTERVAL
; /* Logging interval */
256 pthread_cond_t
*readCompleteConditionPtr
= NULL
; /* Read complete condition */
257 pthread_mutex_t
*readMutexPtr
= NULL
; /* Read mutex */
260 parse_options(options
, serial
, &printer_data
.location
, &printer_data
.waitEOF
);
262 if (resource
[0] == '/')
265 printer_data
.uri
= uri
;
266 printer_data
.make
= CFStringCreateWithCString(NULL
, hostname
, kCFStringEncodingUTF8
);
267 printer_data
.model
= CFStringCreateWithCString(NULL
, resource
, kCFStringEncodingUTF8
);
268 printer_data
.serial
= CFStringCreateWithCString(NULL
, serial
, kCFStringEncodingUTF8
);
270 fputs("STATE: +connecting-to-device\n", stderr
);
273 if (printer_data
.printerObj
!= 0x0) {
274 IOObjectRelease(printer_data
.printerObj
);
275 unload_classdriver(&printer_data
.printerDriver
);
276 printer_data
.printerObj
= 0x0;
277 printer_data
.printerDriver
= 0x0;
280 fprintf(stderr
, "INFO: Looking for '%s %s'\n", hostname
, resource
);
281 iterate_printers(find_device_callback
, &printer_data
);
283 fprintf(stderr
, "INFO: Opening Connection\n");
284 status
= registry_open(&printer_data
);
285 #if defined(__i386__)
287 * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
288 * In this case try to fork & exec this backend as a ppc executable so we can use them...
290 if (status
== -2 /* kPMInvalidIOMContext */) {
291 run_ppc_backend(argc
, argv
, fd
);
292 /* Never returns here */
294 #endif /* __i386__ */
296 if (status
!= noErr
) {
297 sleep( PRINTER_POLLING_INTERVAL
);
298 countdown
-= PRINTER_POLLING_INTERVAL
;
299 if ( countdown
<= 0 ) {
300 fprintf(stderr
, "INFO: Printer busy (status:0x%08x)\n", (int)status
);
301 countdown
= SUBSEQUENT_LOG_INTERVAL
; /* subsequent log entries, every 15 seconds */
304 } while (status
!= noErr
);
306 fputs("STATE: -connecting-to-device\n", stderr
);
309 * Now that we are "connected" to the port, ignore SIGTERM so that we
310 * can finish out any page data the driver sends (e.g. to eject the
311 * current page... Only ignore SIGTERM if we are printing data from
312 * stdin (otherwise you can't cancel raw jobs...)
316 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
317 sigset(SIGTERM
, SIG_IGN
);
318 #elif defined(HAVE_SIGACTION)
319 memset(&action
, 0, sizeof(action
));
321 sigemptyset(&action
.sa_mask
);
322 action
.sa_handler
= SIG_IGN
;
323 sigaction(SIGTERM
, &action
, NULL
);
325 signal(SIGTERM
, SIG_IGN
);
326 #endif /* HAVE_SIGSET */
329 if (status
== noErr
) {
330 if (pthread_cond_init(&printer_data
.readCompleteCondition
, NULL
) == 0)
331 readCompleteConditionPtr
= &printer_data
.readCompleteCondition
;
333 if (pthread_mutex_init(&printer_data
.readMutex
, NULL
) == 0)
334 readMutexPtr
= &printer_data
.readMutex
;
336 if (pthread_create(&thr
, NULL
, read_thread
, &printer_data
) == 0)
339 if (thread_created
== 0)
340 fprintf(stderr
, "WARNING: Couldn't create read channel\n");
344 * The main thread sends the print file...
347 while (status
== noErr
&& copies
-- > 0) {
348 UInt32 wbytes
; /* Number of bytes written */
349 ssize_t nbytes
; /* Number of bytes read */
350 off_t tbytes
= 0; /* Total number of bytes written */
352 fprintf(stderr
, "INFO: Sending data\n");
354 if (STDIN_FILENO
!= fd
) {
355 fputs("PAGE: 1 1", stderr
);
356 lseek( fd
, 0, SEEK_SET
);
359 while (status
== noErr
&& (nbytes
= read(fd
, buffer
, sizeof(buffer
))) > 0) {
360 char *bufptr
= buffer
;
363 while (nbytes
> 0 && status
== noErr
) {
365 status
= (*(printer_data
.printerDriver
))->WritePipe( printer_data
.printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0 /* nbytes > wbytes? 0: feof(fp) */ );
366 if (wbytes
< 0 || noErr
!= status
) {
367 OSStatus err
= (*(printer_data
.printerDriver
))->Abort(printer_data
.printerDriver
);
368 fprintf(stderr
, "ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n", status
, err
);
376 if (fd
!= 0 && status
== noErr
)
377 fprintf(stderr
, "DEBUG: Sending print file, %qd bytes...\n", (off_t
)tbytes
);
381 if (thread_created
) {
382 /* Signal the read thread that we are done... */
383 printer_data
.done
= 1;
385 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
386 * we are not signaled in that time then force the thread to exit by setting
387 * the waiteof to be false. Plese note that this relies on us using the timeout
390 struct timespec sleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
391 pthread_mutex_lock(&printer_data
.readMutex
);
392 if (pthread_cond_timedwait(&printer_data
.readCompleteCondition
, &printer_data
.readMutex
, (const struct timespec
*)&sleepUntil
) != 0)
393 printer_data
.waitEOF
= false;
394 pthread_mutex_unlock(&printer_data
.readMutex
);
395 pthread_join( thr
,NULL
); /* wait for the child thread to return */
399 * Close the connection and input file and general clean up...
401 registry_close(&printer_data
);
403 if (STDIN_FILENO
!= fd
)
406 if (readCompleteConditionPtr
!= NULL
)
407 pthread_cond_destroy(&printer_data
.readCompleteCondition
);
409 if (readMutexPtr
!= NULL
)
410 pthread_mutex_destroy(&printer_data
.readMutex
);
412 if (printer_data
.make
!= NULL
)
413 CFRelease(printer_data
.make
);
415 if (printer_data
.model
!= NULL
)
416 CFRelease(printer_data
.model
);
418 if (printer_data
.serial
!= NULL
)
419 CFRelease(printer_data
.serial
);
421 if (printer_data
.printerObj
!= 0x0)
422 IOObjectRelease(printer_data
.printerObj
);
429 * 'list_device_callback()' - list_device iterator callback.
432 static Boolean
list_device_callback(void *refcon
, io_service_t obj
)
434 Boolean keepRunning
= (obj
!= 0x0);
437 CFStringRef deviceIDString
= NULL
;
438 UInt32 deviceLocation
= 0;
440 copy_devicestring(obj
, &deviceIDString
, &deviceLocation
);
441 if (deviceIDString
!= NULL
) {
442 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
443 char uristr
[1024], makestr
[1024], modelstr
[1024], serialstr
[1024], optionsstr
[1024];
446 copy_deviceinfo(deviceIDString
, &make
, &model
, &serial
);
450 CFStringGetCString(deviceIDString
, idstr
, sizeof(idstr
), kCFStringEncodingUTF8
);
451 CFStringGetCString(make
, makestr
, sizeof(makestr
), kCFStringEncodingUTF8
);
452 CFStringGetCString(model
, &modelstr
[1], sizeof(modelstr
)-1, kCFStringEncodingUTF8
);
455 * Fix common HP 1284 bug...
458 if (!strcasecmp(makestr
, "Hewlett-Packard"))
459 strcpy(makestr
, "HP");
461 if (!strncasecmp(modelstr
+ 1, "hp ", 3))
462 _cups_strcpy(modelstr
+ 1, modelstr
+ 4);
464 optionsstr
[0] = '\0';
467 CFStringGetCString(serial
, serialstr
, sizeof(serialstr
), kCFStringEncodingUTF8
);
468 snprintf(optionsstr
, sizeof(optionsstr
), "?serial=%s", serialstr
);
470 else if (deviceLocation
!= 0)
472 snprintf(optionsstr
, sizeof(optionsstr
), "?location=%lx", deviceLocation
);
475 httpAssembleURI(HTTP_URI_CODING_ALL
, uristr
, sizeof(uristr
), "usb", NULL
, makestr
, 0, modelstr
);
476 strncat(uristr
, optionsstr
, sizeof(uristr
));
478 printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr
, makestr
,
479 &modelstr
[1], makestr
, &modelstr
[1], idstr
);
481 release_deviceinfo(&make
, &model
, &serial
);
482 CFRelease(deviceIDString
);
491 * 'find_device_callback()' - print_device iterator callback.
494 static Boolean
find_device_callback(void *refcon
, io_service_t obj
)
496 Boolean keepLooking
= true;
498 if (obj
!= 0x0 && refcon
!= NULL
) {
499 CFStringRef idString
= NULL
;
500 UInt32 location
= -1;
501 printer_data_t
*userData
= (printer_data_t
*)refcon
;
503 copy_devicestring(obj
, &idString
, &location
);
504 if (idString
!= NULL
) {
505 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
507 copy_deviceinfo(idString
, &make
, &model
, &serial
);
508 if (CFStringCompare(make
, userData
->make
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
509 if (CFStringCompare(model
, userData
->model
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
510 if (userData
->serial
!= NULL
) {
511 if (serial
!= NULL
&& CFStringCompare(model
, userData
->model
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
513 userData
->printerObj
= obj
;
518 if (userData
->printerObj
!= 0) {
519 IOObjectRetain(userData
->printerObj
);
521 userData
->printerObj
= obj
;
524 if (userData
->location
== 0 || userData
->location
== location
) {
531 release_deviceinfo(&make
, &model
, &serial
);
536 keepLooking
= (refcon
!= NULL
&& ((printer_data_t
*)refcon
)->printerObj
== 0);
545 * 'iterate_printers()' - iterate over all the printers.
548 static void iterate_printers(iterator_callback_t callBack
, void *userdata
)
550 mach_port_t masterPort
= 0x0;
551 kern_return_t kr
= IOMasterPort (bootstrap_port
, &masterPort
);
553 if (kr
== kIOReturnSuccess
&& masterPort
!= 0x0) {
554 io_iterator_t addIterator
= 0x0;
556 iterator_reference_t reference
= { callBack
, userdata
, true };
557 IONotificationPortRef addNotification
= IONotificationPortCreate(masterPort
);
559 int klass
= kUSBPrintingClass
;
560 int subklass
= kUSBPrintingSubclass
;
562 CFNumberRef usb_klass
= CFNumberCreate(NULL
, kCFNumberIntType
, &klass
);
563 CFNumberRef usb_subklass
= CFNumberCreate(NULL
, kCFNumberIntType
, &subklass
);
564 CFMutableDictionaryRef usbPrinterMatchDictionary
= IOServiceMatching(kIOUSBInterfaceClassName
);
566 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceClass"), usb_klass
);
567 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceSubClass"), usb_subklass
);
569 CFRelease(usb_klass
);
570 CFRelease(usb_subklass
);
572 kr
= IOServiceAddMatchingNotification(addNotification
, kIOMatchedNotification
, usbPrinterMatchDictionary
, &device_added
, &reference
, &addIterator
);
573 if (addIterator
!= 0x0) {
574 device_added (&reference
, addIterator
);
576 if (reference
.keepRunning
) {
577 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification
), kCFRunLoopDefaultMode
);
580 IOObjectRelease(addIterator
);
582 mach_port_deallocate(mach_task_self(), masterPort
);
588 * 'device_added()' - device added notifier.
591 static void device_added(void *userdata
, io_iterator_t iterator
)
593 iterator_reference_t
*reference
= userdata
;
596 while (reference
->keepRunning
&& (obj
= IOIteratorNext(iterator
)) != 0x0) {
597 if (reference
->callback
!= NULL
) {
598 reference
->keepRunning
= reference
->callback(reference
->userdata
, obj
);
600 IOObjectRelease(obj
);
603 /* One last call to the call back now that we are not longer have printers left to iterate...
605 if (reference
->keepRunning
)
606 reference
->keepRunning
= reference
->callback(reference
->userdata
, 0x0);
608 if (!reference
->keepRunning
) {
609 CFRunLoopStop(CFRunLoopGetCurrent());
616 * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
619 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
621 CFStringRef modelKeys
[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL
};
622 CFStringRef makeKeys
[] = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL
};
623 CFStringRef serialKeys
[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL
};
626 *make
= copy_value_for_key(deviceIDString
, makeKeys
);
628 *model
= copy_value_for_key(deviceIDString
, modelKeys
);
630 *serial
= copy_value_for_key(deviceIDString
, serialKeys
);
635 * 'release_deviceinfo()' - Release deviceinfo strings.
638 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
640 if (make
!= NULL
&& *make
!= NULL
) {
645 if (model
!= NULL
&& *model
!= NULL
) {
650 if (serial
!= NULL
&& *serial
!= NULL
) {
659 * 'load_classdriver()' - Load a classdriver.
662 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***printerDriver
)
664 kern_return_t kr
= kUSBPrinterClassDeviceNotOpen
;
665 classdriver_context_t
**driver
= NULL
;
666 CFStringRef bundle
= (driverPath
== NULL
? kUSBGenericTOPrinterClassDriver
: driverPath
);
668 if ( NULL
!= bundle
) {
669 CFURLRef url
= CFURLCreateWithFileSystemPath(NULL
, bundle
, kCFURLPOSIXPathStyle
, true);
670 CFPlugInRef plugin
= (url
!= NULL
? CFPlugInCreate(NULL
, url
) : NULL
);
675 if (plugin
!= NULL
) {
676 CFArrayRef factories
= CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID
, plugin
);
677 if (factories
!= NULL
&& CFArrayGetCount(factories
) > 0) {
678 CFUUIDRef factoryID
= CFArrayGetValueAtIndex(factories
, 0);
679 IUnknownVTbl
**iunknown
= CFPlugInInstanceCreate(NULL
, factoryID
, kUSBPrinterClassTypeID
);
680 if (NULL
!= iunknown
) {
681 kr
= (*iunknown
)->QueryInterface(iunknown
, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID
), (LPVOID
*)&driver
);
682 if (kr
== kIOReturnSuccess
&& driver
!= NULL
) {
683 classdriver_context_t
**genericDriver
= NULL
;
684 if (driverPath
!= NULL
&& CFStringCompare(driverPath
, kUSBGenericTOPrinterClassDriver
, 0) != kCFCompareEqualTo
) {
685 kr
= load_classdriver(NULL
, intf
, &genericDriver
);
688 if (kr
== kIOReturnSuccess
) {
689 (*driver
)->interface
= intf
;
690 (*driver
)->Initialize(driver
, genericDriver
);
692 (*driver
)->plugin
= plugin
;
693 (*driver
)->interface
= intf
;
694 *printerDriver
= driver
;
697 (*iunknown
)->Release(iunknown
);
699 CFRelease(factories
);
705 char bundlestr
[1024];
706 CFStringGetCString(bundle
, bundlestr
, sizeof(bundlestr
), kCFStringEncodingUTF8
);
707 fprintf(stderr
, "DEBUG:load_classdriver(%s) (kr:0x%08x)\n", bundlestr
, (int)kr
);
715 * 'unload_classdriver()' - Unload a classdriver.
718 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
)
720 if (*classDriver
!= NULL
) {
721 (**classDriver
)->Release(*classDriver
);
725 return kIOReturnSuccess
;
730 * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
733 static kern_return_t
load_printerdriver(printer_data_t
*printer
)
735 IOCFPlugInInterface
**iodev
= NULL
;
738 kern_return_t kr
= IOCreatePlugInInterfaceForService(printer
->printerObj
, kIOUSBInterfaceUserClientTypeID
, kIOCFPlugInInterfaceID
, &iodev
, &score
);
739 if (kr
== kIOReturnSuccess
) {
740 printer_interface_t intf
;
741 HRESULT res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
);
743 CFMutableDictionaryRef properties
= NULL
;
745 kr
= IORegistryEntryCreateCFProperties(printer
->printerObj
, &properties
, NULL
, kNilOptions
);
746 if (kr
== kIOReturnSuccess
) {
747 CFStringRef driverBundlePath
= NULL
;
748 if (properties
!= NULL
) {
749 driverBundlePath
= (CFStringRef
) CFDictionaryGetValue(properties
, kUSBClassDriverProperty
);
751 kr
= load_classdriver(driverBundlePath
, intf
, &printer
->printerDriver
);
754 if (kr
!= kIOReturnSuccess
)
755 (*intf
)->Release(intf
);
757 IODestroyPlugInInterface(iodev
);
764 * 'registry_open()' - Open a connection to the printer.
767 static kern_return_t
registry_open(printer_data_t
*printer
)
769 kern_return_t kr
= load_printerdriver(printer
);
770 if (kr
!= kIOReturnSuccess
) {
774 if (printer
->printerDriver
!= NULL
) {
775 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolBidirectional
);
776 if (kr
!= kIOReturnSuccess
|| (*(printer
->printerDriver
))->interface
== NULL
) {
777 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolUnidirectional
);
778 if (kr
== kIOReturnSuccess
) {
779 if ((*(printer
->printerDriver
))->interface
== NULL
) {
780 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
787 if (kr
!= kIOReturnSuccess
) {
788 unload_classdriver(&printer
->printerDriver
);
796 * 'registry_close()' - Close the connection to the printer.
799 static kern_return_t
registry_close(printer_data_t
*printer
)
801 if (printer
->printerDriver
!= NULL
) {
802 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
804 unload_classdriver(&printer
->printerDriver
);
805 return kIOReturnSuccess
;
810 * 'copy_deviceid()' - Copy the 1284 device id string.
813 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
)
815 CFStringRef devID
= NULL
,
821 OSStatus err
= (*printer
)->GetDeviceID(printer
, &devID
, DEFAULT_TIMEOUT
);
823 copy_deviceinfo(devID
, &deviceMake
, &deviceModel
, &deviceSerial
);
825 if (deviceMake
== NULL
|| deviceModel
== NULL
|| deviceSerial
== NULL
) {
826 IOUSBDeviceDescriptor desc
;
827 iodevice_request_t request
;
829 request
.requestType
= USBmakebmRequestType( kUSBIn
, kUSBStandard
, kUSBDevice
);
830 request
.request
= kUSBRqGetDescriptor
;
831 request
.value
= (kUSBDeviceDesc
<< 8) | 0;
833 request
.length
= sizeof(desc
);
834 request
.buffer
= &desc
;
835 err
= (*printer
)->DeviceRequest(printer
, &request
, DEFAULT_TIMEOUT
);
836 if (err
== kIOReturnSuccess
) {
837 CFMutableStringRef newDevID
= CFStringCreateMutable(NULL
, 0);
839 if (deviceMake
== NULL
) {
840 CFStringRef data
= NULL
;
841 err
= (*printer
)->GetString(printer
, desc
.iManufacturer
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
843 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MFG:%@;"), data
);
848 if (deviceModel
== NULL
) {
849 CFStringRef data
= NULL
;
850 err
= (*printer
)->GetString(printer
, desc
.iProduct
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
852 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MDL:%@;"), data
);
857 if (deviceSerial
== NULL
&& desc
.iSerialNumber
!= 0) {
858 CFStringRef data
= NULL
;
859 err
= (*printer
)->GetString(printer
, desc
.iSerialNumber
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
861 CFStringAppendFormat(newDevID
, NULL
, CFSTR("SERN:%@;"), data
);
867 CFStringAppend(newDevID
, devID
);
871 *deviceID
= newDevID
;
877 release_deviceinfo(&deviceMake
, &deviceModel
, &deviceSerial
);
884 * 'copy_devicestring()' - Copy the 1284 device id string.
887 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
)
889 IOCFPlugInInterface
**iodev
= NULL
;
892 kern_return_t kr
= IOCreatePlugInInterfaceForService(usbInterface
, kIOUSBInterfaceUserClientTypeID
,
893 kIOCFPlugInInterfaceID
, &iodev
, &score
);
894 if (kr
== kIOReturnSuccess
) {
895 printer_interface_t intf
;
897 HRESULT res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
);
899 /* ignore the result for location id... */
900 (void)(*intf
)->GetLocationID(intf
, deviceLocation
);
902 CFMutableDictionaryRef properties
= NULL
;
903 kr
= IORegistryEntryCreateCFProperties(usbInterface
, &properties
, NULL
, kNilOptions
);
904 if (kIOReturnSuccess
== kr
) {
905 classdriver_context_t
**klassDriver
= NULL
;
906 CFStringRef driverBundlePath
= NULL
;
908 if (properties
!= NULL
) {
909 driverBundlePath
= (CFStringRef
) CFDictionaryGetValue(properties
, kUSBClassDriverProperty
);
912 kr
= load_classdriver(driverBundlePath
, intf
, &klassDriver
);
913 if (kr
!= kIOReturnSuccess
&& driverBundlePath
!= NULL
)
914 kr
= load_classdriver(NULL
, intf
, &klassDriver
);
915 if (kr
== kIOReturnSuccess
&& klassDriver
!= NULL
) {
916 kr
= copy_deviceid(klassDriver
, deviceID
);
918 unload_classdriver(&klassDriver
);
920 if (properties
!= NULL
)
921 CFRelease(properties
);
924 /* (*intf)->Release(intf); */
926 IODestroyPlugInInterface(iodev
);
933 * 'copy_value_for_key()' - Copy value string associated with a key.
936 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
)
938 CFStringRef value
= NULL
;
939 CFArrayRef kvPairs
= deviceID
!= NULL
? CFStringCreateArrayBySeparatingStrings(NULL
, deviceID
, CFSTR(";")) : NULL
;
940 CFIndex max
= kvPairs
!= NULL
? CFArrayGetCount(kvPairs
) : 0;
943 while (idx
< max
&& value
== NULL
) {
944 CFStringRef kvpair
= CFArrayGetValueAtIndex(kvPairs
, idx
);
946 while (keys
[idxx
] != NULL
&& value
== NULL
) {
947 CFRange range
= CFStringFind(kvpair
, keys
[idxx
], kCFCompareCaseInsensitive
);
948 if (range
.length
!= -1) {
949 if (range
.location
!= 0) {
950 CFMutableStringRef theString
= CFStringCreateMutableCopy(NULL
, 0, kvpair
);
951 CFStringTrimWhitespace(theString
);
952 range
= CFStringFind(theString
, keys
[idxx
], kCFCompareCaseInsensitive
);
953 if (range
.location
== 0) {
954 value
= CFStringCreateWithSubstring(NULL
, theString
, CFRangeMake(range
.length
, CFStringGetLength(theString
) - range
.length
));
956 CFRelease(theString
);
959 CFStringRef theString
= CFStringCreateWithSubstring(NULL
, kvpair
, CFRangeMake(range
.length
, CFStringGetLength(kvpair
) - range
.length
));
960 CFMutableStringRef theString2
= CFStringCreateMutableCopy(NULL
, 0, theString
);
961 CFRelease(theString
);
963 CFStringTrimWhitespace(theString2
);
980 * 'parse_options()' - Parse uri options.
983 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
)
985 char *serialnumber
; /* ?serial=<serial> or ?location=<location> */
986 char optionName
[255], /* Name of option */
987 value
[255], /* Value of option */
988 *ptr
; /* Pointer into name or value */
1000 while (*options
!= '\0') {
1001 /* Get the name... */
1002 for (ptr
= optionName
; *options
&& *options
!= '=' && *options
!= '+'; )
1003 *ptr
++ = *options
++;
1008 if (*options
== '=') {
1009 /* Get the value... */
1012 for (ptr
= value
; *options
&& *options
!= '+';)
1013 *ptr
++ = *options
++;
1017 if (*options
== '+')
1020 else if (*options
== '+') {
1025 * Process the option...
1027 if (strcasecmp(optionName
, "waiteof") == 0) {
1028 if (strcasecmp(value
, "on") == 0 ||
1029 strcasecmp(value
, "yes") == 0 ||
1030 strcasecmp(value
, "true") == 0) {
1033 else if (strcasecmp(value
, "off") == 0 ||
1034 strcasecmp(value
, "no") == 0 ||
1035 strcasecmp(value
, "false") == 0) {
1039 fprintf(stderr
, "WARNING: Boolean expected for waiteof option \"%s\"\n", value
);
1042 else if (strcasecmp(optionName
, "serial") == 0) {
1043 strcpy(serial
, value
);
1044 serialnumber
= serial
;
1046 else if (strcasecmp(optionName
, "location") == 0 && location
) {
1047 *location
= strtol(value
, NULL
, 16);
1056 * @function setup_cfLanguage
1057 * @abstract Convert the contents of the CUPS 'LANG' environment
1058 * variable into a one element CF array of languages.
1060 * @discussion Each submitted job comes with a natural language. CUPS passes
1061 * that language in an environment variable. We take that language
1062 * and jam it into the AppleLanguages array so that CF will use
1063 * it when reading localized resources. We need to do this before
1064 * any CF code reads and caches the languages array, so this function
1065 * should be called early in main()
1067 static void setup_cfLanguage(void)
1069 CFStringRef lang
[1] = {NULL
};
1070 CFArrayRef langArray
= NULL
;
1071 const char *requestedLang
= NULL
;
1073 requestedLang
= getenv("LANG");
1074 if (requestedLang
!= NULL
) {
1075 lang
[0] = CFStringCreateWithCString(kCFAllocatorDefault
, requestedLang
, kCFStringEncodingUTF8
);
1076 langArray
= CFArrayCreate(kCFAllocatorDefault
, (const void **)lang
, sizeof(lang
) / sizeof(lang
[0]), &kCFTypeArrayCallBacks
);
1078 CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray
, kCFPreferencesCurrentApplication
);
1079 DEBUG_printf((stderr
, "DEBUG: usb: AppleLanguages = \"%s\"\n", requestedLang
));
1082 CFRelease(langArray
);
1084 fprintf(stderr
, "DEBUG: usb: LANG environment variable missing.\n");
1089 #if defined(__i386__)
1091 * @function run_ppc_backend
1093 * @abstract Starts child backend process running as a ppc executable.
1095 * @result Never returns; always calls exit().
1099 static void run_ppc_backend(int argc
, char *argv
[], int fd
)
1104 pid_t waitpid_status
;
1106 char *usb_ppc_status
;
1109 * If we're running as i386 and couldn't load the class driver (because they'it's
1110 * ppc-only) then try to re-exec ourselves in ppc mode to try again. If we don't have
1111 * a ppc architecture we may be running i386 again so guard against this by setting
1112 * and testing an environment variable...
1114 usb_ppc_status
= getenv("USB_PPC_STATUS");
1116 if (usb_ppc_status
== NULL
) {
1117 /* Catch SIGTERM if we are _not_ printing data from
1118 * stdin (otherwise you can't cancel raw jobs...)
1122 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1123 sigset(SIGTERM
, sigterm_handler
);
1124 #elif defined(HAVE_SIGACTION)
1125 struct sigaction action
; /* Actions for POSIX signals */
1126 memset(&action
, 0, sizeof(action
));
1127 sigaddset(&action
.sa_mask
, SIGTERM
);
1128 action
.sa_handler
= sigterm_handler
;
1129 sigaction(SIGTERM
, &action
, NULL
);
1131 signal(SIGTERM
, sigterm_handler
);
1132 #endif /* HAVE_SIGSET */
1135 if ((child_pid
= fork()) == 0) {
1136 /* Child comes here. */
1137 setenv("USB_PPC_STATUS", "1", false);
1139 /* Tell the kernel we want the next exec call to favor the ppc architecture... */
1140 int mib
[] = { CTL_KERN
, KERN_AFFINITY
, 1, 1 };
1142 sysctl(mib
, namelen
, NULL
, NULL
, NULL
, 0);
1144 /* Set up the arguments and call exec... */
1145 for (i
= 0; i
< argc
&& i
< (sizeof(my_argv
)/sizeof(my_argv
[0])) - 1; i
++)
1146 my_argv
[i
] = argv
[i
];
1150 execv("/usr/libexec/cups/backend/usb", my_argv
);
1152 fprintf(stderr
, "DEBUG: execv: %s\n", strerror(errno
));
1155 else if (child_pid
> 0) {
1156 /* Parent comes here.
1158 * Close the fds we won't be using then wait for the child backend to exit.
1163 fprintf(stderr
, "DEBUG: Started usb(ppc) backend (PID %d)\n", (int)child_pid
);
1165 while ((waitpid_status
= waitpid(child_pid
, &childstatus
, 0)) == (pid_t
)-1 && errno
== EINTR
)
1168 if (WIFSIGNALED(childstatus
)) {
1169 exitstatus
= WTERMSIG(childstatus
);
1170 fprintf(stderr
, "DEBUG: usb(ppc) backend %d crashed on signal %d!\n", child_pid
, exitstatus
);
1173 if ((exitstatus
= WEXITSTATUS(childstatus
)) != 0)
1174 fprintf(stderr
, "DEBUG: usb(ppc) backend %d stopped with status %d!\n", child_pid
, exitstatus
);
1176 fprintf(stderr
, "DEBUG: PID %d exited with no errors\n", child_pid
);
1181 fprintf(stderr
, "DEBUG: fork: %s\n", strerror(errno
));
1186 fprintf(stderr
, "DEBUG: usb child running i386 again\n");
1187 exitstatus
= ENOENT
;
1194 * 'sigterm_handler()' - SIGTERM handler.
1197 static void sigterm_handler(int sig
)
1199 /* If we started a child process pass the signal on to it...
1202 kill(child_pid
, sig
);
1207 #endif /* __i386__ */
1210 #ifdef PARSE_PS_ERRORS
1212 * 'next_line()' - Find the next line in a buffer.
1215 static const char *next_line (const char *buffer
)
1217 const char *cptr
, *lptr
= NULL
;
1219 for (cptr
= buffer
; *cptr
&& lptr
== NULL
; cptr
++)
1220 if (*cptr
== '\n' || *cptr
== '\r')
1227 * 'parse_pserror()' - Scan the backchannel data for postscript errors.
1230 static void parse_pserror (char *sockBuffer
, int len
)
1232 static char gErrorBuffer
[1024] = "";
1233 static char *gErrorBufferPtr
= gErrorBuffer
;
1234 static char *gErrorBufferEndPtr
= gErrorBuffer
+ sizeof(gErrorBuffer
);
1236 char *pCommentBegin
, *pCommentEnd
, *pLineEnd
;
1241 if (gErrorBufferPtr
+ len
> gErrorBufferEndPtr
- 1)
1242 gErrorBufferPtr
= gErrorBuffer
;
1243 if (len
> sizeof(gErrorBuffer
) - 1)
1244 len
= sizeof(gErrorBuffer
) - 1;
1246 memcpy(gErrorBufferPtr
, (const void *)sockBuffer
, len
);
1247 gErrorBufferPtr
+= len
;
1248 *(gErrorBufferPtr
+ 1) = '\0';
1251 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1252 while (pLineEnd
!= NULL
) {
1255 pCommentBegin
= strstr(gErrorBuffer
,"%%[");
1256 pCommentEnd
= strstr(gErrorBuffer
, "]%%");
1257 if (pCommentBegin
!= gErrorBuffer
&& pCommentEnd
!= NULL
) {
1258 pCommentEnd
+= 3; /* Skip past "]%%" */
1259 *pCommentEnd
= '\0'; /* There's always room for the nul */
1261 if (strncasecmp(pCommentBegin
, "%%[ Error:", 10) == 0)
1263 else if (strncasecmp(pCommentBegin
, "%%[ Flushing", 12) == 0)
1268 if ((logstrlen
= snprintf(logstr
, sizeof(logstr
), "%s: %s\n", logLevel
, pCommentBegin
)) >= sizeof(logstr
)) {
1269 /* If the string was trucnated make sure it has a linefeed before the nul */
1270 logstrlen
= sizeof(logstr
) - 1;
1271 logstr
[logstrlen
- 1] = '\n';
1273 write(STDERR_FILENO
, logstr
, logstrlen
);
1276 /* move everything over... */
1277 strcpy(gErrorBuffer
, pLineEnd
);
1278 gErrorBufferPtr
= gErrorBuffer
;
1279 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1282 #endif /* PARSE_PS_ERRORS */
1286 * 'read_thread()' - A thread to read the backchannel data.
1289 static void *read_thread(void *reference
)
1291 /* post a read to the device and write results to stdout
1292 * the final pending read will be Aborted in the main thread
1294 UInt8 readbuffer
[512];
1296 kern_return_t readstatus
;
1297 printer_data_t
*userData
= (printer_data_t
*)reference
;
1298 classdriver_context_t
**classdriver
= userData
->printerDriver
;
1299 struct mach_timebase_info timeBaseInfo
;
1303 /* Calculate what 250 milliSeconds are in mach absolute time...
1305 mach_timebase_info(&timeBaseInfo
);
1306 delay
= ((uint64_t)250000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1309 /* Remember when we started so we can throttle the loop after the read call...
1311 start
= mach_absolute_time();
1313 rbytes
= sizeof(readbuffer
);
1314 readstatus
= (*classdriver
)->ReadPipe( classdriver
, readbuffer
, &rbytes
);
1315 if ( kIOReturnSuccess
== readstatus
&& rbytes
> 0 ) {
1317 cupsBackChannelWrite((char*)readbuffer
, rbytes
, 1.0);
1319 /* cntrl-d is echoed by the printer.
1321 * Xerox Phaser 6250D doesn't echo the cntrl-d.
1322 * Xerox Phaser 6250D doesn't always send the product query.
1324 if (userData
->waitEOF
&& readbuffer
[rbytes
-1] == 0x4)
1326 #ifdef PARSE_PS_ERRORS
1327 parse_pserror(readbuffer
, rbytes
);
1331 /* Make sure this loop executes no more than once every 250 miliseconds...
1333 if ((readstatus
!= kIOReturnSuccess
|| rbytes
== 0) && (userData
->waitEOF
|| !userData
->done
))
1334 mach_wait_until(start
+ delay
);
1336 } while ( userData
->waitEOF
|| !userData
->done
); /* Abort from main thread tests error here */
1338 /* Let the other thread (main thread) know that we have completed the read thread...
1340 pthread_mutex_lock(&userData
->readMutex
);
1341 pthread_cond_signal(&userData
->readCompleteCondition
);
1342 pthread_mutex_unlock(&userData
->readMutex
);
1349 * End of "$Id: usb-darwin.c 181 2006-06-22 20:01:18Z jlovell $".