2 * "$Id: usb-darwin.c 5241 2006-03-07 22:07:44Z 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
;
678 fputs("STATE: +connecting-to-device\n", stderr
);
683 /* given a manufacturer and product, bind to a specific printer on the bus */
685 usbPrinters
= UsbGetAllPrinters();
687 /* if we have at least one element of the URI, find a printer module that matches */
689 if ( NULL
!= usbPrinters
&& (manufacturer
|| product
) )
692 numPrinters
= CFArrayGetCount(usbPrinters
);
693 for ( i
= 0; i
< numPrinters
; ++i
)
696 USBPrinterInfo
*printer
= (USBPrinterInfo
*) CFArrayGetValueAtIndex( usbPrinters
, i
);
699 match
= printer
->address
.manufacturer
&& manufacturer
? CFEqual(printer
->address
.manufacturer
, manufacturer
): FALSE
;
702 match
= printer
->address
.product
&& product
? CFEqual(printer
->address
.product
, product
): FALSE
;
704 if ( match
&& serial
)
706 /* Note with old queues (pre Panther) the CUPS uri may have no serial number (serial==NULL). */
707 /* In this case, we will ignore serial number (as before), and we'll match to the first */
708 /* printer that agrees with manufacturer and product. */
709 /* If the CUPS uri does include a serial number, we'll enter this clause */
710 /* which requires the printer's serial number to match the CUPS serial number. */
711 /* The net effect is that for printers with a serial number, */
712 /* new queues must match the serial number, while old queues match any printer */
713 /* that satisfies the manufacturer/product match. */
715 match
= printer
->address
.serial
? CFEqual(printer
->address
.serial
, serial
): FALSE
;
719 targetPrinter
= UsbCopyPrinter( printer
);
720 break; /* for, compare partial address to address for each printer on usb bus */
725 UsbReleaseAllPrinters( usbPrinters
);
726 if ( NULL
!= targetPrinter
)
727 status
= UsbRegistryOpen( &targetPrinter
->address
, &activePrinter
);
729 if ( NULL
== activePrinter
)
731 sleep( PRINTER_POLLING_INTERVAL
);
732 countdown
-= PRINTER_POLLING_INTERVAL
;
735 /* periodically, write to the log so someone knows we're waiting */
736 if (NULL
== targetPrinter
)
737 fprintf( stderr
, "WARNING: Printer not responding\n" );
739 fprintf( stderr
, "INFO: Printer busy\n" );
740 countdown
= SUBSEQUENT_LOG_INTERVAL
; /* subsequent log entries, every 30 minutes */
743 } while ( NULL
== activePrinter
);
745 classdriver
= activePrinter
->classdriver
;
746 if ( NULL
== classdriver
)
748 perror("ERROR: Unable to open USB Printing Class port");
752 fputs("STATE: -connecting-to-device\n", stderr
);
755 * Now that we are "connected" to the port, ignore SIGTERM so that we
756 * can finish out any page data the driver sends (e.g. to eject the
757 * current page... Only ignore SIGTERM if we are printing data from
758 * stdin (otherwise you can't cancel raw jobs...)
763 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
764 sigset(SIGTERM
, SIG_IGN
);
765 #elif defined(HAVE_SIGACTION)
766 memset(&action
, 0, sizeof(action
));
768 sigemptyset(&action
.sa_mask
);
769 action
.sa_handler
= SIG_IGN
;
770 sigaction(SIGTERM
, &action
, NULL
);
772 signal(SIGTERM
, SIG_IGN
);
773 #endif /* HAVE_SIGSET */
776 buffer
= malloc( buffersize
);
778 fprintf( stderr
, "ERROR: Couldn't allocate internal buffer\n" );
783 fprintf(stderr
, "INFO: Sending the print file...\n");
784 if (pthread_cond_init(&readCompleteCondition
, NULL
) == 0)
786 gReadCompleteConditionPtr
= &readCompleteCondition
;
788 if (pthread_mutex_init(&readMutex
, NULL
) == 0)
790 gReadMutexPtr
= &readMutex
;
792 if (pthread_create(&thr
, NULL
, readthread
, classdriver
) > 0)
793 fprintf(stderr
, "WARNING: Couldn't create read channel\n");
800 * the main thread sends the print file...
802 while (noErr
== status
&& copies
> 0)
805 if (STDIN_FILENO
!= fd
)
807 fputs("PAGE: 1 1", stderr
);
808 lseek( fd
, 0, SEEK_SET
); /* rewind */
812 while (noErr
== status
&& (nbytes
= read(fd
, buffer
, buffersize
)) > 0)
815 * Write the print data to the printer...
821 while (nbytes
> 0 && noErr
== status
)
824 status
= (*classdriver
)->WritePipe( classdriver
, (UInt8
*)bufptr
, &wbytes
, 0 /*nbytes > wbytes? 0: feof(fp)*/ );
825 if (wbytes
< 0 || noErr
!= status
)
828 err
= (*classdriver
)->Abort( classdriver
);
829 fprintf(stderr
, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status
, err
);
837 if (fd
!= 0 && noErr
== status
)
838 fprintf(stderr
, "INFO: Sending print file, %qd bytes...\n", (off_t
)tbytes
);
841 done
= 1; /* stop scheduling reads */
843 if ( thread_created
)
845 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
846 * we are not signaled in that time then force the thread to exit by setting
847 * the waiteof to be false. Plese note that this relies on us using the timeout
850 struct timespec sleepUntil
= { time(NULL
) + WAITEOF_DELAY
, 0 };
851 pthread_mutex_lock(&readMutex
);
852 if (pthread_cond_timedwait(&readCompleteCondition
, &readMutex
, (const struct timespec
*)&sleepUntil
) != 0)
854 pthread_mutex_unlock(&readMutex
);
855 pthread_join( thr
,NULL
); /* wait for the child thread to return */
858 (*classdriver
)->Close( classdriver
); /* forces the read to stop incase we are doing a blocking read */
859 UsbUnloadClassDriver( activePrinter
);
861 * Close the socket connection and input file and return...
865 if (STDIN_FILENO
!= fd
)
868 if (gReadCompleteConditionPtr
!= NULL
)
869 pthread_cond_destroy(gReadCompleteConditionPtr
);
870 if (gReadMutexPtr
!= NULL
)
871 pthread_mutex_destroy(gReadMutexPtr
);
873 return status
== kIOReturnSuccess
? 0: status
;
877 encodecfstr( CFStringRef cfsrc
, char *dst
, long len
)
879 return CFStringGetCString(cfsrc
, dst
, len
, kCFStringEncodingUTF8
);
883 * 'list_devices()' - List all USB devices.
885 void list_devices(void)
887 char encodedManufacturer
[1024];
888 char encodedProduct
[1024];
890 CFMutableArrayRef usbBusPrinters
= UsbGetAllPrinters();
891 CFIndex i
, numPrinters
= NULL
!= usbBusPrinters
? CFArrayGetCount( usbBusPrinters
): 0;
893 puts("direct usb \"Unknown\" \"USB Printer (usb)\"");
894 for ( i
= 0; i
< numPrinters
; ++i
)
896 USBPrinterInfo
*printer
= (USBPrinterInfo
*) CFArrayGetValueAtIndex( usbBusPrinters
, i
);
900 CFStringRef addressRef
= UsbMakeFullUriAddress( printer
);
903 if ( CFStringGetCString(addressRef
, uri
, sizeof(uri
), kCFStringEncodingUTF8
) ) {
905 encodecfstr( printer
->address
.manufacturer
, encodedManufacturer
, sizeof(encodedManufacturer
) );
906 encodecfstr( printer
->address
.product
, encodedProduct
, sizeof(encodedProduct
) );
907 printf("direct %s \"%s %s\" \"%s\"\n", uri
, encodedManufacturer
, encodedProduct
, encodedProduct
);
912 UsbReleaseAllPrinters( usbBusPrinters
);
917 static void parseOptions(const char *options
, char *serial
)
919 char *serialnumber
; /* ?serial=<serial> or ?location=<location> */
920 char optionName
[255], /* Name of option */
921 value
[255], /* Value of option */
922 *ptr
; /* Pointer into name or value */
932 while (*options
!= '\0')
937 for (ptr
= optionName
; *options
&& *options
!= '=' && *options
!= '+' && *options
!= '&'; )
951 for (ptr
= value
; *options
&& *options
!= '+' && *options
!= '&';)
956 if (*options
== '+' || *options
== '&')
959 else if (*options
== '+' || *options
== '&')
965 * Process the option...
967 if (strcasecmp(optionName
, "waiteof") == 0)
969 if (strcasecmp(value
, "on") == 0 ||
970 strcasecmp(value
, "yes") == 0 ||
971 strcasecmp(value
, "true") == 0)
975 else if (strcasecmp(value
, "off") == 0 ||
976 strcasecmp(value
, "no") == 0 ||
977 strcasecmp(value
, "false") == 0)
983 fprintf(stderr
, "WARNING: Boolean expected for waiteof option \"%s\"\n", value
);
986 else if (strcasecmp(optionName
, "serial") == 0 ||
987 strcasecmp(optionName
, "location") == 0 )
989 strcpy(serial
, value
);
990 serialnumber
= serial
;
999 * @function addPercentEscapes
1000 * @abstract Encode a string with percent escapes
1002 * @param src The source C string
1003 * @param dst Desination buffer
1004 * @param dstMax Size of desination buffer
1006 * @result A non-zero return value for errors
1008 static int addPercentEscapes(const unsigned char* src
, char* dst
, int dstMax
)
1011 char *dstEnd
= dst
+ dstMax
- 1; /* -1 to leave room for the NUL */
1017 if ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') ||
1018 (c
>= '0' && c
<= '9') || (c
== '.' || c
== '-' || c
== '*' || c
== '_'))
1027 if (dst
>= dstEnd
- 2)
1030 snprintf(dst
, dstEnd
- dst
, "%%%02x", c
);
1041 * @function removePercentEscapes
1042 * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
1044 * @param src Source buffer
1045 * @param srclen Number of bytes in source buffer
1046 * @param dst Desination buffer
1047 * @param dstMax Size of desination buffer
1049 * @result A non-zero return value for errors
1051 static int removePercentEscapes(const char* src
, unsigned char* dst
, int dstMax
)
1054 const unsigned char *dstEnd
= dst
+ dstMax
;
1056 while (*src
&& dst
< dstEnd
)
1062 sscanf(src
, "%02x", &c
);
1075 /*-----------------------------------------------------------------------------*
1079 Desc: Search a string from a starting location, looking for a given
1080 delimiter. Return the range from the start of the search to the
1081 delimiter, or end of string (whichever is shorter).
1083 In: stringToSearch string which contains a substring that we search
1084 delim string which marks the end of the string
1085 bounds start and length of substring of stringToSearch
1086 options case sensitive, anchored, etc.
1088 Out: Range up to the delimiter.
1090 *-----------------------------------------------------------------------------*/
1092 DelimitSubstring( CFStringRef stringToSearch
, CFStringRef delim
, CFRange bounds
, CFStringCompareFlags options
)
1094 CFRange where_delim
, /* where the delimiter was found */
1097 /* trim leading space by changing bounds */
1099 while ( bounds
.length
> 0 && CFStringFindWithOptions( stringToSearch
, CFSTR(" "), bounds
, kCFCompareAnchored
, &where_delim
) )
1101 ++bounds
.location
; /* drop a leading ' ' */
1104 value
= bounds
; /* assume match to the end of string, may be NULL */
1106 /* find the delimiter in the remaining string */
1108 if ( bounds
.length
> 0 && CFStringFindWithOptions( stringToSearch
, delim
, bounds
, options
, &where_delim
) )
1111 /* match to the delimiter */
1113 value
.length
= where_delim
.location
/* delim */ - bounds
.location
/* start of search */;
1115 DEBUG_CFString( "\tFind target", stringToSearch
);
1116 DEBUG_CFString( "\tFind pattern", delim
);
1117 DEBUG_ERR( (int) value
.location
, "\t\tFound %d\n" );
1118 DEBUG_ERR( (int) value
.length
, " length %d" );
1124 /*-----------------------------------------------------------------------------*
1126 DeviceIDCreateValueList
1128 Desc: Create a new string for the value list of the specified key.
1129 The key may be specified as two strings (an abbreviated form
1130 and a standard form). NULL can be passed for either form of
1133 (Although passing NULL for both forms of the key is considered
1134 bad form[!] it is handled correctly.)
1136 In: deviceID the device's IEEE-1284 DeviceID key-value list
1137 abbrevKey the key we're interested in (NULL allowed)
1140 Out: CFString the value list
1141 or NULL key wasn't found in deviceID
1143 *-----------------------------------------------------------------------------*/
1145 DeviceIDCreateValueList( const CFStringRef deviceID
, const CFStringRef abbrevKey
, const CFStringRef key
)
1147 CFRange found
= CFRangeMake( -1,0); /* note CFStringFind sets length 0 if string not found */
1148 CFStringRef valueList
= NULL
;
1150 DEBUG_CFString( "---------DeviceIDCreateValueList DeviceID:", deviceID
);
1151 DEBUG_CFString( "---------DeviceIDCreateValueList key:", key
);
1152 DEBUG_CFString( "---------DeviceIDCreateValueList abbrevkey:", abbrevKey
);
1153 if ( NULL
!= deviceID
&& NULL
!= abbrevKey
)
1154 found
= CFStringFind( deviceID
, abbrevKey
, kCFCompareCaseInsensitive
);
1155 if ( NULL
!= deviceID
&& NULL
!= key
&& found
.length
<= 0 )
1156 found
= CFStringFind( deviceID
, key
, kCFCompareCaseInsensitive
);
1157 if ( found
.length
> 0 )
1159 /* the key is at found */
1160 /* the value follows the key until we reach the semi-colon, or end of string */
1162 CFRange search
= CFRangeMake( found
.location
+ found
.length
,
1163 CFStringGetLength( deviceID
) - (found
.location
+ found
.length
) );
1165 /* finally extract the string */
1167 valueList
= CFStringCreateWithSubstring ( kCFAllocatorDefault
, deviceID
,
1168 DelimitSubstring( deviceID
, kDeviceIDKeyValuePairDelimiter
, search
, kCFCompareCaseInsensitive
) );
1169 DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList
);
1177 /******************************************************************************/
1179 /*-----------------------------------------------------------------------------*
1183 Desc: Return the CFCompare result for two strings, either or both of which
1191 0 if the strings match
1192 non-zero if the strings don't match
1194 *-----------------------------------------------------------------------------*/
1196 CompareSameString( const CFStringRef a
, const CFStringRef b
)
1198 if ( NULL
== a
&& NULL
== b
)
1200 else if ( NULL
!= a
&& NULL
!= b
)
1201 return CFStringCompare( a
, b
, kCFCompareAnchored
);
1203 return 1; /* one of a or b is NULL this time, but wasn't last time */
1207 /******************************************************************************/
1209 UsbLoadClassDriver( USBPrinterInfo
*printer
, CFUUIDRef interfaceID
, CFStringRef classDriverBundle
)
1211 kern_return_t kr
= kUSBPrinterClassDeviceNotOpen
;
1212 if ( NULL
!= classDriverBundle
)
1213 printer
->bundle
= classDriverBundle
; /* vendor-specific class override */
1216 classDriverBundle
= kUSBGenericTOPrinterClassDriver
; /* supply the generic TIMEOUT class driver */
1218 classDriverBundle
= kUSBGenericPrinterClassDriver
; /* supply the generic class driver */
1220 DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle
);
1221 if ( NULL
!= classDriverBundle
)
1223 USBPrinterClassContext
**classdriver
= NULL
;
1224 CFURLRef classDriverURL
= CFURLCreateWithFileSystemPath( NULL
, classDriverBundle
, kCFURLPOSIXPathStyle
, TRUE
);
1225 CFPlugInRef plugin
= NULL
== classDriverURL
? NULL
: CFPlugInCreate( NULL
, classDriverURL
);
1226 if ( NULL
!= plugin
)
1228 /* See if this plug-in implements the Test type. */
1229 CFArrayRef factories
= CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID
, plugin
);
1231 /* If there are factories for the requested type, attempt to */
1232 /* get the IUnknown interface. */
1233 DEBUG_ERR( 0, "UsbLoadClassDriver plugin %x\n" );
1234 if (NULL
!= factories
&& CFArrayGetCount(factories
) > 0)
1236 /* Get the factory ID for the first location in the array of IDs. */
1237 CFUUIDRef factoryID
= CFArrayGetValueAtIndex( factories
, 0 );
1238 /* Use the factory ID to get an IUnknown interface. */
1239 /* Here the code for the PlugIn is loaded. */
1240 IUnknownVTbl
**iunknown
= CFPlugInInstanceCreate( NULL
, factoryID
, kUSBPrinterClassTypeID
);
1241 /* If this is an IUnknown interface, query for the Test interface. */
1242 DEBUG_ERR( 0, "UsbLoadClassDriver factories %x\n" );
1243 if (NULL
!= iunknown
)
1245 DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" );
1246 kr
= (*iunknown
)->QueryInterface( iunknown
, CFUUIDGetUUIDBytes(interfaceID
), (LPVOID
*) &classdriver
);
1248 (*iunknown
)->Release( iunknown
);
1249 if ( S_OK
== kr
&& NULL
!= classdriver
)
1251 DEBUG_ERR( kr
, "UsbLoadClassDriver QueryInterface %x\n" );
1252 printer
->plugin
= plugin
;
1253 kr
= (*classdriver
)->Initialize( classdriver
, printer
->classdriver
);
1255 kr
= kIOReturnSuccess
;
1256 printer
->classdriver
= classdriver
;
1260 DEBUG_ERR( kr
, "UsbLoadClassDriver QueryInterface FAILED %x\n" );
1265 DEBUG_ERR( kr
, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" );
1270 DEBUG_ERR( kr
, "UsbLoadClassDriver factories FAILED %x\n" );
1275 DEBUG_ERR( kr
, "UsbLoadClassDriver plugin FAILED %x\n" );
1277 if ( kr
!= kIOReturnSuccess
|| NULL
== plugin
|| NULL
== classdriver
)
1279 UsbUnloadClassDriver( printer
);
1288 UsbUnloadClassDriver( USBPrinterInfo
*printer
)
1290 DEBUG_ERR( kIOReturnSuccess
, "UsbUnloadClassDriver %x\n" );
1291 if ( NULL
!= printer
->classdriver
)
1292 (*printer
->classdriver
)->Release( printer
->classdriver
);
1293 printer
->classdriver
= NULL
;
1295 if ( NULL
!= printer
->plugin
)
1296 CFRelease( printer
->plugin
);
1297 printer
->plugin
= NULL
;
1299 return kIOReturnSuccess
;
1303 /*-----------------------------------------------------------------------------*
1307 Desc: deallocates anything used to create a persistent printer address
1309 In: address the printer address we've created
1313 *-----------------------------------------------------------------------------*/
1315 UsbAddressDispose( USBPrinterAddress
*address
)
1317 if ( address
->product
!= NULL
) CFRelease( address
->product
);
1318 if ( address
->manufacturer
!= NULL
) CFRelease( address
->manufacturer
);
1319 if ( address
->serial
!= NULL
) CFRelease( address
->serial
);
1320 if ( address
->command
!= NULL
) CFRelease( address
->command
);
1323 address
->manufacturer
=
1325 address
->command
= NULL
;
1329 /*-----------------------------------------------------------------------------*
1331 UsbGetPrinterAddress
1333 Desc: Given a printer we're enumerating, discover it's persistent
1336 A "persistent reference" is one which enables us to identify
1337 a printer regardless of where it resides on the USB topology,
1338 and enumeration sequence.
1340 To do this, we actually construct a reference from information
1341 buried inside the printer. First we look at the USB device
1342 descripton: an ideally defined device will support strings for
1343 manufacturer and product id, and serial number. The serial number
1344 will be unique for each printer.
1346 Our prefered identification fetches the IEEE-1284 device id string.
1347 This transparently handled IEEE-1284 compatible printers which
1348 connected over a USB-parallel cable. Only if we can't get all the
1349 information to uniquely identify the printer do we try the strings
1350 referenced in the printer's USB device descriptor. (These strings
1351 are typically absent in a USB-parallel cable.)
1353 If a device doesn't support serial numbers we have a problem:
1354 we can't distinguish between two identical printers. Unique serial
1355 numbers allow us to distinguish between two same-model, same-manufacturer
1359 thePrinter iterator required for fetching device descriptor
1360 devRefNum required to configure the interface
1363 address->manufacturer
1366 Any (and all) of these may be NULL if we can't retrieve
1367 information for IEEE1284 DeviceID or the USB device
1368 descriptor. Caller should be prepared to handle such a case.
1372 *-----------------------------------------------------------------------------*/
1374 UsbGetPrinterAddress( USBPrinterInfo
*thePrinter
, USBPrinterAddress
*address
, UInt16 timeout
)
1378 /* start by assuming the device is not IEEE-1284 compliant */
1379 /* and that we can't read in the required strings. */
1382 CFStringRef deviceId
= NULL
;
1383 USBPrinterClassContext
**printer
= NULL
== thePrinter
? NULL
: thePrinter
->classdriver
;
1385 address
->manufacturer
=
1387 address
->compatible
=
1389 address
->command
= NULL
;
1391 DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter
, sizeof(USBPrinterInfo
) );
1393 err
= (*printer
)->GetDeviceID( printer
, &deviceId
, timeout
);
1394 if ( noErr
== err
&& NULL
!= deviceId
)
1396 /* the strings embedded here are defined in the IEEE1284 spec */
1398 /* use the MFG/MANUFACTURER for the manufacturer */
1399 /* and the MDL/MODEL for the product */
1400 /* there is no serial number defined in IEEE1284 */
1401 /* but it's been observed in recent HP printers */
1403 address
->command
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyCommandAbbrev
, kDeviceIDKeyCommand
);
1405 address
->product
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyModelAbbrev
, kDeviceIDKeyModel
);
1406 address
->compatible
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyCompatibleAbbrev
, kDeviceIDKeyCompatible
);
1408 address
->manufacturer
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeyManufacturerAbbrev
, kDeviceIDKeyManufacturer
);
1410 address
->serial
= DeviceIDCreateValueList( deviceId
, kDeviceIDKeySerialAbbrev
, kDeviceIDKeySerial
);
1411 CFRelease( deviceId
);
1413 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->product", address
->product
);
1414 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->compatible", address
->compatible
);
1415 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->manufacturer", address
->manufacturer
);
1416 DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->serial", address
->serial
);
1418 if ( NULL
== address
->product
|| NULL
== address
->manufacturer
|| NULL
== address
->serial
)
1421 /* if the manufacturer or the product or serial number were not specified in DeviceID */
1422 /* try to construct the address using USB English string descriptors */
1424 IOUSBDeviceDescriptor desc
;
1425 USBIODeviceRequest request
;
1427 request
.requestType
= USBmakebmRequestType( kUSBIn
, kUSBStandard
, kUSBDevice
);
1428 request
.request
= kUSBRqGetDescriptor
;
1429 request
.value
= (kUSBDeviceDesc
<< 8) | 0;
1430 request
.index
= 0; /* not kUSBLanguageEnglish*/
1431 request
.length
= sizeof(desc
);
1432 request
.buffer
= &desc
;
1433 err
= (*printer
)->DeviceRequest( printer
, &request
, timeout
);
1434 DEBUG_ERR( (kern_return_t
) err
, "UsbGetPrinterAddress: GetDescriptor %x" );
1435 if ( kIOReturnSuccess
== err
)
1437 /* once we've retrieved the device descriptor */
1438 /* try to fill in missing pieces of information */
1440 /* Don't override any information already retrieved from DeviceID. */
1442 if ( NULL
== address
->product
)
1444 err
= (*printer
)->GetString( printer
, desc
.iProduct
, kUSBLanguageEnglish
, timeout
, &address
->product
);
1445 if ( kIOReturnSuccess
!= err
|| address
->product
== NULL
) {
1446 address
->product
= CFSTR("Unknown");
1449 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address
->product
);
1451 if ( NULL
== address
->manufacturer
)
1453 err
= (*printer
)->GetString( printer
, desc
.iManufacturer
, kUSBLanguageEnglish
, timeout
, &address
->manufacturer
);
1454 if (kIOReturnSuccess
!= err
|| address
->manufacturer
== NULL
) {
1455 address
->manufacturer
= CFSTR("Unknown");
1458 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address
->manufacturer
);
1460 if ( NULL
== address
->serial
)
1462 /* if the printer doesn't have a serial number, use locationId */
1463 if ( 0 == desc
.iSerialNumber
)
1465 address
->serial
= CFStringCreateWithFormat( NULL
, NULL
, CFSTR("%lx"), (*printer
)->location
);
1469 err
= (*printer
)->GetString( printer
, desc
.iSerialNumber
, kUSBLanguageEnglish
, timeout
, &address
->serial
);
1470 /* trailing NULs aren't handled correctly in URI */
1471 if ( address
->serial
)
1473 UniChar nulbyte
= { 0 };
1474 CFStringRef trim
= CFStringCreateWithCharacters(NULL
, &nulbyte
, 1);
1475 CFMutableStringRef newserial
= CFStringCreateMutableCopy(NULL
, 0, address
->serial
);
1477 CFStringTrim( newserial
, trim
);
1480 CFRelease( address
->serial
);
1482 address
->serial
= newserial
;
1486 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address
->serial
);
1489 if ( NULL
!= address
->product
)
1490 CFRetain(address
->product
); /* UsbGetString is really a UsbCopyString. */
1491 if ( NULL
!= address
->manufacturer
)
1492 CFRetain( address
->manufacturer
);
1493 if ( NULL
!= address
->serial
)
1494 CFRetain( address
->serial
);
1499 /*-----------------------------------------------------------------------------*
1503 Desc: match two Usb printer address; return TRUE if they are the same.
1505 In: a the persistent address found last time
1506 b the persistent address found this time
1508 Out: non-zero iff the addresses are the same
1510 *-----------------------------------------------------------------------------*/
1512 UsbSamePrinter( const USBPrinterAddress
*a
, const USBPrinterAddress
*b
)
1515 DEBUG_CFCompareString( "UsbSamePrinter serial", a
->serial
, b
->serial
);
1516 DEBUG_CFCompareString( "UsbSamePrinter product", a
->product
, b
->product
);
1517 DEBUG_CFCompareString( "UsbSamePrinter manufacturer", a
->manufacturer
, b
->manufacturer
);
1519 result
= !CompareSameString( a
->serial
, b
->serial
);
1520 if ( result
) result
= !CompareSameString( a
->product
, b
->product
);
1521 if ( result
) result
= !CompareSameString( a
->manufacturer
, b
->manufacturer
);
1527 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1528 Method: UsbMakeFullUriAddress
1535 Fill in missing address information
1537 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1539 UsbMakeFullUriAddress( USBPrinterInfo
*printer
)
1542 /* fill in missing address information. */
1544 CFMutableStringRef printerUri
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("usb://") );
1545 if ( NULL
!= printerUri
)
1547 CFStringRef serial
= printer
->address
.serial
;
1549 CFStringAppend(printerUri
, printer
->address
.manufacturer
? CreateEncodedCFString( printer
->address
.manufacturer
): CFSTR("Unknown") );
1550 CFStringAppend(printerUri
, CFSTR("/") );
1552 CFStringAppend(printerUri
, printer
->address
.product
? CreateEncodedCFString( printer
->address
.product
): CFSTR("Unknown") );
1554 /*Handle the common case where there is no serial number (S450?) */
1555 CFStringAppend(printerUri
, serial
== NULL
? CFSTR("?location="): CFSTR("?serial=") );
1556 if ( serial
== NULL
)
1557 serial
= CFStringCreateWithFormat( NULL
, NULL
, CFSTR("%lx"), printer
->location
);
1559 CFStringAppend(printerUri
, serial
? CreateEncodedCFString( serial
): CFSTR("Unknown") );
1566 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1567 Method: UsbGetAllPrinters
1572 array of all USB printers on the system
1575 Build a list of USB printers by iterating IOKit USB objects
1577 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1579 UsbGetAllPrinters( void )
1581 kern_return_t kr
; /* kernel errors */
1582 mach_port_t master_device_port
= 0;
1583 io_service_t usbInterface
= 0;
1584 io_iterator_t iter
= 0;
1585 CFMutableArrayRef printers
= CFArrayCreateMutable( NULL
, 0, NULL
); /* all printers */
1590 kr
= IOMasterPort( bootstrap_port
, &master_device_port
);
1591 DEBUG_ERR( kr
, "UsbGetAllPrinters IOMasterPort %x\n" );
1592 if(kIOReturnSuccess
!= kr
) break;
1595 CFDictionaryRef usbMatch
= NULL
;
1597 /* iterate over all interfaces. */
1598 usbMatch
= IOServiceMatching(kIOUSBInterfaceClassName
);
1599 if ( !usbMatch
) break;
1600 DEBUG_ERR( kr
, "UsbGetAllPrinters IOServiceMatching %x\n" );
1602 /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */
1603 kr
= IOServiceGetMatchingServices(master_device_port
, usbMatch
, &iter
);
1606 DEBUG_ERR( kr
, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" );
1607 if(kIOReturnSuccess
!= kr
|| iter
== NULL
) break;
1610 while ( NULL
!= (usbInterface
= IOIteratorNext(iter
)) )
1612 IOCFPlugInInterface
**iodev
;
1613 USBPrinterInterface intf
;
1616 CFMutableDictionaryRef properties
;
1617 CFStringRef classDriver
= NULL
;
1619 kr
= IORegistryEntryCreateCFProperties( usbInterface
, &properties
, kCFAllocatorDefault
, kNilOptions
);
1620 if ( kIOReturnSuccess
== kr
&& NULL
!= properties
)
1622 classDriver
= (CFStringRef
) CFDictionaryGetValue( properties
, kUSBClassDriverProperty
);
1623 if ( NULL
!= classDriver
)
1624 CFRetain( classDriver
);
1625 CFRelease( properties
);
1628 kr
= IOCreatePlugInInterfaceForService( usbInterface
,
1629 kIOUSBInterfaceUserClientTypeID
,
1630 kIOCFPlugInInterfaceID
,
1634 DEBUG_ERR( kr
, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" );
1635 if ( kIOReturnSuccess
== kr
)
1637 UInt8 intfClass
= 0;
1638 UInt8 intfSubClass
= 0;
1640 res
= (*iodev
)->QueryInterface( iodev
, USB_INTERFACE_KIND
, (LPVOID
*) &intf
);
1641 DEBUG_ERR( (kern_return_t
) res
, "UsbGetAllPrinters QueryInterface %x\n" );
1643 (*iodev
)->Release(iodev
);
1644 if ( noErr
!= res
) break;
1646 kr
= (*intf
)->GetInterfaceClass(intf
, &intfClass
);
1647 DEBUG_ERR(kr
, "UsbGetAllPrinters GetInterfaceClass %x\n");
1648 if ( kIOReturnSuccess
== kr
)
1649 kr
= (*intf
)->GetInterfaceSubClass(intf
, &intfSubClass
);
1650 DEBUG_ERR(kr
, "UsbGetAllPrinters GetInterfaceSubClass %x\n");
1652 if ( kIOReturnSuccess
== kr
&&
1653 kUSBPrintingClass
== intfClass
&&
1654 kUSBPrintingSubclass
== intfSubClass
)
1657 USBPrinterInfo printer
,
1660 For each type of printer specified in the lookup spec array, find
1661 all of that type of printer and add the results to the list of found
1664 /* create this printer's persistent address */
1665 memset( &printer
, 0, sizeof(USBPrinterInfo
) );
1666 kr
= (*intf
)->GetLocationID(intf
, &printer
.location
);
1667 DEBUG_ERR(kr
, "UsbGetAllPrinters GetLocationID %x\n");
1668 if ( kIOReturnSuccess
== kr
)
1670 kr
= UsbLoadClassDriver( &printer
, kUSBPrinterClassInterfaceID
, classDriver
);
1671 DEBUG_ERR(kr
, "UsbGetAllPrinters UsbLoadClassDriver %x\n");
1672 if ( kIOReturnSuccess
== kr
&& printer
.classdriver
)
1674 (*(printer
.classdriver
))->interface
= intf
;
1675 kr
= UsbGetPrinterAddress( &printer
, &printer
.address
, 60000L );
1677 /* always unload the driver */
1678 /* but don't mask last error */
1679 kern_return_t unload_err
= UsbUnloadClassDriver( &printer
);
1680 if ( kIOReturnSuccess
== kr
)
1686 printerInfo
= UsbCopyPrinter( &printer
);
1687 if ( NULL
!= printerInfo
)
1688 CFArrayAppendValue( printers
, (const void *) printerInfo
); /* keep track of it */
1690 } /* if there's a printer */
1691 kr
= (*intf
)->Release(intf
);
1692 } /* if IOCreatePlugInInterfaceForService */
1694 IOObjectRelease(usbInterface
);
1695 usbInterface
= NULL
;
1697 } /* while there's an interface */
1702 IOObjectRelease(iter
);
1706 if (master_device_port
)
1708 mach_port_deallocate(mach_task_self(), master_device_port
);
1709 master_device_port
= 0;
1713 } /* UsbGetAllPrinters */
1715 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1716 Method: UsbReleasePrinter
1723 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1725 UsbReleasePrinter( USBPrinterInfo
*printer
)
1729 UsbUnloadClassDriver( printer
);
1730 if ( NULL
!= printer
->address
.manufacturer
)
1731 CFRelease( printer
->address
.manufacturer
);
1732 if ( NULL
!= printer
->address
.product
)
1733 CFRelease( printer
->address
.product
);
1734 if ( NULL
!= printer
->address
.serial
)
1735 CFRelease( printer
->address
.serial
);
1736 if ( NULL
!= printer
->address
.command
)
1737 CFRelease( printer
->address
.command
);
1738 if ( NULL
!= printer
->bundle
)
1739 CFRelease( printer
->bundle
);
1744 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1745 Method: UsbReleaseAllPrinters
1752 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1754 UsbReleaseAllPrinters( CFMutableArrayRef printers
)
1756 if ( NULL
!= printers
)
1759 numPrinters
= CFArrayGetCount(printers
);
1760 for ( i
= 0; i
< numPrinters
; ++i
)
1761 UsbReleasePrinter( (USBPrinterInfo
*) CFArrayGetValueAtIndex( printers
, i
) );
1762 CFRelease( printers
);
1767 UsbCopyPrinter( USBPrinterInfo
*aPrinter
)
1770 /* note this does not copy interface information, just address information */
1772 USBPrinterInfo
*printerInfo
= (USBPrinterInfo
*) calloc( 1, sizeof(USBPrinterInfo
));
1773 if ( NULL
!= printerInfo
&& NULL
!= aPrinter
)
1775 printerInfo
->location
= aPrinter
->location
;
1776 if ( NULL
!= (printerInfo
->address
.manufacturer
= aPrinter
->address
.manufacturer
) )
1777 CFRetain( printerInfo
->address
.manufacturer
);
1778 if ( NULL
!= (printerInfo
->address
.product
= aPrinter
->address
.product
) )
1779 CFRetain( printerInfo
->address
.product
);
1780 if ( NULL
!= (printerInfo
->address
.serial
= aPrinter
->address
.serial
) )
1781 CFRetain( printerInfo
->address
.serial
);
1782 if ( NULL
!= (printerInfo
->address
.command
= aPrinter
->address
.command
) )
1783 CFRetain( printerInfo
->address
.command
);
1784 if ( NULL
!= (printerInfo
->bundle
= aPrinter
->bundle
) )
1785 CFRetain( printerInfo
->bundle
);
1791 /*-----------------------------------------------------------------------------*
1795 Desc: opens the USB printer which matches the supplied printerAddress
1797 In: myContext->printerAddress persistent name which identifies the printer
1799 Out: myContext->usbDeviceRef current IOKit address of this printer
1800 *-----------------------------------------------------------------------------*/
1802 UsbRegistryOpen( USBPrinterAddress
*usbAddress
, USBPrinterInfo
**result
)
1804 kern_return_t kr
= -1; /* indeterminate failure */
1805 CFMutableArrayRef printers
= UsbGetAllPrinters();
1806 CFIndex numPrinters
= NULL
!= printers
? CFArrayGetCount( printers
): 0;
1809 *result
= NULL
; /* nothing matched */
1810 for ( i
= 0; i
< numPrinters
; ++i
)
1812 USBPrinterInfo
*thisPrinter
= (USBPrinterInfo
*) CFArrayGetValueAtIndex( printers
, i
);
1813 if ( NULL
!= thisPrinter
&& UsbSamePrinter( usbAddress
, &thisPrinter
->address
) )
1815 *result
= UsbCopyPrinter( thisPrinter
); /* retains reference */
1816 if ( NULL
!= *result
)
1819 /* if we can't find a bi-di interface, settle for a known uni-directional interface */
1821 USBPrinterClassContext
**printer
= NULL
;
1823 /* setup the default class driver */
1824 /* If one is specified, allow the vendor driver to override our default implementation */
1826 kr
= UsbLoadClassDriver( *result
, kUSBPrinterClassInterfaceID
, NULL
);
1827 if ( kIOReturnSuccess
== kr
&& (*result
)->bundle
)
1828 kr
= UsbLoadClassDriver( *result
, kUSBPrinterClassInterfaceID
, (*result
)->bundle
);
1829 if ( kIOReturnSuccess
== kr
&& NULL
!= (*result
)->classdriver
)
1831 printer
= (*result
)->classdriver
;
1832 kr
= (*printer
)->Open( printer
, (*result
)->location
, kUSBPrintingProtocolBidirectional
);
1833 if ( kIOReturnSuccess
!= kr
|| NULL
== (*printer
)->interface
)
1834 kr
= (*printer
)->Open( printer
, (*result
)->location
, kUSBPrintingProtocolUnidirectional
);
1835 /* it's possible kIOReturnSuccess == kr && NULL == (*printer)->interface */
1836 /* in the event that we can't open either Bidirectional or Unidirectional interface */
1837 if ( kIOReturnSuccess
== kr
)
1839 if ( NULL
== (*printer
)->interface
)
1841 (*printer
)->Close( printer
);
1842 UsbReleasePrinter( *result
);
1851 UsbReleaseAllPrinters( printers
); /* but, copied printer is retained */
1852 DEBUG_ERR( kr
, "UsbRegistryOpen return %x\n" );
1858 * @function CreateEncodedCFString
1860 * @abstract Create an encoded version of the string parameter
1861 * so that it can be included in a URI.
1863 * @param string A CFStringRef of the string to be encoded.
1864 * @result An encoded CFString.
1866 * @discussion This function will change all characters in string into URL acceptable format
1867 * by encoding the text using the US-ASCII coded character set. The following
1868 * are invalid characters: the octets 00-1F, 7F, and 80-FF hex. Also called out
1869 * are the chars "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "[", "]", "`".
1870 * The reserved characters for URL syntax are also to be encoded: (so don't pass
1871 * in a full URL here!) ";", "/", "?", ":", "@", "=", "%", and "&".
1873 static CFStringRef
CreateEncodedCFString(CFStringRef string
)
1875 CFStringRef result
= NULL
;
1876 char *bufferUTF8
= NULL
;
1877 char *bufferEncoded
= NULL
;
1881 CFIndex bufferSizeUTF8
= (3 * CFStringGetLength(string
));
1882 if ((bufferUTF8
= (char*)malloc(bufferSizeUTF8
)) != NULL
)
1884 CFStringGetCString(string
, bufferUTF8
, bufferSizeUTF8
, kCFStringEncodingUTF8
);
1886 UInt16 bufferSizeEncoded
= (3 * strlen(bufferUTF8
)) + 1;
1887 if ((bufferEncoded
= (char*)malloc(bufferSizeEncoded
)) != NULL
)
1889 addPercentEscapes(bufferUTF8
, bufferEncoded
, bufferSizeEncoded
);
1890 result
= CFStringCreateWithCString(kCFAllocatorDefault
, bufferEncoded
, kCFStringEncodingUTF8
);
1896 if (bufferUTF8
) free(bufferUTF8
);
1897 if (bufferEncoded
) free(bufferEncoded
);
1903 * End of "$Id: usb-darwin.c 5241 2006-03-07 22:07:44Z mike $".