2 * "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $"
4 * USB port on Darwin backend for the Common UNIX Printing System (CUPS).
6 * This file is included from "usb.c" when compiled on MacOS X or Darwin.
8 * Copyright 2004 Apple Computer, Inc. All rights reserved.
10 * IMPORTANT: This Apple software is supplied to you by Apple Computer,
11 * Inc. ("Apple") in consideration of your agreement to the following
12 * terms, and your use, installation, modification or redistribution of
13 * this Apple software constitutes acceptance of these terms. If you do
14 * not agree with these terms, please do not use, install, modify or
15 * redistribute this Apple software.
17 * In consideration of your agreement to abide by the following terms, and
18 * subject to these terms, Apple grants you a personal, non-exclusive
19 * license, under Apple/s copyrights in this original Apple software (the
20 * "Apple Software"), to use, reproduce, modify and redistribute the Apple
21 * Software, with or without modifications, in source and/or binary forms;
22 * provided that if you redistribute the Apple Software in its entirety and
23 * without modifications, you must retain this notice and the following
24 * text and disclaimers in all such redistributions of the Apple Software.
25 * Neither the name, trademarks, service marks or logos of Apple Computer,
26 * Inc. may be used to endorse or promote products derived from the Apple
27 * Software without specific prior written permission from Apple. Except
28 * as expressly stated in this notice, no other rights or licenses, express
29 * or implied, are granted by Apple herein, including but not limited to
30 * any patent rights that may be infringed by your derivative works or by
31 * other works in which the Apple Software may be incorporated.
33 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
34 * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
35 * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
36 * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
37 * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
39 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
40 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
43 * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
44 * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
45 * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
46 * POSSIBILITY OF SUCH DAMAGE.
50 * print_device() - Send a file to the specified USB port.
51 * list_devices() - List all USB devices.
55 * Include necessary headers...
58 #include <CoreFoundation/CoreFoundation.h>
59 #include <ApplicationServices/ApplicationServices.h>
61 #include <IOKit/usb/IOUSBLib.h>
62 #include <IOKit/IOCFPlugIn.h>
63 #include <mach/mach.h>
64 #include <mach/mach_error.h>
73 #include <pthread.h> /* Used for writegReadMutex */
76 # define kPMPrinterURI CFSTR("Printer URI")
80 * Panther/10.3 kIOUSBInterfaceInterfaceID190
81 * Jaguar/10.2 kIOUSBInterfaceInterfaceID182
84 #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
85 #define kUSBLanguageEnglish 0x409
88 * Section 5.3 USB Printing Class spec
91 #define kUSBPrintingSubclass 1
92 #define kUSBPrintingProtocolNoOpen 0
93 #define kUSBPrintingProtocolUnidirectional 1
94 #define kUSBPrintingProtocolBidirectional 2
96 #define kUSBPrintClassGetDeviceID 0
97 #define kUSBPrintClassGetCentronicsStatus 1
98 #define kUSBPrintClassSoftReset 2
101 * Apple MacOS X printer-class plugins
104 #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
106 #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
108 #define kUSBGenericPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
109 #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
111 #define kUSBClassDriverProperty CFSTR("USB Printing Class")
112 #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
119 unsigned reserved0
: 2;
120 unsigned paperError
: 1;
122 unsigned notError
: 1;
123 unsigned reserved1
: 3;
125 } CentronicsStatusByte
;
129 CFStringRef manufacturer
; /* manufacturer name */
130 CFStringRef product
; /* product name */
131 CFStringRef compatible
; /* compatible product name */
132 CFStringRef serial
; /* serial number */
133 CFStringRef command
; /* command set */
134 CFStringRef ppdURL
; /* url of the selected PPD, if any */
137 typedef IOUSBInterfaceInterface190
**USBPrinterInterface
;
147 } USBIODeviceRequest
;
149 typedef struct classDriverContext
152 CFPlugInRef plugin
; /* release plugin */
153 IUnknownVTbl
**factory
;
154 void *vendorReference
;/* vendor class specific usage */
155 UInt32 location
; /* unique location in bus topology */
156 UInt8 interfaceNumber
;
159 USBPrinterInterface interface
; /* identify the device to IOKit */
160 UInt8 outpipe
; /* mandatory bulkOut pipe */
161 UInt8 inpipe
; /* optional bulkIn pipe */
163 ** general class requests
165 kern_return_t (*DeviceRequest
)( struct classDriverContext
**printer
, USBIODeviceRequest
*iorequest
, UInt16 timeout
);
166 kern_return_t (*GetString
)( struct classDriverContext
**printer
, UInt8 whichString
, UInt16 language
, UInt16 timeout
, CFStringRef
*result
);
168 ** standard printer class requests
170 kern_return_t (*SoftReset
)( struct classDriverContext
**printer
, UInt16 timeout
);
171 kern_return_t (*GetCentronicsStatus
)( struct classDriverContext
**printer
, CentronicsStatusByte
*result
, UInt16 timeout
);
172 kern_return_t (*GetDeviceID
)( struct classDriverContext
**printer
, CFStringRef
*devid
, UInt16 timeout
);
174 ** standard bulk device requests
176 kern_return_t (*ReadPipe
)( struct classDriverContext
**printer
, UInt8
*buffer
, UInt32
*count
);
177 kern_return_t (*WritePipe
)( struct classDriverContext
**printer
, UInt8
*buffer
, UInt32
*count
, Boolean eoj
);
179 ** interface requests
181 kern_return_t (*Open
)( struct classDriverContext
**printer
, UInt32 location
, UInt8 protocol
);
182 kern_return_t (*Abort
)( struct classDriverContext
**printer
);
183 kern_return_t (*Close
)( struct classDriverContext
**printer
);
185 ** initialize and terminate
187 kern_return_t (*Initialize
)( struct classDriverContext
**printer
, struct classDriverContext
**baseclass
);
188 kern_return_t (*Terminate
)( struct classDriverContext
**printer
);
189 } USBPrinterClassContext
;
192 typedef struct usbPrinterClassType
194 USBPrinterClassContext
*classdriver
;
197 } USBPrinterClassType
;
200 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
202 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
205 Debugging output to Console
207 DEBUG=0 production code: suppress console output
209 DEBUG=1 report errors (non-zero results)
210 DEBUG=2 report all results, generate dumps
213 #define DEBUG_ERR(c, x) showint(x, c)
214 #define DEBUG_DUMP( text, buf, len ) dump( text, buf, len )
215 #define DEBUG_CFString( text, a ) showcfstring( text, a )
216 #define DEBUG_CFCompareString( text, a, b ) cmpcfs( text, a, b )
218 #define DEBUG_ERR(c, x) if (c) fprintf(stderr, x, c)
219 #define DEBUG_DUMP( text, buf, len )
220 #define DEBUG_CFString( text, a )
221 #define DEBUG_CFCompareString( text, a, b )
223 #define DEBUG_ERR(c, x)
224 #define DEBUG_DUMP( text, buf, len )
225 #define DEBUG_CFString( text, a )
226 #define DEBUG_CFCompareString( text, a, b )
229 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
236 * Tagged/Tranparent Binary Communications Protocol
239 Boolean tbcpQuoteReads
; /* enable tbcp on reads */
240 Boolean escapeNextRead
; /* last char of last read buffer was escape */
241 UInt8
*tbcpReadData
; /* read buffer */
242 UInt32 readLength
; /* read buffer length (all used) */
243 int match_endoffset
, /* partial match of end TBCP sequence */
244 match_startoffset
; /* partial match of start TBCP sequence */
248 UInt8
*tbcpWriteData
; /* write buffer */
249 UInt32 tbcpBufferLength
, /* write buffer allocated length */
250 tbcpBufferRemaining
; /* write buffer not used */
252 Boolean sendStatusNextWrite
;
258 CFPlugInRef plugin
; /* valid until plugin is release */
259 USBPrinterClassContext
**classdriver
; /* usb printer class in user space */
260 CFStringRef bundle
; /* class driver URI */
261 UInt32 location
; /* unique location in USB topology */
262 USBPrinterAddress address
; /* topology independent bus address */
263 CFURLRef reference
; /* internal use */
266 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
268 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
271 ** IOKit to CF functions
273 USBPrinterInfo
*UsbCopyPrinter( USBPrinterInfo
*aPrinter
);
274 CFMutableArrayRef
UsbGetAllPrinters( void );
275 void UsbReleasePrinter( USBPrinterInfo
*aPrinter
);
276 void UsbReleaseAllPrinters( CFMutableArrayRef printers
);
277 kern_return_t
UsbRegistryOpen( USBPrinterAddress
*usbAddress
, USBPrinterInfo
**result
);
278 kern_return_t
UsbUnloadClassDriver( USBPrinterInfo
*printer
);
279 kern_return_t
UsbLoadClassDriver( USBPrinterInfo
*printer
, CFUUIDRef interfaceID
, CFStringRef classDriverBundle
);
280 CFStringRef
UsbMakeFullUriAddress( USBPrinterInfo
*aPrinter
);
282 int UsbSamePrinter( const USBPrinterAddress
*lastTime
, const USBPrinterAddress
*thisTime
);
284 OSStatus
UsbGetPrinterAddress( USBPrinterInfo
*thePrinter
, USBPrinterAddress
*address
, UInt16 timeout
);
287 /*******************************************************************************
288 Contains: Support IEEE-1284 DeviceID as a CFString.
290 Copyright 2000-2005 by Apple Computer, Inc., all rights reserved.
293 IEEE-1284 Device ID is referenced in USB and PPDT (1394.3). It allows
294 a computer peripheral to convey information about its required software
297 DeviceID is defined as a stream of ASCII bytes, commencing with one 16-bit
298 binary integer in Little-Endian format which describes how many bytes
299 of data are required by the entire DeviceID.
301 The stream of bytes is further characterized as a series of
302 key-value list pairs. In other words each key can be followed by one
303 or more values. Multiple key-value list pairs fill out the DeviceID stream.
305 Some keys are required: COMMAND SET (or CMD), MANUFACTURER (or MFG),
308 One needs to read the first two bytes of DeviceID to allocate storage
309 for the complete DeviceID string. Then a second read operation can
310 retrieve the entire string.
312 Often DeviceID is not very large. By allocating a reasonable buffer one
313 can fetch most device's DeviceID string on the first read.
315 A more formal definition of DeviceID.
317 <DeviceID> = <Length><Key_ValueList_Pair>+
319 <Length> = <low byte of 16 bit integer><high byte of 16 bit integer>
320 <Key_ValueList_Pair> = <Key>:<Value>[,<Value>]*;
322 <Key> = <ASCII Byte>+
323 <Value> = <ASCII Byte>+
325 Some keys are defined in the standard. The standard specifies that
326 keys are case sensitive. White space is allowed in the key.
328 The standard does not say that values are case-sensitive.
329 Lexmark is known to ship printers with mixed-case value:
330 i.e., 'CLASS:Printer'
339 Value PRINTER is referenced in the standard.
343 Used by Hewlett-Packard for the serial number.
346 *******************************************************************************/
348 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
350 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
353 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
355 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
356 #define kDeviceIDKeyCommand CFSTR("COMMAND SET:")
357 #define kDeviceIDKeyCommandAbbrev CFSTR( "CMD:" )
359 #define kDeviceIDKeyManufacturer CFSTR("MANUFACTURER:")
360 #define kDeviceIDKeyManufacturerAbbrev CFSTR( "MFG:" )
362 #define kDeviceIDKeyModel CFSTR("MODEL:")
363 #define kDeviceIDKeyModelAbbrev CFSTR( "MDL:" )
365 #define kDeviceIDKeySerial CFSTR("SN:")
366 #define kDeviceIDKeySerialAbbrev CFSTR("SERN:")
368 #define kDeviceIDKeyCompatible CFSTR("COMPATIBLITY ID:")
369 #define kDeviceIDKeyCompatibleAbbrev CFSTR("CID:")
372 #define kDeviceIDKeyValuePairDelimiter CFSTR(";")
374 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
376 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
378 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
380 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
382 static CFStringRef
CreateEncodedCFString(CFStringRef string
);
383 static CFRange
DelimitSubstring( CFStringRef stringToSearch
, CFStringRef delim
, CFRange bounds
, CFStringCompareFlags options
);
384 static void parseOptions(const char *options
, char *serial
);
387 DeviceIDCreateValueList( const CFStringRef deviceID
,
388 const CFStringRef abbrevKey
,
389 const CFStringRef key
);
391 static int addPercentEscapes(const unsigned char* src
, char* dst
, int dstMax
);
392 static int removePercentEscapes(const char* src
, unsigned char* dst
, int dstMax
);
394 /* Required to suppress redefinition warnings for these two symbols
396 #if defined(TCP_NODELAY)
399 #if defined(TCP_MAXSEG)
403 #include <cups/cups.h>
406 #define PRINTER_POLLING_INTERVAL 5 /* seconds */
407 #define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
408 #define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
410 /* WAITEOF_DELAY is number of seconds we'll wait for responses from the printer */
411 /* after we've finished sending all the data */
412 #define WAITEOF_DELAY 7
414 #define USB_MAX_STR_SIZE 1024
417 static volatile int done
= 0;
418 static int gWaitEOF
= false;
419 static pthread_cond_t
*gReadCompleteConditionPtr
= NULL
;
420 static pthread_mutex_t
*gReadMutexPtr
= NULL
;
429 return ( c
< 0 || c
> 15 )? '?': (c
< 10)? c
+ '0': c
- 10 + 'A';
435 return (c
< 20 || c
> 0x7E)? '.': c
;
439 dump( char *text
, void *s
, int len
)
442 char *p
= (char *) s
;
443 char m
[1+2*16+1+16+1];
445 fprintf( stderr
, "%s pointer %x len %d\n", text
, (unsigned int) p
, len
);
447 for ( ; len
> 0; len
-= 16 )
452 for ( i
= 0; i
< 16 && i
< len
; ++i
, ++p
)
454 *out
++ = hexdigit( (*p
>> 4) & 0x0F );
455 *out
++ = hexdigit( *p
& 0x0F );
463 for ( i
= 0; i
< 16 && i
< len
; ++i
, ++q
)
464 *out
++ = asciidigit( *q
);
466 m
[ strlen( m
) ] = '\0';
467 fprintf( stderr
, "%s\n", m
);
472 printcfs( char *text
, CFStringRef s
)
477 if ( CFStringGetCString(s
, dest
, sizeof(dest
), kCFStringEncodingUTF8
) )
478 sprintf( dest
, "%s <%s>\n", text
, dest
);
480 sprintf( dest
, "%s [Unknown string]\n", text
);
482 sprintf( dest
, "%s [NULL]\n", text
);
488 cmpcfs( char *text
, CFStringRef a
, CFStringRef b
)
490 CFRange found
= {0, 0};
495 if (a
!= NULL
&& b
!= NULL
) {
496 found
= CFStringFind( a
, b
, kCFCompareCaseInsensitive
);
498 } else if (a
== NULL
&& b
== NULL
) {
499 found
.length
= 1; /* Match */
502 found
.length
= 0; /* No match. */
505 if ( found
.length
> 0 )
506 fprintf( stderr
, "matched @%d:%d\n", (int) found
.location
, (int) found
.length
);
508 fprintf( stderr
, "not matched\n" );
512 #ifdef PARSE_PS_ERRORS
513 static const char *nextLine (const char *buffer
);
514 static void parsePSError (char *sockBuffer
, int len
);
517 static const char *nextLine (const char *buffer
)
519 const char *cptr
, *lptr
= NULL
;
520 for (cptr
= buffer
; *cptr
&& lptr
== NULL
; cptr
++)
521 if (*cptr
== '\n' || *cptr
== '\r')
526 static void parsePSError (char *sockBuffer
, int len
)
528 static char gErrorBuffer
[1024] = "";
529 static char *gErrorBufferPtr
= gErrorBuffer
;
530 static char *gErrorBufferEndPtr
= gErrorBuffer
+ sizeof(gErrorBuffer
);
532 char *pCommentBegin
, *pCommentEnd
, *pLineEnd
;
537 if (gErrorBufferPtr
+ len
> gErrorBufferEndPtr
- 1)
538 gErrorBufferPtr
= gErrorBuffer
;
539 if (len
> sizeof(gErrorBuffer
) - 1)
540 len
= sizeof(gErrorBuffer
) - 1;
542 memcpy(gErrorBufferPtr
, (const void *)sockBuffer
, len
);
543 gErrorBufferPtr
+= len
;
544 *(gErrorBufferPtr
+ 1) = '\0';
547 pLineEnd
= (char *)nextLine((const char *)gErrorBuffer
);
548 while (pLineEnd
!= NULL
)
552 pCommentBegin
= strstr(gErrorBuffer
,"%%[");
553 pCommentEnd
= strstr(gErrorBuffer
, "]%%");
554 if (pCommentBegin
!= gErrorBuffer
&& pCommentEnd
!= NULL
)
556 pCommentEnd
+= 3; /* Skip past "]%%" */
557 *pCommentEnd
= '\0'; /* There's always room for the nul */
559 if (strncasecmp(pCommentBegin
, "%%[ Error:", 10) == 0)
561 else if (strncasecmp(pCommentBegin
, "%%[ Flushing", 12) == 0)
566 if ((logstrlen
= snprintf(logstr
, sizeof(logstr
), "%s: %s\n", logLevel
, pCommentBegin
)) >= sizeof(logstr
))
568 /* If the string was trucnated make sure it has a linefeed before the nul */
569 logstrlen
= sizeof(logstr
) - 1;
570 logstr
[logstrlen
- 1] = '\n';
572 write(STDERR_FILENO
, logstr
, logstrlen
);
575 /* move everything over... */
576 strcpy(gErrorBuffer
, pLineEnd
);
577 gErrorBufferPtr
= gErrorBuffer
;
578 pLineEnd
= (char *)nextLine((const char *)gErrorBuffer
);
581 #endif /* PARSE_PS_ERRORS */
584 readthread( void *reference
)
587 ** post a read to the device and write results to stdout
588 ** the final pending read will be Aborted in the main thread
590 UInt8 readbuffer
[512];
592 kern_return_t readstatus
;
593 USBPrinterClassContext
**classdriver
= (USBPrinterClassContext
**) reference
;
598 rbytes
= sizeof(readbuffer
) - 1;
599 readstatus
= (*classdriver
)->ReadPipe( classdriver
, readbuffer
, &rbytes
);
600 if ( kIOReturnSuccess
== readstatus
&& rbytes
> 0 )
602 write( STDOUT_FILENO
, readbuffer
, rbytes
);
603 /* cntrl-d is echoed by the printer.
605 * Xerox Phaser 6250D doesn't echo the cntrl-d.
606 * Xerox Phaser 6250D doesn't always send the product query.
608 if (gWaitEOF
&& readbuffer
[rbytes
-1] == 0x4)
610 #ifdef PARSE_PS_ERRORS
611 parsePSError(readbuffer
, rbytes
);
614 } while ( gWaitEOF
|| !done
); /* Abort from main thread tests error here */
616 /* Let the other thread (main thread) know that we have
617 * completed the read thread...
619 pthread_mutex_lock(gReadMutexPtr
);
620 pthread_cond_signal(gReadCompleteConditionPtr
);
621 pthread_mutex_unlock(gReadMutexPtr
);
627 * 'print_device()' - Send a file to the specified USB port.
630 int print_device(const char *uri
, const char *hostname
, const char *resource
, const char *options
, int fd
, int copies
)
632 UInt32 wbytes
, /* Number of bytes written */
634 size_t nbytes
; /* Number of bytes read */
635 off_t tbytes
; /* Total number of bytes written */
636 char *buffer
, /* Output buffer */
637 *bufptr
; /* Pointer into buffer */
639 pthread_cond_t readCompleteCondition
;
640 pthread_mutex_t readMutex
;
642 int thread_created
= 0;
644 USBPrinterInfo
*targetPrinter
= NULL
;
645 CFMutableArrayRef usbPrinters
;
646 char manufacturer_buf
[USB_MAX_STR_SIZE
],
647 product_buf
[USB_MAX_STR_SIZE
],
648 serial_buf
[USB_MAX_STR_SIZE
];
649 CFStringRef manufacturer
;
653 OSStatus status
= noErr
;
656 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
657 struct sigaction action
; /* Actions for POSIX signals */
658 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
660 fprintf(stderr
, "INFO: Opening the print file and connection...\n");
662 parseOptions(options
, serial_buf
);
664 if (resource
[0] == '/')
667 removePercentEscapes(hostname
, manufacturer_buf
, sizeof(manufacturer_buf
));
668 removePercentEscapes(resource
, product_buf
, sizeof(product_buf
));
670 manufacturer
= CFStringCreateWithCString(NULL
, manufacturer_buf
, kCFStringEncodingUTF8
);
671 product
= CFStringCreateWithCString(NULL
, product_buf
, kCFStringEncodingUTF8
);
672 serial
= CFStringCreateWithCString(NULL
, serial_buf
, kCFStringEncodingUTF8
);
674 USBPrinterInfo
*activePrinter
= NULL
;
675 USBPrinterClassContext
**classdriver
;
676 int countdown
= INITIAL_LOG_INTERVAL
;
680 /* given a manufacturer and product, bind to a specific printer on the bus */
682 usbPrinters
= UsbGetAllPrinters();
684 /* if we have at least one element of the URI, find a printer module that matches */
686 if ( NULL
!= usbPrinters
&& (manufacturer
|| product
) )
689 numPrinters
= CFArrayGetCount(usbPrinters
);
690 for ( i
= 0; i
< numPrinters
; ++i
)
693 USBPrinterInfo
*printer
= (USBPrinterInfo
*) CFArrayGetValueAtIndex( usbPrinters
, i
);
696 match
= printer
->address
.manufacturer
&& manufacturer
? CFEqual(printer
->address
.manufacturer
, manufacturer
): FALSE
;
699 match
= printer
->address
.product
&& product
? CFEqual(printer
->address
.product
, product
): FALSE
;
701 if ( match
&& serial
)
703 /* Note with old queues (pre Panther) the CUPS uri may have no serial number (serial==NULL). */
704 /* In this case, we will ignore serial number (as before), and we'll match to the first */
705 /* printer that agrees with manufacturer and product. */
706 /* If the CUPS uri does include a serial number, we'll enter this clause */
707 /* which requires the printer's serial number to match the CUPS serial number. */
708 /* The net effect is that for printers with a serial number, */
709 /* new queues must match the serial number, while old queues match any printer */
710 /* that satisfies the manufacturer/product match. */
712 match
= printer
->address
.serial
? CFEqual(printer
->address
.serial
, serial
): FALSE
;
716 targetPrinter
= UsbCopyPrinter( printer
);
717 break; /* for, compare partial address to address for each printer on usb bus */
722 UsbReleaseAllPrinters( usbPrinters
);
723 if ( NULL
!= targetPrinter
)
724 status
= UsbRegistryOpen( &targetPrinter
->address
, &activePrinter
);
726 if ( NULL
== activePrinter
)
728 sleep( PRINTER_POLLING_INTERVAL
);
729 countdown
-= PRINTER_POLLING_INTERVAL
;
732 /* periodically, write to the log so someone knows we're waiting */
733 if (NULL
== targetPrinter
)
734 fprintf( stderr
, "WARNING: Printer not responding\n" );
736 fprintf( stderr
, "INFO: Printer busy\n" );
737 countdown
= SUBSEQUENT_LOG_INTERVAL
; /* subsequent log entries, every 30 minutes */
740 } while ( NULL
== activePrinter
);
742 classdriver
= activePrinter
->classdriver
;
743 if ( NULL
== classdriver
)
745 perror("ERROR: Unable to open USB Printing Class port");
750 * Now that we are "connected" to the port, ignore SIGTERM so that we
751 * can finish out any page data the driver sends (e.g. to eject the
752 * current page... Only ignore SIGTERM if we are printing data from
753 * stdin (otherwise you can't cancel raw jobs...)
758 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
759 sigset(SIGTERM
, SIG_IGN
);
760 #elif defined(HAVE_SIGACTION)
761 memset(&action
, 0, sizeof(action
));
763 sigemptyset(&action
.sa_mask
);
764 action
.sa_handler
= SIG_IGN
;
765 sigaction(SIGTERM
, &action
, NULL
);
767 signal(SIGTERM
, SIG_IGN
);
768 #endif /* HAVE_SIGSET */
771 buffer
= malloc( buffersize
);
773 fprintf( stderr
, "ERROR: Couldn't allocate internal buffer\n" );
778 fprintf(stderr
, "INFO: Sending the print file...\n");
779 if (pthread_cond_init(&readCompleteCondition
, NULL
) == 0)
781 gReadCompleteConditionPtr
= &readCompleteCondition
;
783 if (pthread_mutex_init(&readMutex
, NULL
) == 0)
785 gReadMutexPtr
= &readMutex
;
787 if (pthread_create(&thr
, NULL
, readthread
, classdriver
) > 0)
788 fprintf(stderr
, "WARNING: Couldn't create read channel\n");
795 * the main thread sends the print file...
797 while (noErr
== status
&& copies
> 0)
800 if (STDIN_FILENO
!= fd
)
802 fputs("PAGE: 1 1", stderr
);
803 lseek( fd
, 0, SEEK_SET
); /* rewind */
807 while (noErr
== status
&& (nbytes
= read(fd
, buffer
, buffersize
)) > 0)
810 * Write the print data to the printer...
816 while (nbytes
> 0 && noErr
== status
)
819 status
= (*classdriver
)->WritePipe( classdriver
, (UInt8
*)bufptr
, &wbytes
, 0 /*nbytes > wbytes? 0: feof(fp)*/ );
820 if (wbytes
< 0 || noErr
!= status
)
823 err
= (*classdriver
)->Abort( classdriver
);
824 fprintf(stderr
, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status
, err
);
832 if (fd
!= 0 && noErr
== status
)
833 fprintf(stderr
, "INFO: Sending print file, %qd bytes...\n", (off_t
)tbytes
);
836 done
= 1; /* stop scheduling reads */
838 if ( thread_created
)
840 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
841 * we are not signaled in that time then force the thread to exit by setting
842 * the waiteof to be false. Plese note that this relies on us using the timeout
845 struct timespec sleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
846 pthread_mutex_lock(&readMutex
);
847 if (pthread_cond_timedwait(&readCompleteCondition
, &readMutex
, (const struct timespec
*)&sleepUntil
) != 0)
849 pthread_mutex_unlock(&readMutex
);
850 pthread_join( thr
,NULL
); /* wait for the child thread to return */
853 (*classdriver
)->Close( classdriver
); /* forces the read to stop incase we are doing a blocking read */
854 UsbUnloadClassDriver( activePrinter
);
856 * Close the socket connection and input file and return...
860 if (STDIN_FILENO
!= fd
)
863 if (gReadCompleteConditionPtr
!= NULL
)
864 pthread_cond_destroy(gReadCompleteConditionPtr
);
865 if (gReadMutexPtr
!= NULL
)
866 pthread_mutex_destroy(gReadMutexPtr
);
868 return status
== kIOReturnSuccess
? 0: status
;
872 encodecfstr( CFStringRef cfsrc
, char *dst
, long len
)
874 return CFStringGetCString(cfsrc
, dst
, len
, kCFStringEncodingUTF8
);
878 * 'list_devices()' - List all USB devices.
880 void list_devices(void)
882 char encodedManufacturer
[1024];
883 char encodedProduct
[1024];
885 CFMutableArrayRef usbBusPrinters
= UsbGetAllPrinters();
886 CFIndex i
, numPrinters
= NULL
!= usbBusPrinters
? CFArrayGetCount( usbBusPrinters
): 0;
888 puts("direct usb \"Unknown\" \"USB Printer (usb)\"");
889 for ( i
= 0; i
< numPrinters
; ++i
)
891 USBPrinterInfo
*printer
= (USBPrinterInfo
*) CFArrayGetValueAtIndex( usbBusPrinters
, i
);
895 CFStringRef addressRef
= UsbMakeFullUriAddress( printer
);
898 if ( CFStringGetCString(addressRef
, uri
, sizeof(uri
), kCFStringEncodingUTF8
) ) {
900 encodecfstr( printer
->address
.manufacturer
, encodedManufacturer
, sizeof(encodedManufacturer
) );
901 encodecfstr( printer
->address
.product
, encodedProduct
, sizeof(encodedProduct
) );
902 printf("direct %s \"%s %s\" \"%s\"\n", uri
, encodedManufacturer
, encodedProduct
, encodedProduct
);
907 UsbReleaseAllPrinters( usbBusPrinters
);
912 static void parseOptions(const char *options
, char *serial
)
914 char *serialnumber
; /* ?serial=<serial> or ?location=<location> */
915 char optionName
[255], /* Name of option */
916 value
[255], /* Value of option */
917 *ptr
; /* Pointer into name or value */
927 while (*options
!= '\0')
932 for (ptr
= optionName
; *options
&& *options
!= '=' && *options
!= '+' && *options
!= '&'; )
946 for (ptr
= value
; *options
&& *options
!= '+' && *options
!= '&';)
951 if (*options
== '+' || *options
== '&')
954 else if (*options
== '+' || *options
== '&')
960 * Process the option...
962 if (strcasecmp(optionName
, "waiteof") == 0)
964 if (strcasecmp(value
, "on") == 0 ||
965 strcasecmp(value
, "yes") == 0 ||
966 strcasecmp(value
, "true") == 0)
970 else if (strcasecmp(value
, "off") == 0 ||
971 strcasecmp(value
, "no") == 0 ||
972 strcasecmp(value
, "false") == 0)
978 fprintf(stderr
, "WARNING: Boolean expected for waiteof option \"%s\"\n", value
);
981 else if (strcasecmp(optionName
, "serial") == 0 ||
982 strcasecmp(optionName
, "location") == 0 )
984 strcpy(serial
, value
);
985 serialnumber
= serial
;
994 * @function addPercentEscapes
995 * @abstract Encode a string with percent escapes
997 * @param src The source C string
998 * @param dst Desination buffer
999 * @param dstMax Size of desination buffer
1001 * @result A non-zero return value for errors
1003 static int addPercentEscapes(const unsigned char* src
, char* dst
, int dstMax
)
1006 char *dstEnd
= dst
+ dstMax
- 1; /* -1 to leave room for the NUL */
1012 if ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') ||
1013 (c
>= '0' && c
<= '9') || (c
== '.' || c
== '-' || c
== '*' || c
== '_'))
1022 if (dst
>= dstEnd
- 2)
1025 snprintf(dst
, dstEnd
- dst
, "%%%02x", c
);
1036 * @function removePercentEscapes
1037 * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
1039 * @param src Source buffer
1040 * @param srclen Number of bytes in source buffer
1041 * @param dst Desination buffer
1042 * @param dstMax Size of desination buffer
1044 * @result A non-zero return value for errors
1046 static int removePercentEscapes(const char* src
, unsigned char* dst
, int dstMax
)
1049 const unsigned char *dstEnd
= dst
+ dstMax
;
1051 while (*src
&& dst
< dstEnd
)
1057 sscanf(src
, "%02x", &c
);
1070 /*-----------------------------------------------------------------------------*
1074 Desc: Search a string from a starting location, looking for a given
1075 delimiter. Return the range from the start of the search to the
1076 delimiter, or end of string (whichever is shorter).
1078 In: stringToSearch string which contains a substring that we search
1079 delim string which marks the end of the string
1080 bounds start and length of substring of stringToSearch
1081 options case sensitive, anchored, etc.
1083 Out: Range up to the delimiter.
1085 *-----------------------------------------------------------------------------*/
1087 DelimitSubstring( CFStringRef stringToSearch
, CFStringRef delim
, CFRange bounds
, CFStringCompareFlags options
)
1089 CFRange where_delim
, /* where the delimiter was found */
1092 /* trim leading space by changing bounds */
1094 while ( bounds
.length
> 0 && CFStringFindWithOptions( stringToSearch
, CFSTR(" "), bounds
, kCFCompareAnchored
, &where_delim
) )
1096 ++bounds
.location
; /* drop a leading ' ' */
1099 value
= bounds
; /* assume match to the end of string, may be NULL */
1101 /* find the delimiter in the remaining string */
1103 if ( bounds
.length
> 0 && CFStringFindWithOptions( stringToSearch
, delim
, bounds
, options
, &where_delim
) )
1106 /* match to the delimiter */
1108 value
.length
= where_delim
.location
/* delim */ - bounds
.location
/* start of search */;
1110 DEBUG_CFString( "\tFind target", stringToSearch
);
1111 DEBUG_CFString( "\tFind pattern", delim
);
1112 DEBUG_ERR( (int) value
.location
, "\t\tFound %d\n" );
1113 DEBUG_ERR( (int) value
.length
, " length %d" );
1119 /*-----------------------------------------------------------------------------*
1121 DeviceIDCreateValueList
1123 Desc: Create a new string for the value list of the specified key.
1124 The key may be specified as two strings (an abbreviated form
1125 and a standard form). NULL can be passed for either form of
1128 (Although passing NULL for both forms of the key is considered
1129 bad form[!] it is handled correctly.)
1131 In: deviceID the device's IEEE-1284 DeviceID key-value list
1132 abbrevKey the key we're interested in (NULL allowed)
1135 Out: CFString the value list
1136 or NULL key wasn't found in deviceID
1138 *-----------------------------------------------------------------------------*/
1140 DeviceIDCreateValueList( const CFStringRef deviceID
, const CFStringRef abbrevKey
, const CFStringRef key
)
1142 CFRange found
= CFRangeMake( -1,0); /* note CFStringFind sets length 0 if string not found */
1143 CFStringRef valueList
= NULL
;
1145 DEBUG_CFString( "---------DeviceIDCreateValueList DeviceID:", deviceID
);
1146 DEBUG_CFString( "---------DeviceIDCreateValueList key:", key
);
1147 DEBUG_CFString( "---------DeviceIDCreateValueList abbrevkey:", abbrevKey
);
1148 if ( NULL
!= deviceID
&& NULL
!= abbrevKey
)
1149 found
= CFStringFind( deviceID
, abbrevKey
, kCFCompareCaseInsensitive
);
1150 if ( NULL
!= deviceID
&& NULL
!= key
&& found
.length
<= 0 )
1151 found
= CFStringFind( deviceID
, key
, kCFCompareCaseInsensitive
);
1152 if ( found
.length
> 0 )
1154 /* the key is at found */
1155 /* the value follows the key until we reach the semi-colon, or end of string */
1157 CFRange search
= CFRangeMake( found
.location
+ found
.length
,
1158 CFStringGetLength( deviceID
) - (found
.location
+ found
.length
) );
1160 /* finally extract the string */
1162 valueList
= CFStringCreateWithSubstring ( kCFAllocatorDefault
, deviceID
,
1163 DelimitSubstring( deviceID
, kDeviceIDKeyValuePairDelimiter
, search
, kCFCompareCaseInsensitive
) );
1164 DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList
);
1172 /******************************************************************************/
1174 /*-----------------------------------------------------------------------------*
1178 Desc: Return the CFCompare result for two strings, either or both of which
1186 0 if the strings match
1187 non-zero if the strings don't match
1189 *-----------------------------------------------------------------------------*/
1191 CompareSameString( const CFStringRef a
, const CFStringRef b
)
1193 if ( NULL
== a
&& NULL
== b
)
1195 else if ( NULL
!= a
&& NULL
!= b
)
1196 return CFStringCompare( a
, b
, kCFCompareAnchored
);
1198 return 1; /* one of a or b is NULL this time, but wasn't last time */
1202 /******************************************************************************/
1204 UsbLoadClassDriver( USBPrinterInfo
*printer
, CFUUIDRef interfaceID
, CFStringRef classDriverBundle
)
1206 kern_return_t kr
= kUSBPrinterClassDeviceNotOpen
;
1207 if ( NULL
!= classDriverBundle
)
1208 printer
->bundle
= classDriverBundle
; /* vendor-specific class override */
1211 classDriverBundle
= kUSBGenericTOPrinterClassDriver
; /* supply the generic TIMEOUT class driver */
1213 classDriverBundle
= kUSBGenericPrinterClassDriver
; /* supply the generic class driver */
1215 DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle
);
1216 if ( NULL
!= classDriverBundle
)
1218 USBPrinterClassContext
**classdriver
= NULL
;
1219 CFURLRef classDriverURL
= CFURLCreateWithFileSystemPath( NULL
, classDriverBundle
, kCFURLPOSIXPathStyle
, TRUE
);
1220 CFPlugInRef plugin
= NULL
== classDriverURL
? NULL
: CFPlugInCreate( NULL
, classDriverURL
);
1221 if ( NULL
!= plugin
)
1223 /* See if this plug-in implements the Test type. */
1224 CFArrayRef factories
= CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID
, plugin
);
1226 /* If there are factories for the requested type, attempt to */
1227 /* get the IUnknown interface. */
1228 DEBUG_ERR( 0, "UsbLoadClassDriver plugin %x\n" );
1229 if (NULL
!= factories
&& CFArrayGetCount(factories
) > 0)
1231 /* Get the factory ID for the first location in the array of IDs. */
1232 CFUUIDRef factoryID
= CFArrayGetValueAtIndex( factories
, 0 );
1233 /* Use the factory ID to get an IUnknown interface. */
1234 /* Here the code for the PlugIn is loaded. */
1235 IUnknownVTbl
**iunknown
= CFPlugInInstanceCreate( NULL
, factoryID
, kUSBPrinterClassTypeID
);
1236 /* If this is an IUnknown interface, query for the Test interface. */
1237 DEBUG_ERR( 0, "UsbLoadClassDriver factories %x\n" );
1238 if (NULL
!= iunknown
)
1240 DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" );
1241 kr
= (*iunknown
)->QueryInterface( iunknown
, CFUUIDGetUUIDBytes(interfaceID
), (LPVOID
*) &classdriver
);
1243 (*iunknown
)->Release( iunknown
);
1244 if ( S_OK
== kr
&& NULL
!= classdriver
)
1246 DEBUG_ERR( kr
, "UsbLoadClassDriver QueryInterface %x\n" );
1247 printer
->plugin
= plugin
;
1248 kr
= (*classdriver
)->Initialize( classdriver
, printer
->classdriver
);
1250 kr
= kIOReturnSuccess
;
1251 printer
->classdriver
= classdriver
;
1255 DEBUG_ERR( kr
, "UsbLoadClassDriver QueryInterface FAILED %x\n" );
1260 DEBUG_ERR( kr
, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" );
1265 DEBUG_ERR( kr
, "UsbLoadClassDriver factories FAILED %x\n" );
1270 DEBUG_ERR( kr
, "UsbLoadClassDriver plugin FAILED %x\n" );
1272 if ( kr
!= kIOReturnSuccess
|| NULL
== plugin
|| NULL
== classdriver
)
1274 UsbUnloadClassDriver( printer
);
1283 UsbUnloadClassDriver( USBPrinterInfo
*printer
)
1285 DEBUG_ERR( kIOReturnSuccess
, "UsbUnloadClassDriver %x\n" );
1286 if ( NULL
!= printer
->classdriver
)
1287 (*printer
->classdriver
)->Release( printer
->classdriver
);
1288 printer
->classdriver
= NULL
;
1290 if ( NULL
!= printer
->plugin
)
1291 CFRelease( printer
->plugin
);
1292 printer
->plugin
= NULL
;
1294 return kIOReturnSuccess
;
1298 /*-----------------------------------------------------------------------------*
1302 Desc: deallocates anything used to create a persistent printer address
1304 In: address the printer address we've created
1308 *-----------------------------------------------------------------------------*/
1310 UsbAddressDispose( USBPrinterAddress
*address
)
1312 if ( address
->product
!= NULL
) CFRelease( address
->product
);
1313 if ( address
->manufacturer
!= NULL
) CFRelease( address
->manufacturer
);
1314 if ( address
->serial
!= NULL
) CFRelease( address
->serial
);
1315 if ( address
->command
!= NULL
) CFRelease( address
->command
);
1318 address
->manufacturer
=
1320 address
->command
= NULL
;
1324 /*-----------------------------------------------------------------------------*
1326 UsbGetPrinterAddress
1328 Desc: Given a printer we're enumerating, discover it's persistent
1331 A "persistent reference" is one which enables us to identify
1332 a printer regardless of where it resides on the USB topology,
1333 and enumeration sequence.
1335 To do this, we actually construct a reference from information
1336 buried inside the printer. First we look at the USB device
1337 descripton: an ideally defined device will support strings for
1338 manufacturer and product id, and serial number. The serial number
1339 will be unique for each printer.
1341 Our prefered identification fetches the IEEE-1284 device id string.
1342 This transparently handled IEEE-1284 compatible printers which
1343 connected over a USB-parallel cable. Only if we can't get all the
1344 information to uniquely identify the printer do we try the strings
1345 referenced in the printer's USB device descriptor. (These strings
1346 are typically absent in a USB-parallel cable.)
1348 If a device doesn't support serial numbers we have a problem:
1349 we can't distinguish between two identical printers. Unique serial
1350 numbers allow us to distinguish between two same-model, same-manufacturer
1354 thePrinter iterator required for fetching device descriptor
1355 devRefNum required to configure the interface
1358 address->manufacturer
1361 Any (and all) of these may be NULL if we can't retrieve
1362 information for IEEE1284 DeviceID or the USB device
1363 descriptor. Caller should be prepared to handle such a case.
1367 *-----------------------------------------------------------------------------*/
1369 UsbGetPrinterAddress( USBPrinterInfo
*thePrinter
, USBPrinterAddress
*address
, UInt16 timeout
)
1373 /* start by assuming the device is not IEEE-1284 compliant */
1374 /* and that we can't read in the required strings. */
1377 CFStringRef deviceId
= NULL
;
1378 USBPrinterClassContext
**printer
= NULL
== thePrinter
? NULL
: thePrinter
->classdriver
;
1380 address
->manufacturer
=
1382 address
->compatible
=
1384 address
->command
= NULL
;
1386 DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter
, sizeof(USBPrinterInfo
) );
1388 err
= (*printer
)->GetDeviceID( printer
, &deviceId
, timeout
);
1389 if ( noErr
== err
&& NULL
!= deviceId
)
1391 /* the strings embedded here are defined in the IEEE1284 spec */
1393 /* use the MFG/MANUFACTURER for the manufacturer */
1394 /* and the MDL/MODEL for the product */
1395 /* there is no serial number defined in IEEE1284 */
1396 /* but it's been observed in recent HP printers */
1398 address
->command
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyCommandAbbrev
, kDeviceIDKeyCommand
);
1400 address
->product
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyModelAbbrev
, kDeviceIDKeyModel
);
1401 address
->compatible
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyCompatibleAbbrev
, kDeviceIDKeyCompatible
);
1403 address
->manufacturer
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyManufacturerAbbrev
, kDeviceIDKeyManufacturer
);
1405 address
->serial
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeySerialAbbrev
, kDeviceIDKeySerial
);
1406 CFRelease( deviceId
);
1408 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->product", address
->product
);
1409 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->compatible", address
->compatible
);
1410 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->manufacturer", address
->manufacturer
);
1411 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->serial", address
->serial
);
1413 if ( NULL
== address
->product
|| NULL
== address
->manufacturer
|| NULL
== address
->serial
)
1416 /* if the manufacturer or the product or serial number were not specified in DeviceID */
1417 /* try to construct the address using USB English string descriptors */
1419 IOUSBDeviceDescriptor desc
;
1420 USBIODeviceRequest request
;
1422 request
.requestType
= USBmakebmRequestType( kUSBIn
, kUSBStandard
, kUSBDevice
);
1423 request
.request
= kUSBRqGetDescriptor
;
1424 request
.value
= (kUSBDeviceDesc
<< 8) | 0;
1425 request
.index
= 0; /* not kUSBLanguageEnglish*/
1426 request
.length
= sizeof(desc
);
1427 request
.buffer
= &desc
;
1428 err
= (*printer
)->DeviceRequest( printer
, &request
, timeout
);
1429 DEBUG_ERR( (kern_return_t
) err
, "UsbGetPrinterAddress: GetDescriptor %x" );
1430 if ( kIOReturnSuccess
== err
)
1432 /* once we've retrieved the device descriptor */
1433 /* try to fill in missing pieces of information */
1435 /* Don't override any information already retrieved from DeviceID. */
1437 if ( NULL
== address
->product
)
1439 err
= (*printer
)->GetString( printer
, desc
.iProduct
, kUSBLanguageEnglish
, timeout
, &address
->product
);
1440 if ( kIOReturnSuccess
!= err
|| address
->product
== NULL
) {
1441 address
->product
= CFSTR("Unknown");
1444 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address
->product
);
1446 if ( NULL
== address
->manufacturer
)
1448 err
= (*printer
)->GetString( printer
, desc
.iManufacturer
, kUSBLanguageEnglish
, timeout
, &address
->manufacturer
);
1449 if (kIOReturnSuccess
!= err
|| address
->manufacturer
== NULL
) {
1450 address
->manufacturer
= CFSTR("Unknown");
1453 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address
->manufacturer
);
1455 if ( NULL
== address
->serial
)
1457 /* if the printer doesn't have a serial number, use locationId */
1458 if ( 0 == desc
.iSerialNumber
)
1460 address
->serial
= CFStringCreateWithFormat( NULL
, NULL
, CFSTR("%lx"), (*printer
)->location
);
1464 err
= (*printer
)->GetString( printer
, desc
.iSerialNumber
, kUSBLanguageEnglish
, timeout
, &address
->serial
);
1465 /* trailing NULs aren't handled correctly in URI */
1466 if ( address
->serial
)
1468 UniChar nulbyte
= { 0 };
1469 CFStringRef trim
= CFStringCreateWithCharacters(NULL
, &nulbyte
, 1);
1470 CFMutableStringRef newserial
= CFStringCreateMutableCopy(NULL
, 0, address
->serial
);
1472 CFStringTrim( newserial
, trim
);
1475 CFRelease( address
->serial
);
1477 address
->serial
= newserial
;
1481 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address
->serial
);
1484 if ( NULL
!= address
->product
)
1485 CFRetain(address
->product
); /* UsbGetString is really a UsbCopyString. */
1486 if ( NULL
!= address
->manufacturer
)
1487 CFRetain( address
->manufacturer
);
1488 if ( NULL
!= address
->serial
)
1489 CFRetain( address
->serial
);
1494 /*-----------------------------------------------------------------------------*
1498 Desc: match two Usb printer address; return TRUE if they are the same.
1500 In: a the persistent address found last time
1501 b the persistent address found this time
1503 Out: non-zero iff the addresses are the same
1505 *-----------------------------------------------------------------------------*/
1507 UsbSamePrinter( const USBPrinterAddress
*a
, const USBPrinterAddress
*b
)
1510 DEBUG_CFCompareString( "UsbSamePrinter serial", a
->serial
, b
->serial
);
1511 DEBUG_CFCompareString( "UsbSamePrinter product", a
->product
, b
->product
);
1512 DEBUG_CFCompareString( "UsbSamePrinter manufacturer", a
->manufacturer
, b
->manufacturer
);
1514 result
= !CompareSameString( a
->serial
, b
->serial
);
1515 if ( result
) result
= !CompareSameString( a
->product
, b
->product
);
1516 if ( result
) result
= !CompareSameString( a
->manufacturer
, b
->manufacturer
);
1522 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1523 Method: UsbMakeFullUriAddress
1530 Fill in missing address information
1532 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1534 UsbMakeFullUriAddress( USBPrinterInfo
*printer
)
1537 /* fill in missing address information. */
1539 CFMutableStringRef printerUri
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("usb://") );
1540 if ( NULL
!= printerUri
)
1542 CFStringRef serial
= printer
->address
.serial
;
1544 CFStringAppend(printerUri
, printer
->address
.manufacturer
? CreateEncodedCFString( printer
->address
.manufacturer
): CFSTR("Unknown") );
1545 CFStringAppend(printerUri
, CFSTR("/") );
1547 CFStringAppend(printerUri
, printer
->address
.product
? CreateEncodedCFString( printer
->address
.product
): CFSTR("Unknown") );
1549 /*Handle the common case where there is no serial number (S450?) */
1550 CFStringAppend(printerUri
, serial
== NULL
? CFSTR("?location="): CFSTR("?serial=") );
1551 if ( serial
== NULL
)
1552 serial
= CFStringCreateWithFormat( NULL
, NULL
, CFSTR("%lx"), printer
->location
);
1554 CFStringAppend(printerUri
, serial
? CreateEncodedCFString( serial
): CFSTR("Unknown") );
1561 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1562 Method: UsbGetAllPrinters
1567 array of all USB printers on the system
1570 Build a list of USB printers by iterating IOKit USB objects
1572 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1574 UsbGetAllPrinters( void )
1576 kern_return_t kr
; /* kernel errors */
1577 mach_port_t master_device_port
= 0;
1578 io_service_t usbInterface
= 0;
1579 io_iterator_t iter
= 0;
1580 CFMutableArrayRef printers
= CFArrayCreateMutable( NULL
, 0, NULL
); /* all printers */
1585 kr
= IOMasterPort( bootstrap_port
, &master_device_port
);
1586 DEBUG_ERR( kr
, "UsbGetAllPrinters IOMasterPort %x\n" );
1587 if(kIOReturnSuccess
!= kr
) break;
1590 CFDictionaryRef usbMatch
= NULL
;
1592 /* iterate over all interfaces. */
1593 usbMatch
= IOServiceMatching(kIOUSBInterfaceClassName
);
1594 if ( !usbMatch
) break;
1595 DEBUG_ERR( kr
, "UsbGetAllPrinters IOServiceMatching %x\n" );
1597 /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */
1598 kr
= IOServiceGetMatchingServices(master_device_port
, usbMatch
, &iter
);
1601 DEBUG_ERR( kr
, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" );
1602 if(kIOReturnSuccess
!= kr
|| iter
== NULL
) break;
1605 while ( NULL
!= (usbInterface
= IOIteratorNext(iter
)) )
1607 IOCFPlugInInterface
**iodev
;
1608 USBPrinterInterface intf
;
1611 CFMutableDictionaryRef properties
;
1612 CFStringRef classDriver
= NULL
;
1614 kr
= IORegistryEntryCreateCFProperties( usbInterface
, &properties
, kCFAllocatorDefault
, kNilOptions
);
1615 if ( kIOReturnSuccess
== kr
&& NULL
!= properties
)
1617 classDriver
= (CFStringRef
) CFDictionaryGetValue( properties
, kUSBClassDriverProperty
);
1618 if ( NULL
!= classDriver
)
1619 CFRetain( classDriver
);
1620 CFRelease( properties
);
1623 kr
= IOCreatePlugInInterfaceForService( usbInterface
,
1624 kIOUSBInterfaceUserClientTypeID
,
1625 kIOCFPlugInInterfaceID
,
1629 DEBUG_ERR( kr
, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" );
1630 if ( kIOReturnSuccess
== kr
)
1632 UInt8 intfClass
= 0;
1633 UInt8 intfSubClass
= 0;
1635 res
= (*iodev
)->QueryInterface( iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
);
1636 DEBUG_ERR( (kern_return_t
) res
, "UsbGetAllPrinters QueryInterface %x\n" );
1638 (*iodev
)->Release(iodev
);
1639 if ( noErr
!= res
) break;
1641 kr
= (*intf
)->GetInterfaceClass(intf
, &intfClass
);
1642 DEBUG_ERR(kr
, "UsbGetAllPrinters GetInterfaceClass %x\n");
1643 if ( kIOReturnSuccess
== kr
)
1644 kr
= (*intf
)->GetInterfaceSubClass(intf
, &intfSubClass
);
1645 DEBUG_ERR(kr
, "UsbGetAllPrinters GetInterfaceSubClass %x\n");
1647 if ( kIOReturnSuccess
== kr
&&
1648 kUSBPrintingClass
== intfClass
&&
1649 kUSBPrintingSubclass
== intfSubClass
)
1652 USBPrinterInfo printer
,
1655 For each type of printer specified in the lookup spec array, find
1656 all of that type of printer and add the results to the list of found
1659 /* create this printer's persistent address */
1660 memset( &printer
, 0, sizeof(USBPrinterInfo
) );
1661 kr
= (*intf
)->GetLocationID(intf
, &printer
.location
);
1662 DEBUG_ERR(kr
, "UsbGetAllPrinters GetLocationID %x\n");
1663 if ( kIOReturnSuccess
== kr
)
1665 kr
= UsbLoadClassDriver( &printer
, kUSBPrinterClassInterfaceID
, classDriver
);
1666 DEBUG_ERR(kr
, "UsbGetAllPrinters UsbLoadClassDriver %x\n");
1667 if ( kIOReturnSuccess
== kr
&& printer
.classdriver
)
1669 (*(printer
.classdriver
))->interface
= intf
;
1670 kr
= UsbGetPrinterAddress( &printer
, &printer
.address
, 60000L );
1672 /* always unload the driver */
1673 /* but don't mask last error */
1674 kern_return_t unload_err
= UsbUnloadClassDriver( &printer
);
1675 if ( kIOReturnSuccess
== kr
)
1681 printerInfo
= UsbCopyPrinter( &printer
);
1682 if ( NULL
!= printerInfo
)
1683 CFArrayAppendValue( printers
, (const void *) printerInfo
); /* keep track of it */
1685 } /* if there's a printer */
1686 kr
= (*intf
)->Release(intf
);
1687 } /* if IOCreatePlugInInterfaceForService */
1689 IOObjectRelease(usbInterface
);
1690 usbInterface
= NULL
;
1692 } /* while there's an interface */
1697 IOObjectRelease(iter
);
1701 if (master_device_port
)
1703 mach_port_deallocate(mach_task_self(), master_device_port
);
1704 master_device_port
= 0;
1708 } /* UsbGetAllPrinters */
1710 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1711 Method: UsbReleasePrinter
1718 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1720 UsbReleasePrinter( USBPrinterInfo
*printer
)
1724 UsbUnloadClassDriver( printer
);
1725 if ( NULL
!= printer
->address
.manufacturer
)
1726 CFRelease( printer
->address
.manufacturer
);
1727 if ( NULL
!= printer
->address
.product
)
1728 CFRelease( printer
->address
.product
);
1729 if ( NULL
!= printer
->address
.serial
)
1730 CFRelease( printer
->address
.serial
);
1731 if ( NULL
!= printer
->address
.command
)
1732 CFRelease( printer
->address
.command
);
1733 if ( NULL
!= printer
->bundle
)
1734 CFRelease( printer
->bundle
);
1739 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1740 Method: UsbReleaseAllPrinters
1747 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1749 UsbReleaseAllPrinters( CFMutableArrayRef printers
)
1751 if ( NULL
!= printers
)
1754 numPrinters
= CFArrayGetCount(printers
);
1755 for ( i
= 0; i
< numPrinters
; ++i
)
1756 UsbReleasePrinter( (USBPrinterInfo
*) CFArrayGetValueAtIndex( printers
, i
) );
1757 CFRelease( printers
);
1762 UsbCopyPrinter( USBPrinterInfo
*aPrinter
)
1765 /* note this does not copy interface information, just address information */
1767 USBPrinterInfo
*printerInfo
= (USBPrinterInfo
*) calloc( 1, sizeof(USBPrinterInfo
));
1768 if ( NULL
!= printerInfo
&& NULL
!= aPrinter
)
1770 printerInfo
->location
= aPrinter
->location
;
1771 if ( NULL
!= (printerInfo
->address
.manufacturer
= aPrinter
->address
.manufacturer
) )
1772 CFRetain( printerInfo
->address
.manufacturer
);
1773 if ( NULL
!= (printerInfo
->address
.product
= aPrinter
->address
.product
) )
1774 CFRetain( printerInfo
->address
.product
);
1775 if ( NULL
!= (printerInfo
->address
.serial
= aPrinter
->address
.serial
) )
1776 CFRetain( printerInfo
->address
.serial
);
1777 if ( NULL
!= (printerInfo
->address
.command
= aPrinter
->address
.command
) )
1778 CFRetain( printerInfo
->address
.command
);
1779 if ( NULL
!= (printerInfo
->bundle
= aPrinter
->bundle
) )
1780 CFRetain( printerInfo
->bundle
);
1786 /*-----------------------------------------------------------------------------*
1790 Desc: opens the USB printer which matches the supplied printerAddress
1792 In: myContext->printerAddress persistent name which identifies the printer
1794 Out: myContext->usbDeviceRef current IOKit address of this printer
1795 *-----------------------------------------------------------------------------*/
1797 UsbRegistryOpen( USBPrinterAddress
*usbAddress
, USBPrinterInfo
**result
)
1799 kern_return_t kr
= -1; /* indeterminate failure */
1800 CFMutableArrayRef printers
= UsbGetAllPrinters();
1801 CFIndex numPrinters
= NULL
!= printers
? CFArrayGetCount( printers
): 0;
1804 *result
= NULL
; /* nothing matched */
1805 for ( i
= 0; i
< numPrinters
; ++i
)
1807 USBPrinterInfo
*thisPrinter
= (USBPrinterInfo
*) CFArrayGetValueAtIndex( printers
, i
);
1808 if ( NULL
!= thisPrinter
&& UsbSamePrinter( usbAddress
, &thisPrinter
->address
) )
1810 *result
= UsbCopyPrinter( thisPrinter
); /* retains reference */
1811 if ( NULL
!= *result
)
1814 /* if we can't find a bi-di interface, settle for a known uni-directional interface */
1816 USBPrinterClassContext
**printer
= NULL
;
1818 /* setup the default class driver */
1819 /* If one is specified, allow the vendor driver to override our default implementation */
1821 kr
= UsbLoadClassDriver( *result
, kUSBPrinterClassInterfaceID
, NULL
);
1822 if ( kIOReturnSuccess
== kr
&& (*result
)->bundle
)
1823 kr
= UsbLoadClassDriver( *result
, kUSBPrinterClassInterfaceID
, (*result
)->bundle
);
1824 if ( kIOReturnSuccess
== kr
&& NULL
!= (*result
)->classdriver
)
1826 printer
= (*result
)->classdriver
;
1827 kr
= (*printer
)->Open( printer
, (*result
)->location
, kUSBPrintingProtocolBidirectional
);
1828 if ( kIOReturnSuccess
!= kr
|| NULL
== (*printer
)->interface
)
1829 kr
= (*printer
)->Open( printer
, (*result
)->location
, kUSBPrintingProtocolUnidirectional
);
1830 /* it's possible kIOReturnSuccess == kr && NULL == (*printer)->interface */
1831 /* in the event that we can't open either Bidirectional or Unidirectional interface */
1832 if ( kIOReturnSuccess
== kr
)
1834 if ( NULL
== (*printer
)->interface
)
1836 (*printer
)->Close( printer
);
1837 UsbReleasePrinter( *result
);
1846 UsbReleaseAllPrinters( printers
); /* but, copied printer is retained */
1847 DEBUG_ERR( kr
, "UsbRegistryOpen return %x\n" );
1853 * @function CreateEncodedCFString
1855 * @abstract Create an encoded version of the string parameter
1856 * so that it can be included in a URI.
1858 * @param string A CFStringRef of the string to be encoded.
1859 * @result An encoded CFString.
1861 * @discussion This function will change all characters in string into URL acceptable format
1862 * by encoding the text using the US-ASCII coded character set. The following
1863 * are invalid characters: the octets 00-1F, 7F, and 80-FF hex. Also called out
1864 * are the chars "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "[", "]", "`".
1865 * The reserved characters for URL syntax are also to be encoded: (so don't pass
1866 * in a full URL here!) ";", "/", "?", ":", "@", "=", "%", and "&".
1868 static CFStringRef
CreateEncodedCFString(CFStringRef string
)
1870 CFStringRef result
= NULL
;
1871 char *bufferUTF8
= NULL
;
1872 char *bufferEncoded
= NULL
;
1876 CFIndex bufferSizeUTF8
= (3 * CFStringGetLength(string
));
1877 if ((bufferUTF8
= (char*)malloc(bufferSizeUTF8
)) != NULL
)
1879 CFStringGetCString(string
, bufferUTF8
, bufferSizeUTF8
, kCFStringEncodingUTF8
);
1881 UInt16 bufferSizeEncoded
= (3 * strlen(bufferUTF8
)) + 1;
1882 if ((bufferEncoded
= (char*)malloc(bufferSizeEncoded
)) != NULL
)
1884 addPercentEscapes(bufferUTF8
, bufferEncoded
, bufferSizeEncoded
);
1885 result
= CFStringCreateWithCString(kCFAllocatorDefault
, bufferEncoded
, kCFStringEncodingUTF8
);
1891 if (bufferUTF8
) free(bufferUTF8
);
1892 if (bufferEncoded
) free(bufferEncoded
);
1898 * End of "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $".