]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/usb-darwin.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / usb-darwin.c
CommitLineData
ef416fc2 1/*
2 * "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $"
3 *
4 * USB port on Darwin backend for the Common UNIX Printing System (CUPS).
5 *
6 * This file is included from "usb.c" when compiled on MacOS X or Darwin.
7 *
8 * Copyright 2004 Apple Computer, Inc. All rights reserved.
9 *
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.
16 *
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.
32 *
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.
38 *
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.
47 *
48 * Contents:
49 *
50 * print_device() - Send a file to the specified USB port.
51 * list_devices() - List all USB devices.
52 */
53
54/*
55 * Include necessary headers...
56 */
57
58#include <CoreFoundation/CoreFoundation.h>
59#include <ApplicationServices/ApplicationServices.h>
60
61#include <IOKit/usb/IOUSBLib.h>
62#include <IOKit/IOCFPlugIn.h>
63#include <mach/mach.h>
64#include <mach/mach_error.h>
65
66#include <stdio.h>
67#include <stdlib.h>
68#include <errno.h>
69#include <signal.h>
70#include <fcntl.h>
71#include <termios.h>
72#include <unistd.h>
73#include <pthread.h> /* Used for writegReadMutex */
74
75#ifndef kPMPrinterURI
76# define kPMPrinterURI CFSTR("Printer URI")
77#endif
78
79/*
80 * Panther/10.3 kIOUSBInterfaceInterfaceID190
81 * Jaguar/10.2 kIOUSBInterfaceInterfaceID182
82 */
83
84#define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
85#define kUSBLanguageEnglish 0x409
86
87/*
88 * Section 5.3 USB Printing Class spec
89 */
90
91#define kUSBPrintingSubclass 1
92#define kUSBPrintingProtocolNoOpen 0
93#define kUSBPrintingProtocolUnidirectional 1
94#define kUSBPrintingProtocolBidirectional 2
95
96#define kUSBPrintClassGetDeviceID 0
97#define kUSBPrintClassGetCentronicsStatus 1
98#define kUSBPrintClassSoftReset 2
99
100/*
101 * Apple MacOS X printer-class plugins
102 */
103
104#define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
105
106#define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
107
108#define kUSBGenericPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
109#define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
110
111#define kUSBClassDriverProperty CFSTR("USB Printing Class")
112#define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
113
114typedef union
115{
116 char b;
117 struct
118 {
119 unsigned reserved0 : 2;
120 unsigned paperError : 1;
121 unsigned select : 1;
122 unsigned notError : 1;
123 unsigned reserved1 : 3;
124 } status;
125} CentronicsStatusByte;
126
127typedef struct
128{
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 */
135} USBPrinterAddress;
136
137typedef IOUSBInterfaceInterface190 **USBPrinterInterface;
138
139typedef struct
140{
141 UInt8 requestType;
142 UInt8 request;
143 UInt16 value;
144 UInt16 index;
145 UInt16 length;
146 void *buffer;
147} USBIODeviceRequest;
148
149typedef struct classDriverContext
150{
151 IUNKNOWN_C_GUTS;
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;
157 UInt16 vendorID;
158 UInt16 productID;
159 USBPrinterInterface interface; /* identify the device to IOKit */
160 UInt8 outpipe; /* mandatory bulkOut pipe */
161 UInt8 inpipe; /* optional bulkIn pipe */
162 /*
163 ** general class requests
164 */
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 );
167 /*
168 ** standard printer class requests
169 */
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 );
173 /*
174 ** standard bulk device requests
175 */
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 );
178 /*
179 ** interface requests
180 */
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 );
184 /*
185 ** initialize and terminate
186 */
187 kern_return_t (*Initialize)( struct classDriverContext **printer, struct classDriverContext **baseclass );
188 kern_return_t (*Terminate)( struct classDriverContext **printer );
189} USBPrinterClassContext;
190
191
192typedef struct usbPrinterClassType
193{
194 USBPrinterClassContext *classdriver;
195 CFUUIDRef factoryID;
196 UInt32 refCount;
197} USBPrinterClassType;
198
199
200/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201 Constants
202~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
203
204/*
205 Debugging output to Console
206 DEBUG undefined or
207 DEBUG=0 production code: suppress console output
208
209 DEBUG=1 report errors (non-zero results)
210 DEBUG=2 report all results, generate dumps
211*/
212#if DEBUG==2
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 )
217#elif DEBUG==1
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 )
222#else
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 )
227#endif
228
229/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
230 Type Definitions
231~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
232
233typedef struct
234{
235 /*
236 * Tagged/Tranparent Binary Communications Protocol
237 * TBCP read
238 */
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 */
245 /*
246 * TBCP write
247 */
248 UInt8 *tbcpWriteData; /* write buffer */
249 UInt32 tbcpBufferLength, /* write buffer allocated length */
250 tbcpBufferRemaining; /* write buffer not used */
251
252 Boolean sendStatusNextWrite;
253
254} PostScriptData;
255
256typedef struct
257{
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 */
264} USBPrinterInfo;
265
266/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
267 Functions
268~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
269
270/*
271** IOKit to CF functions
272*/
273USBPrinterInfo *UsbCopyPrinter( USBPrinterInfo *aPrinter );
274CFMutableArrayRef UsbGetAllPrinters( void );
275void UsbReleasePrinter( USBPrinterInfo *aPrinter );
276void UsbReleaseAllPrinters( CFMutableArrayRef printers );
277kern_return_t UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result );
278kern_return_t UsbUnloadClassDriver( USBPrinterInfo *printer );
279kern_return_t UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle );
280CFStringRef UsbMakeFullUriAddress( USBPrinterInfo *aPrinter );
281
282int UsbSamePrinter( const USBPrinterAddress *lastTime, const USBPrinterAddress *thisTime );
283
284OSStatus UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout );
285
286
287/*******************************************************************************
288 Contains: Support IEEE-1284 DeviceID as a CFString.
289
290 Copyright 2000-2005 by Apple Computer, Inc., all rights reserved.
291
292 Description:
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
295 to the host system.
296
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.
300
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.
304
305 Some keys are required: COMMAND SET (or CMD), MANUFACTURER (or MFG),
306 and MODEL (or MDL).
307
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.
311
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.
314
315 A more formal definition of DeviceID.
316
317 <DeviceID> = <Length><Key_ValueList_Pair>+
318
319 <Length> = <low byte of 16 bit integer><high byte of 16 bit integer>
320 <Key_ValueList_Pair> = <Key>:<Value>[,<Value>]*;
321
322 <Key> = <ASCII Byte>+
323 <Value> = <ASCII Byte>+
324
325 Some keys are defined in the standard. The standard specifies that
326 keys are case sensitive. White space is allowed in the key.
327
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'
331
332 Required Keys:
333 'COMMAND SET' or CMD
334 MANUFACTURER or MFG
335 MODEL or MDL
336
337 Optional Keys:
338 CLASS
339 Value PRINTER is referenced in the standard.
340
341 Observed Keys:
342 SN,SERN
343 Used by Hewlett-Packard for the serial number.
344
345
346*******************************************************************************/
347
348/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
349 Pragmas
350~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
351
352
353/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
354 Constants
355~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
356#define kDeviceIDKeyCommand CFSTR("COMMAND SET:")
357#define kDeviceIDKeyCommandAbbrev CFSTR( "CMD:" )
358
359#define kDeviceIDKeyManufacturer CFSTR("MANUFACTURER:")
360#define kDeviceIDKeyManufacturerAbbrev CFSTR( "MFG:" )
361
362#define kDeviceIDKeyModel CFSTR("MODEL:")
363#define kDeviceIDKeyModelAbbrev CFSTR( "MDL:" )
364
365#define kDeviceIDKeySerial CFSTR("SN:")
366#define kDeviceIDKeySerialAbbrev CFSTR("SERN:")
367
368#define kDeviceIDKeyCompatible CFSTR("COMPATIBLITY ID:")
369#define kDeviceIDKeyCompatibleAbbrev CFSTR("CID:")
370
371/* delimiters */
372#define kDeviceIDKeyValuePairDelimiter CFSTR(";")
373
374/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
375 Type definitions
376~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
377
378/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
379 Function prototypes
380~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
381
382static CFStringRef CreateEncodedCFString(CFStringRef string);
383static CFRange DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options );
384static void parseOptions(const char *options, char *serial);
385
386CFStringRef
387DeviceIDCreateValueList( const CFStringRef deviceID,
388 const CFStringRef abbrevKey,
389 const CFStringRef key );
390
391static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax);
392static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax);
393
394/* Required to suppress redefinition warnings for these two symbols
395*/
396#if defined(TCP_NODELAY)
397#undef TCP_NODELAY
398#endif
399#if defined(TCP_MAXSEG)
400#undef TCP_MAXSEG
401#endif
402
403#include <cups/cups.h>
404
405
406#define PRINTER_POLLING_INTERVAL 5 /* seconds */
407#define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
408#define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
409
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
413
414#define USB_MAX_STR_SIZE 1024
415
416
417static volatile int done = 0;
418static int gWaitEOF = false;
419static pthread_cond_t *gReadCompleteConditionPtr = NULL;
420static pthread_mutex_t *gReadMutexPtr = NULL;
421
422
423
424#if DEBUG==2
425
426static char
427hexdigit( char c )
428{
429 return ( c < 0 || c > 15 )? '?': (c < 10)? c + '0': c - 10 + 'A';
430}
431
432static char
433asciidigit( char c )
434{
435 return (c< 20 || c > 0x7E)? '.': c;
436}
437
438void
439dump( char *text, void *s, int len )
440{
441 int i;
442 char *p = (char *) s;
443 char m[1+2*16+1+16+1];
444
445 fprintf( stderr, "%s pointer %x len %d\n", text, (unsigned int) p, len );
446
447 for ( ; len > 0; len -= 16 )
448 {
449 char *q = p;
450 char *out = m;
451 *out++ = '\t';
452 for ( i = 0; i < 16 && i < len; ++i, ++p )
453 {
454 *out++ = hexdigit( (*p >> 4) & 0x0F );
455 *out++ = hexdigit( *p & 0x0F );
456 }
457 for ( ;i < 16; ++i )
458 {
459 *out++ = ' ';
460 *out++ = ' ';
461 }
462 *out++ = '\t';
463 for ( i = 0; i < 16 && i < len; ++i, ++q )
464 *out++ = asciidigit( *q );
465 *out = 0;
466 m[ strlen( m ) ] = '\0';
467 fprintf( stderr, "%s\n", m );
468 }
469}
470
471void
472printcfs( char *text, CFStringRef s )
473{
474 char dest[1024];
475 if ( s != NULL )
476 {
477 if ( CFStringGetCString(s, dest, sizeof(dest), kCFStringEncodingUTF8) )
478 sprintf( dest, "%s <%s>\n", text, dest );
479 else
480 sprintf( dest, "%s [Unknown string]\n", text );
481 } else {
482 sprintf( dest, "%s [NULL]\n", text );
483 }
484 perror( dest );
485}
486
487void
488cmpcfs( char *text, CFStringRef a, CFStringRef b )
489{
490 CFRange found = {0, 0};
491
492 printcfs( text, a );
493 printcfs( " ", b );
494
495 if (a != NULL && b != NULL) {
496 found = CFStringFind( a, b, kCFCompareCaseInsensitive );
497
498 } else if (a == NULL && b == NULL) {
499 found.length = 1; /* Match */
500 found.location = 0;
501 } else {
502 found.length = 0; /* No match. */
503 }
504
505 if ( found.length > 0 )
506 fprintf( stderr, "matched @%d:%d\n", (int) found.location, (int) found.length);
507 else
508 fprintf( stderr, "not matched\n" );
509}
510#endif /*DEBUG==2 */
511
512#ifdef PARSE_PS_ERRORS
513static const char *nextLine (const char *buffer);
514static void parsePSError (char *sockBuffer, int len);
515
516
517static const char *nextLine (const char *buffer)
518{
519 const char *cptr, *lptr = NULL;
520 for (cptr = buffer; *cptr && lptr == NULL; cptr++)
521 if (*cptr == '\n' || *cptr == '\r')
522 lptr = cptr;
523 return lptr;
524}
525
526static void parsePSError (char *sockBuffer, int len)
527{
528 static char gErrorBuffer[1024] = "";
529 static char *gErrorBufferPtr = gErrorBuffer;
530 static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
531
532 char *pCommentBegin, *pCommentEnd, *pLineEnd;
533 char *logLevel;
534 char logstr[1024];
535 int logstrlen;
536
537 if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
538 gErrorBufferPtr = gErrorBuffer;
539 if (len > sizeof(gErrorBuffer) - 1)
540 len = sizeof(gErrorBuffer) - 1;
541
542 memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
543 gErrorBufferPtr += len;
544 *(gErrorBufferPtr + 1) = '\0';
545
546
547 pLineEnd = (char *)nextLine((const char *)gErrorBuffer);
548 while (pLineEnd != NULL)
549 {
550 *pLineEnd++ = '\0';
551
552 pCommentBegin = strstr(gErrorBuffer,"%%[");
553 pCommentEnd = strstr(gErrorBuffer, "]%%");
554 if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL)
555 {
556 pCommentEnd += 3; /* Skip past "]%%" */
557 *pCommentEnd = '\0'; /* There's always room for the nul */
558
559 if (strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
560 logLevel = "DEBUG";
561 else if (strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
562 logLevel = "DEBUG";
563 else
564 logLevel = "INFO";
565
566 if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr))
567 {
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';
571 }
572 write(STDERR_FILENO, logstr, logstrlen);
573 }
574
575 /* move everything over... */
576 strcpy(gErrorBuffer, pLineEnd);
577 gErrorBufferPtr = gErrorBuffer;
578 pLineEnd = (char *)nextLine((const char *)gErrorBuffer);
579 }
580}
581#endif /* PARSE_PS_ERRORS */
582
583void *
584readthread( void *reference )
585{
586 /*
587 ** post a read to the device and write results to stdout
588 ** the final pending read will be Aborted in the main thread
589 */
590 UInt8 readbuffer[512];
591 UInt32 rbytes;
592 kern_return_t readstatus;
593 USBPrinterClassContext **classdriver = (USBPrinterClassContext **) reference;
594
595
596 do
597 {
598 rbytes = sizeof(readbuffer) - 1;
599 readstatus = (*classdriver)->ReadPipe( classdriver, readbuffer, &rbytes );
600 if ( kIOReturnSuccess == readstatus && rbytes > 0 )
601 {
602 write( STDOUT_FILENO, readbuffer, rbytes );
603 /* cntrl-d is echoed by the printer.
604 * NOTES:
605 * Xerox Phaser 6250D doesn't echo the cntrl-d.
606 * Xerox Phaser 6250D doesn't always send the product query.
607 */
608 if (gWaitEOF && readbuffer[rbytes-1] == 0x4)
609 break;
610#ifdef PARSE_PS_ERRORS
611 parsePSError(readbuffer, rbytes);
612#endif
613 }
614 } while ( gWaitEOF || !done ); /* Abort from main thread tests error here */
615
616 /* Let the other thread (main thread) know that we have
617 * completed the read thread...
618 */
619 pthread_mutex_lock(gReadMutexPtr);
620 pthread_cond_signal(gReadCompleteConditionPtr);
621 pthread_mutex_unlock(gReadMutexPtr);
622
623 return NULL;
624}
625
626/*
627* 'print_device()' - Send a file to the specified USB port.
628*/
629
630int print_device(const char *uri, const char *hostname, const char *resource, const char *options, int fd, int copies)
631{
632 UInt32 wbytes, /* Number of bytes written */
633 buffersize = 2048;
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 */
638
639 pthread_cond_t readCompleteCondition;
640 pthread_mutex_t readMutex;
641 pthread_t thr;
642 int thread_created = 0;
643
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;
650 CFStringRef product;
651 CFStringRef serial;
652
653 OSStatus status = noErr;
654
655
656 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
657 struct sigaction action; /* Actions for POSIX signals */
658 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
659
660 fprintf(stderr, "INFO: Opening the print file and connection...\n");
661
662 parseOptions(options, serial_buf);
663
664 if (resource[0] == '/')
665 resource++;
666
667 removePercentEscapes(hostname, manufacturer_buf, sizeof(manufacturer_buf));
668 removePercentEscapes(resource, product_buf, sizeof(product_buf));
669
670 manufacturer = CFStringCreateWithCString(NULL, manufacturer_buf, kCFStringEncodingUTF8);
671 product = CFStringCreateWithCString(NULL, product_buf, kCFStringEncodingUTF8);
672 serial = CFStringCreateWithCString(NULL, serial_buf, kCFStringEncodingUTF8);
673
674 USBPrinterInfo *activePrinter = NULL;
675 USBPrinterClassContext **classdriver;
676 int countdown = INITIAL_LOG_INTERVAL;
677 do
678 {
679 /* */
680 /* given a manufacturer and product, bind to a specific printer on the bus */
681 /* */
682 usbPrinters = UsbGetAllPrinters();
683 /* */
684 /* if we have at least one element of the URI, find a printer module that matches */
685 /* */
686 if ( NULL != usbPrinters && (manufacturer || product ) )
687 {
688 int i,
689 numPrinters = CFArrayGetCount(usbPrinters);
690 for ( i = 0; i < numPrinters; ++i )
691 {
692 int match = FALSE;
693 USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbPrinters, i );
694 if ( printer )
695 {
696 match = printer->address.manufacturer && manufacturer? CFEqual(printer->address.manufacturer, manufacturer ): FALSE;
697 if ( match )
698 {
699 match = printer->address.product && product? CFEqual(printer->address.product, product ): FALSE;
700 }
701 if ( match && serial )
702 {
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. */
711 /* */
712 match = printer->address.serial? CFEqual(printer->address.serial, serial ): FALSE;
713 }
714 if ( match )
715 {
716 targetPrinter = UsbCopyPrinter( printer );
717 break; /* for, compare partial address to address for each printer on usb bus */
718 }
719 }
720 }
721 }
722 UsbReleaseAllPrinters( usbPrinters );
723 if ( NULL != targetPrinter )
724 status = UsbRegistryOpen( &targetPrinter->address, &activePrinter );
725
726 if ( NULL == activePrinter )
727 {
728 sleep( PRINTER_POLLING_INTERVAL );
729 countdown -= PRINTER_POLLING_INTERVAL;
730 if ( !countdown )
731 {
732 /* periodically, write to the log so someone knows we're waiting */
733 if (NULL == targetPrinter)
734 fprintf( stderr, "WARNING: Printer not responding\n" );
735 else
736 fprintf( stderr, "INFO: Printer busy\n" );
737 countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 30 minutes */
738 }
739 }
740 } while ( NULL == activePrinter );
741
742 classdriver = activePrinter->classdriver;
743 if ( NULL == classdriver )
744 {
745 perror("ERROR: Unable to open USB Printing Class port");
746 return (status);
747 }
748
749 /*
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...)
754 */
755
756 if (fd != 0)
757 {
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));
762
763 sigemptyset(&action.sa_mask);
764 action.sa_handler = SIG_IGN;
765 sigaction(SIGTERM, &action, NULL);
766#else
767 signal(SIGTERM, SIG_IGN);
768#endif /* HAVE_SIGSET */
769 }
770
771 buffer = malloc( buffersize );
772 if ( !buffer ) {
773 fprintf( stderr, "ERROR: Couldn't allocate internal buffer\n" );
774 status = -1;
775 }
776 else
777 {
778 fprintf(stderr, "INFO: Sending the print file...\n");
779 if (pthread_cond_init(&readCompleteCondition, NULL) == 0)
780 {
781 gReadCompleteConditionPtr = &readCompleteCondition;
782
783 if (pthread_mutex_init(&readMutex, NULL) == 0)
784 {
785 gReadMutexPtr = &readMutex;
786
787 if (pthread_create(&thr, NULL, readthread, classdriver ) > 0)
788 fprintf(stderr, "WARNING: Couldn't create read channel\n");
789 else
790 thread_created = 1;
791 }
792 }
793 }
794 /*
795 * the main thread sends the print file...
796 */
797 while (noErr == status && copies > 0)
798 {
799 copies --;
800 if (STDIN_FILENO != fd)
801 {
802 fputs("PAGE: 1 1", stderr);
803 lseek( fd, 0, SEEK_SET ); /* rewind */
804 }
805
806 tbytes = 0;
807 while (noErr == status && (nbytes = read(fd, buffer, buffersize)) > 0)
808 {
809 /*
810 * Write the print data to the printer...
811 */
812
813 tbytes += nbytes;
814 bufptr = buffer;
815
816 while (nbytes > 0 && noErr == status )
817 {
818 wbytes = nbytes;
819 status = (*classdriver)->WritePipe( classdriver, (UInt8*)bufptr, &wbytes, 0 /*nbytes > wbytes? 0: feof(fp)*/ );
820 if (wbytes < 0 || noErr != status)
821 {
822 OSStatus err;
823 err = (*classdriver)->Abort( classdriver );
824 fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status, err );
825 break;
826 }
827
828 nbytes -= wbytes;
829 bufptr += wbytes;
830 }
831
832 if (fd != 0 && noErr == status)
833 fprintf(stderr, "INFO: Sending print file, %qd bytes...\n", (off_t)tbytes);
834 }
835 }
836 done = 1; /* stop scheduling reads */
837
838 if ( thread_created )
839 {
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
843 * class driver.
844 */
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)
848 gWaitEOF = false;
849 pthread_mutex_unlock(&readMutex);
850 pthread_join( thr,NULL); /* wait for the child thread to return */
851 }
852
853 (*classdriver)->Close( classdriver ); /* forces the read to stop incase we are doing a blocking read */
854 UsbUnloadClassDriver( activePrinter );
855 /*
856 * Close the socket connection and input file and return...
857 */
858 free( buffer );
859
860 if (STDIN_FILENO != fd)
861 close(fd);
862
863 if (gReadCompleteConditionPtr != NULL)
864 pthread_cond_destroy(gReadCompleteConditionPtr);
865 if (gReadMutexPtr != NULL)
866 pthread_mutex_destroy(gReadMutexPtr);
867
868 return status == kIOReturnSuccess? 0: status;
869}
870
871static Boolean
872encodecfstr( CFStringRef cfsrc, char *dst, long len )
873{
874 return CFStringGetCString(cfsrc, dst, len, kCFStringEncodingUTF8 );
875}
876
877/*
878* 'list_devices()' - List all USB devices.
879*/
880void list_devices(void)
881{
882 char encodedManufacturer[1024];
883 char encodedProduct[1024];
884 char uri[1024];
885 CFMutableArrayRef usbBusPrinters = UsbGetAllPrinters();
886 CFIndex i, numPrinters = NULL != usbBusPrinters? CFArrayGetCount( usbBusPrinters ): 0;
887
888 puts("direct usb \"Unknown\" \"USB Printer (usb)\"");
889 for ( i = 0; i < numPrinters; ++i )
890 {
891 USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbBusPrinters, i );
892
893 if ( printer )
894 {
895 CFStringRef addressRef = UsbMakeFullUriAddress( printer );
896 if ( addressRef )
897 {
898 if ( CFStringGetCString(addressRef, uri, sizeof(uri), kCFStringEncodingUTF8) ) {
899
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);
903 }
904 }
905 }
906 }
907 UsbReleaseAllPrinters( usbBusPrinters );
908 fflush(NULL);
909}
910
911
912static void parseOptions(const char *options, char *serial)
913{
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 */
918
919 if (serial)
920 *serial = '\0';
921
922 if (!options)
923 return;
924
925 serialnumber = NULL;
926
927 while (*options != '\0')
928 {
929 /*
930 * Get the name...
931 */
932 for (ptr = optionName; *options && *options != '=' && *options != '+' && *options != '&'; )
933 *ptr++ = *options++;
934
935 *ptr = '\0';
936 value[0] = '\0';
937
938 if (*options == '=')
939 {
940 /*
941 * Get the value...
942 */
943
944 options ++;
945
946 for (ptr = value; *options && *options != '+' && *options != '&';)
947 *ptr++ = *options++;
948
949 *ptr = '\0';
950
951 if (*options == '+' || *options == '&')
952 options ++;
953 }
954 else if (*options == '+' || *options == '&')
955 {
956 options ++;
957 }
958
959 /*
960 * Process the option...
961 */
962 if (strcasecmp(optionName, "waiteof") == 0)
963 {
964 if (strcasecmp(value, "on") == 0 ||
965 strcasecmp(value, "yes") == 0 ||
966 strcasecmp(value, "true") == 0)
967 {
968 gWaitEOF = true;
969 }
970 else if (strcasecmp(value, "off") == 0 ||
971 strcasecmp(value, "no") == 0 ||
972 strcasecmp(value, "false") == 0)
973 {
974 gWaitEOF = false;
975 }
976 else
977 {
978 fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value);
979 }
980 }
981 else if (strcasecmp(optionName, "serial") == 0 ||
982 strcasecmp(optionName, "location") == 0 )
983 {
984 strcpy(serial, value);
985 serialnumber = serial;
986 }
987 }
988
989 return;
990}
991
992
993/*!
994 * @function addPercentEscapes
995 * @abstract Encode a string with percent escapes
996 *
997 * @param src The source C string
998 * @param dst Desination buffer
999 * @param dstMax Size of desination buffer
1000 *
1001 * @result A non-zero return value for errors
1002 */
1003static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax)
1004{
1005 unsigned char c;
1006 char *dstEnd = dst + dstMax - 1; /* -1 to leave room for the NUL */
1007
1008 while (*src)
1009 {
1010 c = *src++;
1011
1012 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1013 (c >= '0' && c <= '9') || (c == '.' || c == '-' || c == '*' || c == '_'))
1014 {
1015 if (dst >= dstEnd)
1016 return -1;
1017
1018 *dst++ = c;
1019 }
1020 else
1021 {
1022 if (dst >= dstEnd - 2)
1023 return -1;
1024
1025 snprintf(dst, dstEnd - dst, "%%%02x", c);
1026 dst += 3;
1027 }
1028 }
1029
1030 *dst = '\0';
1031 return 0;
1032}
1033
1034
1035/*!
1036 * @function removePercentEscapes
1037 * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
1038 *
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
1043 *
1044 * @result A non-zero return value for errors
1045 */
1046static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax)
1047{
1048 int c;
1049 const unsigned char *dstEnd = dst + dstMax;
1050
1051 while (*src && dst < dstEnd)
1052 {
1053 c = *src++;
1054
1055 if (c == '%')
1056 {
1057 sscanf(src, "%02x", &c);
1058 src += 2;
1059 }
1060 *dst++ = (char)c;
1061 }
1062
1063 if (dst >= dstEnd)
1064 return -1;
1065
1066 *dst = '\0';
1067 return 0;
1068}
1069
1070/*-----------------------------------------------------------------------------*
1071
1072 DelimitSubstring
1073
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).
1077
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.
1082
1083 Out: Range up to the delimiter.
1084
1085*-----------------------------------------------------------------------------*/
1086static CFRange
1087DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options )
1088{
1089 CFRange where_delim, /* where the delimiter was found */
1090 value;
1091 /* */
1092 /* trim leading space by changing bounds */
1093 /* */
1094 while ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, CFSTR(" "), bounds, kCFCompareAnchored, &where_delim ) )
1095 {
1096 ++bounds.location; /* drop a leading ' ' */
1097 --bounds.length;
1098 }
1099 value = bounds; /* assume match to the end of string, may be NULL */
1100 /* */
1101 /* find the delimiter in the remaining string */
1102 /* */
1103 if ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, delim, bounds, options, &where_delim ) )
1104 {
1105 /* */
1106 /* match to the delimiter */
1107 /* */
1108 value.length = where_delim.location /* delim */ - bounds.location /* start of search */;
1109 }
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" );
1114
1115 return value;
1116}
1117
1118
1119/*-----------------------------------------------------------------------------*
1120
1121 DeviceIDCreateValueList
1122
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
1126 the key.
1127
1128 (Although passing NULL for both forms of the key is considered
1129 bad form[!] it is handled correctly.)
1130
1131 In: deviceID the device's IEEE-1284 DeviceID key-value list
1132 abbrevKey the key we're interested in (NULL allowed)
1133 key
1134
1135 Out: CFString the value list
1136 or NULL key wasn't found in deviceID
1137
1138*-----------------------------------------------------------------------------*/
1139CFStringRef
1140DeviceIDCreateValueList( const CFStringRef deviceID, const CFStringRef abbrevKey, const CFStringRef key )
1141{
1142 CFRange found = CFRangeMake( -1,0); /* note CFStringFind sets length 0 if string not found */
1143 CFStringRef valueList = NULL;
1144
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 )
1153 {
1154 /* the key is at found */
1155 /* the value follows the key until we reach the semi-colon, or end of string */
1156 /* */
1157 CFRange search = CFRangeMake( found.location + found.length,
1158 CFStringGetLength( deviceID ) - (found.location + found.length) );
1159 /* */
1160 /* finally extract the string */
1161 /* */
1162 valueList = CFStringCreateWithSubstring ( kCFAllocatorDefault, deviceID,
1163 DelimitSubstring( deviceID, kDeviceIDKeyValuePairDelimiter, search, kCFCompareCaseInsensitive ) );
1164 DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList );
1165 }
1166 return valueList;
1167
1168}
1169
1170
1171
1172/******************************************************************************/
1173
1174/*-----------------------------------------------------------------------------*
1175
1176CompareSameString
1177
1178Desc: Return the CFCompare result for two strings, either or both of which
1179 can be NULL.
1180
1181In:
1182 a current value
1183 b last value
1184
1185Out:
1186 0 if the strings match
1187 non-zero if the strings don't match
1188
1189*-----------------------------------------------------------------------------*/
1190static int
1191CompareSameString( const CFStringRef a, const CFStringRef b )
1192{
1193 if ( NULL == a && NULL == b )
1194 return 0;
1195 else if ( NULL != a && NULL != b )
1196 return CFStringCompare( a, b, kCFCompareAnchored );
1197 else
1198 return 1; /* one of a or b is NULL this time, but wasn't last time */
1199}
1200
1201
1202/******************************************************************************/
1203kern_return_t
1204UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle )
1205{
1206 kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
1207 if ( NULL != classDriverBundle )
1208 printer->bundle = classDriverBundle; /* vendor-specific class override */
1209 else
1210#ifdef TIMEOUT
1211 classDriverBundle = kUSBGenericTOPrinterClassDriver; /* supply the generic TIMEOUT class driver */
1212#else
1213 classDriverBundle = kUSBGenericPrinterClassDriver; /* supply the generic class driver */
1214#endif
1215 DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle );
1216 if ( NULL != classDriverBundle )
1217 {
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)
1222 {
1223 /* See if this plug-in implements the Test type. */
1224 CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID, plugin );
1225
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)
1230 {
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)
1239 {
1240 DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" );
1241 kr = (*iunknown)->QueryInterface( iunknown, CFUUIDGetUUIDBytes(interfaceID), (LPVOID *) &classdriver );
1242
1243 (*iunknown)->Release( iunknown );
1244 if ( S_OK == kr && NULL != classdriver )
1245 {
1246 DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface %x\n" );
1247 printer->plugin = plugin;
1248 kr = (*classdriver)->Initialize( classdriver, printer->classdriver );
1249
1250 kr = kIOReturnSuccess;
1251 printer->classdriver = classdriver;
1252 }
1253 else
1254 {
1255 DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface FAILED %x\n" );
1256 }
1257 }
1258 else
1259 {
1260 DEBUG_ERR( kr, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" );
1261 }
1262 }
1263 else
1264 {
1265 DEBUG_ERR( kr, "UsbLoadClassDriver factories FAILED %x\n" );
1266 }
1267 }
1268 else
1269 {
1270 DEBUG_ERR( kr, "UsbLoadClassDriver plugin FAILED %x\n" );
1271 }
1272 if ( kr != kIOReturnSuccess || NULL == plugin || NULL == classdriver )
1273 {
1274 UsbUnloadClassDriver( printer );
1275 }
1276 }
1277
1278 return kr;
1279}
1280
1281
1282kern_return_t
1283UsbUnloadClassDriver( USBPrinterInfo *printer )
1284{
1285 DEBUG_ERR( kIOReturnSuccess, "UsbUnloadClassDriver %x\n" );
1286 if ( NULL != printer->classdriver )
1287 (*printer->classdriver)->Release( printer->classdriver );
1288 printer->classdriver = NULL;
1289
1290 if ( NULL != printer->plugin )
1291 CFRelease( printer->plugin );
1292 printer->plugin = NULL;
1293
1294 return kIOReturnSuccess;
1295}
1296
1297
1298/*-----------------------------------------------------------------------------*
1299
1300 UsbAddressDispose
1301
1302 Desc: deallocates anything used to create a persistent printer address
1303
1304 In: address the printer address we've created
1305
1306 Out: <none>
1307
1308*-----------------------------------------------------------------------------*/
1309void
1310UsbAddressDispose( USBPrinterAddress *address )
1311{
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 );
1316
1317 address->product =
1318 address->manufacturer =
1319 address->serial =
1320 address->command = NULL;
1321
1322}
1323
1324/*-----------------------------------------------------------------------------*
1325
1326 UsbGetPrinterAddress
1327
1328 Desc: Given a printer we're enumerating, discover it's persistent
1329 reference.
1330
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.
1334
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.
1340
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.)
1347
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
1351 USB printers.
1352
1353 In:
1354 thePrinter iterator required for fetching device descriptor
1355 devRefNum required to configure the interface
1356
1357 Out:
1358 address->manufacturer
1359 address->product
1360 address->serial
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.
1364 address->command
1365 May be updated.
1366
1367*-----------------------------------------------------------------------------*/
1368OSStatus
1369UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout )
1370{
1371
1372 /* */
1373 /* start by assuming the device is not IEEE-1284 compliant */
1374 /* and that we can't read in the required strings. */
1375 /* */
1376 OSStatus err;
1377 CFStringRef deviceId = NULL;
1378 USBPrinterClassContext **printer = NULL == thePrinter? NULL: thePrinter->classdriver;
1379
1380 address->manufacturer =
1381 address->product =
1382 address->compatible =
1383 address->serial =
1384 address->command = NULL;
1385
1386 DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter, sizeof(USBPrinterInfo) );
1387
1388 err = (*printer)->GetDeviceID( printer, &deviceId, timeout );
1389 if ( noErr == err && NULL != deviceId )
1390 {
1391 /* the strings embedded here are defined in the IEEE1284 spec */
1392 /* */
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 */
1397 /* */
1398 address->command = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCommandAbbrev, kDeviceIDKeyCommand );
1399
1400 address->product = DeviceIDCreateValueList( deviceId, kDeviceIDKeyModelAbbrev, kDeviceIDKeyModel );
1401 address->compatible = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCompatibleAbbrev, kDeviceIDKeyCompatible );
1402
1403 address->manufacturer = DeviceIDCreateValueList( deviceId, kDeviceIDKeyManufacturerAbbrev, kDeviceIDKeyManufacturer );
1404
1405 address->serial = DeviceIDCreateValueList( deviceId, kDeviceIDKeySerialAbbrev, kDeviceIDKeySerial );
1406 CFRelease( deviceId );
1407 }
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 );
1412
1413 if ( NULL == address->product || NULL == address->manufacturer || NULL == address->serial )
1414 {
1415 /* */
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 */
1418 /* */
1419 IOUSBDeviceDescriptor desc;
1420 USBIODeviceRequest request;
1421
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 )
1431 {
1432 /* once we've retrieved the device descriptor */
1433 /* try to fill in missing pieces of information */
1434 /* */
1435 /* Don't override any information already retrieved from DeviceID. */
1436
1437 if ( NULL == address->product)
1438 {
1439 err = (*printer)->GetString( printer, desc.iProduct, kUSBLanguageEnglish, timeout, &address->product );
1440 if ( kIOReturnSuccess != err || address->product == NULL) {
1441 address->product = CFSTR("Unknown");
1442 }
1443 }
1444 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address->product );
1445
1446 if ( NULL == address->manufacturer )
1447 {
1448 err = (*printer)->GetString( printer, desc.iManufacturer, kUSBLanguageEnglish, timeout, &address->manufacturer );
1449 if (kIOReturnSuccess != err || address->manufacturer == NULL) {
1450 address->manufacturer = CFSTR("Unknown");
1451 }
1452 }
1453 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address->manufacturer );
1454
1455 if ( NULL == address->serial )
1456 {
1457 /* if the printer doesn't have a serial number, use locationId */
1458 if ( 0 == desc.iSerialNumber )
1459 {
1460 address->serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), (*printer)->location );
1461 }
1462 else
1463 {
1464 err = (*printer)->GetString( printer, desc.iSerialNumber, kUSBLanguageEnglish, timeout, &address->serial );
1465 /* trailing NULs aren't handled correctly in URI */
1466 if ( address->serial )
1467 {
1468 UniChar nulbyte = { 0 };
1469 CFStringRef trim = CFStringCreateWithCharacters(NULL, &nulbyte, 1);
1470 CFMutableStringRef newserial = CFStringCreateMutableCopy(NULL, 0, address->serial);
1471
1472 CFStringTrim( newserial, trim );
1473
1474 CFRelease(trim);
1475 CFRelease( address->serial );
1476
1477 address->serial = newserial;
1478 }
1479 }
1480 }
1481 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address->serial );
1482 }
1483 }
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 );
1490 return err;
1491}
1492
1493
1494/*-----------------------------------------------------------------------------*
1495
1496UsbSamePrinter
1497
1498 Desc: match two Usb printer address; return TRUE if they are the same.
1499
1500 In: a the persistent address found last time
1501 b the persistent address found this time
1502
1503 Out: non-zero iff the addresses are the same
1504
1505*-----------------------------------------------------------------------------*/
1506int
1507UsbSamePrinter( const USBPrinterAddress *a, const USBPrinterAddress *b )
1508{
1509 int result = 0;
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 );
1513
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 );
1517
1518 return result;
1519}
1520
1521
1522/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1523 Method: UsbMakeFullUriAddress
1524
1525 Input Parameters:
1526
1527 Output Parameters:
1528
1529 Description:
1530 Fill in missing address information
1531
1532~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1533CFStringRef
1534UsbMakeFullUriAddress( USBPrinterInfo *printer )
1535{
1536 /* */
1537 /* fill in missing address information. */
1538 /* */
1539 CFMutableStringRef printerUri = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("usb://") );
1540 if ( NULL != printerUri )
1541 {
1542 CFStringRef serial = printer->address.serial;
1543
1544 CFStringAppend(printerUri, printer->address.manufacturer? CreateEncodedCFString( printer->address.manufacturer ): CFSTR("Unknown") );
1545 CFStringAppend(printerUri, CFSTR("/") );
1546
1547 CFStringAppend(printerUri, printer->address.product? CreateEncodedCFString( printer->address.product ): CFSTR("Unknown") );
1548
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 );
1553
1554 CFStringAppend(printerUri, serial? CreateEncodedCFString( serial ): CFSTR("Unknown") );
1555 }
1556
1557 return printerUri;
1558}
1559
1560
1561/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1562 Method: UsbGetAllPrinters
1563
1564 Input Parameters:
1565
1566 Output Parameters:
1567 array of all USB printers on the system
1568
1569 Description:
1570 Build a list of USB printers by iterating IOKit USB objects
1571
1572~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1573CFMutableArrayRef
1574UsbGetAllPrinters( void )
1575{
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 */
1581
1582 do
1583 {
1584
1585 kr = IOMasterPort( bootstrap_port, &master_device_port );
1586 DEBUG_ERR( kr, "UsbGetAllPrinters IOMasterPort %x\n" );
1587 if(kIOReturnSuccess != kr) break;
1588
1589 {
1590 CFDictionaryRef usbMatch = NULL;
1591
1592 /* iterate over all interfaces. */
1593 usbMatch = IOServiceMatching(kIOUSBInterfaceClassName);
1594 if ( !usbMatch ) break;
1595 DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceMatching %x\n" );
1596
1597 /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */
1598 kr = IOServiceGetMatchingServices(master_device_port, usbMatch, &iter);
1599 usbMatch = NULL;
1600
1601 DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" );
1602 if(kIOReturnSuccess != kr || iter == NULL) break;
1603 }
1604
1605 while ( NULL != (usbInterface = IOIteratorNext(iter)) )
1606 {
1607 IOCFPlugInInterface **iodev;
1608 USBPrinterInterface intf;
1609 HRESULT res;
1610 SInt32 score;
1611 CFMutableDictionaryRef properties;
1612 CFStringRef classDriver = NULL;
1613
1614 kr = IORegistryEntryCreateCFProperties( usbInterface, &properties, kCFAllocatorDefault, kNilOptions);
1615 if ( kIOReturnSuccess == kr && NULL != properties)
1616 {
1617 classDriver = (CFStringRef) CFDictionaryGetValue( properties, kUSBClassDriverProperty );
1618 if ( NULL != classDriver )
1619 CFRetain( classDriver );
1620 CFRelease( properties );
1621 }
1622
1623 kr = IOCreatePlugInInterfaceForService( usbInterface,
1624 kIOUSBInterfaceUserClientTypeID,
1625 kIOCFPlugInInterfaceID,
1626 &iodev,
1627 &score);
1628
1629 DEBUG_ERR( kr, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" );
1630 if ( kIOReturnSuccess == kr )
1631 {
1632 UInt8 intfClass = 0;
1633 UInt8 intfSubClass = 0;
1634
1635 res = (*iodev)->QueryInterface( iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
1636 DEBUG_ERR( (kern_return_t) res, "UsbGetAllPrinters QueryInterface %x\n" );
1637
1638 (*iodev)->Release(iodev);
1639 if ( noErr != res ) break;
1640
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");
1646
1647 if ( kIOReturnSuccess == kr &&
1648 kUSBPrintingClass == intfClass &&
1649 kUSBPrintingSubclass == intfSubClass )
1650 {
1651
1652 USBPrinterInfo printer,
1653 *printerInfo;
1654 /*
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
1657 printers.
1658 */
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 )
1664 {
1665 kr = UsbLoadClassDriver( &printer, kUSBPrinterClassInterfaceID, classDriver );
1666 DEBUG_ERR(kr, "UsbGetAllPrinters UsbLoadClassDriver %x\n");
1667 if ( kIOReturnSuccess == kr && printer.classdriver )
1668 {
1669 (*(printer.classdriver))->interface = intf;
1670 kr = UsbGetPrinterAddress( &printer, &printer.address, 60000L );
1671 {
1672 /* always unload the driver */
1673 /* but don't mask last error */
1674 kern_return_t unload_err = UsbUnloadClassDriver( &printer );
1675 if ( kIOReturnSuccess == kr )
1676 kr = unload_err;
1677 }
1678 }
1679 }
1680
1681 printerInfo = UsbCopyPrinter( &printer );
1682 if ( NULL != printerInfo )
1683 CFArrayAppendValue( printers, (const void *) printerInfo ); /* keep track of it */
1684
1685 } /* if there's a printer */
1686 kr = (*intf)->Release(intf);
1687 } /* if IOCreatePlugInInterfaceForService */
1688
1689 IOObjectRelease(usbInterface);
1690 usbInterface = NULL;
1691
1692 } /* while there's an interface */
1693 } while ( 0 );
1694
1695 if (iter)
1696 {
1697 IOObjectRelease(iter);
1698 iter = 0;
1699 }
1700
1701 if (master_device_port)
1702 {
1703 mach_port_deallocate(mach_task_self(), master_device_port);
1704 master_device_port = 0;
1705 }
1706 return printers;
1707
1708} /* UsbGetAllPrinters */
1709
1710/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1711 Method: UsbReleasePrinter
1712
1713 Input Parameters:
1714
1715 Output Parameters:
1716
1717 Description:
1718~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1719void
1720UsbReleasePrinter( USBPrinterInfo *printer )
1721{
1722 if ( printer )
1723 {
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 );
1735 free( printer );
1736 }
1737}
1738
1739/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1740 Method: UsbReleaseAllPrinters
1741
1742 Input Parameters:
1743
1744 Output Parameters:
1745
1746 Description:
1747~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1748void
1749UsbReleaseAllPrinters( CFMutableArrayRef printers )
1750{
1751 if ( NULL != printers )
1752 {
1753 CFIndex i,
1754 numPrinters = CFArrayGetCount(printers);
1755 for ( i = 0; i < numPrinters; ++i )
1756 UsbReleasePrinter( (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i ) );
1757 CFRelease( printers );
1758 }
1759}
1760
1761USBPrinterInfo *
1762UsbCopyPrinter( USBPrinterInfo *aPrinter )
1763{
1764 /* */
1765 /* note this does not copy interface information, just address information */
1766 /* */
1767 USBPrinterInfo *printerInfo = (USBPrinterInfo *) calloc( 1, sizeof(USBPrinterInfo));
1768 if ( NULL != printerInfo && NULL != aPrinter )
1769 {
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 );
1781 }
1782
1783 return printerInfo;
1784}
1785
1786/*-----------------------------------------------------------------------------*
1787
1788 UsbRegistryOpen
1789
1790 Desc: opens the USB printer which matches the supplied printerAddress
1791
1792 In: myContext->printerAddress persistent name which identifies the printer
1793
1794 Out: myContext->usbDeviceRef current IOKit address of this printer
1795*-----------------------------------------------------------------------------*/
1796kern_return_t
1797UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result )
1798{
1799 kern_return_t kr = -1; /* indeterminate failure */
1800 CFMutableArrayRef printers = UsbGetAllPrinters();
1801 CFIndex numPrinters = NULL != printers? CFArrayGetCount( printers): 0;
1802 CFIndex i;
1803
1804 *result = NULL; /* nothing matched */
1805 for ( i = 0; i < numPrinters; ++i )
1806 {
1807 USBPrinterInfo *thisPrinter = (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i );
1808 if ( NULL != thisPrinter && UsbSamePrinter( usbAddress, &thisPrinter->address ) )
1809 {
1810 *result = UsbCopyPrinter( thisPrinter ); /* retains reference */
1811 if ( NULL != *result )
1812 {
1813 /* */
1814 /* if we can't find a bi-di interface, settle for a known uni-directional interface */
1815 /* */
1816 USBPrinterClassContext **printer = NULL;
1817 /* */
1818 /* setup the default class driver */
1819 /* If one is specified, allow the vendor driver to override our default implementation */
1820 /* */
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 )
1825 {
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 )
1833 {
1834 if ( NULL == (*printer)->interface )
1835 {
1836 (*printer)->Close( printer );
1837 UsbReleasePrinter( *result );
1838 *result = NULL;
1839 }
1840 }
1841 }
1842 }
1843 break;
1844 }
1845 }
1846 UsbReleaseAllPrinters( printers ); /* but, copied printer is retained */
1847 DEBUG_ERR( kr, "UsbRegistryOpen return %x\n" );
1848
1849 return kr;
1850}
1851
1852/*!
1853 * @function CreateEncodedCFString
1854 *
1855 * @abstract Create an encoded version of the string parameter
1856 * so that it can be included in a URI.
1857 *
1858 * @param string A CFStringRef of the string to be encoded.
1859 * @result An encoded CFString.
1860 *
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 "&".
1867 */
1868static CFStringRef CreateEncodedCFString(CFStringRef string)
1869{
1870 CFStringRef result = NULL;
1871 char *bufferUTF8 = NULL;
1872 char *bufferEncoded = NULL;
1873
1874 if (string != NULL)
1875 {
1876 CFIndex bufferSizeUTF8 = (3 * CFStringGetLength(string));
1877 if ((bufferUTF8 = (char*)malloc(bufferSizeUTF8)) != NULL)
1878 {
1879 CFStringGetCString(string, bufferUTF8, bufferSizeUTF8, kCFStringEncodingUTF8);
1880 {
1881 UInt16 bufferSizeEncoded = (3 * strlen(bufferUTF8)) + 1;
1882 if ((bufferEncoded = (char*)malloc(bufferSizeEncoded)) != NULL)
1883 {
1884 addPercentEscapes(bufferUTF8, bufferEncoded, bufferSizeEncoded);
1885 result = CFStringCreateWithCString(kCFAllocatorDefault, bufferEncoded, kCFStringEncodingUTF8);
1886 }
1887 }
1888 }
1889 }
1890
1891 if (bufferUTF8) free(bufferUTF8);
1892 if (bufferEncoded) free(bufferEncoded);
1893
1894 return result;
1895}
1896
1897/*
1898 * End of "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $".
1899 */