2 * "$Id: usb-darwin.c 6302 2007-02-22 19:36:36Z mike $"
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>
61 #include <cups/sidechannel.h>
63 #include <CoreFoundation/CoreFoundation.h>
64 #include <IOKit/usb/IOUSBLib.h>
65 #include <IOKit/IOCFPlugIn.h>
71 * WAITEOF_DELAY is number of seconds we'll wait for responses from
72 * the printer after we've finished sending all the data
74 #define WAITEOF_DELAY 7
75 #define DEFAULT_TIMEOUT 60L
77 #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
78 #define kUSBLanguageEnglish 0x409
80 #define PRINTER_POLLING_INTERVAL 5 /* seconds */
81 #define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
82 #define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
84 #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
85 #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
87 #define kUSBClassDriverProperty CFSTR("USB Printing Class")
89 #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
90 #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
91 #define kWriteBufferSize 2048
96 * Section 5.3 USB Printing Class spec
98 #define kUSBPrintingSubclass 1
99 #define kUSBPrintingProtocolNoOpen 0
100 #define kUSBPrintingProtocolUnidirectional 1
101 #define kUSBPrintingProtocolBidirectional 2
103 typedef IOUSBInterfaceInterface190
**printer_interface_t
;
105 typedef struct iodevice_request_s
/**** Device request ****/
113 } iodevice_request_t
;
115 typedef union { /**** Centronics status byte ****/
118 unsigned reserved0
:2;
119 unsigned paperError
:1;
122 unsigned reserved1
:3;
124 } centronics_status_t
;
126 typedef struct classdriver_context_s
/**** Classdriver context ****/
129 CFPlugInRef plugin
; /* release plugin */
130 IUnknownVTbl
**factory
; /* Factory */
131 void *vendorReference
; /* vendor class specific usage */
132 UInt32 location
; /* unique location in bus topology */
133 UInt8 interfaceNumber
; /* Interface number */
134 UInt16 vendorID
; /* Vendor id */
135 UInt16 productID
; /* Product id */
136 printer_interface_t interface
; /* identify the device to IOKit */
137 UInt8 outpipe
; /* mandatory bulkOut pipe */
138 UInt8 inpipe
; /* optional bulkIn pipe */
140 /* general class requests */
141 kern_return_t (*DeviceRequest
)( struct classdriver_context_s
**printer
, iodevice_request_t
*iorequest
, UInt16 timeout
);
142 kern_return_t (*GetString
)( struct classdriver_context_s
**printer
, UInt8 whichString
, UInt16 language
, UInt16 timeout
, CFStringRef
*result
);
144 /* standard printer class requests */
145 kern_return_t (*SoftReset
)( struct classdriver_context_s
**printer
, UInt16 timeout
);
146 kern_return_t (*GetCentronicsStatus
)( struct classdriver_context_s
**printer
, centronics_status_t
*result
, UInt16 timeout
);
147 kern_return_t (*GetDeviceID
)( struct classdriver_context_s
**printer
, CFStringRef
*devid
, UInt16 timeout
);
149 /* standard bulk device requests */
150 kern_return_t (*ReadPipe
)( struct classdriver_context_s
**printer
, UInt8
*buffer
, UInt32
*count
);
151 kern_return_t (*WritePipe
)( struct classdriver_context_s
**printer
, UInt8
*buffer
, UInt32
*count
, Boolean eoj
);
153 /* interface requests */
154 kern_return_t (*Open
)( struct classdriver_context_s
**printer
, UInt32 location
, UInt8 protocol
);
155 kern_return_t (*Abort
)( struct classdriver_context_s
**printer
);
156 kern_return_t (*Close
)( struct classdriver_context_s
**printer
);
158 /* initialize and terminate */
159 kern_return_t (*Initialize
)( struct classdriver_context_s
**printer
, struct classdriver_context_s
**baseclass
);
160 kern_return_t (*Terminate
)( struct classdriver_context_s
**printer
);
162 } classdriver_context_t
;
165 typedef Boolean (*iterator_callback_t
)(void *refcon
, io_service_t obj
);
167 typedef struct iterator_reference_s
{ /**** Iterator reference data */
168 iterator_callback_t callback
;
171 } iterator_reference_t
;
173 typedef struct printer_data_s
{ /**** Printer context data ****/
174 io_service_t printerObj
;
175 classdriver_context_t
**printerDriver
;
177 pthread_cond_t readCompleteCondition
;
178 pthread_mutex_t readMutex
;
189 pthread_cond_t reqWaitCompCond
;
190 pthread_mutex_t reqWaitMutex
;
191 pthread_mutex_t waitCloseMutex
;
192 pthread_mutex_t writeCompMutex
;
196 int directionalFlag
; /* 0=uni, 1=bidi */
199 char dataBuffer
[kWriteBufferSize
];
207 static Boolean
list_device_callback(void *refcon
, io_service_t obj
);
208 static Boolean
find_device_callback(void *refcon
, io_service_t obj
);
209 static void iterate_printers(iterator_callback_t callBack
, void *userdata
);
210 static void device_added(void *userdata
, io_iterator_t iterator
);
211 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
212 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
213 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***driver
);
214 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
);
215 static kern_return_t
load_printerdriver(printer_data_t
*printer
, CFStringRef
*driverBundlePath
);
216 static kern_return_t
registry_open(printer_data_t
*printer
, CFStringRef
*driverBundlePath
);
217 static kern_return_t
registry_close(printer_data_t
*printer
);
218 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
);
219 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
);
220 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
);
221 static CFStringRef
cfstr_create_and_trim(const char *cstr
);
222 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
);
223 static void setup_cfLanguage(void);
224 static void *read_thread(void *reference
);
225 static void *reqestWait_thread(void *reference
);
226 static void usbSoftReset(printer_data_t
*userData
, cups_sc_status_t
*status
);
227 static void usbDrainOutput(printer_data_t
*userData
, cups_sc_status_t
*status
);
228 static void usbGetBidirectional(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
229 static void usbGetDeviceID(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
230 static void usbGetDevState(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
233 #if defined(__i386__)
234 static pid_t child_pid
; /* Child PID */
235 static void run_ppc_backend(int argc
, char *argv
[], int fd
); /* Starts child backend process running as a ppc executable */
236 static void sigterm_handler(int sig
); /* SIGTERM handler */
237 #endif /* __i386__ */
239 #ifdef PARSE_PS_ERRORS
240 static const char *next_line (const char *buffer
);
241 static void parse_pserror (char *sockBuffer
, int len
);
242 #endif /* PARSE_PS_ERRORS */
247 * 'list_devices()' - List all USB devices.
252 iterate_printers(list_device_callback
, NULL
);
257 * 'print_device()' - Print a file to a USB device.
260 int /* O - Exit status */
261 print_device(const char *uri
, /* I - Device URI */
262 const char *hostname
, /* I - Hostname/manufacturer */
263 const char *resource
, /* I - Resource/modelname */
264 const char *options
, /* I - Device options/serial number */
265 int fd
, /* I - File descriptor to print */
266 int copies
, /* I - Copies to print */
267 int argc
, /* I - Number of command-line arguments (6 or 7) */
268 char *argv
[]) /* I - Command-line arguments */
270 printer_data_t printer_data
= { 0x0 }; /* Printer context */
271 char serial
[1024]; /* Serial number buffer */
272 OSStatus status
= noErr
; /* Function results */
273 pthread_t thr
; /* Read thread */
274 char buffer
[2048]; /* Write buffer */
275 int thread_created
= 0; /* Thread created? */
276 int countdown
= INITIAL_LOG_INTERVAL
; /* Logging interval */
277 pthread_cond_t
*readCompleteConditionPtr
= NULL
; /* Read complete condition */
278 pthread_mutex_t
*readMutexPtr
= NULL
; /* Read mutex */
279 CFStringRef driverBundlePath
; /* Class driver path */
280 int reqWait_create
= 0; /* RequestWait thread created? */
281 pthread_t reqWaitThread
; /* RequestWait thread */
282 pthread_cond_t
*reqWaitCompCondPtr
= NULL
; /* RequestWait complete condition */
283 pthread_mutex_t
*reqWaitMutexPtr
= NULL
; /* RequestWait mutex */
284 pthread_mutex_t
*waitCloseMutexPtr
= NULL
; /* wait close mutex */
285 pthread_mutex_t
*writeCompMutexPtr
= NULL
; /* write complete mutex */
288 parse_options(options
, serial
, &printer_data
.location
, &printer_data
.waitEOF
);
290 if (resource
[0] == '/')
293 printer_data
.uri
= uri
;
295 printer_data
.make
= cfstr_create_and_trim(hostname
);
296 printer_data
.model
= cfstr_create_and_trim(resource
);
297 printer_data
.serial
= cfstr_create_and_trim(serial
);
299 fputs("STATE: +connecting-to-device\n", stderr
);
302 if (printer_data
.printerObj
!= 0x0) {
303 IOObjectRelease(printer_data
.printerObj
);
304 unload_classdriver(&printer_data
.printerDriver
);
305 printer_data
.printerObj
= 0x0;
306 printer_data
.printerDriver
= 0x0;
309 fprintf(stderr
, "INFO: Looking for '%s %s'\n", hostname
, resource
);
310 iterate_printers(find_device_callback
, &printer_data
);
312 fprintf(stderr
, "INFO: Opening Connection\n");
314 driverBundlePath
= NULL
;
315 status
= registry_open(&printer_data
, &driverBundlePath
);
316 #if defined(__i386__)
318 * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
319 * In this case try to fork & exec this backend as a ppc executable so we can use them...
321 if (status
== -2 /* kPMInvalidIOMContext */) {
322 run_ppc_backend(argc
, argv
, fd
);
323 /* Never returns here */
325 #endif /* __i386__ */
328 * If we still were unable to load the class drivers for this printer log
329 * the error and stop the queue...
332 if (driverBundlePath
== NULL
|| !CFStringGetCString(driverBundlePath
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
333 strlcpy(buffer
, "USB class driver", sizeof(buffer
));
335 fprintf(stderr
, "STATE: +apple-missing-usbclassdriver-error\n" \
336 "FATAL: Could not load %s\n", buffer
);
338 if (driverBundlePath
)
339 CFRelease(driverBundlePath
);
341 return CUPS_BACKEND_STOP
;
344 if (driverBundlePath
)
345 CFRelease(driverBundlePath
);
347 if (status
!= noErr
) {
348 sleep( PRINTER_POLLING_INTERVAL
);
349 countdown
-= PRINTER_POLLING_INTERVAL
;
350 if ( countdown
<= 0 ) {
351 fprintf(stderr
, "INFO: Printer busy (status:0x%08x)\n", (int)status
);
352 countdown
= SUBSEQUENT_LOG_INTERVAL
; /* subsequent log entries, every 15 seconds */
355 } while (status
!= noErr
);
357 fputs("STATE: -connecting-to-device\n", stderr
);
360 * Now that we are "connected" to the port, ignore SIGTERM so that we
361 * can finish out any page data the driver sends (e.g. to eject the
362 * current page... Only ignore SIGTERM if we are printing data from
363 * stdin (otherwise you can't cancel raw jobs...)
367 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
368 sigset(SIGTERM
, SIG_IGN
);
369 #elif defined(HAVE_SIGACTION)
370 memset(&action
, 0, sizeof(action
));
372 sigemptyset(&action
.sa_mask
);
373 action
.sa_handler
= SIG_IGN
;
374 sigaction(SIGTERM
, &action
, NULL
);
376 signal(SIGTERM
, SIG_IGN
);
377 #endif /* HAVE_SIGSET */
380 if (status
== noErr
) {
381 if (pthread_cond_init(&printer_data
.readCompleteCondition
, NULL
) == 0)
382 readCompleteConditionPtr
= &printer_data
.readCompleteCondition
;
384 if (pthread_mutex_init(&printer_data
.readMutex
, NULL
) == 0)
385 readMutexPtr
= &printer_data
.readMutex
;
387 printer_data
.done
= 0;
389 if (pthread_create(&thr
, NULL
, read_thread
, &printer_data
) == 0)
392 if (thread_created
== 0)
393 fprintf(stderr
, "WARNING: Couldn't create read channel\n");
395 if (pthread_cond_init(&printer_data
.reqWaitCompCond
, NULL
) == 0)
396 reqWaitCompCondPtr
= &printer_data
.reqWaitCompCond
;
398 if (pthread_mutex_init(&printer_data
.reqWaitMutex
, NULL
) == 0)
399 reqWaitMutexPtr
= &printer_data
.reqWaitMutex
;
401 printer_data
.reqWaitDone
= 0;
402 printer_data
.reqWqitFlag
= 0;
404 if (pthread_create(&reqWaitThread
, NULL
, reqestWait_thread
, &printer_data
) == 0)
407 if (reqWait_create
== 0)
408 fprintf(stderr
, "WARNING: Couldn't create sidechannel thread!\n");
410 if (pthread_mutex_init(&printer_data
.waitCloseMutex
, NULL
) == 0)
411 waitCloseMutexPtr
= &printer_data
.waitCloseMutex
;
413 if (pthread_mutex_init(&printer_data
.writeCompMutex
, NULL
) == 0)
414 writeCompMutexPtr
= &printer_data
.writeCompMutex
;
418 * The main thread sends the print file...
421 printer_data
.writeDone
= 0;
422 printer_data
.dataSize
= 0;
423 printer_data
.dataOffset
= 0;
424 pthread_mutex_lock(writeCompMutexPtr
);
426 while (status
== noErr
&& copies
-- > 0) {
427 UInt32 wbytes
; /* Number of bytes written */
428 ssize_t nbytes
; /* Number of bytes read */
429 off_t tbytes
= 0; /* Total number of bytes written */
431 fprintf(stderr
, "INFO: Sending data\n");
433 if (STDIN_FILENO
!= fd
) {
434 fputs("PAGE: 1 1", stderr
);
435 lseek( fd
, 0, SEEK_SET
);
438 while (status
== noErr
&& (nbytes
= read(fd
, buffer
, sizeof(buffer
))) > 0) {
439 char *bufptr
= buffer
;
442 while (nbytes
> 0 && status
== noErr
) {
443 if (printer_data
.writeDone
) {
444 printer_data
.dataSize
= nbytes
;
445 printer_data
.dataOffset
= bufptr
- buffer
;
446 memcpy(printer_data
.dataBuffer
, buffer
, nbytes
);
453 status
= (*(printer_data
.printerDriver
))->WritePipe( printer_data
.printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0 /* nbytes > wbytes? 0: feof(fp) */ );
454 if (wbytes
< 0 || noErr
!= status
) {
455 OSStatus err
= (*(printer_data
.printerDriver
))->Abort(printer_data
.printerDriver
);
456 fprintf(stderr
, "ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n", status
, err
);
464 if (fd
!= 0 && status
== noErr
)
465 fprintf(stderr
, "DEBUG: Sending print file, %qd bytes...\n", (off_t
)tbytes
);
469 printer_data
.writeDone
= 1;
470 pthread_mutex_unlock(writeCompMutexPtr
);
472 if (thread_created
) {
473 /* Signal the read thread that we are done... */
474 printer_data
.done
= 1;
476 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
477 * we are not signaled in that time then force the thread to exit by setting
478 * the waiteof to be false. Plese note that this relies on us using the timeout
481 struct timespec sleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
482 pthread_mutex_lock(&printer_data
.readMutex
);
483 if (pthread_cond_timedwait(&printer_data
.readCompleteCondition
, &printer_data
.readMutex
, (const struct timespec
*)&sleepUntil
) != 0)
484 printer_data
.waitEOF
= false;
485 pthread_mutex_unlock(&printer_data
.readMutex
);
486 pthread_join( thr
,NULL
); /* wait for the child thread to return */
489 if (reqWait_create
) {
490 /* Signal the cupsSideChannelDoRequest wait thread that we are done... */
491 printer_data
.reqWaitDone
= 1;
494 * Give the cupsSideChannelDoRequest wait thread WAITEOF_DELAY seconds to complete
495 * all the data. If we are not signaled in that time then force the thread to exit
496 * by setting the waiteof to be false. Plese note that this relies on us using the
497 * timeout class driver.
499 struct timespec reqWaitSleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
500 pthread_mutex_lock(&printer_data
.reqWaitMutex
);
502 while (!printer_data
.reqWqitFlag
) {
503 if (pthread_cond_timedwait(&printer_data
.reqWaitCompCond
,
504 &printer_data
.reqWaitMutex
,
505 (const struct timespec
*)&reqWaitSleepUntil
) != 0) {
506 printer_data
.waitEOF
= false;
507 printer_data
.reqWqitFlag
= 1;
510 pthread_mutex_unlock(&printer_data
.reqWaitMutex
);
511 pthread_join(reqWaitThread
,NULL
); /* wait for the child thread to return */
514 /* interface close wait mutex(for softreset) */
515 pthread_mutex_lock(waitCloseMutexPtr
);
516 pthread_mutex_unlock(waitCloseMutexPtr
);
519 * Close the connection and input file and general clean up...
521 registry_close(&printer_data
);
523 if (STDIN_FILENO
!= fd
)
526 if (readCompleteConditionPtr
!= NULL
)
527 pthread_cond_destroy(&printer_data
.readCompleteCondition
);
529 if (readMutexPtr
!= NULL
)
530 pthread_mutex_destroy(&printer_data
.readMutex
);
532 if (waitCloseMutexPtr
!= NULL
)
533 pthread_mutex_destroy(&printer_data
.waitCloseMutex
);
535 if (writeCompMutexPtr
!= NULL
)
536 pthread_mutex_destroy(&printer_data
.writeCompMutex
);
538 if (reqWaitCompCondPtr
!= NULL
)
539 pthread_cond_destroy(&printer_data
.reqWaitCompCond
);
541 if (reqWaitMutexPtr
!= NULL
)
542 pthread_mutex_destroy(&printer_data
.reqWaitMutex
);
544 if (printer_data
.make
!= NULL
)
545 CFRelease(printer_data
.make
);
547 if (printer_data
.model
!= NULL
)
548 CFRelease(printer_data
.model
);
550 if (printer_data
.serial
!= NULL
)
551 CFRelease(printer_data
.serial
);
553 if (printer_data
.printerObj
!= 0x0)
554 IOObjectRelease(printer_data
.printerObj
);
561 * 'list_device_callback()' - list_device iterator callback.
564 static Boolean
list_device_callback(void *refcon
, io_service_t obj
)
566 Boolean keepRunning
= (obj
!= 0x0);
569 CFStringRef deviceIDString
= NULL
;
570 UInt32 deviceLocation
= 0;
572 copy_devicestring(obj
, &deviceIDString
, &deviceLocation
);
573 if (deviceIDString
!= NULL
) {
574 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
575 char uristr
[1024], makestr
[1024], modelstr
[1024], serialstr
[1024];
576 char optionsstr
[1024], idstr
[1024];
578 copy_deviceinfo(deviceIDString
, &make
, &model
, &serial
);
582 CFStringGetCString(deviceIDString
, idstr
, sizeof(idstr
),
583 kCFStringEncodingUTF8
);
586 CFStringGetCString(make
, makestr
, sizeof(makestr
),
587 kCFStringEncodingUTF8
);
589 strcpy(makestr
, "Unknown");
592 CFStringGetCString(model
, &modelstr
[1], sizeof(modelstr
)-1,
593 kCFStringEncodingUTF8
);
595 strcpy(modelstr
+ 1, "Printer");
598 * Fix common HP 1284 bug...
601 if (!strcasecmp(makestr
, "Hewlett-Packard"))
602 strcpy(makestr
, "HP");
604 if (!strncasecmp(modelstr
+ 1, "hp ", 3))
605 _cups_strcpy(modelstr
+ 1, modelstr
+ 4);
607 optionsstr
[0] = '\0';
610 CFStringGetCString(serial
, serialstr
, sizeof(serialstr
), kCFStringEncodingUTF8
);
611 snprintf(optionsstr
, sizeof(optionsstr
), "?serial=%s", serialstr
);
613 else if (deviceLocation
!= 0)
615 snprintf(optionsstr
, sizeof(optionsstr
), "?location=%lx", deviceLocation
);
618 httpAssembleURI(HTTP_URI_CODING_ALL
, uristr
, sizeof(uristr
), "usb", NULL
, makestr
, 0, modelstr
);
619 strncat(uristr
, optionsstr
, sizeof(uristr
));
621 printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr
, makestr
,
622 &modelstr
[1], makestr
, &modelstr
[1], idstr
);
624 release_deviceinfo(&make
, &model
, &serial
);
625 CFRelease(deviceIDString
);
634 * 'find_device_callback()' - print_device iterator callback.
637 static Boolean
find_device_callback(void *refcon
, io_service_t obj
)
639 Boolean keepLooking
= true;
641 if (obj
!= 0x0 && refcon
!= NULL
) {
642 CFStringRef idString
= NULL
;
643 UInt32 location
= -1;
644 printer_data_t
*userData
= (printer_data_t
*)refcon
;
646 copy_devicestring(obj
, &idString
, &location
);
647 if (idString
!= NULL
) {
648 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
650 copy_deviceinfo(idString
, &make
, &model
, &serial
);
651 if (CFStringCompare(make
, userData
->make
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
652 if (CFStringCompare(model
, userData
->model
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
653 if (userData
->serial
!= NULL
) {
654 if (serial
!= NULL
&& CFStringCompare(serial
, userData
->serial
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
656 userData
->printerObj
= obj
;
661 if (userData
->printerObj
!= 0) {
662 IOObjectRetain(userData
->printerObj
);
664 userData
->printerObj
= obj
;
667 if (userData
->location
== 0 || userData
->location
== location
) {
674 release_deviceinfo(&make
, &model
, &serial
);
679 keepLooking
= (refcon
!= NULL
&& ((printer_data_t
*)refcon
)->printerObj
== 0);
688 * 'iterate_printers()' - iterate over all the printers.
691 static void iterate_printers(iterator_callback_t callBack
, void *userdata
)
693 mach_port_t masterPort
= 0x0;
694 kern_return_t kr
= IOMasterPort (bootstrap_port
, &masterPort
);
696 if (kr
== kIOReturnSuccess
&& masterPort
!= 0x0) {
697 io_iterator_t addIterator
= 0x0;
699 iterator_reference_t reference
= { callBack
, userdata
, true };
700 IONotificationPortRef addNotification
= IONotificationPortCreate(masterPort
);
702 int klass
= kUSBPrintingClass
;
703 int subklass
= kUSBPrintingSubclass
;
705 CFNumberRef usb_klass
= CFNumberCreate(NULL
, kCFNumberIntType
, &klass
);
706 CFNumberRef usb_subklass
= CFNumberCreate(NULL
, kCFNumberIntType
, &subklass
);
707 CFMutableDictionaryRef usbPrinterMatchDictionary
= IOServiceMatching(kIOUSBInterfaceClassName
);
709 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceClass"), usb_klass
);
710 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceSubClass"), usb_subklass
);
712 CFRelease(usb_klass
);
713 CFRelease(usb_subklass
);
715 kr
= IOServiceAddMatchingNotification(addNotification
, kIOMatchedNotification
, usbPrinterMatchDictionary
, &device_added
, &reference
, &addIterator
);
716 if (addIterator
!= 0x0) {
717 device_added (&reference
, addIterator
);
719 if (reference
.keepRunning
) {
720 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification
), kCFRunLoopDefaultMode
);
723 IOObjectRelease(addIterator
);
725 mach_port_deallocate(mach_task_self(), masterPort
);
731 * 'device_added()' - device added notifier.
734 static void device_added(void *userdata
, io_iterator_t iterator
)
736 iterator_reference_t
*reference
= userdata
;
739 while (reference
->keepRunning
&& (obj
= IOIteratorNext(iterator
)) != 0x0) {
740 if (reference
->callback
!= NULL
) {
741 reference
->keepRunning
= reference
->callback(reference
->userdata
, obj
);
743 IOObjectRelease(obj
);
746 /* One last call to the call back now that we are not longer have printers left to iterate...
748 if (reference
->keepRunning
)
749 reference
->keepRunning
= reference
->callback(reference
->userdata
, 0x0);
751 if (!reference
->keepRunning
) {
752 CFRunLoopStop(CFRunLoopGetCurrent());
759 * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
762 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
764 CFStringRef modelKeys
[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL
};
765 CFStringRef makeKeys
[] = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL
};
766 CFStringRef serialKeys
[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL
};
769 *make
= copy_value_for_key(deviceIDString
, makeKeys
);
771 *model
= copy_value_for_key(deviceIDString
, modelKeys
);
773 *serial
= copy_value_for_key(deviceIDString
, serialKeys
);
778 * 'release_deviceinfo()' - Release deviceinfo strings.
781 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
783 if (make
!= NULL
&& *make
!= NULL
) {
788 if (model
!= NULL
&& *model
!= NULL
) {
793 if (serial
!= NULL
&& *serial
!= NULL
) {
802 * 'load_classdriver()' - Load a classdriver.
805 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***printerDriver
)
807 kern_return_t kr
= kUSBPrinterClassDeviceNotOpen
;
808 classdriver_context_t
**driver
= NULL
;
809 CFStringRef bundle
= (driverPath
== NULL
? kUSBGenericTOPrinterClassDriver
: driverPath
);
811 if ( NULL
!= bundle
) {
812 CFURLRef url
= CFURLCreateWithFileSystemPath(NULL
, bundle
, kCFURLPOSIXPathStyle
, true);
813 CFPlugInRef plugin
= (url
!= NULL
? CFPlugInCreate(NULL
, url
) : NULL
);
818 if (plugin
!= NULL
) {
819 CFArrayRef factories
= CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID
, plugin
);
820 if (factories
!= NULL
&& CFArrayGetCount(factories
) > 0) {
821 CFUUIDRef factoryID
= CFArrayGetValueAtIndex(factories
, 0);
822 IUnknownVTbl
**iunknown
= CFPlugInInstanceCreate(NULL
, factoryID
, kUSBPrinterClassTypeID
);
823 if (NULL
!= iunknown
) {
824 kr
= (*iunknown
)->QueryInterface(iunknown
, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID
), (LPVOID
*)&driver
);
825 if (kr
== kIOReturnSuccess
&& driver
!= NULL
) {
826 classdriver_context_t
**genericDriver
= NULL
;
827 if (driverPath
!= NULL
&& CFStringCompare(driverPath
, kUSBGenericTOPrinterClassDriver
, 0) != kCFCompareEqualTo
) {
828 kr
= load_classdriver(NULL
, intf
, &genericDriver
);
831 if (kr
== kIOReturnSuccess
) {
832 (*driver
)->interface
= intf
;
833 (*driver
)->Initialize(driver
, genericDriver
);
835 (*driver
)->plugin
= plugin
;
836 (*driver
)->interface
= intf
;
837 *printerDriver
= driver
;
840 (*iunknown
)->Release(iunknown
);
842 CFRelease(factories
);
848 char bundlestr
[1024];
849 CFStringGetCString(bundle
, bundlestr
, sizeof(bundlestr
), kCFStringEncodingUTF8
);
850 fprintf(stderr
, "DEBUG:load_classdriver(%s) (kr:0x%08x)\n", bundlestr
, (int)kr
);
858 * 'unload_classdriver()' - Unload a classdriver.
861 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
)
863 if (*classDriver
!= NULL
) {
864 (**classDriver
)->Release(*classDriver
);
868 return kIOReturnSuccess
;
873 * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
875 * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
878 static kern_return_t
load_printerdriver(printer_data_t
*printer
, CFStringRef
*driverBundlePath
)
880 IOCFPlugInInterface
**iodev
= NULL
;
883 kern_return_t kr
= IOCreatePlugInInterfaceForService(printer
->printerObj
, kIOUSBInterfaceUserClientTypeID
, kIOCFPlugInInterfaceID
, &iodev
, &score
);
884 if (kr
== kIOReturnSuccess
) {
885 printer_interface_t intf
;
886 HRESULT res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
);
888 CFMutableDictionaryRef properties
= NULL
;
890 kr
= IORegistryEntryCreateCFProperties(printer
->printerObj
, &properties
, NULL
, kNilOptions
);
891 if (kr
== kIOReturnSuccess
) {
892 if (properties
!= NULL
) {
893 *driverBundlePath
= (CFStringRef
) CFDictionaryGetValue(properties
, kUSBClassDriverProperty
);
895 kr
= load_classdriver(*driverBundlePath
, intf
, &printer
->printerDriver
);
898 if (kr
!= kIOReturnSuccess
)
899 (*intf
)->Release(intf
);
901 IODestroyPlugInInterface(iodev
);
908 * 'registry_open()' - Open a connection to the printer.
911 static kern_return_t
registry_open(printer_data_t
*printer
, CFStringRef
*driverBundlePath
)
913 printer
->directionalFlag
= 0;
915 kern_return_t kr
= load_printerdriver(printer
, driverBundlePath
);
916 if (kr
!= kIOReturnSuccess
) {
920 if (printer
->printerDriver
!= NULL
) {
921 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolBidirectional
);
922 if (kr
!= kIOReturnSuccess
|| (*(printer
->printerDriver
))->interface
== NULL
) {
923 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolUnidirectional
);
924 if (kr
== kIOReturnSuccess
) {
925 if ((*(printer
->printerDriver
))->interface
== NULL
) {
926 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
931 printer
->directionalFlag
= 1;
935 if (kr
!= kIOReturnSuccess
) {
936 unload_classdriver(&printer
->printerDriver
);
944 * 'registry_close()' - Close the connection to the printer.
947 static kern_return_t
registry_close(printer_data_t
*printer
)
949 if (printer
->printerDriver
!= NULL
) {
950 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
952 unload_classdriver(&printer
->printerDriver
);
953 return kIOReturnSuccess
;
958 * 'copy_deviceid()' - Copy the 1284 device id string.
961 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
)
963 CFStringRef devID
= NULL
,
969 OSStatus err
= (*printer
)->GetDeviceID(printer
, &devID
, DEFAULT_TIMEOUT
);
971 copy_deviceinfo(devID
, &deviceMake
, &deviceModel
, &deviceSerial
);
973 if (deviceMake
== NULL
|| deviceModel
== NULL
|| deviceSerial
== NULL
) {
974 IOUSBDeviceDescriptor desc
;
975 iodevice_request_t request
;
977 request
.requestType
= USBmakebmRequestType( kUSBIn
, kUSBStandard
, kUSBDevice
);
978 request
.request
= kUSBRqGetDescriptor
;
979 request
.value
= (kUSBDeviceDesc
<< 8) | 0;
981 request
.length
= sizeof(desc
);
982 request
.buffer
= &desc
;
983 err
= (*printer
)->DeviceRequest(printer
, &request
, DEFAULT_TIMEOUT
);
984 if (err
== kIOReturnSuccess
) {
985 CFMutableStringRef newDevID
= CFStringCreateMutable(NULL
, 0);
987 if (deviceMake
== NULL
) {
988 CFStringRef data
= NULL
;
989 err
= (*printer
)->GetString(printer
, desc
.iManufacturer
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
991 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MFG:%@;"), data
);
996 if (deviceModel
== NULL
) {
997 CFStringRef data
= NULL
;
998 err
= (*printer
)->GetString(printer
, desc
.iProduct
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1000 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MDL:%@;"), data
);
1005 if (deviceSerial
== NULL
&& desc
.iSerialNumber
!= 0) {
1006 CFStringRef data
= NULL
;
1007 err
= (*printer
)->GetString(printer
, desc
.iSerialNumber
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1009 CFStringAppendFormat(newDevID
, NULL
, CFSTR("SERN:%@;"), data
);
1014 if (devID
!= NULL
) {
1015 CFStringAppend(newDevID
, devID
);
1019 *deviceID
= newDevID
;
1025 release_deviceinfo(&deviceMake
, &deviceModel
, &deviceSerial
);
1032 * 'copy_devicestring()' - Copy the 1284 device id string.
1035 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
)
1037 IOCFPlugInInterface
**iodev
= NULL
;
1040 kern_return_t kr
= IOCreatePlugInInterfaceForService(usbInterface
, kIOUSBInterfaceUserClientTypeID
,
1041 kIOCFPlugInInterfaceID
, &iodev
, &score
);
1042 if (kr
== kIOReturnSuccess
) {
1043 printer_interface_t intf
;
1045 HRESULT res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
);
1047 /* ignore the result for location id... */
1048 (void)(*intf
)->GetLocationID(intf
, deviceLocation
);
1050 CFMutableDictionaryRef properties
= NULL
;
1051 kr
= IORegistryEntryCreateCFProperties(usbInterface
, &properties
, NULL
, kNilOptions
);
1052 if (kIOReturnSuccess
== kr
) {
1053 classdriver_context_t
**klassDriver
= NULL
;
1054 CFStringRef driverBundlePath
= NULL
;
1056 if (properties
!= NULL
) {
1057 driverBundlePath
= (CFStringRef
) CFDictionaryGetValue(properties
, kUSBClassDriverProperty
);
1060 kr
= load_classdriver(driverBundlePath
, intf
, &klassDriver
);
1061 if (kr
!= kIOReturnSuccess
&& driverBundlePath
!= NULL
)
1062 kr
= load_classdriver(NULL
, intf
, &klassDriver
);
1063 if (kr
== kIOReturnSuccess
&& klassDriver
!= NULL
) {
1064 kr
= copy_deviceid(klassDriver
, deviceID
);
1066 unload_classdriver(&klassDriver
);
1068 if (properties
!= NULL
)
1069 CFRelease(properties
);
1072 /* (*intf)->Release(intf); */
1074 IODestroyPlugInInterface(iodev
);
1081 * 'copy_value_for_key()' - Copy value string associated with a key.
1084 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
)
1086 CFStringRef value
= NULL
;
1087 CFArrayRef kvPairs
= deviceID
!= NULL
? CFStringCreateArrayBySeparatingStrings(NULL
, deviceID
, CFSTR(";")) : NULL
;
1088 CFIndex max
= kvPairs
!= NULL
? CFArrayGetCount(kvPairs
) : 0;
1091 while (idx
< max
&& value
== NULL
) {
1092 CFStringRef kvpair
= CFArrayGetValueAtIndex(kvPairs
, idx
);
1094 while (keys
[idxx
] != NULL
&& value
== NULL
) {
1095 CFRange range
= CFStringFind(kvpair
, keys
[idxx
], kCFCompareCaseInsensitive
);
1096 if (range
.length
!= -1) {
1097 if (range
.location
!= 0) {
1098 CFMutableStringRef theString
= CFStringCreateMutableCopy(NULL
, 0, kvpair
);
1099 CFStringTrimWhitespace(theString
);
1100 range
= CFStringFind(theString
, keys
[idxx
], kCFCompareCaseInsensitive
);
1101 if (range
.location
== 0) {
1102 value
= CFStringCreateWithSubstring(NULL
, theString
, CFRangeMake(range
.length
, CFStringGetLength(theString
) - range
.length
));
1104 CFRelease(theString
);
1107 CFStringRef theString
= CFStringCreateWithSubstring(NULL
, kvpair
, CFRangeMake(range
.length
, CFStringGetLength(kvpair
) - range
.length
));
1108 CFMutableStringRef theString2
= CFStringCreateMutableCopy(NULL
, 0, theString
);
1109 CFRelease(theString
);
1111 CFStringTrimWhitespace(theString2
);
1120 if (kvPairs
!= NULL
)
1127 * 'cfstr_create_and_trim()' - Create a CFString from a c-string and
1128 * trim it's whitespace characters.
1131 CFStringRef
cfstr_create_and_trim(const char *cstr
)
1134 CFMutableStringRef cfmutablestr
= NULL
;
1136 if ((cfstr
= CFStringCreateWithCString(NULL
, cstr
, kCFStringEncodingUTF8
)) != NULL
)
1138 if ((cfmutablestr
= CFStringCreateMutableCopy(NULL
, 1024, cfstr
)) != NULL
)
1139 CFStringTrimWhitespace(cfmutablestr
);
1143 return (CFStringRef
) cfmutablestr
;
1149 * 'parse_options()' - Parse uri options.
1152 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
)
1154 char *serialnumber
; /* ?serial=<serial> or ?location=<location> */
1155 char optionName
[255], /* Name of option */
1156 value
[255], /* Value of option */
1157 *ptr
; /* Pointer into name or value */
1167 serialnumber
= NULL
;
1169 while (*options
!= '\0') {
1170 /* Get the name... */
1171 for (ptr
= optionName
; *options
&& *options
!= '=' && *options
!= '+'; )
1172 *ptr
++ = *options
++;
1177 if (*options
== '=') {
1178 /* Get the value... */
1181 for (ptr
= value
; *options
&& *options
!= '+';)
1182 *ptr
++ = *options
++;
1186 if (*options
== '+')
1189 else if (*options
== '+') {
1194 * Process the option...
1196 if (strcasecmp(optionName
, "waiteof") == 0) {
1197 if (strcasecmp(value
, "on") == 0 ||
1198 strcasecmp(value
, "yes") == 0 ||
1199 strcasecmp(value
, "true") == 0) {
1202 else if (strcasecmp(value
, "off") == 0 ||
1203 strcasecmp(value
, "no") == 0 ||
1204 strcasecmp(value
, "false") == 0) {
1208 fprintf(stderr
, "WARNING: Boolean expected for waiteof option \"%s\"\n", value
);
1211 else if (strcasecmp(optionName
, "serial") == 0) {
1212 strcpy(serial
, value
);
1213 serialnumber
= serial
;
1215 else if (strcasecmp(optionName
, "location") == 0 && location
) {
1216 *location
= strtol(value
, NULL
, 16);
1225 * @function setup_cfLanguage
1226 * @abstract Convert the contents of the CUPS 'LANG' environment
1227 * variable into a one element CF array of languages.
1229 * @discussion Each submitted job comes with a natural language. CUPS passes
1230 * that language in an environment variable. We take that language
1231 * and jam it into the AppleLanguages array so that CF will use
1232 * it when reading localized resources. We need to do this before
1233 * any CF code reads and caches the languages array, so this function
1234 * should be called early in main()
1236 static void setup_cfLanguage(void)
1238 CFStringRef lang
[1] = {NULL
};
1239 CFArrayRef langArray
= NULL
;
1240 const char *requestedLang
= NULL
;
1242 requestedLang
= getenv("LANG");
1243 if (requestedLang
!= NULL
) {
1244 lang
[0] = CFStringCreateWithCString(kCFAllocatorDefault
, requestedLang
, kCFStringEncodingUTF8
);
1245 langArray
= CFArrayCreate(kCFAllocatorDefault
, (const void **)lang
, sizeof(lang
) / sizeof(lang
[0]), &kCFTypeArrayCallBacks
);
1247 CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray
, kCFPreferencesCurrentApplication
);
1248 DEBUG_printf((stderr
, "DEBUG: usb: AppleLanguages = \"%s\"\n", requestedLang
));
1251 CFRelease(langArray
);
1253 fprintf(stderr
, "DEBUG: usb: LANG environment variable missing.\n");
1258 #if defined(__i386__)
1260 * @function run_ppc_backend
1262 * @abstract Starts child backend process running as a ppc executable.
1264 * @result Never returns; always calls exit().
1268 static void run_ppc_backend(int argc
, char *argv
[], int fd
)
1273 pid_t waitpid_status
;
1275 char *usb_ppc_status
;
1278 * If we're running as i386 and couldn't load the class driver (because they'it's
1279 * ppc-only) then try to re-exec ourselves in ppc mode to try again. If we don't have
1280 * a ppc architecture we may be running i386 again so guard against this by setting
1281 * and testing an environment variable...
1283 usb_ppc_status
= getenv("USB_PPC_STATUS");
1285 if (usb_ppc_status
== NULL
) {
1286 /* Catch SIGTERM if we are _not_ printing data from
1287 * stdin (otherwise you can't cancel raw jobs...)
1291 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1292 sigset(SIGTERM
, sigterm_handler
);
1293 #elif defined(HAVE_SIGACTION)
1294 struct sigaction action
; /* Actions for POSIX signals */
1295 memset(&action
, 0, sizeof(action
));
1296 sigaddset(&action
.sa_mask
, SIGTERM
);
1297 action
.sa_handler
= sigterm_handler
;
1298 sigaction(SIGTERM
, &action
, NULL
);
1300 signal(SIGTERM
, sigterm_handler
);
1301 #endif /* HAVE_SIGSET */
1304 if ((child_pid
= fork()) == 0) {
1305 /* Child comes here. */
1306 setenv("USB_PPC_STATUS", "1", false);
1308 /* Tell the kernel we want the next exec call to favor the ppc architecture... */
1309 int mib
[] = { CTL_KERN
, KERN_AFFINITY
, 1, 1 };
1311 sysctl(mib
, namelen
, NULL
, NULL
, NULL
, 0);
1313 /* Set up the arguments and call exec... */
1314 for (i
= 0; i
< argc
&& i
< (sizeof(my_argv
)/sizeof(my_argv
[0])) - 1; i
++)
1315 my_argv
[i
] = argv
[i
];
1319 execv("/usr/libexec/cups/backend/usb", my_argv
);
1321 fprintf(stderr
, "DEBUG: execv: %s\n", strerror(errno
));
1324 else if (child_pid
> 0) {
1325 /* Parent comes here.
1327 * Close the fds we won't be using then wait for the child backend to exit.
1332 fprintf(stderr
, "DEBUG: Started usb(ppc) backend (PID %d)\n", (int)child_pid
);
1334 while ((waitpid_status
= waitpid(child_pid
, &childstatus
, 0)) == (pid_t
)-1 && errno
== EINTR
)
1337 if (WIFSIGNALED(childstatus
)) {
1338 exitstatus
= WTERMSIG(childstatus
);
1339 fprintf(stderr
, "DEBUG: usb(ppc) backend %d crashed on signal %d!\n", child_pid
, exitstatus
);
1342 if ((exitstatus
= WEXITSTATUS(childstatus
)) != 0)
1343 fprintf(stderr
, "DEBUG: usb(ppc) backend %d stopped with status %d!\n", child_pid
, exitstatus
);
1345 fprintf(stderr
, "DEBUG: PID %d exited with no errors\n", child_pid
);
1350 fprintf(stderr
, "DEBUG: fork: %s\n", strerror(errno
));
1355 fprintf(stderr
, "DEBUG: usb child running i386 again\n");
1356 exitstatus
= ENOENT
;
1363 * 'sigterm_handler()' - SIGTERM handler.
1366 static void sigterm_handler(int sig
)
1368 /* If we started a child process pass the signal on to it...
1371 kill(child_pid
, sig
);
1376 #endif /* __i386__ */
1379 #ifdef PARSE_PS_ERRORS
1381 * 'next_line()' - Find the next line in a buffer.
1384 static const char *next_line (const char *buffer
)
1386 const char *cptr
, *lptr
= NULL
;
1388 for (cptr
= buffer
; *cptr
&& lptr
== NULL
; cptr
++)
1389 if (*cptr
== '\n' || *cptr
== '\r')
1396 * 'parse_pserror()' - Scan the backchannel data for postscript errors.
1399 static void parse_pserror (char *sockBuffer
, int len
)
1401 static char gErrorBuffer
[1024] = "";
1402 static char *gErrorBufferPtr
= gErrorBuffer
;
1403 static char *gErrorBufferEndPtr
= gErrorBuffer
+ sizeof(gErrorBuffer
);
1405 char *pCommentBegin
, *pCommentEnd
, *pLineEnd
;
1410 if (gErrorBufferPtr
+ len
> gErrorBufferEndPtr
- 1)
1411 gErrorBufferPtr
= gErrorBuffer
;
1412 if (len
> sizeof(gErrorBuffer
) - 1)
1413 len
= sizeof(gErrorBuffer
) - 1;
1415 memcpy(gErrorBufferPtr
, (const void *)sockBuffer
, len
);
1416 gErrorBufferPtr
+= len
;
1417 *(gErrorBufferPtr
+ 1) = '\0';
1420 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1421 while (pLineEnd
!= NULL
) {
1424 pCommentBegin
= strstr(gErrorBuffer
,"%%[");
1425 pCommentEnd
= strstr(gErrorBuffer
, "]%%");
1426 if (pCommentBegin
!= gErrorBuffer
&& pCommentEnd
!= NULL
) {
1427 pCommentEnd
+= 3; /* Skip past "]%%" */
1428 *pCommentEnd
= '\0'; /* There's always room for the nul */
1430 if (strncasecmp(pCommentBegin
, "%%[ Error:", 10) == 0)
1432 else if (strncasecmp(pCommentBegin
, "%%[ Flushing", 12) == 0)
1437 if ((logstrlen
= snprintf(logstr
, sizeof(logstr
), "%s: %s\n", logLevel
, pCommentBegin
)) >= sizeof(logstr
)) {
1438 /* If the string was trucnated make sure it has a linefeed before the nul */
1439 logstrlen
= sizeof(logstr
) - 1;
1440 logstr
[logstrlen
- 1] = '\n';
1442 write(STDERR_FILENO
, logstr
, logstrlen
);
1445 /* move everything over... */
1446 strcpy(gErrorBuffer
, pLineEnd
);
1447 gErrorBufferPtr
= gErrorBuffer
;
1448 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1451 #endif /* PARSE_PS_ERRORS */
1455 * 'read_thread()' - A thread to read the backchannel data.
1458 static void *read_thread(void *reference
)
1460 /* post a read to the device and write results to stdout
1461 * the final pending read will be Aborted in the main thread
1463 UInt8 readbuffer
[512];
1465 kern_return_t readstatus
;
1466 printer_data_t
*userData
= (printer_data_t
*)reference
;
1467 classdriver_context_t
**classdriver
= userData
->printerDriver
;
1468 struct mach_timebase_info timeBaseInfo
;
1472 /* Calculate what 250 milliSeconds are in mach absolute time...
1474 mach_timebase_info(&timeBaseInfo
);
1475 delay
= ((uint64_t)250000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1478 /* Remember when we started so we can throttle the loop after the read call...
1480 start
= mach_absolute_time();
1482 rbytes
= sizeof(readbuffer
);
1483 readstatus
= (*classdriver
)->ReadPipe( classdriver
, readbuffer
, &rbytes
);
1484 if ( kIOReturnSuccess
== readstatus
&& rbytes
> 0 ) {
1486 cupsBackChannelWrite((char*)readbuffer
, rbytes
, 1.0);
1488 /* cntrl-d is echoed by the printer.
1490 * Xerox Phaser 6250D doesn't echo the cntrl-d.
1491 * Xerox Phaser 6250D doesn't always send the product query.
1493 if (userData
->waitEOF
&& readbuffer
[rbytes
-1] == 0x4)
1495 #ifdef PARSE_PS_ERRORS
1496 parse_pserror(readbuffer
, rbytes
);
1500 /* Make sure this loop executes no more than once every 250 miliseconds...
1502 if ((readstatus
!= kIOReturnSuccess
|| rbytes
== 0) && (userData
->waitEOF
|| !userData
->done
))
1503 mach_wait_until(start
+ delay
);
1505 } while ( userData
->waitEOF
|| !userData
->done
); /* Abort from main thread tests error here */
1507 /* Let the other thread (main thread) know that we have completed the read thread...
1509 pthread_mutex_lock(&userData
->readMutex
);
1510 pthread_cond_signal(&userData
->readCompleteCondition
);
1511 pthread_mutex_unlock(&userData
->readMutex
);
1517 * 'reqestWait_thread()' - A thread cupsSideChannelDoRequest wait.
1519 static void *reqestWait_thread(void *reference
) {
1520 printer_data_t
*userData
= (printer_data_t
*)reference
;
1522 cups_sc_command_t command
;
1523 cups_sc_status_t status
;
1524 uint64_t start
, delay
;
1525 struct mach_timebase_info timeBaseInfo
;
1529 * Calculate what 100 milliSeconds are in mach absolute time...
1531 mach_timebase_info(&timeBaseInfo
);
1532 delay
= ((uint64_t)100000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1534 /* interface close wait mutex lock. */
1535 pthread_mutex_lock(&(userData
->waitCloseMutex
));
1539 * Remember when we started so we can throttle the loop after the cupsSideChannelDoRequest call...
1541 start
= mach_absolute_time();
1543 /* Poll for a command... */
1545 datalen
= sizeof(data
);
1546 bzero(data
, sizeof(data
));
1548 if (!cupsSideChannelRead(&command
, &status
, data
, &datalen
, 0.0)) {
1549 datalen
= sizeof(data
);
1552 case CUPS_SC_CMD_SOFT_RESET
:
1553 /* do a soft reset */
1554 usbSoftReset(userData
, &status
);
1556 userData
->reqWaitDone
= 1;
1558 case CUPS_SC_CMD_DRAIN_OUTPUT
:
1559 /* drain all pending output */
1560 usbDrainOutput(userData
, &status
);
1563 case CUPS_SC_CMD_GET_BIDI
:
1564 /* return whether the connection is bidirectional */
1565 usbGetBidirectional(userData
, &status
, data
, &datalen
);
1567 case CUPS_SC_CMD_GET_DEVICE_ID
:
1568 /* return the IEEE-1284 device ID */
1569 usbGetDeviceID(userData
, &status
, data
, &datalen
);
1571 case CUPS_SC_CMD_GET_STATE
:
1572 /* return the device state */
1573 usbGetDevState(userData
, &status
, data
, &datalen
);
1576 status
= CUPS_SC_STATUS_NOT_IMPLEMENTED
;
1581 if (userData
->writeDone
) {
1582 status
= CUPS_SC_STATUS_NONE
;
1585 /* Send a response... */
1586 cupsSideChannelWrite(command
, status
, data
, datalen
, 1.0);
1590 * Make sure this loop executes no more than once every 500 miliseconds...
1592 if ((userData
->waitEOF
) || (!userData
->reqWaitDone
)) {
1593 mach_wait_until(start
+ delay
);
1595 } while(!userData
->reqWaitDone
);
1598 pthread_mutex_lock(&userData
->reqWaitMutex
);
1599 userData
->reqWqitFlag
= 1;
1600 pthread_cond_signal(&userData
->reqWaitCompCond
);
1601 pthread_mutex_unlock(&userData
->reqWaitMutex
);
1603 /* interface close wait mutex unlock. */
1604 pthread_mutex_unlock(&(userData
->waitCloseMutex
));
1613 static void usbSoftReset(printer_data_t
*userData
, cups_sc_status_t
*status
) {
1617 userData
->writeDone
= 1;
1619 /* Abort (print_device()-WritePipe kIOReturnAborted return) */
1620 if (userData
->printerDriver
!= NULL
)
1621 err
= (*(userData
->printerDriver
))->Abort(userData
->printerDriver
);
1623 /* print_device() WritePipe_Loop break wait. */
1624 pthread_mutex_lock(&(userData
->writeCompMutex
));
1625 pthread_mutex_unlock(&(userData
->writeCompMutex
));
1628 if (userData
->printerDriver
!= NULL
)
1629 (*(userData
->printerDriver
))->SoftReset(userData
->printerDriver
, 0);
1632 *status
= CUPS_SC_STATUS_OK
;
1638 static void usbDrainOutput(printer_data_t
*userData
, cups_sc_status_t
*status
) {
1639 OSStatus osSts
= noErr
; /* Function results */
1640 OSStatus err
= noErr
;
1641 UInt32 wbytes
; /* Number of bytes written */
1642 ssize_t nbytes
; /* Number of bytes read */
1645 bufptr
= userData
->dataBuffer
+userData
->dataOffset
;
1646 nbytes
= userData
->dataSize
;
1648 while((nbytes
> 0) && (osSts
== noErr
)) {
1650 osSts
= (*(userData
->printerDriver
))->WritePipe(userData
->printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0);
1652 if (wbytes
< 0 || noErr
!= osSts
) {
1653 if (osSts
!= kIOReturnAborted
) {
1654 err
= (*(userData
->printerDriver
))->Abort(userData
->printerDriver
);
1663 if (status
!= NULL
) {
1664 if ((osSts
!= noErr
) || (err
!= noErr
)) {
1665 *status
= CUPS_SC_STATUS_IO_ERROR
;
1667 *status
= CUPS_SC_STATUS_OK
;
1673 * 'usbGetBidirectional'
1675 static void usbGetBidirectional(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1676 *data
= userData
->directionalFlag
;
1680 *status
= CUPS_SC_STATUS_OK
;
1686 static void usbGetDeviceID(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1687 UInt32 deviceLocation
= 0;
1688 CFStringRef deviceIDString
= NULL
;
1691 copy_devicestring(userData
->printerObj
, &deviceIDString
, &deviceLocation
);
1692 CFStringGetCString(deviceIDString
, data
, *datalen
, kCFStringEncodingUTF8
);
1693 *datalen
= strlen(data
);
1695 if (status
!= NULL
) {
1696 *status
= CUPS_SC_STATUS_OK
;
1703 static void usbGetDevState(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1704 *data
= CUPS_SC_STATE_ONLINE
;
1707 if (status
!= NULL
) {
1708 *status
= CUPS_SC_STATUS_OK
;
1713 * End of "$Id: usb-darwin.c 6302 2007-02-22 19:36:36Z mike $".