]> 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/*
757d2cad 2 * "$Id: usb-darwin.c 5241 2006-03-07 22:07:44Z mike $"
ef416fc2 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;
757d2cad 677
678 fputs("STATE: +connecting-to-device\n", stderr);
679
ef416fc2 680 do
681 {
682 /* */
683 /* given a manufacturer and product, bind to a specific printer on the bus */
684 /* */
685 usbPrinters = UsbGetAllPrinters();
686 /* */
687 /* if we have at least one element of the URI, find a printer module that matches */
688 /* */
689 if ( NULL != usbPrinters && (manufacturer || product ) )
690 {
691 int i,
692 numPrinters = CFArrayGetCount(usbPrinters);
693 for ( i = 0; i < numPrinters; ++i )
694 {
695 int match = FALSE;
696 USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbPrinters, i );
697 if ( printer )
698 {
699 match = printer->address.manufacturer && manufacturer? CFEqual(printer->address.manufacturer, manufacturer ): FALSE;
700 if ( match )
701 {
702 match = printer->address.product && product? CFEqual(printer->address.product, product ): FALSE;
703 }
704 if ( match && serial )
705 {
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. */
714 /* */
715 match = printer->address.serial? CFEqual(printer->address.serial, serial ): FALSE;
716 }
717 if ( match )
718 {
719 targetPrinter = UsbCopyPrinter( printer );
720 break; /* for, compare partial address to address for each printer on usb bus */
721 }
722 }
723 }
724 }
725 UsbReleaseAllPrinters( usbPrinters );
726 if ( NULL != targetPrinter )
727 status = UsbRegistryOpen( &targetPrinter->address, &activePrinter );
728
729 if ( NULL == activePrinter )
730 {
731 sleep( PRINTER_POLLING_INTERVAL );
732 countdown -= PRINTER_POLLING_INTERVAL;
733 if ( !countdown )
734 {
735 /* periodically, write to the log so someone knows we're waiting */
736 if (NULL == targetPrinter)
737 fprintf( stderr, "WARNING: Printer not responding\n" );
738 else
739 fprintf( stderr, "INFO: Printer busy\n" );
740 countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 30 minutes */
741 }
742 }
743 } while ( NULL == activePrinter );
744
745 classdriver = activePrinter->classdriver;
746 if ( NULL == classdriver )
747 {
748 perror("ERROR: Unable to open USB Printing Class port");
749 return (status);
750 }
751
757d2cad 752 fputs("STATE: -connecting-to-device\n", stderr);
753
ef416fc2 754 /*
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...)
759 */
760
761 if (fd != 0)
762 {
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));
767
768 sigemptyset(&action.sa_mask);
769 action.sa_handler = SIG_IGN;
770 sigaction(SIGTERM, &action, NULL);
771#else
772 signal(SIGTERM, SIG_IGN);
773#endif /* HAVE_SIGSET */
774 }
775
776 buffer = malloc( buffersize );
777 if ( !buffer ) {
778 fprintf( stderr, "ERROR: Couldn't allocate internal buffer\n" );
779 status = -1;
780 }
781 else
782 {
783 fprintf(stderr, "INFO: Sending the print file...\n");
784 if (pthread_cond_init(&readCompleteCondition, NULL) == 0)
785 {
786 gReadCompleteConditionPtr = &readCompleteCondition;
787
788 if (pthread_mutex_init(&readMutex, NULL) == 0)
789 {
790 gReadMutexPtr = &readMutex;
791
792 if (pthread_create(&thr, NULL, readthread, classdriver ) > 0)
793 fprintf(stderr, "WARNING: Couldn't create read channel\n");
794 else
795 thread_created = 1;
796 }
797 }
798 }
799 /*
800 * the main thread sends the print file...
801 */
802 while (noErr == status && copies > 0)
803 {
804 copies --;
805 if (STDIN_FILENO != fd)
806 {
807 fputs("PAGE: 1 1", stderr);
808 lseek( fd, 0, SEEK_SET ); /* rewind */
809 }
810
811 tbytes = 0;
812 while (noErr == status && (nbytes = read(fd, buffer, buffersize)) > 0)
813 {
814 /*
815 * Write the print data to the printer...
816 */
817
818 tbytes += nbytes;
819 bufptr = buffer;
820
821 while (nbytes > 0 && noErr == status )
822 {
823 wbytes = nbytes;
824 status = (*classdriver)->WritePipe( classdriver, (UInt8*)bufptr, &wbytes, 0 /*nbytes > wbytes? 0: feof(fp)*/ );
825 if (wbytes < 0 || noErr != status)
826 {
827 OSStatus err;
828 err = (*classdriver)->Abort( classdriver );
829 fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status, err );
830 break;
831 }
832
833 nbytes -= wbytes;
834 bufptr += wbytes;
835 }
836
837 if (fd != 0 && noErr == status)
838 fprintf(stderr, "INFO: Sending print file, %qd bytes...\n", (off_t)tbytes);
839 }
840 }
841 done = 1; /* stop scheduling reads */
842
843 if ( thread_created )
844 {
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
848 * class driver.
849 */
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)
853 gWaitEOF = false;
854 pthread_mutex_unlock(&readMutex);
855 pthread_join( thr,NULL); /* wait for the child thread to return */
856 }
857
858 (*classdriver)->Close( classdriver ); /* forces the read to stop incase we are doing a blocking read */
859 UsbUnloadClassDriver( activePrinter );
860 /*
861 * Close the socket connection and input file and return...
862 */
863 free( buffer );
864
865 if (STDIN_FILENO != fd)
866 close(fd);
867
868 if (gReadCompleteConditionPtr != NULL)
869 pthread_cond_destroy(gReadCompleteConditionPtr);
870 if (gReadMutexPtr != NULL)
871 pthread_mutex_destroy(gReadMutexPtr);
872
873 return status == kIOReturnSuccess? 0: status;
874}
875
876static Boolean
877encodecfstr( CFStringRef cfsrc, char *dst, long len )
878{
879 return CFStringGetCString(cfsrc, dst, len, kCFStringEncodingUTF8 );
880}
881
882/*
883* 'list_devices()' - List all USB devices.
884*/
885void list_devices(void)
886{
887 char encodedManufacturer[1024];
888 char encodedProduct[1024];
889 char uri[1024];
890 CFMutableArrayRef usbBusPrinters = UsbGetAllPrinters();
891 CFIndex i, numPrinters = NULL != usbBusPrinters? CFArrayGetCount( usbBusPrinters ): 0;
892
893 puts("direct usb \"Unknown\" \"USB Printer (usb)\"");
894 for ( i = 0; i < numPrinters; ++i )
895 {
896 USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbBusPrinters, i );
897
898 if ( printer )
899 {
900 CFStringRef addressRef = UsbMakeFullUriAddress( printer );
901 if ( addressRef )
902 {
903 if ( CFStringGetCString(addressRef, uri, sizeof(uri), kCFStringEncodingUTF8) ) {
904
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);
908 }
909 }
910 }
911 }
912 UsbReleaseAllPrinters( usbBusPrinters );
913 fflush(NULL);
914}
915
916
917static void parseOptions(const char *options, char *serial)
918{
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 */
923
924 if (serial)
925 *serial = '\0';
926
927 if (!options)
928 return;
929
930 serialnumber = NULL;
931
932 while (*options != '\0')
933 {
934 /*
935 * Get the name...
936 */
937 for (ptr = optionName; *options && *options != '=' && *options != '+' && *options != '&'; )
938 *ptr++ = *options++;
939
940 *ptr = '\0';
941 value[0] = '\0';
942
943 if (*options == '=')
944 {
945 /*
946 * Get the value...
947 */
948
949 options ++;
950
951 for (ptr = value; *options && *options != '+' && *options != '&';)
952 *ptr++ = *options++;
953
954 *ptr = '\0';
955
956 if (*options == '+' || *options == '&')
957 options ++;
958 }
959 else if (*options == '+' || *options == '&')
960 {
961 options ++;
962 }
963
964 /*
965 * Process the option...
966 */
967 if (strcasecmp(optionName, "waiteof") == 0)
968 {
969 if (strcasecmp(value, "on") == 0 ||
970 strcasecmp(value, "yes") == 0 ||
971 strcasecmp(value, "true") == 0)
972 {
973 gWaitEOF = true;
974 }
975 else if (strcasecmp(value, "off") == 0 ||
976 strcasecmp(value, "no") == 0 ||
977 strcasecmp(value, "false") == 0)
978 {
979 gWaitEOF = false;
980 }
981 else
982 {
983 fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value);
984 }
985 }
986 else if (strcasecmp(optionName, "serial") == 0 ||
987 strcasecmp(optionName, "location") == 0 )
988 {
989 strcpy(serial, value);
990 serialnumber = serial;
991 }
992 }
993
994 return;
995}
996
997
998/*!
999 * @function addPercentEscapes
1000 * @abstract Encode a string with percent escapes
1001 *
1002 * @param src The source C string
1003 * @param dst Desination buffer
1004 * @param dstMax Size of desination buffer
1005 *
1006 * @result A non-zero return value for errors
1007 */
1008static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax)
1009{
1010 unsigned char c;
1011 char *dstEnd = dst + dstMax - 1; /* -1 to leave room for the NUL */
1012
1013 while (*src)
1014 {
1015 c = *src++;
1016
1017 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1018 (c >= '0' && c <= '9') || (c == '.' || c == '-' || c == '*' || c == '_'))
1019 {
1020 if (dst >= dstEnd)
1021 return -1;
1022
1023 *dst++ = c;
1024 }
1025 else
1026 {
1027 if (dst >= dstEnd - 2)
1028 return -1;
1029
1030 snprintf(dst, dstEnd - dst, "%%%02x", c);
1031 dst += 3;
1032 }
1033 }
1034
1035 *dst = '\0';
1036 return 0;
1037}
1038
1039
1040/*!
1041 * @function removePercentEscapes
1042 * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
1043 *
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
1048 *
1049 * @result A non-zero return value for errors
1050 */
1051static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax)
1052{
1053 int c;
1054 const unsigned char *dstEnd = dst + dstMax;
1055
1056 while (*src && dst < dstEnd)
1057 {
1058 c = *src++;
1059
1060 if (c == '%')
1061 {
1062 sscanf(src, "%02x", &c);
1063 src += 2;
1064 }
1065 *dst++ = (char)c;
1066 }
1067
1068 if (dst >= dstEnd)
1069 return -1;
1070
1071 *dst = '\0';
1072 return 0;
1073}
1074
1075/*-----------------------------------------------------------------------------*
1076
1077 DelimitSubstring
1078
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).
1082
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.
1087
1088 Out: Range up to the delimiter.
1089
1090*-----------------------------------------------------------------------------*/
1091static CFRange
1092DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options )
1093{
1094 CFRange where_delim, /* where the delimiter was found */
1095 value;
1096 /* */
1097 /* trim leading space by changing bounds */
1098 /* */
1099 while ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, CFSTR(" "), bounds, kCFCompareAnchored, &where_delim ) )
1100 {
1101 ++bounds.location; /* drop a leading ' ' */
1102 --bounds.length;
1103 }
1104 value = bounds; /* assume match to the end of string, may be NULL */
1105 /* */
1106 /* find the delimiter in the remaining string */
1107 /* */
1108 if ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, delim, bounds, options, &where_delim ) )
1109 {
1110 /* */
1111 /* match to the delimiter */
1112 /* */
1113 value.length = where_delim.location /* delim */ - bounds.location /* start of search */;
1114 }
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" );
1119
1120 return value;
1121}
1122
1123
1124/*-----------------------------------------------------------------------------*
1125
1126 DeviceIDCreateValueList
1127
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
1131 the key.
1132
1133 (Although passing NULL for both forms of the key is considered
1134 bad form[!] it is handled correctly.)
1135
1136 In: deviceID the device's IEEE-1284 DeviceID key-value list
1137 abbrevKey the key we're interested in (NULL allowed)
1138 key
1139
1140 Out: CFString the value list
1141 or NULL key wasn't found in deviceID
1142
1143*-----------------------------------------------------------------------------*/
1144CFStringRef
1145DeviceIDCreateValueList( const CFStringRef deviceID, const CFStringRef abbrevKey, const CFStringRef key )
1146{
1147 CFRange found = CFRangeMake( -1,0); /* note CFStringFind sets length 0 if string not found */
1148 CFStringRef valueList = NULL;
1149
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 )
1158 {
1159 /* the key is at found */
1160 /* the value follows the key until we reach the semi-colon, or end of string */
1161 /* */
1162 CFRange search = CFRangeMake( found.location + found.length,
1163 CFStringGetLength( deviceID ) - (found.location + found.length) );
1164 /* */
1165 /* finally extract the string */
1166 /* */
1167 valueList = CFStringCreateWithSubstring ( kCFAllocatorDefault, deviceID,
1168 DelimitSubstring( deviceID, kDeviceIDKeyValuePairDelimiter, search, kCFCompareCaseInsensitive ) );
1169 DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList );
1170 }
1171 return valueList;
1172
1173}
1174
1175
1176
1177/******************************************************************************/
1178
1179/*-----------------------------------------------------------------------------*
1180
1181CompareSameString
1182
1183Desc: Return the CFCompare result for two strings, either or both of which
1184 can be NULL.
1185
1186In:
1187 a current value
1188 b last value
1189
1190Out:
1191 0 if the strings match
1192 non-zero if the strings don't match
1193
1194*-----------------------------------------------------------------------------*/
1195static int
1196CompareSameString( const CFStringRef a, const CFStringRef b )
1197{
1198 if ( NULL == a && NULL == b )
1199 return 0;
1200 else if ( NULL != a && NULL != b )
1201 return CFStringCompare( a, b, kCFCompareAnchored );
1202 else
1203 return 1; /* one of a or b is NULL this time, but wasn't last time */
1204}
1205
1206
1207/******************************************************************************/
1208kern_return_t
1209UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle )
1210{
1211 kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
1212 if ( NULL != classDriverBundle )
1213 printer->bundle = classDriverBundle; /* vendor-specific class override */
1214 else
1215#ifdef TIMEOUT
1216 classDriverBundle = kUSBGenericTOPrinterClassDriver; /* supply the generic TIMEOUT class driver */
1217#else
1218 classDriverBundle = kUSBGenericPrinterClassDriver; /* supply the generic class driver */
1219#endif
1220 DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle );
1221 if ( NULL != classDriverBundle )
1222 {
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)
1227 {
1228 /* See if this plug-in implements the Test type. */
1229 CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID, plugin );
1230
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)
1235 {
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)
1244 {
1245 DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" );
1246 kr = (*iunknown)->QueryInterface( iunknown, CFUUIDGetUUIDBytes(interfaceID), (LPVOID *) &classdriver );
1247
1248 (*iunknown)->Release( iunknown );
1249 if ( S_OK == kr && NULL != classdriver )
1250 {
1251 DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface %x\n" );
1252 printer->plugin = plugin;
1253 kr = (*classdriver)->Initialize( classdriver, printer->classdriver );
1254
1255 kr = kIOReturnSuccess;
1256 printer->classdriver = classdriver;
1257 }
1258 else
1259 {
1260 DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface FAILED %x\n" );
1261 }
1262 }
1263 else
1264 {
1265 DEBUG_ERR( kr, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" );
1266 }
1267 }
1268 else
1269 {
1270 DEBUG_ERR( kr, "UsbLoadClassDriver factories FAILED %x\n" );
1271 }
1272 }
1273 else
1274 {
1275 DEBUG_ERR( kr, "UsbLoadClassDriver plugin FAILED %x\n" );
1276 }
1277 if ( kr != kIOReturnSuccess || NULL == plugin || NULL == classdriver )
1278 {
1279 UsbUnloadClassDriver( printer );
1280 }
1281 }
1282
1283 return kr;
1284}
1285
1286
1287kern_return_t
1288UsbUnloadClassDriver( USBPrinterInfo *printer )
1289{
1290 DEBUG_ERR( kIOReturnSuccess, "UsbUnloadClassDriver %x\n" );
1291 if ( NULL != printer->classdriver )
1292 (*printer->classdriver)->Release( printer->classdriver );
1293 printer->classdriver = NULL;
1294
1295 if ( NULL != printer->plugin )
1296 CFRelease( printer->plugin );
1297 printer->plugin = NULL;
1298
1299 return kIOReturnSuccess;
1300}
1301
1302
1303/*-----------------------------------------------------------------------------*
1304
1305 UsbAddressDispose
1306
1307 Desc: deallocates anything used to create a persistent printer address
1308
1309 In: address the printer address we've created
1310
1311 Out: <none>
1312
1313*-----------------------------------------------------------------------------*/
1314void
1315UsbAddressDispose( USBPrinterAddress *address )
1316{
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 );
1321
1322 address->product =
1323 address->manufacturer =
1324 address->serial =
1325 address->command = NULL;
1326
1327}
1328
1329/*-----------------------------------------------------------------------------*
1330
1331 UsbGetPrinterAddress
1332
1333 Desc: Given a printer we're enumerating, discover it's persistent
1334 reference.
1335
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.
1339
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.
1345
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.)
1352
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
1356 USB printers.
1357
1358 In:
1359 thePrinter iterator required for fetching device descriptor
1360 devRefNum required to configure the interface
1361
1362 Out:
1363 address->manufacturer
1364 address->product
1365 address->serial
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.
1369 address->command
1370 May be updated.
1371
1372*-----------------------------------------------------------------------------*/
1373OSStatus
1374UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout )
1375{
1376
1377 /* */
1378 /* start by assuming the device is not IEEE-1284 compliant */
1379 /* and that we can't read in the required strings. */
1380 /* */
1381 OSStatus err;
1382 CFStringRef deviceId = NULL;
1383 USBPrinterClassContext **printer = NULL == thePrinter? NULL: thePrinter->classdriver;
1384
1385 address->manufacturer =
1386 address->product =
1387 address->compatible =
1388 address->serial =
1389 address->command = NULL;
1390
1391 DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter, sizeof(USBPrinterInfo) );
1392
1393 err = (*printer)->GetDeviceID( printer, &deviceId, timeout );
1394 if ( noErr == err && NULL != deviceId )
1395 {
1396 /* the strings embedded here are defined in the IEEE1284 spec */
1397 /* */
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 */
1402 /* */
1403 address->command = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCommandAbbrev, kDeviceIDKeyCommand );
1404
1405 address->product = DeviceIDCreateValueList( deviceId, kDeviceIDKeyModelAbbrev, kDeviceIDKeyModel );
1406 address->compatible = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCompatibleAbbrev, kDeviceIDKeyCompatible );
1407
1408 address->manufacturer = DeviceIDCreateValueList( deviceId, kDeviceIDKeyManufacturerAbbrev, kDeviceIDKeyManufacturer );
1409
1410 address->serial = DeviceIDCreateValueList( deviceId, kDeviceIDKeySerialAbbrev, kDeviceIDKeySerial );
1411 CFRelease( deviceId );
1412 }
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 );
1417
1418 if ( NULL == address->product || NULL == address->manufacturer || NULL == address->serial )
1419 {
1420 /* */
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 */
1423 /* */
1424 IOUSBDeviceDescriptor desc;
1425 USBIODeviceRequest request;
1426
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 )
1436 {
1437 /* once we've retrieved the device descriptor */
1438 /* try to fill in missing pieces of information */
1439 /* */
1440 /* Don't override any information already retrieved from DeviceID. */
1441
1442 if ( NULL == address->product)
1443 {
1444 err = (*printer)->GetString( printer, desc.iProduct, kUSBLanguageEnglish, timeout, &address->product );
1445 if ( kIOReturnSuccess != err || address->product == NULL) {
1446 address->product = CFSTR("Unknown");
1447 }
1448 }
1449 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address->product );
1450
1451 if ( NULL == address->manufacturer )
1452 {
1453 err = (*printer)->GetString( printer, desc.iManufacturer, kUSBLanguageEnglish, timeout, &address->manufacturer );
1454 if (kIOReturnSuccess != err || address->manufacturer == NULL) {
1455 address->manufacturer = CFSTR("Unknown");
1456 }
1457 }
1458 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address->manufacturer );
1459
1460 if ( NULL == address->serial )
1461 {
1462 /* if the printer doesn't have a serial number, use locationId */
1463 if ( 0 == desc.iSerialNumber )
1464 {
1465 address->serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), (*printer)->location );
1466 }
1467 else
1468 {
1469 err = (*printer)->GetString( printer, desc.iSerialNumber, kUSBLanguageEnglish, timeout, &address->serial );
1470 /* trailing NULs aren't handled correctly in URI */
1471 if ( address->serial )
1472 {
1473 UniChar nulbyte = { 0 };
1474 CFStringRef trim = CFStringCreateWithCharacters(NULL, &nulbyte, 1);
1475 CFMutableStringRef newserial = CFStringCreateMutableCopy(NULL, 0, address->serial);
1476
1477 CFStringTrim( newserial, trim );
1478
1479 CFRelease(trim);
1480 CFRelease( address->serial );
1481
1482 address->serial = newserial;
1483 }
1484 }
1485 }
1486 DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address->serial );
1487 }
1488 }
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 );
1495 return err;
1496}
1497
1498
1499/*-----------------------------------------------------------------------------*
1500
1501UsbSamePrinter
1502
1503 Desc: match two Usb printer address; return TRUE if they are the same.
1504
1505 In: a the persistent address found last time
1506 b the persistent address found this time
1507
1508 Out: non-zero iff the addresses are the same
1509
1510*-----------------------------------------------------------------------------*/
1511int
1512UsbSamePrinter( const USBPrinterAddress *a, const USBPrinterAddress *b )
1513{
1514 int result = 0;
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 );
1518
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 );
1522
1523 return result;
1524}
1525
1526
1527/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1528 Method: UsbMakeFullUriAddress
1529
1530 Input Parameters:
1531
1532 Output Parameters:
1533
1534 Description:
1535 Fill in missing address information
1536
1537~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1538CFStringRef
1539UsbMakeFullUriAddress( USBPrinterInfo *printer )
1540{
1541 /* */
1542 /* fill in missing address information. */
1543 /* */
1544 CFMutableStringRef printerUri = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("usb://") );
1545 if ( NULL != printerUri )
1546 {
1547 CFStringRef serial = printer->address.serial;
1548
1549 CFStringAppend(printerUri, printer->address.manufacturer? CreateEncodedCFString( printer->address.manufacturer ): CFSTR("Unknown") );
1550 CFStringAppend(printerUri, CFSTR("/") );
1551
1552 CFStringAppend(printerUri, printer->address.product? CreateEncodedCFString( printer->address.product ): CFSTR("Unknown") );
1553
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 );
1558
1559 CFStringAppend(printerUri, serial? CreateEncodedCFString( serial ): CFSTR("Unknown") );
1560 }
1561
1562 return printerUri;
1563}
1564
1565
1566/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1567 Method: UsbGetAllPrinters
1568
1569 Input Parameters:
1570
1571 Output Parameters:
1572 array of all USB printers on the system
1573
1574 Description:
1575 Build a list of USB printers by iterating IOKit USB objects
1576
1577~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1578CFMutableArrayRef
1579UsbGetAllPrinters( void )
1580{
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 */
1586
1587 do
1588 {
1589
1590 kr = IOMasterPort( bootstrap_port, &master_device_port );
1591 DEBUG_ERR( kr, "UsbGetAllPrinters IOMasterPort %x\n" );
1592 if(kIOReturnSuccess != kr) break;
1593
1594 {
1595 CFDictionaryRef usbMatch = NULL;
1596
1597 /* iterate over all interfaces. */
1598 usbMatch = IOServiceMatching(kIOUSBInterfaceClassName);
1599 if ( !usbMatch ) break;
1600 DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceMatching %x\n" );
1601
1602 /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */
1603 kr = IOServiceGetMatchingServices(master_device_port, usbMatch, &iter);
1604 usbMatch = NULL;
1605
1606 DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" );
1607 if(kIOReturnSuccess != kr || iter == NULL) break;
1608 }
1609
1610 while ( NULL != (usbInterface = IOIteratorNext(iter)) )
1611 {
1612 IOCFPlugInInterface **iodev;
1613 USBPrinterInterface intf;
1614 HRESULT res;
1615 SInt32 score;
1616 CFMutableDictionaryRef properties;
1617 CFStringRef classDriver = NULL;
1618
1619 kr = IORegistryEntryCreateCFProperties( usbInterface, &properties, kCFAllocatorDefault, kNilOptions);
1620 if ( kIOReturnSuccess == kr && NULL != properties)
1621 {
1622 classDriver = (CFStringRef) CFDictionaryGetValue( properties, kUSBClassDriverProperty );
1623 if ( NULL != classDriver )
1624 CFRetain( classDriver );
1625 CFRelease( properties );
1626 }
1627
1628 kr = IOCreatePlugInInterfaceForService( usbInterface,
1629 kIOUSBInterfaceUserClientTypeID,
1630 kIOCFPlugInInterfaceID,
1631 &iodev,
1632 &score);
1633
1634 DEBUG_ERR( kr, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" );
1635 if ( kIOReturnSuccess == kr )
1636 {
1637 UInt8 intfClass = 0;
1638 UInt8 intfSubClass = 0;
1639
1640 res = (*iodev)->QueryInterface( iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
1641 DEBUG_ERR( (kern_return_t) res, "UsbGetAllPrinters QueryInterface %x\n" );
1642
1643 (*iodev)->Release(iodev);
1644 if ( noErr != res ) break;
1645
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");
1651
1652 if ( kIOReturnSuccess == kr &&
1653 kUSBPrintingClass == intfClass &&
1654 kUSBPrintingSubclass == intfSubClass )
1655 {
1656
1657 USBPrinterInfo printer,
1658 *printerInfo;
1659 /*
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
1662 printers.
1663 */
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 )
1669 {
1670 kr = UsbLoadClassDriver( &printer, kUSBPrinterClassInterfaceID, classDriver );
1671 DEBUG_ERR(kr, "UsbGetAllPrinters UsbLoadClassDriver %x\n");
1672 if ( kIOReturnSuccess == kr && printer.classdriver )
1673 {
1674 (*(printer.classdriver))->interface = intf;
1675 kr = UsbGetPrinterAddress( &printer, &printer.address, 60000L );
1676 {
1677 /* always unload the driver */
1678 /* but don't mask last error */
1679 kern_return_t unload_err = UsbUnloadClassDriver( &printer );
1680 if ( kIOReturnSuccess == kr )
1681 kr = unload_err;
1682 }
1683 }
1684 }
1685
1686 printerInfo = UsbCopyPrinter( &printer );
1687 if ( NULL != printerInfo )
1688 CFArrayAppendValue( printers, (const void *) printerInfo ); /* keep track of it */
1689
1690 } /* if there's a printer */
1691 kr = (*intf)->Release(intf);
1692 } /* if IOCreatePlugInInterfaceForService */
1693
1694 IOObjectRelease(usbInterface);
1695 usbInterface = NULL;
1696
1697 } /* while there's an interface */
1698 } while ( 0 );
1699
1700 if (iter)
1701 {
1702 IOObjectRelease(iter);
1703 iter = 0;
1704 }
1705
1706 if (master_device_port)
1707 {
1708 mach_port_deallocate(mach_task_self(), master_device_port);
1709 master_device_port = 0;
1710 }
1711 return printers;
1712
1713} /* UsbGetAllPrinters */
1714
1715/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1716 Method: UsbReleasePrinter
1717
1718 Input Parameters:
1719
1720 Output Parameters:
1721
1722 Description:
1723~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1724void
1725UsbReleasePrinter( USBPrinterInfo *printer )
1726{
1727 if ( printer )
1728 {
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 );
1740 free( printer );
1741 }
1742}
1743
1744/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1745 Method: UsbReleaseAllPrinters
1746
1747 Input Parameters:
1748
1749 Output Parameters:
1750
1751 Description:
1752~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1753void
1754UsbReleaseAllPrinters( CFMutableArrayRef printers )
1755{
1756 if ( NULL != printers )
1757 {
1758 CFIndex i,
1759 numPrinters = CFArrayGetCount(printers);
1760 for ( i = 0; i < numPrinters; ++i )
1761 UsbReleasePrinter( (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i ) );
1762 CFRelease( printers );
1763 }
1764}
1765
1766USBPrinterInfo *
1767UsbCopyPrinter( USBPrinterInfo *aPrinter )
1768{
1769 /* */
1770 /* note this does not copy interface information, just address information */
1771 /* */
1772 USBPrinterInfo *printerInfo = (USBPrinterInfo *) calloc( 1, sizeof(USBPrinterInfo));
1773 if ( NULL != printerInfo && NULL != aPrinter )
1774 {
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 );
1786 }
1787
1788 return printerInfo;
1789}
1790
1791/*-----------------------------------------------------------------------------*
1792
1793 UsbRegistryOpen
1794
1795 Desc: opens the USB printer which matches the supplied printerAddress
1796
1797 In: myContext->printerAddress persistent name which identifies the printer
1798
1799 Out: myContext->usbDeviceRef current IOKit address of this printer
1800*-----------------------------------------------------------------------------*/
1801kern_return_t
1802UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result )
1803{
1804 kern_return_t kr = -1; /* indeterminate failure */
1805 CFMutableArrayRef printers = UsbGetAllPrinters();
1806 CFIndex numPrinters = NULL != printers? CFArrayGetCount( printers): 0;
1807 CFIndex i;
1808
1809 *result = NULL; /* nothing matched */
1810 for ( i = 0; i < numPrinters; ++i )
1811 {
1812 USBPrinterInfo *thisPrinter = (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i );
1813 if ( NULL != thisPrinter && UsbSamePrinter( usbAddress, &thisPrinter->address ) )
1814 {
1815 *result = UsbCopyPrinter( thisPrinter ); /* retains reference */
1816 if ( NULL != *result )
1817 {
1818 /* */
1819 /* if we can't find a bi-di interface, settle for a known uni-directional interface */
1820 /* */
1821 USBPrinterClassContext **printer = NULL;
1822 /* */
1823 /* setup the default class driver */
1824 /* If one is specified, allow the vendor driver to override our default implementation */
1825 /* */
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 )
1830 {
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 )
1838 {
1839 if ( NULL == (*printer)->interface )
1840 {
1841 (*printer)->Close( printer );
1842 UsbReleasePrinter( *result );
1843 *result = NULL;
1844 }
1845 }
1846 }
1847 }
1848 break;
1849 }
1850 }
1851 UsbReleaseAllPrinters( printers ); /* but, copied printer is retained */
1852 DEBUG_ERR( kr, "UsbRegistryOpen return %x\n" );
1853
1854 return kr;
1855}
1856
1857/*!
1858 * @function CreateEncodedCFString
1859 *
1860 * @abstract Create an encoded version of the string parameter
1861 * so that it can be included in a URI.
1862 *
1863 * @param string A CFStringRef of the string to be encoded.
1864 * @result An encoded CFString.
1865 *
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 "&".
1872 */
1873static CFStringRef CreateEncodedCFString(CFStringRef string)
1874{
1875 CFStringRef result = NULL;
1876 char *bufferUTF8 = NULL;
1877 char *bufferEncoded = NULL;
1878
1879 if (string != NULL)
1880 {
1881 CFIndex bufferSizeUTF8 = (3 * CFStringGetLength(string));
1882 if ((bufferUTF8 = (char*)malloc(bufferSizeUTF8)) != NULL)
1883 {
1884 CFStringGetCString(string, bufferUTF8, bufferSizeUTF8, kCFStringEncodingUTF8);
1885 {
1886 UInt16 bufferSizeEncoded = (3 * strlen(bufferUTF8)) + 1;
1887 if ((bufferEncoded = (char*)malloc(bufferSizeEncoded)) != NULL)
1888 {
1889 addPercentEscapes(bufferUTF8, bufferEncoded, bufferSizeEncoded);
1890 result = CFStringCreateWithCString(kCFAllocatorDefault, bufferEncoded, kCFStringEncodingUTF8);
1891 }
1892 }
1893 }
1894 }
1895
1896 if (bufferUTF8) free(bufferUTF8);
1897 if (bufferEncoded) free(bufferEncoded);
1898
1899 return result;
1900}
1901
1902/*
757d2cad 1903 * End of "$Id: usb-darwin.c 5241 2006-03-07 22:07:44Z mike $".
ef416fc2 1904 */