2 * "$Id: usb-darwin.c 6365 2007-03-19 20:56:57Z 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>
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 CFRunLoopTimerRef statusTimer
;
191 pthread_cond_t reqWaitCompCond
;
192 pthread_mutex_t reqWaitMutex
;
193 pthread_mutex_t waitCloseMutex
;
194 pthread_mutex_t writeCompMutex
;
198 int directionalFlag
; /* 0=uni, 1=bidi */
201 char dataBuffer
[kWriteBufferSize
];
209 static Boolean
list_device_callback(void *refcon
, io_service_t obj
);
210 static Boolean
find_device_callback(void *refcon
, io_service_t obj
);
211 static void statusTimerCallback(CFRunLoopTimerRef timer
, void *info
);
212 static void iterate_printers(iterator_callback_t callBack
, void *userdata
);
213 static void device_added(void *userdata
, io_iterator_t iterator
);
214 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
215 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
);
216 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***driver
);
217 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
);
218 static kern_return_t
load_printerdriver(printer_data_t
*printer
, CFStringRef
*driverBundlePath
);
219 static kern_return_t
registry_open(printer_data_t
*printer
, CFStringRef
*driverBundlePath
);
220 static kern_return_t
registry_close(printer_data_t
*printer
);
221 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
);
222 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
);
223 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
);
224 static CFStringRef
cfstr_create_and_trim(const char *cstr
);
225 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
);
226 static void setup_cfLanguage(void);
227 static void *read_thread(void *reference
);
228 static void *reqestWait_thread(void *reference
);
229 static void usbSoftReset(printer_data_t
*userData
, cups_sc_status_t
*status
);
230 static void usbDrainOutput(printer_data_t
*userData
, cups_sc_status_t
*status
);
231 static void usbGetBidirectional(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
232 static void usbGetDeviceID(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
233 static void usbGetDevState(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
);
236 #if defined(__i386__)
237 static pid_t child_pid
; /* Child PID */
238 static void run_ppc_backend(int argc
, char *argv
[], int fd
); /* Starts child backend process running as a ppc executable */
239 static void sigterm_handler(int sig
); /* SIGTERM handler */
240 #endif /* __i386__ */
242 #ifdef PARSE_PS_ERRORS
243 static const char *next_line (const char *buffer
);
244 static void parse_pserror (char *sockBuffer
, int len
);
245 #endif /* PARSE_PS_ERRORS */
250 * 'list_devices()' - List all USB devices.
255 iterate_printers(list_device_callback
, NULL
);
260 * 'print_device()' - Print a file to a USB device.
263 int /* O - Exit status */
264 print_device(const char *uri
, /* I - Device URI */
265 const char *hostname
, /* I - Hostname/manufacturer */
266 const char *resource
, /* I - Resource/modelname */
267 const char *options
, /* I - Device options/serial number */
268 int fd
, /* I - File descriptor to print */
269 int copies
, /* I - Copies to print */
270 int argc
, /* I - Number of command-line arguments (6 or 7) */
271 char *argv
[]) /* I - Command-line arguments */
273 printer_data_t printer_data
= { 0x0 }; /* Printer context */
274 char serial
[1024]; /* Serial number buffer */
275 OSStatus status
= noErr
; /* Function results */
276 pthread_t thr
; /* Read thread */
277 char buffer
[2048]; /* Write buffer */
278 int thread_created
= 0; /* Thread created? */
279 int countdown
= INITIAL_LOG_INTERVAL
; /* Logging interval */
280 pthread_cond_t
*readCompleteConditionPtr
= NULL
; /* Read complete condition */
281 pthread_mutex_t
*readMutexPtr
= NULL
; /* Read mutex */
282 CFStringRef driverBundlePath
; /* Class driver path */
283 int reqWait_create
= 0; /* RequestWait thread created? */
284 pthread_t reqWaitThread
; /* RequestWait thread */
285 pthread_cond_t
*reqWaitCompCondPtr
= NULL
; /* RequestWait complete condition */
286 pthread_mutex_t
*reqWaitMutexPtr
= NULL
; /* RequestWait mutex */
287 pthread_mutex_t
*waitCloseMutexPtr
= NULL
; /* wait close mutex */
288 pthread_mutex_t
*writeCompMutexPtr
= NULL
; /* write complete mutex */
291 parse_options(options
, serial
, &printer_data
.location
, &printer_data
.waitEOF
);
293 if (resource
[0] == '/')
296 printer_data
.uri
= uri
;
298 printer_data
.make
= cfstr_create_and_trim(hostname
);
299 printer_data
.model
= cfstr_create_and_trim(resource
);
300 printer_data
.serial
= cfstr_create_and_trim(serial
);
302 fputs("STATE: +connecting-to-device\n", stderr
);
305 if (printer_data
.printerObj
!= 0x0) {
306 IOObjectRelease(printer_data
.printerObj
);
307 unload_classdriver(&printer_data
.printerDriver
);
308 printer_data
.printerObj
= 0x0;
309 printer_data
.printerDriver
= 0x0;
312 fprintf(stderr
, "INFO: Looking for '%s %s'\n", hostname
, resource
);
313 iterate_printers(find_device_callback
, &printer_data
);
315 fprintf(stderr
, "INFO: Opening Connection\n");
317 driverBundlePath
= NULL
;
318 status
= registry_open(&printer_data
, &driverBundlePath
);
319 #if defined(__i386__)
321 * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
322 * In this case try to fork & exec this backend as a ppc executable so we can use them...
324 if (status
== -2 /* kPMInvalidIOMContext */) {
325 run_ppc_backend(argc
, argv
, fd
);
326 /* Never returns here */
328 #endif /* __i386__ */
331 * If we still were unable to load the class drivers for this printer log
332 * the error and stop the queue...
335 if (driverBundlePath
== NULL
|| !CFStringGetCString(driverBundlePath
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
336 strlcpy(buffer
, "USB class driver", sizeof(buffer
));
338 fprintf(stderr
, "STATE: +apple-missing-usbclassdriver-error\n" \
339 "FATAL: Could not load %s\n", buffer
);
341 if (driverBundlePath
)
342 CFRelease(driverBundlePath
);
344 return CUPS_BACKEND_STOP
;
347 if (driverBundlePath
)
348 CFRelease(driverBundlePath
);
350 if (status
!= noErr
) {
351 sleep( PRINTER_POLLING_INTERVAL
);
352 countdown
-= PRINTER_POLLING_INTERVAL
;
353 if ( countdown
<= 0 ) {
354 fprintf(stderr
, "INFO: Printer busy (status:0x%08x)\n", (int)status
);
355 countdown
= SUBSEQUENT_LOG_INTERVAL
; /* subsequent log entries, every 15 seconds */
358 } while (status
!= noErr
);
360 fputs("STATE: -connecting-to-device\n", stderr
);
363 * Now that we are "connected" to the port, ignore SIGTERM so that we
364 * can finish out any page data the driver sends (e.g. to eject the
365 * current page... Only ignore SIGTERM if we are printing data from
366 * stdin (otherwise you can't cancel raw jobs...)
370 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
371 sigset(SIGTERM
, SIG_IGN
);
372 #elif defined(HAVE_SIGACTION)
373 memset(&action
, 0, sizeof(action
));
375 sigemptyset(&action
.sa_mask
);
376 action
.sa_handler
= SIG_IGN
;
377 sigaction(SIGTERM
, &action
, NULL
);
379 signal(SIGTERM
, SIG_IGN
);
380 #endif /* HAVE_SIGSET */
383 if (status
== noErr
) {
384 if (pthread_cond_init(&printer_data
.readCompleteCondition
, NULL
) == 0)
385 readCompleteConditionPtr
= &printer_data
.readCompleteCondition
;
387 if (pthread_mutex_init(&printer_data
.readMutex
, NULL
) == 0)
388 readMutexPtr
= &printer_data
.readMutex
;
390 printer_data
.done
= 0;
392 if (pthread_create(&thr
, NULL
, read_thread
, &printer_data
) == 0)
395 if (thread_created
== 0)
396 fprintf(stderr
, "WARNING: Couldn't create read channel\n");
398 if (pthread_cond_init(&printer_data
.reqWaitCompCond
, NULL
) == 0)
399 reqWaitCompCondPtr
= &printer_data
.reqWaitCompCond
;
401 if (pthread_mutex_init(&printer_data
.reqWaitMutex
, NULL
) == 0)
402 reqWaitMutexPtr
= &printer_data
.reqWaitMutex
;
404 printer_data
.reqWaitDone
= 0;
405 printer_data
.reqWqitFlag
= 0;
407 if (pthread_create(&reqWaitThread
, NULL
, reqestWait_thread
, &printer_data
) == 0)
410 if (reqWait_create
== 0)
411 fprintf(stderr
, "WARNING: Couldn't create sidechannel thread!\n");
413 if (pthread_mutex_init(&printer_data
.waitCloseMutex
, NULL
) == 0)
414 waitCloseMutexPtr
= &printer_data
.waitCloseMutex
;
416 if (pthread_mutex_init(&printer_data
.writeCompMutex
, NULL
) == 0)
417 writeCompMutexPtr
= &printer_data
.writeCompMutex
;
421 * The main thread sends the print file...
424 printer_data
.writeDone
= 0;
425 printer_data
.dataSize
= 0;
426 printer_data
.dataOffset
= 0;
427 pthread_mutex_lock(writeCompMutexPtr
);
429 while (status
== noErr
&& copies
-- > 0) {
430 UInt32 wbytes
; /* Number of bytes written */
431 ssize_t nbytes
; /* Number of bytes read */
432 off_t tbytes
= 0; /* Total number of bytes written */
434 fprintf(stderr
, "INFO: Sending data\n");
436 if (STDIN_FILENO
!= fd
) {
437 fputs("PAGE: 1 1", stderr
);
438 lseek( fd
, 0, SEEK_SET
);
441 while (status
== noErr
&& (nbytes
= read(fd
, buffer
, sizeof(buffer
))) > 0) {
442 char *bufptr
= buffer
;
445 while (nbytes
> 0 && status
== noErr
) {
446 if (printer_data
.writeDone
) {
447 printer_data
.dataSize
= nbytes
;
448 printer_data
.dataOffset
= bufptr
- buffer
;
449 memcpy(printer_data
.dataBuffer
, buffer
, nbytes
);
456 status
= (*(printer_data
.printerDriver
))->WritePipe( printer_data
.printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0 /* nbytes > wbytes? 0: feof(fp) */ );
457 if (wbytes
< 0 || noErr
!= status
) {
458 OSStatus err
= (*(printer_data
.printerDriver
))->Abort(printer_data
.printerDriver
);
459 fprintf(stderr
, "ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n", status
, err
);
467 if (fd
!= 0 && status
== noErr
)
468 fprintf(stderr
, "DEBUG: Sending print file, %qd bytes...\n", (off_t
)tbytes
);
472 printer_data
.writeDone
= 1;
473 pthread_mutex_unlock(writeCompMutexPtr
);
475 if (thread_created
) {
476 /* Signal the read thread that we are done... */
477 printer_data
.done
= 1;
479 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
480 * we are not signaled in that time then force the thread to exit by setting
481 * the waiteof to be false. Plese note that this relies on us using the timeout
484 struct timespec sleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
485 pthread_mutex_lock(&printer_data
.readMutex
);
486 if (pthread_cond_timedwait(&printer_data
.readCompleteCondition
, &printer_data
.readMutex
, (const struct timespec
*)&sleepUntil
) != 0)
487 printer_data
.waitEOF
= false;
488 pthread_mutex_unlock(&printer_data
.readMutex
);
489 pthread_join( thr
,NULL
); /* wait for the child thread to return */
492 if (reqWait_create
) {
493 /* Signal the cupsSideChannelDoRequest wait thread that we are done... */
494 printer_data
.reqWaitDone
= 1;
497 * Give the cupsSideChannelDoRequest wait thread WAITEOF_DELAY seconds to complete
498 * all the data. If we are not signaled in that time then force the thread to exit
499 * by setting the waiteof to be false. Plese note that this relies on us using the
500 * timeout class driver.
502 struct timespec reqWaitSleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
503 pthread_mutex_lock(&printer_data
.reqWaitMutex
);
505 while (!printer_data
.reqWqitFlag
) {
506 if (pthread_cond_timedwait(&printer_data
.reqWaitCompCond
,
507 &printer_data
.reqWaitMutex
,
508 (const struct timespec
*)&reqWaitSleepUntil
) != 0) {
509 printer_data
.waitEOF
= false;
510 printer_data
.reqWqitFlag
= 1;
513 pthread_mutex_unlock(&printer_data
.reqWaitMutex
);
514 pthread_join(reqWaitThread
,NULL
); /* wait for the child thread to return */
517 /* interface close wait mutex(for softreset) */
518 pthread_mutex_lock(waitCloseMutexPtr
);
519 pthread_mutex_unlock(waitCloseMutexPtr
);
522 * Close the connection and input file and general clean up...
524 registry_close(&printer_data
);
526 if (STDIN_FILENO
!= fd
)
529 if (readCompleteConditionPtr
!= NULL
)
530 pthread_cond_destroy(&printer_data
.readCompleteCondition
);
532 if (readMutexPtr
!= NULL
)
533 pthread_mutex_destroy(&printer_data
.readMutex
);
535 if (waitCloseMutexPtr
!= NULL
)
536 pthread_mutex_destroy(&printer_data
.waitCloseMutex
);
538 if (writeCompMutexPtr
!= NULL
)
539 pthread_mutex_destroy(&printer_data
.writeCompMutex
);
541 if (reqWaitCompCondPtr
!= NULL
)
542 pthread_cond_destroy(&printer_data
.reqWaitCompCond
);
544 if (reqWaitMutexPtr
!= NULL
)
545 pthread_mutex_destroy(&printer_data
.reqWaitMutex
);
547 if (printer_data
.make
!= NULL
)
548 CFRelease(printer_data
.make
);
550 if (printer_data
.model
!= NULL
)
551 CFRelease(printer_data
.model
);
553 if (printer_data
.serial
!= NULL
)
554 CFRelease(printer_data
.serial
);
556 if (printer_data
.printerObj
!= 0x0)
557 IOObjectRelease(printer_data
.printerObj
);
564 * 'list_device_callback()' - list_device iterator callback.
567 static Boolean
list_device_callback(void *refcon
, io_service_t obj
)
569 Boolean keepRunning
= (obj
!= 0x0);
572 CFStringRef deviceIDString
= NULL
;
573 UInt32 deviceLocation
= 0;
575 copy_devicestring(obj
, &deviceIDString
, &deviceLocation
);
576 if (deviceIDString
!= NULL
) {
577 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
578 char uristr
[1024], makestr
[1024], modelstr
[1024], serialstr
[1024];
579 char optionsstr
[1024], idstr
[1024];
581 copy_deviceinfo(deviceIDString
, &make
, &model
, &serial
);
585 CFStringGetCString(deviceIDString
, idstr
, sizeof(idstr
),
586 kCFStringEncodingUTF8
);
589 CFStringGetCString(make
, makestr
, sizeof(makestr
),
590 kCFStringEncodingUTF8
);
592 strcpy(makestr
, "Unknown");
595 CFStringGetCString(model
, &modelstr
[1], sizeof(modelstr
)-1,
596 kCFStringEncodingUTF8
);
598 strcpy(modelstr
+ 1, "Printer");
601 * Fix common HP 1284 bug...
604 if (!strcasecmp(makestr
, "Hewlett-Packard"))
605 strcpy(makestr
, "HP");
607 if (!strncasecmp(modelstr
+ 1, "hp ", 3))
608 _cups_strcpy(modelstr
+ 1, modelstr
+ 4);
610 optionsstr
[0] = '\0';
613 CFStringGetCString(serial
, serialstr
, sizeof(serialstr
), kCFStringEncodingUTF8
);
614 snprintf(optionsstr
, sizeof(optionsstr
), "?serial=%s", serialstr
);
616 else if (deviceLocation
!= 0)
618 snprintf(optionsstr
, sizeof(optionsstr
), "?location=%lx", deviceLocation
);
621 httpAssembleURI(HTTP_URI_CODING_ALL
, uristr
, sizeof(uristr
), "usb", NULL
, makestr
, 0, modelstr
);
622 strncat(uristr
, optionsstr
, sizeof(uristr
));
624 printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr
, makestr
,
625 &modelstr
[1], makestr
, &modelstr
[1], idstr
);
627 release_deviceinfo(&make
, &model
, &serial
);
628 CFRelease(deviceIDString
);
637 * 'find_device_callback()' - print_device iterator callback.
640 static Boolean
find_device_callback(void *refcon
, io_service_t obj
)
642 Boolean keepLooking
= true;
643 printer_data_t
*userData
= (printer_data_t
*)refcon
;
646 CFStringRef idString
= NULL
;
647 UInt32 location
= -1;
649 copy_devicestring(obj
, &idString
, &location
);
650 if (idString
!= NULL
) {
651 CFStringRef make
= NULL
, model
= NULL
, serial
= NULL
;
653 copy_deviceinfo(idString
, &make
, &model
, &serial
);
654 if (CFStringCompare(make
, userData
->make
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
655 if (CFStringCompare(model
, userData
->model
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
656 if (userData
->serial
!= NULL
) {
657 if (serial
!= NULL
&& CFStringCompare(serial
, userData
->serial
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
659 userData
->printerObj
= obj
;
664 if (userData
->printerObj
!= 0) {
665 IOObjectRetain(userData
->printerObj
);
667 userData
->printerObj
= obj
;
670 if (userData
->location
== 0 || userData
->location
== location
) {
677 release_deviceinfo(&make
, &model
, &serial
);
682 keepLooking
= (userData
->printerObj
== 0);
683 if (obj
== 0x0 && keepLooking
) {
684 CFRunLoopTimerContext context
= { 0, userData
, NULL
, NULL
, NULL
};
685 CFRunLoopTimerRef timer
= CFRunLoopTimerCreate(NULL
, CFAbsoluteTimeGetCurrent() + 1.0, 10, 0x0, 0x0, statusTimerCallback
, &context
);
687 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
688 userData
->statusTimer
= timer
;
693 if (!keepLooking
&& userData
->statusTimer
!= NULL
) {
694 fputs("STATE: -offline-error\n", stderr
);
695 fputs("INFO: Printer is now on-line.\n", stderr
);
696 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), userData
->statusTimer
, kCFRunLoopDefaultMode
);
697 CFRelease(userData
->statusTimer
);
698 userData
->statusTimer
= NULL
;
704 static void statusTimerCallback (CFRunLoopTimerRef timer
, void *info
)
706 fputs("STATE: +offline-error\n", stderr
);
707 fputs("INFO: Printer is currently off-line.\n", stderr
);
712 * 'iterate_printers()' - iterate over all the printers.
715 static void iterate_printers(iterator_callback_t callBack
, void *userdata
)
717 mach_port_t masterPort
= 0x0;
718 kern_return_t kr
= IOMasterPort (bootstrap_port
, &masterPort
);
720 if (kr
== kIOReturnSuccess
&& masterPort
!= 0x0) {
721 io_iterator_t addIterator
= 0x0;
723 iterator_reference_t reference
= { callBack
, userdata
, true };
724 IONotificationPortRef addNotification
= IONotificationPortCreate(masterPort
);
726 int klass
= kUSBPrintingClass
;
727 int subklass
= kUSBPrintingSubclass
;
729 CFNumberRef usb_klass
= CFNumberCreate(NULL
, kCFNumberIntType
, &klass
);
730 CFNumberRef usb_subklass
= CFNumberCreate(NULL
, kCFNumberIntType
, &subklass
);
731 CFMutableDictionaryRef usbPrinterMatchDictionary
= IOServiceMatching(kIOUSBInterfaceClassName
);
733 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceClass"), usb_klass
);
734 CFDictionaryAddValue(usbPrinterMatchDictionary
, CFSTR("bInterfaceSubClass"), usb_subklass
);
736 CFRelease(usb_klass
);
737 CFRelease(usb_subklass
);
739 kr
= IOServiceAddMatchingNotification(addNotification
, kIOMatchedNotification
, usbPrinterMatchDictionary
, &device_added
, &reference
, &addIterator
);
740 if (addIterator
!= 0x0) {
741 device_added (&reference
, addIterator
);
743 if (reference
.keepRunning
) {
744 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification
), kCFRunLoopDefaultMode
);
747 IOObjectRelease(addIterator
);
749 mach_port_deallocate(mach_task_self(), masterPort
);
755 * 'device_added()' - device added notifier.
758 static void device_added(void *userdata
, io_iterator_t iterator
)
760 iterator_reference_t
*reference
= userdata
;
763 while (reference
->keepRunning
&& (obj
= IOIteratorNext(iterator
)) != 0x0) {
764 if (reference
->callback
!= NULL
) {
765 reference
->keepRunning
= reference
->callback(reference
->userdata
, obj
);
767 IOObjectRelease(obj
);
770 /* One last call to the call back now that we are not longer have printers left to iterate...
772 if (reference
->keepRunning
)
773 reference
->keepRunning
= reference
->callback(reference
->userdata
, 0x0);
775 if (!reference
->keepRunning
) {
776 CFRunLoopStop(CFRunLoopGetCurrent());
783 * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
786 static void copy_deviceinfo(CFStringRef deviceIDString
, CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
788 CFStringRef modelKeys
[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL
};
789 CFStringRef makeKeys
[] = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL
};
790 CFStringRef serialKeys
[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL
};
793 *make
= copy_value_for_key(deviceIDString
, makeKeys
);
795 *model
= copy_value_for_key(deviceIDString
, modelKeys
);
797 *serial
= copy_value_for_key(deviceIDString
, serialKeys
);
802 * 'release_deviceinfo()' - Release deviceinfo strings.
805 static void release_deviceinfo(CFStringRef
*make
, CFStringRef
*model
, CFStringRef
*serial
)
807 if (make
!= NULL
&& *make
!= NULL
) {
812 if (model
!= NULL
&& *model
!= NULL
) {
817 if (serial
!= NULL
&& *serial
!= NULL
) {
826 * 'load_classdriver()' - Load a classdriver.
829 static kern_return_t
load_classdriver(CFStringRef driverPath
, printer_interface_t intf
, classdriver_context_t
***printerDriver
)
831 kern_return_t kr
= kUSBPrinterClassDeviceNotOpen
;
832 classdriver_context_t
**driver
= NULL
;
833 CFStringRef bundle
= (driverPath
== NULL
? kUSBGenericTOPrinterClassDriver
: driverPath
);
835 if ( NULL
!= bundle
) {
836 CFURLRef url
= CFURLCreateWithFileSystemPath(NULL
, bundle
, kCFURLPOSIXPathStyle
, true);
837 CFPlugInRef plugin
= (url
!= NULL
? CFPlugInCreate(NULL
, url
) : NULL
);
842 if (plugin
!= NULL
) {
843 CFArrayRef factories
= CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID
, plugin
);
844 if (factories
!= NULL
&& CFArrayGetCount(factories
) > 0) {
845 CFUUIDRef factoryID
= CFArrayGetValueAtIndex(factories
, 0);
846 IUnknownVTbl
**iunknown
= CFPlugInInstanceCreate(NULL
, factoryID
, kUSBPrinterClassTypeID
);
847 if (NULL
!= iunknown
) {
848 kr
= (*iunknown
)->QueryInterface(iunknown
, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID
), (LPVOID
*)&driver
);
849 if (kr
== kIOReturnSuccess
&& driver
!= NULL
) {
850 classdriver_context_t
**genericDriver
= NULL
;
851 if (driverPath
!= NULL
&& CFStringCompare(driverPath
, kUSBGenericTOPrinterClassDriver
, 0) != kCFCompareEqualTo
) {
852 kr
= load_classdriver(NULL
, intf
, &genericDriver
);
855 if (kr
== kIOReturnSuccess
) {
856 (*driver
)->interface
= intf
;
857 (*driver
)->Initialize(driver
, genericDriver
);
859 (*driver
)->plugin
= plugin
;
860 (*driver
)->interface
= intf
;
861 *printerDriver
= driver
;
864 (*iunknown
)->Release(iunknown
);
866 CFRelease(factories
);
872 char bundlestr
[1024];
873 CFStringGetCString(bundle
, bundlestr
, sizeof(bundlestr
), kCFStringEncodingUTF8
);
874 fprintf(stderr
, "DEBUG:load_classdriver(%s) (kr:0x%08x)\n", bundlestr
, (int)kr
);
882 * 'unload_classdriver()' - Unload a classdriver.
885 static kern_return_t
unload_classdriver(classdriver_context_t
***classDriver
)
887 if (*classDriver
!= NULL
) {
888 (**classDriver
)->Release(*classDriver
);
892 return kIOReturnSuccess
;
897 * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
899 * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
902 static kern_return_t
load_printerdriver(printer_data_t
*printer
, CFStringRef
*driverBundlePath
)
904 IOCFPlugInInterface
**iodev
= NULL
;
907 printer_interface_t intf
;
910 kr
= IOCreatePlugInInterfaceForService(printer
->printerObj
, kIOUSBInterfaceUserClientTypeID
, kIOCFPlugInInterfaceID
, &iodev
, &score
);
911 if (kr
== kIOReturnSuccess
)
913 if ((res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
)) == noErr
)
915 *driverBundlePath
= IORegistryEntryCreateCFProperty(printer
->printerObj
, kUSBClassDriverProperty
, NULL
, kNilOptions
);
917 kr
= load_classdriver(*driverBundlePath
, intf
, &printer
->printerDriver
);
919 if (kr
!= kIOReturnSuccess
)
920 (*intf
)->Release(intf
);
922 IODestroyPlugInInterface(iodev
);
929 * 'registry_open()' - Open a connection to the printer.
932 static kern_return_t
registry_open(printer_data_t
*printer
, CFStringRef
*driverBundlePath
)
934 printer
->directionalFlag
= 0;
936 kern_return_t kr
= load_printerdriver(printer
, driverBundlePath
);
937 if (kr
!= kIOReturnSuccess
) {
941 if (printer
->printerDriver
!= NULL
) {
942 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolBidirectional
);
943 if (kr
!= kIOReturnSuccess
|| (*(printer
->printerDriver
))->interface
== NULL
) {
944 kr
= (*(printer
->printerDriver
))->Open(printer
->printerDriver
, printer
->location
, kUSBPrintingProtocolUnidirectional
);
945 if (kr
== kIOReturnSuccess
) {
946 if ((*(printer
->printerDriver
))->interface
== NULL
) {
947 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
952 printer
->directionalFlag
= 1;
956 if (kr
!= kIOReturnSuccess
) {
957 unload_classdriver(&printer
->printerDriver
);
965 * 'registry_close()' - Close the connection to the printer.
968 static kern_return_t
registry_close(printer_data_t
*printer
)
970 if (printer
->printerDriver
!= NULL
) {
971 (*(printer
->printerDriver
))->Close(printer
->printerDriver
);
973 unload_classdriver(&printer
->printerDriver
);
974 return kIOReturnSuccess
;
979 * 'copy_deviceid()' - Copy the 1284 device id string.
982 static OSStatus
copy_deviceid(classdriver_context_t
**printer
, CFStringRef
*deviceID
)
984 CFStringRef devID
= NULL
,
990 OSStatus err
= (*printer
)->GetDeviceID(printer
, &devID
, DEFAULT_TIMEOUT
);
992 copy_deviceinfo(devID
, &deviceMake
, &deviceModel
, &deviceSerial
);
994 if (deviceMake
== NULL
|| deviceModel
== NULL
|| deviceSerial
== NULL
) {
995 IOUSBDeviceDescriptor desc
;
996 iodevice_request_t request
;
998 request
.requestType
= USBmakebmRequestType( kUSBIn
, kUSBStandard
, kUSBDevice
);
999 request
.request
= kUSBRqGetDescriptor
;
1000 request
.value
= (kUSBDeviceDesc
<< 8) | 0;
1002 request
.length
= sizeof(desc
);
1003 request
.buffer
= &desc
;
1004 err
= (*printer
)->DeviceRequest(printer
, &request
, DEFAULT_TIMEOUT
);
1005 if (err
== kIOReturnSuccess
) {
1006 CFMutableStringRef newDevID
= CFStringCreateMutable(NULL
, 0);
1008 if (deviceMake
== NULL
) {
1009 CFStringRef data
= NULL
;
1010 err
= (*printer
)->GetString(printer
, desc
.iManufacturer
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1012 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MFG:%@;"), data
);
1017 if (deviceModel
== NULL
) {
1018 CFStringRef data
= NULL
;
1019 err
= (*printer
)->GetString(printer
, desc
.iProduct
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1021 CFStringAppendFormat(newDevID
, NULL
, CFSTR("MDL:%@;"), data
);
1026 if (deviceSerial
== NULL
&& desc
.iSerialNumber
!= 0) {
1027 CFStringRef data
= NULL
;
1028 err
= (*printer
)->GetString(printer
, desc
.iSerialNumber
, kUSBLanguageEnglish
, DEFAULT_TIMEOUT
, &data
);
1030 CFStringAppendFormat(newDevID
, NULL
, CFSTR("SERN:%@;"), data
);
1035 if (devID
!= NULL
) {
1036 CFStringAppend(newDevID
, devID
);
1040 *deviceID
= newDevID
;
1046 release_deviceinfo(&deviceMake
, &deviceModel
, &deviceSerial
);
1053 * 'copy_devicestring()' - Copy the 1284 device id string.
1056 static void copy_devicestring(io_service_t usbInterface
, CFStringRef
*deviceID
, UInt32
*deviceLocation
)
1058 IOCFPlugInInterface
**iodev
= NULL
;
1061 printer_interface_t intf
;
1063 classdriver_context_t
**klassDriver
= NULL
;
1064 CFStringRef driverBundlePath
;
1066 kr
= IOCreatePlugInInterfaceForService(usbInterface
, kIOUSBInterfaceUserClientTypeID
,
1067 kIOCFPlugInInterfaceID
, &iodev
, &score
);
1068 if (kr
== kIOReturnSuccess
)
1070 if ((res
= (*iodev
)->QueryInterface(iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
)) == noErr
)
1072 /* ignore the result for location id... */
1073 (void)(*intf
)->GetLocationID(intf
, deviceLocation
);
1075 driverBundlePath
= IORegistryEntryCreateCFProperty( usbInterface
, kUSBClassDriverProperty
, NULL
, kNilOptions
);
1077 kr
= load_classdriver(driverBundlePath
, intf
, &klassDriver
);
1079 if (kr
!= kIOReturnSuccess
&& driverBundlePath
!= NULL
)
1080 kr
= load_classdriver(NULL
, intf
, &klassDriver
);
1082 if (kr
== kIOReturnSuccess
&& klassDriver
!= NULL
)
1083 kr
= copy_deviceid(klassDriver
, deviceID
);
1085 unload_classdriver(&klassDriver
);
1087 if (driverBundlePath
!= NULL
)
1088 CFRelease(driverBundlePath
);
1090 /* (*intf)->Release(intf); */
1092 IODestroyPlugInInterface(iodev
);
1099 * 'copy_value_for_key()' - Copy value string associated with a key.
1102 static CFStringRef
copy_value_for_key(CFStringRef deviceID
, CFStringRef
*keys
)
1104 CFStringRef value
= NULL
;
1105 CFArrayRef kvPairs
= deviceID
!= NULL
? CFStringCreateArrayBySeparatingStrings(NULL
, deviceID
, CFSTR(";")) : NULL
;
1106 CFIndex max
= kvPairs
!= NULL
? CFArrayGetCount(kvPairs
) : 0;
1109 while (idx
< max
&& value
== NULL
) {
1110 CFStringRef kvpair
= CFArrayGetValueAtIndex(kvPairs
, idx
);
1112 while (keys
[idxx
] != NULL
&& value
== NULL
) {
1113 CFRange range
= CFStringFind(kvpair
, keys
[idxx
], kCFCompareCaseInsensitive
);
1114 if (range
.length
!= -1) {
1115 if (range
.location
!= 0) {
1116 CFMutableStringRef theString
= CFStringCreateMutableCopy(NULL
, 0, kvpair
);
1117 CFStringTrimWhitespace(theString
);
1118 range
= CFStringFind(theString
, keys
[idxx
], kCFCompareCaseInsensitive
);
1119 if (range
.location
== 0) {
1120 value
= CFStringCreateWithSubstring(NULL
, theString
, CFRangeMake(range
.length
, CFStringGetLength(theString
) - range
.length
));
1122 CFRelease(theString
);
1125 CFStringRef theString
= CFStringCreateWithSubstring(NULL
, kvpair
, CFRangeMake(range
.length
, CFStringGetLength(kvpair
) - range
.length
));
1126 CFMutableStringRef theString2
= CFStringCreateMutableCopy(NULL
, 0, theString
);
1127 CFRelease(theString
);
1129 CFStringTrimWhitespace(theString2
);
1138 if (kvPairs
!= NULL
)
1145 * 'cfstr_create_and_trim()' - Create a CFString from a c-string and
1146 * trim it's whitespace characters.
1149 CFStringRef
cfstr_create_and_trim(const char *cstr
)
1152 CFMutableStringRef cfmutablestr
= NULL
;
1154 if ((cfstr
= CFStringCreateWithCString(NULL
, cstr
, kCFStringEncodingUTF8
)) != NULL
)
1156 if ((cfmutablestr
= CFStringCreateMutableCopy(NULL
, 1024, cfstr
)) != NULL
)
1157 CFStringTrimWhitespace(cfmutablestr
);
1161 return (CFStringRef
) cfmutablestr
;
1167 * 'parse_options()' - Parse uri options.
1170 static void parse_options(const char *options
, char *serial
, UInt32
*location
, Boolean
*waitEOF
)
1172 char *serialnumber
; /* ?serial=<serial> or ?location=<location> */
1173 char optionName
[255], /* Name of option */
1174 value
[255], /* Value of option */
1175 *ptr
; /* Pointer into name or value */
1185 serialnumber
= NULL
;
1187 while (*options
!= '\0') {
1188 /* Get the name... */
1189 for (ptr
= optionName
; *options
&& *options
!= '=' && *options
!= '+'; )
1190 *ptr
++ = *options
++;
1195 if (*options
== '=') {
1196 /* Get the value... */
1199 for (ptr
= value
; *options
&& *options
!= '+';)
1200 *ptr
++ = *options
++;
1204 if (*options
== '+')
1207 else if (*options
== '+') {
1212 * Process the option...
1214 if (strcasecmp(optionName
, "waiteof") == 0) {
1215 if (strcasecmp(value
, "on") == 0 ||
1216 strcasecmp(value
, "yes") == 0 ||
1217 strcasecmp(value
, "true") == 0) {
1220 else if (strcasecmp(value
, "off") == 0 ||
1221 strcasecmp(value
, "no") == 0 ||
1222 strcasecmp(value
, "false") == 0) {
1226 fprintf(stderr
, "WARNING: Boolean expected for waiteof option \"%s\"\n", value
);
1229 else if (strcasecmp(optionName
, "serial") == 0) {
1230 strcpy(serial
, value
);
1231 serialnumber
= serial
;
1233 else if (strcasecmp(optionName
, "location") == 0 && location
) {
1234 *location
= strtol(value
, NULL
, 16);
1243 * @function setup_cfLanguage
1244 * @abstract Convert the contents of the CUPS 'LANG' environment
1245 * variable into a one element CF array of languages.
1247 * @discussion Each submitted job comes with a natural language. CUPS passes
1248 * that language in an environment variable. We take that language
1249 * and jam it into the AppleLanguages array so that CF will use
1250 * it when reading localized resources. We need to do this before
1251 * any CF code reads and caches the languages array, so this function
1252 * should be called early in main()
1254 static void setup_cfLanguage(void)
1256 CFStringRef lang
[1] = {NULL
};
1257 CFArrayRef langArray
= NULL
;
1258 const char *requestedLang
= NULL
;
1260 requestedLang
= getenv("LANG");
1261 if (requestedLang
!= NULL
) {
1262 lang
[0] = CFStringCreateWithCString(kCFAllocatorDefault
, requestedLang
, kCFStringEncodingUTF8
);
1263 langArray
= CFArrayCreate(kCFAllocatorDefault
, (const void **)lang
, sizeof(lang
) / sizeof(lang
[0]), &kCFTypeArrayCallBacks
);
1265 CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray
, kCFPreferencesCurrentApplication
);
1266 DEBUG_printf((stderr
, "DEBUG: usb: AppleLanguages = \"%s\"\n", requestedLang
));
1269 CFRelease(langArray
);
1271 fprintf(stderr
, "DEBUG: usb: LANG environment variable missing.\n");
1276 #if defined(__i386__)
1278 * @function run_ppc_backend
1280 * @abstract Starts child backend process running as a ppc executable.
1282 * @result Never returns; always calls exit().
1286 static void run_ppc_backend(int argc
, char *argv
[], int fd
)
1291 pid_t waitpid_status
;
1293 char *usb_ppc_status
;
1296 * If we're running as i386 and couldn't load the class driver (because they'it's
1297 * ppc-only) then try to re-exec ourselves in ppc mode to try again. If we don't have
1298 * a ppc architecture we may be running i386 again so guard against this by setting
1299 * and testing an environment variable...
1301 usb_ppc_status
= getenv("USB_PPC_STATUS");
1303 if (usb_ppc_status
== NULL
) {
1304 /* Catch SIGTERM if we are _not_ printing data from
1305 * stdin (otherwise you can't cancel raw jobs...)
1309 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1310 sigset(SIGTERM
, sigterm_handler
);
1311 #elif defined(HAVE_SIGACTION)
1312 struct sigaction action
; /* Actions for POSIX signals */
1313 memset(&action
, 0, sizeof(action
));
1314 sigaddset(&action
.sa_mask
, SIGTERM
);
1315 action
.sa_handler
= sigterm_handler
;
1316 sigaction(SIGTERM
, &action
, NULL
);
1318 signal(SIGTERM
, sigterm_handler
);
1319 #endif /* HAVE_SIGSET */
1322 if ((child_pid
= fork()) == 0) {
1323 /* Child comes here. */
1324 setenv("USB_PPC_STATUS", "1", false);
1326 /* Tell the kernel we want the next exec call to favor the ppc architecture... */
1327 int mib
[] = { CTL_KERN
, KERN_AFFINITY
, 1, 1 };
1329 sysctl(mib
, namelen
, NULL
, NULL
, NULL
, 0);
1331 /* Set up the arguments and call exec... */
1332 for (i
= 0; i
< argc
&& i
< (sizeof(my_argv
)/sizeof(my_argv
[0])) - 1; i
++)
1333 my_argv
[i
] = argv
[i
];
1337 execv("/usr/libexec/cups/backend/usb", my_argv
);
1339 fprintf(stderr
, "DEBUG: execv: %s\n", strerror(errno
));
1342 else if (child_pid
> 0) {
1343 /* Parent comes here.
1345 * Close the fds we won't be using then wait for the child backend to exit.
1350 fprintf(stderr
, "DEBUG: Started usb(ppc) backend (PID %d)\n", (int)child_pid
);
1352 while ((waitpid_status
= waitpid(child_pid
, &childstatus
, 0)) == (pid_t
)-1 && errno
== EINTR
)
1355 if (WIFSIGNALED(childstatus
)) {
1356 exitstatus
= WTERMSIG(childstatus
);
1357 fprintf(stderr
, "DEBUG: usb(ppc) backend %d crashed on signal %d!\n", child_pid
, exitstatus
);
1360 if ((exitstatus
= WEXITSTATUS(childstatus
)) != 0)
1361 fprintf(stderr
, "DEBUG: usb(ppc) backend %d stopped with status %d!\n", child_pid
, exitstatus
);
1363 fprintf(stderr
, "DEBUG: PID %d exited with no errors\n", child_pid
);
1368 fprintf(stderr
, "DEBUG: fork: %s\n", strerror(errno
));
1373 fprintf(stderr
, "DEBUG: usb child running i386 again\n");
1374 exitstatus
= ENOENT
;
1381 * 'sigterm_handler()' - SIGTERM handler.
1384 static void sigterm_handler(int sig
)
1386 /* If we started a child process pass the signal on to it...
1389 kill(child_pid
, sig
);
1394 #endif /* __i386__ */
1397 #ifdef PARSE_PS_ERRORS
1399 * 'next_line()' - Find the next line in a buffer.
1402 static const char *next_line (const char *buffer
)
1404 const char *cptr
, *lptr
= NULL
;
1406 for (cptr
= buffer
; *cptr
&& lptr
== NULL
; cptr
++)
1407 if (*cptr
== '\n' || *cptr
== '\r')
1414 * 'parse_pserror()' - Scan the backchannel data for postscript errors.
1417 static void parse_pserror (char *sockBuffer
, int len
)
1419 static char gErrorBuffer
[1024] = "";
1420 static char *gErrorBufferPtr
= gErrorBuffer
;
1421 static char *gErrorBufferEndPtr
= gErrorBuffer
+ sizeof(gErrorBuffer
);
1423 char *pCommentBegin
, *pCommentEnd
, *pLineEnd
;
1428 if (gErrorBufferPtr
+ len
> gErrorBufferEndPtr
- 1)
1429 gErrorBufferPtr
= gErrorBuffer
;
1430 if (len
> sizeof(gErrorBuffer
) - 1)
1431 len
= sizeof(gErrorBuffer
) - 1;
1433 memcpy(gErrorBufferPtr
, (const void *)sockBuffer
, len
);
1434 gErrorBufferPtr
+= len
;
1435 *(gErrorBufferPtr
+ 1) = '\0';
1438 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1439 while (pLineEnd
!= NULL
) {
1442 pCommentBegin
= strstr(gErrorBuffer
,"%%[");
1443 pCommentEnd
= strstr(gErrorBuffer
, "]%%");
1444 if (pCommentBegin
!= gErrorBuffer
&& pCommentEnd
!= NULL
) {
1445 pCommentEnd
+= 3; /* Skip past "]%%" */
1446 *pCommentEnd
= '\0'; /* There's always room for the nul */
1448 if (strncasecmp(pCommentBegin
, "%%[ Error:", 10) == 0)
1450 else if (strncasecmp(pCommentBegin
, "%%[ Flushing", 12) == 0)
1455 if ((logstrlen
= snprintf(logstr
, sizeof(logstr
), "%s: %s\n", logLevel
, pCommentBegin
)) >= sizeof(logstr
)) {
1456 /* If the string was trucnated make sure it has a linefeed before the nul */
1457 logstrlen
= sizeof(logstr
) - 1;
1458 logstr
[logstrlen
- 1] = '\n';
1460 write(STDERR_FILENO
, logstr
, logstrlen
);
1463 /* move everything over... */
1464 strcpy(gErrorBuffer
, pLineEnd
);
1465 gErrorBufferPtr
= gErrorBuffer
;
1466 pLineEnd
= (char *)next_line((const char *)gErrorBuffer
);
1469 #endif /* PARSE_PS_ERRORS */
1473 * 'read_thread()' - A thread to read the backchannel data.
1476 static void *read_thread(void *reference
)
1478 /* post a read to the device and write results to stdout
1479 * the final pending read will be Aborted in the main thread
1481 UInt8 readbuffer
[512];
1483 kern_return_t readstatus
;
1484 printer_data_t
*userData
= (printer_data_t
*)reference
;
1485 classdriver_context_t
**classdriver
= userData
->printerDriver
;
1486 struct mach_timebase_info timeBaseInfo
;
1490 /* Calculate what 250 milliSeconds are in mach absolute time...
1492 mach_timebase_info(&timeBaseInfo
);
1493 delay
= ((uint64_t)250000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1496 /* Remember when we started so we can throttle the loop after the read call...
1498 start
= mach_absolute_time();
1500 rbytes
= sizeof(readbuffer
);
1501 readstatus
= (*classdriver
)->ReadPipe( classdriver
, readbuffer
, &rbytes
);
1502 if ( kIOReturnSuccess
== readstatus
&& rbytes
> 0 ) {
1504 cupsBackChannelWrite((char*)readbuffer
, rbytes
, 1.0);
1506 /* cntrl-d is echoed by the printer.
1508 * Xerox Phaser 6250D doesn't echo the cntrl-d.
1509 * Xerox Phaser 6250D doesn't always send the product query.
1511 if (userData
->waitEOF
&& readbuffer
[rbytes
-1] == 0x4)
1513 #ifdef PARSE_PS_ERRORS
1514 parse_pserror(readbuffer
, rbytes
);
1518 /* Make sure this loop executes no more than once every 250 miliseconds...
1520 if ((readstatus
!= kIOReturnSuccess
|| rbytes
== 0) && (userData
->waitEOF
|| !userData
->done
))
1521 mach_wait_until(start
+ delay
);
1523 } while ( userData
->waitEOF
|| !userData
->done
); /* Abort from main thread tests error here */
1525 /* Let the other thread (main thread) know that we have completed the read thread...
1527 pthread_mutex_lock(&userData
->readMutex
);
1528 pthread_cond_signal(&userData
->readCompleteCondition
);
1529 pthread_mutex_unlock(&userData
->readMutex
);
1535 * 'reqestWait_thread()' - A thread cupsSideChannelDoRequest wait.
1537 static void *reqestWait_thread(void *reference
) {
1538 printer_data_t
*userData
= (printer_data_t
*)reference
;
1540 cups_sc_command_t command
;
1541 cups_sc_status_t status
;
1542 uint64_t start
, delay
;
1543 struct mach_timebase_info timeBaseInfo
;
1547 * Calculate what 100 milliSeconds are in mach absolute time...
1549 mach_timebase_info(&timeBaseInfo
);
1550 delay
= ((uint64_t)100000000 * (uint64_t)timeBaseInfo
.denom
) / (uint64_t)timeBaseInfo
.numer
;
1552 /* interface close wait mutex lock. */
1553 pthread_mutex_lock(&(userData
->waitCloseMutex
));
1557 * Remember when we started so we can throttle the loop after the cupsSideChannelDoRequest call...
1559 start
= mach_absolute_time();
1561 /* Poll for a command... */
1563 datalen
= sizeof(data
);
1564 bzero(data
, sizeof(data
));
1566 if (!cupsSideChannelRead(&command
, &status
, data
, &datalen
, 0.0)) {
1567 datalen
= sizeof(data
);
1570 case CUPS_SC_CMD_SOFT_RESET
:
1571 /* do a soft reset */
1572 usbSoftReset(userData
, &status
);
1574 userData
->reqWaitDone
= 1;
1576 case CUPS_SC_CMD_DRAIN_OUTPUT
:
1577 /* drain all pending output */
1578 usbDrainOutput(userData
, &status
);
1581 case CUPS_SC_CMD_GET_BIDI
:
1582 /* return whether the connection is bidirectional */
1583 usbGetBidirectional(userData
, &status
, data
, &datalen
);
1585 case CUPS_SC_CMD_GET_DEVICE_ID
:
1586 /* return the IEEE-1284 device ID */
1587 usbGetDeviceID(userData
, &status
, data
, &datalen
);
1589 case CUPS_SC_CMD_GET_STATE
:
1590 /* return the device state */
1591 usbGetDevState(userData
, &status
, data
, &datalen
);
1594 status
= CUPS_SC_STATUS_NOT_IMPLEMENTED
;
1599 if (userData
->writeDone
) {
1600 status
= CUPS_SC_STATUS_NONE
;
1603 /* Send a response... */
1604 cupsSideChannelWrite(command
, status
, data
, datalen
, 1.0);
1608 * Make sure this loop executes no more than once every 500 miliseconds...
1610 if ((userData
->waitEOF
) || (!userData
->reqWaitDone
)) {
1611 mach_wait_until(start
+ delay
);
1613 } while(!userData
->reqWaitDone
);
1616 pthread_mutex_lock(&userData
->reqWaitMutex
);
1617 userData
->reqWqitFlag
= 1;
1618 pthread_cond_signal(&userData
->reqWaitCompCond
);
1619 pthread_mutex_unlock(&userData
->reqWaitMutex
);
1621 /* interface close wait mutex unlock. */
1622 pthread_mutex_unlock(&(userData
->waitCloseMutex
));
1631 static void usbSoftReset(printer_data_t
*userData
, cups_sc_status_t
*status
) {
1635 userData
->writeDone
= 1;
1637 /* Abort (print_device()-WritePipe kIOReturnAborted return) */
1638 if (userData
->printerDriver
!= NULL
)
1639 err
= (*(userData
->printerDriver
))->Abort(userData
->printerDriver
);
1641 /* print_device() WritePipe_Loop break wait. */
1642 pthread_mutex_lock(&(userData
->writeCompMutex
));
1643 pthread_mutex_unlock(&(userData
->writeCompMutex
));
1646 if (userData
->printerDriver
!= NULL
)
1647 (*(userData
->printerDriver
))->SoftReset(userData
->printerDriver
, 0);
1650 *status
= CUPS_SC_STATUS_OK
;
1656 static void usbDrainOutput(printer_data_t
*userData
, cups_sc_status_t
*status
) {
1657 OSStatus osSts
= noErr
; /* Function results */
1658 OSStatus err
= noErr
;
1659 UInt32 wbytes
; /* Number of bytes written */
1660 ssize_t nbytes
; /* Number of bytes read */
1663 bufptr
= userData
->dataBuffer
+userData
->dataOffset
;
1664 nbytes
= userData
->dataSize
;
1666 while((nbytes
> 0) && (osSts
== noErr
)) {
1668 osSts
= (*(userData
->printerDriver
))->WritePipe(userData
->printerDriver
, (UInt8
*)bufptr
, &wbytes
, 0);
1670 if (wbytes
< 0 || noErr
!= osSts
) {
1671 if (osSts
!= kIOReturnAborted
) {
1672 err
= (*(userData
->printerDriver
))->Abort(userData
->printerDriver
);
1681 if (status
!= NULL
) {
1682 if ((osSts
!= noErr
) || (err
!= noErr
)) {
1683 *status
= CUPS_SC_STATUS_IO_ERROR
;
1685 *status
= CUPS_SC_STATUS_OK
;
1691 * 'usbGetBidirectional'
1693 static void usbGetBidirectional(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1694 *data
= userData
->directionalFlag
;
1698 *status
= CUPS_SC_STATUS_OK
;
1704 static void usbGetDeviceID(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1705 UInt32 deviceLocation
= 0;
1706 CFStringRef deviceIDString
= NULL
;
1709 copy_devicestring(userData
->printerObj
, &deviceIDString
, &deviceLocation
);
1710 CFStringGetCString(deviceIDString
, data
, *datalen
, kCFStringEncodingUTF8
);
1711 *datalen
= strlen(data
);
1713 if (status
!= NULL
) {
1714 *status
= CUPS_SC_STATUS_OK
;
1721 static void usbGetDevState(printer_data_t
*userData
, cups_sc_status_t
*status
, char *data
, int *datalen
) {
1722 *data
= CUPS_SC_STATE_ONLINE
;
1725 if (status
!= NULL
) {
1726 *status
= CUPS_SC_STATUS_OK
;
1731 * End of "$Id: usb-darwin.c 6365 2007-03-19 20:56:57Z mike $".