2 * "$Id: usb-darwin.c 6491 2007-04-30 18:21:52Z mike $"
4 * Copyright © 2005-2007 Apple 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>
62 #include <cups/i18n.h>
64 #include <CoreFoundation/CoreFoundation.h>
65 #include <IOKit/usb/IOUSBLib.h>
66 #include <IOKit/IOCFPlugIn.h>
72 * WAITEOF_DELAY is number of seconds we'll wait for responses from
73 * the printer after we've finished sending all the data
75 #define WAITEOF_DELAY 7
76 #define DEFAULT_TIMEOUT 60L
78 #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
79 #define kUSBLanguageEnglish 0x409
81 #define PRINTER_POLLING_INTERVAL 5 /* seconds */
82 #define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
83 #define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
85 #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
86 #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
88 #define kUSBClassDriverProperty CFSTR("USB Printing Class")
90 #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
91 #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
92 #define kWriteBufferSize 2048
97 * Section 5.3 USB Printing Class spec
99 #define kUSBPrintingSubclass 1
100 #define kUSBPrintingProtocolNoOpen 0
101 #define kUSBPrintingProtocolUnidirectional 1
102 #define kUSBPrintingProtocolBidirectional 2
104 typedef IOUSBInterfaceInterface190
**printer_interface_t
;
106 typedef struct iodevice_request_s
/**** Device request ****/
114 } iodevice_request_t
;
116 typedef union { /**** Centronics status byte ****/
119 unsigned reserved0
:2;
120 unsigned paperError
:1;
123 unsigned reserved1
:3;
125 } centronics_status_t
;
127 typedef struct classdriver_context_s
/**** Classdriver context ****/
130 CFPlugInRef plugin
; /* release plugin */
131 IUnknownVTbl
**factory
; /* Factory */
132 void *vendorReference
; /* vendor class specific usage */
133 UInt32 location
; /* unique location in bus topology */
134 UInt8 interfaceNumber
; /* Interface number */
135 UInt16 vendorID
; /* Vendor id */
136 UInt16 productID
; /* Product id */
137 printer_interface_t interface
; /* identify the device to IOKit */
138 UInt8 outpipe
; /* mandatory bulkOut pipe */
139 UInt8 inpipe
; /* optional bulkIn pipe */
141 /* general class requests */
142 kern_return_t (*DeviceRequest
)( struct classdriver_context_s
**printer
, iodevice_request_t
*iorequest
, UInt16 timeout
);
143 kern_return_t (*GetString
)( struct classdriver_context_s
**printer
, UInt8 whichString
, UInt16 language
, UInt16 timeout
, CFStringRef
*result
);
145 /* standard printer class requests */
146 kern_return_t (*SoftReset
)( struct classdriver_context_s
**printer
, UInt16 timeout
);
147 kern_return_t (*GetCentronicsStatus
)( struct classdriver_context_s
**printer
, centronics_status_t
*result
, UInt16 timeout
);
148 kern_return_t (*GetDeviceID
)( struct classdriver_context_s
**printer
, CFStringRef
*devid
, UInt16 timeout
);
150 /* standard bulk device requests */
151 kern_return_t (*ReadPipe
)( struct classdriver_context_s
**printer
, UInt8
*buffer
, UInt32
*count
);
152 kern_return_t (*WritePipe
)( struct classdriver_context_s
**printer
, UInt8
*buffer
, UInt32
*count
, Boolean eoj
);
154 /* interface requests */
155 kern_return_t (*Open
)( struct classdriver_context_s
**printer
, UInt32 location
, UInt8 protocol
);
156 kern_return_t (*Abort
)( struct classdriver_context_s
**printer
);
157 kern_return_t (*Close
)( struct classdriver_context_s
**printer
);
159 /* initialize and terminate */
160 kern_return_t (*Initialize
)( struct classdriver_context_s
**printer
, struct classdriver_context_s
**baseclass
);
161 kern_return_t (*Terminate
)( struct classdriver_context_s
**printer
);
163 } classdriver_context_t
;
166 typedef Boolean (*iterator_callback_t
)(void *refcon
, io_service_t obj
);
168 typedef struct iterator_reference_s
{ /**** Iterator reference data */
169 iterator_callback_t callback
;
172 } iterator_reference_t
;
174 typedef struct printer_data_s
{ /**** Printer context data ****/
175 io_service_t printerObj
;
176 classdriver_context_t
**printerDriver
;
178 pthread_cond_t readCompleteCondition
;
179 pthread_mutex_t readMutex
;
190 CFRunLoopTimerRef statusTimer
;
192 pthread_cond_t reqWaitCompCond
;
193 pthread_mutex_t reqWaitMutex
;
194 pthread_mutex_t waitCloseMutex
;
195 pthread_mutex_t writeCompMutex
;
199 int directionalFlag
; /* 0=uni, 1=bidi */
202 char dataBuffer
[kWriteBufferSize
];
210 static Boolean
list_device_callback(void *refcon
, io_service_t obj
);
211 static Boolean
find_device_callback(void *refcon
, io_service_t obj
);
212 static void statusTimerCallback(CFRunLoopTimerRef timer
, void *info
);
213 static void iterate_printers(iterator_callback_t callBack
, void *userdata
);
214 static void device_added(void *userdata
, io_iterator_t iterator
);
215 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
216 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
217 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***driver
);
218 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
);
219 static kern_return_t
load_printerdriver(printer_data_t
*printer
, CFStringRef
*driverBundlePath
);
220 static kern_return_t
registry_open(printer_data_t
*printer
, CFStringRef
*driverBundlePath
);
221 static kern_return_t
registry_close(printer_data_t
*printer
);
222 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
);
223 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
);
224 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
);
225 static CFStringRef
cfstr_create_and_trim(const char *cstr
);
226 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
);
227 static void setup_cfLanguage(void);
228 static void *read_thread(void *reference
);
229 static void *reqestWait_thread(void *reference
);
230 static void usbSoftReset(printer_data_t
*userData
, cups_sc_status_t
*status
);
231 static void usbDrainOutput(printer_data_t
*userData
, cups_sc_status_t
*status
);
232 static void usbGetBidirectional(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
233 static void usbGetDeviceID(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
234 static void usbGetDevState(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
237 #if defined(__i386__)
238 static pid_t child_pid
; /* Child PID */
239 static void run_ppc_backend(int argc
, char *argv
[], int fd
); /* Starts child backend process running as a ppc executable */
240 static void sigterm_handler(int sig
); /* SIGTERM handler */
241 #endif /* __i386__ */
243 #ifdef PARSE_PS_ERRORS
244 static const char *next_line (const char *buffer
);
245 static void parse_pserror (char *sockBuffer
, int len
);
246 #endif /* PARSE_PS_ERRORS */
251 * 'list_devices()' - List all USB devices.
256 iterate_printers(list_device_callback
, NULL
);
261 * 'print_device()' - Print a file to a USB device.
264 int /* O - Exit status */
265 print_device(const char *uri
, /* I - Device URI */
266 const char *hostname
, /* I - Hostname/manufacturer */
267 const char *resource
, /* I - Resource/modelname */
268 const char *options
, /* I - Device options/serial number */
269 int fd
, /* I - File descriptor to print */
270 int copies
, /* I - Copies to print */
271 int argc
, /* I - Number of command-line arguments (6 or 7) */
272 char *argv
[]) /* I - Command-line arguments */
274 printer_data_t printer_data
= { 0x0 }; /* Printer context */
275 char serial
[1024]; /* Serial number buffer */
276 OSStatus status
= noErr
; /* Function results */
277 pthread_t thr
; /* Read thread */
278 char buffer
[2048]; /* Write buffer */
279 int thread_created
= 0; /* Thread created? */
280 int countdown
= INITIAL_LOG_INTERVAL
; /* Logging interval */
281 pthread_cond_t
*readCompleteConditionPtr
= NULL
; /* Read complete condition */
282 pthread_mutex_t
*readMutexPtr
= NULL
; /* Read mutex */
283 CFStringRef driverBundlePath
; /* Class driver path */
284 int reqWait_create
= 0; /* RequestWait thread created? */
285 pthread_t reqWaitThread
; /* RequestWait thread */
286 pthread_cond_t
*reqWaitCompCondPtr
= NULL
; /* RequestWait complete condition */
287 pthread_mutex_t
*reqWaitMutexPtr
= NULL
; /* RequestWait mutex */
288 pthread_mutex_t
*waitCloseMutexPtr
= NULL
; /* wait close mutex */
289 pthread_mutex_t
*writeCompMutexPtr
= NULL
; /* write complete mutex */
292 parse_options(options
, serial
, &printer_data
.location
, &printer_data
.waitEOF
);
294 if (resource
[0] == '/')
297 printer_data
.uri
= uri
;
299 printer_data
.make
= cfstr_create_and_trim(hostname
);
300 printer_data
.model
= cfstr_create_and_trim(resource
);
301 printer_data
.serial
= cfstr_create_and_trim(serial
);
303 fputs("STATE: +connecting-to-device\n", stderr
);
306 if (printer_data
.printerObj
!= 0x0) {
307 IOObjectRelease(printer_data
.printerObj
);
308 unload_classdriver(&printer_data
.printerDriver
);
309 printer_data
.printerObj
= 0x0;
310 printer_data
.printerDriver
= 0x0;
313 fprintf(stderr
, "DEBUG: Looking for '%s %s'\n", hostname
, resource
);
314 iterate_printers(find_device_callback
, &printer_data
);
316 fputs("DEBUG: Opening connection\n", stderr
);
318 driverBundlePath
= NULL
;
319 status
= registry_open(&printer_data
, &driverBundlePath
);
320 #if defined(__i386__)
322 * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
323 * In this case try to fork & exec this backend as a ppc executable so we can use them...
325 if (status
== -2 /* kPMInvalidIOMContext */) {
326 run_ppc_backend(argc
, argv
, fd
);
327 /* Never returns here */
329 #endif /* __i386__ */
332 * If we still were unable to load the class drivers for this printer log
333 * the error and stop the queue...
336 if (driverBundlePath
== NULL
|| !CFStringGetCString(driverBundlePath
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
337 strlcpy(buffer
, "USB class driver", sizeof(buffer
));
339 fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr
);
340 fprintf(stderr
, _("FATAL: Could not load %s\n"), buffer
);
342 if (driverBundlePath
)
343 CFRelease(driverBundlePath
);
345 return CUPS_BACKEND_STOP
;
348 if (driverBundlePath
)
349 CFRelease(driverBundlePath
);
351 if (status
!= noErr
) {
352 sleep( PRINTER_POLLING_INTERVAL
);
353 countdown
-= PRINTER_POLLING_INTERVAL
;
354 if ( countdown
<= 0 ) {
355 fprintf(stderr
, _("INFO: Printer busy (status:0x%08x)\n"), (int)status
);
356 countdown
= SUBSEQUENT_LOG_INTERVAL
; /* subsequent log entries, every 15 seconds */
359 } while (status
!= noErr
);
361 fputs("STATE: -connecting-to-device\n", stderr
);
364 * Now that we are "connected" to the port, ignore SIGTERM so that we
365 * can finish out any page data the driver sends (e.g. to eject the
366 * current page... Only ignore SIGTERM if we are printing data from
367 * stdin (otherwise you can't cancel raw jobs...)
371 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
372 sigset(SIGTERM
, SIG_IGN
);
373 #elif defined(HAVE_SIGACTION)
374 memset(&action
, 0, sizeof(action
));
376 sigemptyset(&action
.sa_mask
);
377 action
.sa_handler
= SIG_IGN
;
378 sigaction(SIGTERM
, &action
, NULL
);
380 signal(SIGTERM
, SIG_IGN
);
381 #endif /* HAVE_SIGSET */
384 if (status
== noErr
) {
385 if (pthread_cond_init(&printer_data
.readCompleteCondition
, NULL
) == 0)
386 readCompleteConditionPtr
= &printer_data
.readCompleteCondition
;
388 if (pthread_mutex_init(&printer_data
.readMutex
, NULL
) == 0)
389 readMutexPtr
= &printer_data
.readMutex
;
391 printer_data
.done
= 0;
393 if (pthread_create(&thr
, NULL
, read_thread
, &printer_data
) == 0)
396 if (thread_created
== 0)
397 fputs(_("WARNING: Couldn't create read channel\n"), stderr
);
399 if (pthread_cond_init(&printer_data
.reqWaitCompCond
, NULL
) == 0)
400 reqWaitCompCondPtr
= &printer_data
.reqWaitCompCond
;
402 if (pthread_mutex_init(&printer_data
.reqWaitMutex
, NULL
) == 0)
403 reqWaitMutexPtr
= &printer_data
.reqWaitMutex
;
405 printer_data
.reqWaitDone
= 0;
406 printer_data
.reqWqitFlag
= 0;
408 if (pthread_create(&reqWaitThread
, NULL
, reqestWait_thread
, &printer_data
) == 0)
411 if (reqWait_create
== 0)
412 fputs(_("WARNING: Couldn't create sidechannel thread!\n"), stderr
);
414 if (pthread_mutex_init(&printer_data
.waitCloseMutex
, NULL
) == 0)
415 waitCloseMutexPtr
= &printer_data
.waitCloseMutex
;
417 if (pthread_mutex_init(&printer_data
.writeCompMutex
, NULL
) == 0)
418 writeCompMutexPtr
= &printer_data
.writeCompMutex
;
422 * The main thread sends the print file...
425 printer_data
.writeDone
= 0;
426 printer_data
.dataSize
= 0;
427 printer_data
.dataOffset
= 0;
428 pthread_mutex_lock(writeCompMutexPtr
);
430 while (status
== noErr
&& copies
-- > 0) {
431 UInt32 wbytes
; /* Number of bytes written */
432 ssize_t nbytes
; /* Number of bytes read */
433 off_t tbytes
= 0; /* Total number of bytes written */
435 fputs(_("INFO: Sending data\n"), stderr
);
437 if (STDIN_FILENO
!= fd
) {
438 fputs("PAGE: 1 1", stderr
);
439 lseek( fd
, 0, SEEK_SET
);
442 while (status
== noErr
&& (nbytes
= read(fd
, buffer
, sizeof(buffer
))) > 0) {
443 char *bufptr
= buffer
;
446 while (nbytes
> 0 && status
== noErr
) {
447 if (printer_data
.writeDone
) {
448 printer_data
.dataSize
= nbytes
;
449 printer_data
.dataOffset
= bufptr
- buffer
;
450 memcpy(printer_data
.dataBuffer
, buffer
, nbytes
);
457 status
= (*(printer_data
.printerDriver
))->WritePipe( printer_data
.printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0 /* nbytes > wbytes? 0: feof(fp) */ );
458 if (wbytes
< 0 || noErr
!= status
) {
459 OSStatus err
= (*(printer_data
.printerDriver
))->Abort(printer_data
.printerDriver
);
460 fprintf(stderr
, _("ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n"), status
, err
);
468 if (fd
!= 0 && status
== noErr
)
469 fprintf(stderr
, "DEBUG: Sending print file, %lld bytes...\n", (off_t
)tbytes
);
473 printer_data
.writeDone
= 1;
474 pthread_mutex_unlock(writeCompMutexPtr
);
476 if (thread_created
) {
477 /* Signal the read thread that we are done... */
478 printer_data
.done
= 1;
480 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
481 * we are not signaled in that time then force the thread to exit by setting
482 * the waiteof to be false. Plese note that this relies on us using the timeout
485 struct timespec sleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
486 pthread_mutex_lock(&printer_data
.readMutex
);
487 if (pthread_cond_timedwait(&printer_data
.readCompleteCondition
, &printer_data
.readMutex
, (const struct timespec
*)&sleepUntil
) != 0)
488 printer_data
.waitEOF
= false;
489 pthread_mutex_unlock(&printer_data
.readMutex
);
490 pthread_join( thr
,NULL
); /* wait for the child thread to return */
493 if (reqWait_create
) {
494 /* Signal the cupsSideChannelDoRequest wait thread that we are done... */
495 printer_data
.reqWaitDone
= 1;
498 * Give the cupsSideChannelDoRequest wait thread WAITEOF_DELAY seconds to complete
499 * all the data. If we are not signaled in that time then force the thread to exit
500 * by setting the waiteof to be false. Plese note that this relies on us using the
501 * timeout class driver.
503 struct timespec reqWaitSleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
504 pthread_mutex_lock(&printer_data
.reqWaitMutex
);
506 while (!printer_data
.reqWqitFlag
) {
507 if (pthread_cond_timedwait(&printer_data
.reqWaitCompCond
,
508 &printer_data
.reqWaitMutex
,
509 (const struct timespec
*)&reqWaitSleepUntil
) != 0) {
510 printer_data
.waitEOF
= false;
511 printer_data
.reqWqitFlag
= 1;
514 pthread_mutex_unlock(&printer_data
.reqWaitMutex
);
515 pthread_join(reqWaitThread
,NULL
); /* wait for the child thread to return */
518 /* interface close wait mutex(for softreset) */
519 pthread_mutex_lock(waitCloseMutexPtr
);
520 pthread_mutex_unlock(waitCloseMutexPtr
);
523 * Close the connection and input file and general clean up...
525 registry_close(&printer_data
);
527 if (STDIN_FILENO
!= fd
)
530 if (readCompleteConditionPtr
!= NULL
)
531 pthread_cond_destroy(&printer_data
.readCompleteCondition
);
533 if (readMutexPtr
!= NULL
)
534 pthread_mutex_destroy(&printer_data
.readMutex
);
536 if (waitCloseMutexPtr
!= NULL
)
537 pthread_mutex_destroy(&printer_data
.waitCloseMutex
);
539 if (writeCompMutexPtr
!= NULL
)
540 pthread_mutex_destroy(&printer_data
.writeCompMutex
);
542 if (reqWaitCompCondPtr
!= NULL
)
543 pthread_cond_destroy(&printer_data
.reqWaitCompCond
);
545 if (reqWaitMutexPtr
!= NULL
)
546 pthread_mutex_destroy(&printer_data
.reqWaitMutex
);
548 if (printer_data
.make
!= NULL
)
549 CFRelease(printer_data
.make
);
551 if (printer_data
.model
!= NULL
)
552 CFRelease(printer_data
.model
);
554 if (printer_data
.serial
!= NULL
)
555 CFRelease(printer_data
.serial
);
557 if (printer_data
.printerObj
!= 0x0)
558 IOObjectRelease(printer_data
.printerObj
);
565 * 'list_device_callback()' - list_device iterator callback.
568 static Boolean
list_device_callback(void *refcon
, io_service_t obj
)
570 Boolean keepRunning
= (obj
!= 0x0);
573 CFStringRef deviceIDString
= NULL
;
574 UInt32 deviceLocation
= 0;
576 copy_devicestring(obj
, &deviceIDString
, &deviceLocation
);
577 if (deviceIDString
!= NULL
) {
578 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
579 char uristr
[1024], makestr
[1024], modelstr
[1024], serialstr
[1024];
580 char optionsstr
[1024], idstr
[1024];
582 copy_deviceinfo(deviceIDString
, &make
, &model
, &serial
);
586 CFStringGetCString(deviceIDString
, idstr
, sizeof(idstr
),
587 kCFStringEncodingUTF8
);
590 CFStringGetCString(make
, makestr
, sizeof(makestr
),
591 kCFStringEncodingUTF8
);
593 strcpy(makestr
, "Unknown");
596 CFStringGetCString(model
, &modelstr
[1], sizeof(modelstr
)-1,
597 kCFStringEncodingUTF8
);
599 strcpy(modelstr
+ 1, "Printer");
601 optionsstr
[0] = '\0';
604 CFStringGetCString(serial
, serialstr
, sizeof(serialstr
), kCFStringEncodingUTF8
);
605 snprintf(optionsstr
, sizeof(optionsstr
), "?serial=%s", serialstr
);
607 else if (deviceLocation
!= 0)
609 snprintf(optionsstr
, sizeof(optionsstr
), "?location=%lx", deviceLocation
);
612 httpAssembleURI(HTTP_URI_CODING_ALL
, uristr
, sizeof(uristr
), "usb", NULL
, makestr
, 0, modelstr
);
613 strncat(uristr
, optionsstr
, sizeof(uristr
));
616 * Fix common HP 1284 bug...
619 if (!strcasecmp(makestr
, "Hewlett-Packard"))
620 strcpy(makestr
, "HP");
622 if (!strncasecmp(modelstr
+ 1, "hp ", 3))
623 _cups_strcpy(modelstr
+ 1, modelstr
+ 4);
625 printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr
, makestr
,
626 &modelstr
[1], makestr
, &modelstr
[1], idstr
);
628 release_deviceinfo(&make
, &model
, &serial
);
629 CFRelease(deviceIDString
);
638 * 'find_device_callback()' - print_device iterator callback.
641 static Boolean
find_device_callback(void *refcon
, io_service_t obj
)
643 Boolean keepLooking
= true;
644 printer_data_t
*userData
= (printer_data_t
*)refcon
;
647 CFStringRef idString
= NULL
;
648 UInt32 location
= -1;
650 copy_devicestring(obj
, &idString
, &location
);
651 if (idString
!= NULL
) {
652 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
654 copy_deviceinfo(idString
, &make
, &model
, &serial
);
655 if (CFStringCompare(make
, userData
->make
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
656 if (CFStringCompare(model
, userData
->model
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
657 if (userData
->serial
!= NULL
&& CFStringGetLength(userData
->serial
) > 0 ) {
658 if (serial
!= NULL
&& CFStringCompare(serial
, userData
->serial
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
660 userData
->printerObj
= obj
;
665 if (userData
->printerObj
!= 0) {
666 IOObjectRetain(userData
->printerObj
);
668 userData
->printerObj
= obj
;
671 if (userData
->location
== 0 || userData
->location
== location
) {
678 release_deviceinfo(&make
, &model
, &serial
);
683 keepLooking
= (userData
->printerObj
== 0);
684 if (obj
== 0x0 && keepLooking
) {
685 CFRunLoopTimerContext context
= { 0, userData
, NULL
, NULL
, NULL
};
686 CFRunLoopTimerRef timer
= CFRunLoopTimerCreate(NULL
, CFAbsoluteTimeGetCurrent() + 1.0, 10, 0x0, 0x0, statusTimerCallback
, &context
);
688 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
689 userData
->statusTimer
= timer
;
694 if (!keepLooking
&& userData
->statusTimer
!= NULL
) {
695 fputs("STATE: -offline-error\n", stderr
);
696 fputs(_("INFO: Printer is now on-line.\n"), stderr
);
697 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), userData
->statusTimer
, kCFRunLoopDefaultMode
);
698 CFRelease(userData
->statusTimer
);
699 userData
->statusTimer
= NULL
;
705 static void statusTimerCallback (CFRunLoopTimerRef timer
, void *info
)
707 fputs("STATE: +offline-error\n", stderr
);
708 fputs(_("INFO: Printer is currently off-line.\n"), stderr
);
713 * 'iterate_printers()' - iterate over all the printers.
716 static void iterate_printers(iterator_callback_t callBack
, void *userdata
)
718 mach_port_t masterPort
= 0x0;
719 kern_return_t kr
= IOMasterPort (bootstrap_port
, &masterPort
);
721 if (kr
== kIOReturnSuccess
&& masterPort
!= 0x0) {
722 io_iterator_t addIterator
= 0x0;
724 iterator_reference_t reference
= { callBack
, userdata
, true };
725 IONotificationPortRef addNotification
= IONotificationPortCreate(masterPort
);
727 int klass
= kUSBPrintingClass
;
728 int subklass
= kUSBPrintingSubclass
;
730 CFNumberRef usb_klass
= CFNumberCreate(NULL
, kCFNumberIntType
, &klass
);
731 CFNumberRef usb_subklass
= CFNumberCreate(NULL
, kCFNumberIntType
, &subklass
);
732 CFMutableDictionaryRef usbPrinterMatchDictionary
= IOServiceMatching(kIOUSBInterfaceClassName
);
734 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceClass"), usb_klass
);
735 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceSubClass"), usb_subklass
);
737 CFRelease(usb_klass
);
738 CFRelease(usb_subklass
);
740 kr
= IOServiceAddMatchingNotification(addNotification
, kIOMatchedNotification
, usbPrinterMatchDictionary
, &device_added
, &reference
, &addIterator
);
741 if (addIterator
!= 0x0) {
742 device_added (&reference
, addIterator
);
744 if (reference
.keepRunning
) {
745 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification
), kCFRunLoopDefaultMode
);
748 IOObjectRelease(addIterator
);
750 mach_port_deallocate(mach_task_self(), masterPort
);
756 * 'device_added()' - device added notifier.
759 static void device_added(void *userdata
, io_iterator_t iterator
)
761 iterator_reference_t
*reference
= userdata
;
764 while (reference
->keepRunning
&& (obj
= IOIteratorNext(iterator
)) != 0x0) {
765 if (reference
->callback
!= NULL
) {
766 reference
->keepRunning
= reference
->callback(reference
->userdata
, obj
);
768 IOObjectRelease(obj
);
771 /* One last call to the call back now that we are not longer have printers left to iterate...
773 if (reference
->keepRunning
)
774 reference
->keepRunning
= reference
->callback(reference
->userdata
, 0x0);
776 if (!reference
->keepRunning
) {
777 CFRunLoopStop(CFRunLoopGetCurrent());
784 * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
787 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
789 CFStringRef modelKeys
[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL
};
790 CFStringRef makeKeys
[] = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL
};
791 CFStringRef serialKeys
[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL
};
794 *make
= copy_value_for_key(deviceIDString
, makeKeys
);
796 *model
= copy_value_for_key(deviceIDString
, modelKeys
);
798 *serial
= copy_value_for_key(deviceIDString
, serialKeys
);
803 * 'release_deviceinfo()' - Release deviceinfo strings.
806 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
808 if (make
!= NULL
&& *make
!= NULL
) {
813 if (model
!= NULL
&& *model
!= NULL
) {
818 if (serial
!= NULL
&& *serial
!= NULL
) {
827 * 'load_classdriver()' - Load a classdriver.
830 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***printerDriver
)
832 kern_return_t kr
= kUSBPrinterClassDeviceNotOpen
;
833 classdriver_context_t
**driver
= NULL
;
834 CFStringRef bundle
= (driverPath
== NULL
? kUSBGenericTOPrinterClassDriver
: driverPath
);
836 if ( NULL
!= bundle
) {
837 CFURLRef url
= CFURLCreateWithFileSystemPath(NULL
, bundle
, kCFURLPOSIXPathStyle
, true);
838 CFPlugInRef plugin
= (url
!= NULL
? CFPlugInCreate(NULL
, url
) : NULL
);
843 if (plugin
!= NULL
) {
844 CFArrayRef factories
= CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID
, plugin
);
845 if (factories
!= NULL
&& CFArrayGetCount(factories
) > 0) {
846 CFUUIDRef factoryID
= CFArrayGetValueAtIndex(factories
, 0);
847 IUnknownVTbl
**iunknown
= CFPlugInInstanceCreate(NULL
, factoryID
, kUSBPrinterClassTypeID
);
848 if (NULL
!= iunknown
) {
849 kr
= (*iunknown
)->QueryInterface(iunknown
, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID
), (LPVOID
*)&driver
);
850 if (kr
== kIOReturnSuccess
&& driver
!= NULL
) {
851 classdriver_context_t
**genericDriver
= NULL
;
852 if (driverPath
!= NULL
&& CFStringCompare(driverPath
, kUSBGenericTOPrinterClassDriver
, 0) != kCFCompareEqualTo
) {
853 kr
= load_classdriver(NULL
, intf
, &genericDriver
);
856 if (kr
== kIOReturnSuccess
) {
857 (*driver
)->interface
= intf
;
858 (*driver
)->Initialize(driver
, genericDriver
);
860 (*driver
)->plugin
= plugin
;
861 (*driver
)->interface
= intf
;
862 *printerDriver
= driver
;
865 (*iunknown
)->Release(iunknown
);
867 CFRelease(factories
);
873 char bundlestr
[1024];
874 CFStringGetCString(bundle
, bundlestr
, sizeof(bundlestr
), kCFStringEncodingUTF8
);
875 fprintf(stderr
, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr
, (int)kr
);
883 * 'unload_classdriver()' - Unload a classdriver.
886 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
)
888 if (*classDriver
!= NULL
) {
889 (**classDriver
)->Release(*classDriver
);
893 return kIOReturnSuccess
;
898 * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
900 * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
903 static kern_return_t
load_printerdriver(printer_data_t
*printer
, CFStringRef
*driverBundlePath
)
905 IOCFPlugInInterface
**iodev
= NULL
;
908 printer_interface_t intf
;
911 kr
= IOCreatePlugInInterfaceForService(printer
->printerObj
, kIOUSBInterfaceUserClientTypeID
, kIOCFPlugInInterfaceID
, &iodev
, &score
);
912 if (kr
== kIOReturnSuccess
)
914 if ((res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
)) == noErr
)
916 *driverBundlePath
= IORegistryEntryCreateCFProperty(printer
->printerObj
, kUSBClassDriverProperty
, NULL
, kNilOptions
);
918 kr
= load_classdriver(*driverBundlePath
, intf
, &printer
->printerDriver
);
920 if (kr
!= kIOReturnSuccess
)
921 (*intf
)->Release(intf
);
923 IODestroyPlugInInterface(iodev
);
930 * 'registry_open()' - Open a connection to the printer.
933 static kern_return_t
registry_open(printer_data_t
*printer
, CFStringRef
*driverBundlePath
)
935 printer
->directionalFlag
= 0;
937 kern_return_t kr
= load_printerdriver(printer
, driverBundlePath
);
938 if (kr
!= kIOReturnSuccess
) {
942 if (printer
->printerDriver
!= NULL
) {
943 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolBidirectional
);
944 if (kr
!= kIOReturnSuccess
|| (*(printer
->printerDriver
))->interface
== NULL
) {
945 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolUnidirectional
);
946 if (kr
== kIOReturnSuccess
) {
947 if ((*(printer
->printerDriver
))->interface
== NULL
) {
948 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
953 printer
->directionalFlag
= 1;
957 if (kr
!= kIOReturnSuccess
) {
958 unload_classdriver(&printer
->printerDriver
);
966 * 'registry_close()' - Close the connection to the printer.
969 static kern_return_t
registry_close(printer_data_t
*printer
)
971 if (printer
->printerDriver
!= NULL
) {
972 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
974 unload_classdriver(&printer
->printerDriver
);
975 return kIOReturnSuccess
;
980 * 'copy_deviceid()' - Copy the 1284 device id string.
983 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
)
985 CFStringRef devID
= NULL
,
991 OSStatus err
= (*printer
)->GetDeviceID(printer
, &devID
, DEFAULT_TIMEOUT
);
993 copy_deviceinfo(devID
, &deviceMake
, &deviceModel
, &deviceSerial
);
995 if (deviceMake
== NULL
|| deviceModel
== NULL
|| deviceSerial
== NULL
) {
996 IOUSBDeviceDescriptor desc
;
997 iodevice_request_t request
;
999 request
.requestType
= USBmakebmRequestType( kUSBIn
, kUSBStandard
, kUSBDevice
);
1000 request
.request
= kUSBRqGetDescriptor
;
1001 request
.value
= (kUSBDeviceDesc
<< 8) | 0;
1003 request
.length
= sizeof(desc
);
1004 request
.buffer
= &desc
;
1005 err
= (*printer
)->DeviceRequest(printer
, &request
, DEFAULT_TIMEOUT
);
1006 if (err
== kIOReturnSuccess
) {
1007 CFMutableStringRef newDevID
= CFStringCreateMutable(NULL
, 0);
1009 if (deviceMake
== NULL
) {
1010 CFStringRef data
= NULL
;
1011 err
= (*printer
)->GetString(printer
, desc
.iManufacturer
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1013 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MFG:%@;"), data
);
1018 if (deviceModel
== NULL
) {
1019 CFStringRef data
= NULL
;
1020 err
= (*printer
)->GetString(printer
, desc
.iProduct
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1022 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MDL:%@;"), data
);
1027 if (deviceSerial
== NULL
&& desc
.iSerialNumber
!= 0) {
1028 CFStringRef data
= NULL
;
1029 err
= (*printer
)->GetString(printer
, desc
.iSerialNumber
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1031 CFStringAppendFormat(newDevID
, NULL
, CFSTR("SERN:%@;"), data
);
1036 if (devID
!= NULL
) {
1037 CFStringAppend(newDevID
, devID
);
1041 *deviceID
= newDevID
;
1047 release_deviceinfo(&deviceMake
, &deviceModel
, &deviceSerial
);
1054 * 'copy_devicestring()' - Copy the 1284 device id string.
1057 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
)
1059 IOCFPlugInInterface
**iodev
= NULL
;
1062 printer_interface_t intf
;
1064 classdriver_context_t
**klassDriver
= NULL
;
1065 CFStringRef driverBundlePath
;
1067 kr
= IOCreatePlugInInterfaceForService(usbInterface
, kIOUSBInterfaceUserClientTypeID
,
1068 kIOCFPlugInInterfaceID
, &iodev
, &score
);
1069 if (kr
== kIOReturnSuccess
)
1071 if ((res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
)) == noErr
)
1073 /* ignore the result for location id... */
1074 (void)(*intf
)->GetLocationID(intf
, deviceLocation
);
1076 driverBundlePath
= IORegistryEntryCreateCFProperty( usbInterface
, kUSBClassDriverProperty
, NULL
, kNilOptions
);
1078 kr
= load_classdriver(driverBundlePath
, intf
, &klassDriver
);
1080 if (kr
!= kIOReturnSuccess
&& driverBundlePath
!= NULL
)
1081 kr
= load_classdriver(NULL
, intf
, &klassDriver
);
1083 if (kr
== kIOReturnSuccess
&& klassDriver
!= NULL
)
1084 kr
= copy_deviceid(klassDriver
, deviceID
);
1086 unload_classdriver(&klassDriver
);
1088 if (driverBundlePath
!= NULL
)
1089 CFRelease(driverBundlePath
);
1091 /* (*intf)->Release(intf); */
1093 IODestroyPlugInInterface(iodev
);
1100 * 'copy_value_for_key()' - Copy value string associated with a key.
1103 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
)
1105 CFStringRef value
= NULL
;
1106 CFArrayRef kvPairs
= deviceID
!= NULL
? CFStringCreateArrayBySeparatingStrings(NULL
, deviceID
, CFSTR(";")) : NULL
;
1107 CFIndex max
= kvPairs
!= NULL
? CFArrayGetCount(kvPairs
) : 0;
1110 while (idx
< max
&& value
== NULL
) {
1111 CFStringRef kvpair
= CFArrayGetValueAtIndex(kvPairs
, idx
);
1113 while (keys
[idxx
] != NULL
&& value
== NULL
) {
1114 CFRange range
= CFStringFind(kvpair
, keys
[idxx
], kCFCompareCaseInsensitive
);
1115 if (range
.length
!= -1) {
1116 if (range
.location
!= 0) {
1117 CFMutableStringRef theString
= CFStringCreateMutableCopy(NULL
, 0, kvpair
);
1118 CFStringTrimWhitespace(theString
);
1119 range
= CFStringFind(theString
, keys
[idxx
], kCFCompareCaseInsensitive
);
1120 if (range
.location
== 0) {
1121 value
= CFStringCreateWithSubstring(NULL
, theString
, CFRangeMake(range
.length
, CFStringGetLength(theString
) - range
.length
));
1123 CFRelease(theString
);
1126 CFStringRef theString
= CFStringCreateWithSubstring(NULL
, kvpair
, CFRangeMake(range
.length
, CFStringGetLength(kvpair
) - range
.length
));
1127 CFMutableStringRef theString2
= CFStringCreateMutableCopy(NULL
, 0, theString
);
1128 CFRelease(theString
);
1130 CFStringTrimWhitespace(theString2
);
1139 if (kvPairs
!= NULL
)
1146 * 'cfstr_create_and_trim()' - Create a CFString from a c-string and
1147 * trim it's whitespace characters.
1150 CFStringRef
cfstr_create_and_trim(const char *cstr
)
1153 CFMutableStringRef cfmutablestr
= NULL
;
1155 if ((cfstr
= CFStringCreateWithCString(NULL
, cstr
, kCFStringEncodingUTF8
)) != NULL
)
1157 if ((cfmutablestr
= CFStringCreateMutableCopy(NULL
, 1024, cfstr
)) != NULL
)
1158 CFStringTrimWhitespace(cfmutablestr
);
1162 return (CFStringRef
) cfmutablestr
;
1168 * 'parse_options()' - Parse uri options.
1171 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
)
1173 char *serialnumber
; /* ?serial=<serial> or ?location=<location> */
1174 char optionName
[255], /* Name of option */
1175 value
[255], /* Value of option */
1176 *ptr
; /* Pointer into name or value */
1186 serialnumber
= NULL
;
1188 while (*options
!= '\0') {
1189 /* Get the name... */
1190 for (ptr
= optionName
; *options
&& *options
!= '=' && *options
!= '+'; )
1191 *ptr
++ = *options
++;
1196 if (*options
== '=') {
1197 /* Get the value... */
1200 for (ptr
= value
; *options
&& *options
!= '+';)
1201 *ptr
++ = *options
++;
1205 if (*options
== '+')
1208 else if (*options
== '+') {
1213 * Process the option...
1215 if (strcasecmp(optionName
, "waiteof") == 0) {
1216 if (strcasecmp(value
, "on") == 0 ||
1217 strcasecmp(value
, "yes") == 0 ||
1218 strcasecmp(value
, "true") == 0) {
1221 else if (strcasecmp(value
, "off") == 0 ||
1222 strcasecmp(value
, "no") == 0 ||
1223 strcasecmp(value
, "false") == 0) {
1227 fprintf(stderr
, _("WARNING: Boolean expected for waiteof option \"%s\"\n"), value
);
1230 else if (strcasecmp(optionName
, "serial") == 0) {
1231 strcpy(serial
, value
);
1232 serialnumber
= serial
;
1234 else if (strcasecmp(optionName
, "location") == 0 && location
) {
1235 *location
= strtol(value
, NULL
, 16);
1244 * @function setup_cfLanguage
1245 * @abstract Convert the contents of the CUPS 'LANG' environment
1246 * variable into a one element CF array of languages.
1248 * @discussion Each submitted job comes with a natural language. CUPS passes
1249 * that language in an environment variable. We take that language
1250 * and jam it into the AppleLanguages array so that CF will use
1251 * it when reading localized resources. We need to do this before
1252 * any CF code reads and caches the languages array, so this function
1253 * should be called early in main()
1255 static void setup_cfLanguage(void)
1257 CFStringRef lang
[1] = {NULL
};
1258 CFArrayRef langArray
= NULL
;
1259 const char *requestedLang
= NULL
;
1261 requestedLang
= getenv("LANG");
1262 if (requestedLang
!= NULL
) {
1263 lang
[0] = CFStringCreateWithCString(kCFAllocatorDefault
, requestedLang
, kCFStringEncodingUTF8
);
1264 langArray
= CFArrayCreate(kCFAllocatorDefault
, (const void **)lang
, sizeof(lang
) / sizeof(lang
[0]), &kCFTypeArrayCallBacks
);
1266 CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray
, kCFPreferencesCurrentApplication
);
1267 DEBUG_printf((stderr
, "DEBUG: usb: AppleLanguages = \"%s\"\n", requestedLang
));
1270 CFRelease(langArray
);
1272 fputs("DEBUG: usb: LANG environment variable missing.\n", stderr
);
1277 #if defined(__i386__)
1279 * @function run_ppc_backend
1281 * @abstract Starts child backend process running as a ppc executable.
1283 * @result Never returns; always calls exit().
1287 static void run_ppc_backend(int argc
, char *argv
[], int fd
)
1292 pid_t waitpid_status
;
1294 char *usb_ppc_status
;
1297 * If we're running as i386 and couldn't load the class driver (because they'it's
1298 * ppc-only) then try to re-exec ourselves in ppc mode to try again. If we don't have
1299 * a ppc architecture we may be running i386 again so guard against this by setting
1300 * and testing an environment variable...
1302 usb_ppc_status
= getenv("USB_PPC_STATUS");
1304 if (usb_ppc_status
== NULL
) {
1305 /* Catch SIGTERM if we are _not_ printing data from
1306 * stdin (otherwise you can't cancel raw jobs...)
1310 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1311 sigset(SIGTERM
, sigterm_handler
);
1312 #elif defined(HAVE_SIGACTION)
1313 struct sigaction action
; /* Actions for POSIX signals */
1314 memset(&action
, 0, sizeof(action
));
1315 sigaddset(&action
.sa_mask
, SIGTERM
);
1316 action
.sa_handler
= sigterm_handler
;
1317 sigaction(SIGTERM
, &action
, NULL
);
1319 signal(SIGTERM
, sigterm_handler
);
1320 #endif /* HAVE_SIGSET */
1323 if ((child_pid
= fork()) == 0) {
1324 /* Child comes here. */
1325 setenv("USB_PPC_STATUS", "1", false);
1327 /* Tell the kernel we want the next exec call to favor the ppc architecture... */
1328 int mib
[] = { CTL_KERN
, KERN_AFFINITY
, 1, 1 };
1330 sysctl(mib
, namelen
, NULL
, NULL
, NULL
, 0);
1332 /* Set up the arguments and call exec... */
1333 for (i
= 0; i
< argc
&& i
< (sizeof(my_argv
)/sizeof(my_argv
[0])) - 1; i
++)
1334 my_argv
[i
] = argv
[i
];
1338 execv("/usr/libexec/cups/backend/usb", my_argv
);
1340 fprintf(stderr
, "DEBUG: execv: %s\n", strerror(errno
));
1343 else if (child_pid
> 0) {
1344 /* Parent comes here.
1346 * Close the fds we won't be using then wait for the child backend to exit.
1351 fprintf(stderr
, "DEBUG: Started usb(ppc) backend (PID %d)\n", (int)child_pid
);
1353 while ((waitpid_status
= waitpid(child_pid
, &childstatus
, 0)) == (pid_t
)-1 && errno
== EINTR
)
1356 if (WIFSIGNALED(childstatus
)) {
1357 exitstatus
= WTERMSIG(childstatus
);
1358 fprintf(stderr
, "DEBUG: usb(ppc) backend %d crashed on signal %d!\n", child_pid
, exitstatus
);
1361 if ((exitstatus
= WEXITSTATUS(childstatus
)) != 0)
1362 fprintf(stderr
, "DEBUG: usb(ppc) backend %d stopped with status %d!\n", child_pid
, exitstatus
);
1364 fprintf(stderr
, "DEBUG: PID %d exited with no errors\n", child_pid
);
1369 fprintf(stderr
, "DEBUG: fork: %s\n", strerror(errno
));
1374 fputs("DEBUG: usb child running i386 again\n", stderr
);
1375 exitstatus
= ENOENT
;
1382 * 'sigterm_handler()' - SIGTERM handler.
1385 static void sigterm_handler(int sig
)
1387 /* If we started a child process pass the signal on to it...
1390 kill(child_pid
, sig
);
1395 #endif /* __i386__ */
1398 #ifdef PARSE_PS_ERRORS
1400 * 'next_line()' - Find the next line in a buffer.
1403 static const char *next_line (const char *buffer
)
1405 const char *cptr
, *lptr
= NULL
;
1407 for (cptr
= buffer
; *cptr
&& lptr
== NULL
; cptr
++)
1408 if (*cptr
== '\n' || *cptr
== '\r')
1415 * 'parse_pserror()' - Scan the backchannel data for postscript errors.
1418 static void parse_pserror (char *sockBuffer
, int len
)
1420 static char gErrorBuffer
[1024] = "";
1421 static char *gErrorBufferPtr
= gErrorBuffer
;
1422 static char *gErrorBufferEndPtr
= gErrorBuffer
+ sizeof(gErrorBuffer
);
1424 char *pCommentBegin
, *pCommentEnd
, *pLineEnd
;
1429 if (gErrorBufferPtr
+ len
> gErrorBufferEndPtr
- 1)
1430 gErrorBufferPtr
= gErrorBuffer
;
1431 if (len
> sizeof(gErrorBuffer
) - 1)
1432 len
= sizeof(gErrorBuffer
) - 1;
1434 memcpy(gErrorBufferPtr
, (const void *)sockBuffer
, len
);
1435 gErrorBufferPtr
+= len
;
1436 *(gErrorBufferPtr
+ 1) = '\0';
1439 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1440 while (pLineEnd
!= NULL
) {
1443 pCommentBegin
= strstr(gErrorBuffer
,"%%[");
1444 pCommentEnd
= strstr(gErrorBuffer
, "]%%");
1445 if (pCommentBegin
!= gErrorBuffer
&& pCommentEnd
!= NULL
) {
1446 pCommentEnd
+= 3; /* Skip past "]%%" */
1447 *pCommentEnd
= '\0'; /* There's always room for the nul */
1449 if (strncasecmp(pCommentBegin
, "%%[ Error:", 10) == 0)
1451 else if (strncasecmp(pCommentBegin
, "%%[ Flushing", 12) == 0)
1456 if ((logstrlen
= snprintf(logstr
, sizeof(logstr
), "%s: %s\n", logLevel
, pCommentBegin
)) >= sizeof(logstr
)) {
1457 /* If the string was trucnated make sure it has a linefeed before the nul */
1458 logstrlen
= sizeof(logstr
) - 1;
1459 logstr
[logstrlen
- 1] = '\n';
1461 write(STDERR_FILENO
, logstr
, logstrlen
);
1464 /* move everything over... */
1465 strcpy(gErrorBuffer
, pLineEnd
);
1466 gErrorBufferPtr
= gErrorBuffer
;
1467 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1470 #endif /* PARSE_PS_ERRORS */
1474 * 'read_thread()' - A thread to read the backchannel data.
1477 static void *read_thread(void *reference
)
1479 /* post a read to the device and write results to stdout
1480 * the final pending read will be Aborted in the main thread
1482 UInt8 readbuffer
[512];
1484 kern_return_t readstatus
;
1485 printer_data_t
*userData
= (printer_data_t
*)reference
;
1486 classdriver_context_t
**classdriver
= userData
->printerDriver
;
1487 struct mach_timebase_info timeBaseInfo
;
1491 /* Calculate what 250 milliSeconds are in mach absolute time...
1493 mach_timebase_info(&timeBaseInfo
);
1494 delay
= ((uint64_t)250000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1497 /* Remember when we started so we can throttle the loop after the read call...
1499 start
= mach_absolute_time();
1501 rbytes
= sizeof(readbuffer
);
1502 readstatus
= (*classdriver
)->ReadPipe( classdriver
, readbuffer
, &rbytes
);
1503 if ( kIOReturnSuccess
== readstatus
&& rbytes
> 0 ) {
1505 cupsBackChannelWrite((char*)readbuffer
, rbytes
, 1.0);
1507 /* cntrl-d is echoed by the printer.
1509 * Xerox Phaser 6250D doesn't echo the cntrl-d.
1510 * Xerox Phaser 6250D doesn't always send the product query.
1512 if (userData
->waitEOF
&& readbuffer
[rbytes
-1] == 0x4)
1514 #ifdef PARSE_PS_ERRORS
1515 parse_pserror(readbuffer
, rbytes
);
1519 /* Make sure this loop executes no more than once every 250 miliseconds...
1521 if ((readstatus
!= kIOReturnSuccess
|| rbytes
== 0) && (userData
->waitEOF
|| !userData
->done
))
1522 mach_wait_until(start
+ delay
);
1524 } while ( userData
->waitEOF
|| !userData
->done
); /* Abort from main thread tests error here */
1526 /* Let the other thread (main thread) know that we have completed the read thread...
1528 pthread_mutex_lock(&userData
->readMutex
);
1529 pthread_cond_signal(&userData
->readCompleteCondition
);
1530 pthread_mutex_unlock(&userData
->readMutex
);
1536 * 'reqestWait_thread()' - A thread cupsSideChannelDoRequest wait.
1538 static void *reqestWait_thread(void *reference
) {
1539 printer_data_t
*userData
= (printer_data_t
*)reference
;
1541 cups_sc_command_t command
;
1542 cups_sc_status_t status
;
1543 uint64_t start
, delay
;
1544 struct mach_timebase_info timeBaseInfo
;
1548 * Calculate what 100 milliSeconds are in mach absolute time...
1550 mach_timebase_info(&timeBaseInfo
);
1551 delay
= ((uint64_t)100000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1553 /* interface close wait mutex lock. */
1554 pthread_mutex_lock(&(userData
->waitCloseMutex
));
1558 * Remember when we started so we can throttle the loop after the cupsSideChannelDoRequest call...
1560 start
= mach_absolute_time();
1562 /* Poll for a command... */
1564 datalen
= sizeof(data
);
1565 bzero(data
, sizeof(data
));
1567 if (!cupsSideChannelRead(&command
, &status
, data
, &datalen
, 0.0)) {
1568 datalen
= sizeof(data
);
1571 case CUPS_SC_CMD_SOFT_RESET
:
1572 /* do a soft reset */
1573 usbSoftReset(userData
, &status
);
1575 userData
->reqWaitDone
= 1;
1577 case CUPS_SC_CMD_DRAIN_OUTPUT
:
1578 /* drain all pending output */
1579 usbDrainOutput(userData
, &status
);
1582 case CUPS_SC_CMD_GET_BIDI
:
1583 /* return whether the connection is bidirectional */
1584 usbGetBidirectional(userData
, &status
, data
, &datalen
);
1586 case CUPS_SC_CMD_GET_DEVICE_ID
:
1587 /* return the IEEE-1284 device ID */
1588 usbGetDeviceID(userData
, &status
, data
, &datalen
);
1590 case CUPS_SC_CMD_GET_STATE
:
1591 /* return the device state */
1592 usbGetDevState(userData
, &status
, data
, &datalen
);
1595 status
= CUPS_SC_STATUS_NOT_IMPLEMENTED
;
1600 if (userData
->writeDone
) {
1601 status
= CUPS_SC_STATUS_NONE
;
1604 /* Send a response... */
1605 cupsSideChannelWrite(command
, status
, data
, datalen
, 1.0);
1609 * Make sure this loop executes no more than once every 500 miliseconds...
1611 if ((userData
->waitEOF
) || (!userData
->reqWaitDone
)) {
1612 mach_wait_until(start
+ delay
);
1614 } while(!userData
->reqWaitDone
);
1617 pthread_mutex_lock(&userData
->reqWaitMutex
);
1618 userData
->reqWqitFlag
= 1;
1619 pthread_cond_signal(&userData
->reqWaitCompCond
);
1620 pthread_mutex_unlock(&userData
->reqWaitMutex
);
1622 /* interface close wait mutex unlock. */
1623 pthread_mutex_unlock(&(userData
->waitCloseMutex
));
1632 static void usbSoftReset(printer_data_t
*userData
, cups_sc_status_t
*status
) {
1636 userData
->writeDone
= 1;
1638 /* Abort (print_device()-WritePipe kIOReturnAborted return) */
1639 if (userData
->printerDriver
!= NULL
)
1640 err
= (*(userData
->printerDriver
))->Abort(userData
->printerDriver
);
1642 /* print_device() WritePipe_Loop break wait. */
1643 pthread_mutex_lock(&(userData
->writeCompMutex
));
1644 pthread_mutex_unlock(&(userData
->writeCompMutex
));
1647 if (userData
->printerDriver
!= NULL
)
1648 (*(userData
->printerDriver
))->SoftReset(userData
->printerDriver
, 0);
1651 *status
= CUPS_SC_STATUS_OK
;
1657 static void usbDrainOutput(printer_data_t
*userData
, cups_sc_status_t
*status
) {
1658 OSStatus osSts
= noErr
; /* Function results */
1659 OSStatus err
= noErr
;
1660 UInt32 wbytes
; /* Number of bytes written */
1661 ssize_t nbytes
; /* Number of bytes read */
1664 bufptr
= userData
->dataBuffer
+userData
->dataOffset
;
1665 nbytes
= userData
->dataSize
;
1667 while((nbytes
> 0) && (osSts
== noErr
)) {
1669 osSts
= (*(userData
->printerDriver
))->WritePipe(userData
->printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0);
1671 if (wbytes
< 0 || noErr
!= osSts
) {
1672 if (osSts
!= kIOReturnAborted
) {
1673 err
= (*(userData
->printerDriver
))->Abort(userData
->printerDriver
);
1682 if (status
!= NULL
) {
1683 if ((osSts
!= noErr
) || (err
!= noErr
)) {
1684 *status
= CUPS_SC_STATUS_IO_ERROR
;
1686 *status
= CUPS_SC_STATUS_OK
;
1692 * 'usbGetBidirectional'
1694 static void usbGetBidirectional(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1695 *data
= userData
->directionalFlag
;
1699 *status
= CUPS_SC_STATUS_OK
;
1705 static void usbGetDeviceID(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1706 UInt32 deviceLocation
= 0;
1707 CFStringRef deviceIDString
= NULL
;
1710 copy_devicestring(userData
->printerObj
, &deviceIDString
, &deviceLocation
);
1711 CFStringGetCString(deviceIDString
, data
, *datalen
, kCFStringEncodingUTF8
);
1712 *datalen
= strlen(data
);
1714 if (status
!= NULL
) {
1715 *status
= CUPS_SC_STATUS_OK
;
1722 static void usbGetDevState(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1723 *data
= CUPS_SC_STATE_ONLINE
;
1726 if (status
!= NULL
) {
1727 *status
= CUPS_SC_STATUS_OK
;
1732 * End of "$Id: usb-darwin.c 6491 2007-04-30 18:21:52Z mike $".