]>
Commit | Line | Data |
---|---|---|
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 | ||
114 | typedef 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 | ||
127 | typedef 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 | ||
137 | typedef IOUSBInterfaceInterface190 **USBPrinterInterface; | |
138 | ||
139 | typedef struct | |
140 | { | |
141 | UInt8 requestType; | |
142 | UInt8 request; | |
143 | UInt16 value; | |
144 | UInt16 index; | |
145 | UInt16 length; | |
146 | void *buffer; | |
147 | } USBIODeviceRequest; | |
148 | ||
149 | typedef 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 | ||
192 | typedef 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 | ||
233 | typedef 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 | ||
256 | typedef 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 | */ | |
273 | USBPrinterInfo *UsbCopyPrinter( USBPrinterInfo *aPrinter ); | |
274 | CFMutableArrayRef UsbGetAllPrinters( void ); | |
275 | void UsbReleasePrinter( USBPrinterInfo *aPrinter ); | |
276 | void UsbReleaseAllPrinters( CFMutableArrayRef printers ); | |
277 | kern_return_t UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result ); | |
278 | kern_return_t UsbUnloadClassDriver( USBPrinterInfo *printer ); | |
279 | kern_return_t UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle ); | |
280 | CFStringRef UsbMakeFullUriAddress( USBPrinterInfo *aPrinter ); | |
281 | ||
282 | int UsbSamePrinter( const USBPrinterAddress *lastTime, const USBPrinterAddress *thisTime ); | |
283 | ||
284 | OSStatus 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 | ||
382 | static CFStringRef CreateEncodedCFString(CFStringRef string); | |
383 | static CFRange DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options ); | |
384 | static void parseOptions(const char *options, char *serial); | |
385 | ||
386 | CFStringRef | |
387 | DeviceIDCreateValueList( const CFStringRef deviceID, | |
388 | const CFStringRef abbrevKey, | |
389 | const CFStringRef key ); | |
390 | ||
391 | static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax); | |
392 | static 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 | ||
417 | static volatile int done = 0; | |
418 | static int gWaitEOF = false; | |
419 | static pthread_cond_t *gReadCompleteConditionPtr = NULL; | |
420 | static pthread_mutex_t *gReadMutexPtr = NULL; | |
421 | ||
422 | ||
423 | ||
424 | #if DEBUG==2 | |
425 | ||
426 | static char | |
427 | hexdigit( char c ) | |
428 | { | |
429 | return ( c < 0 || c > 15 )? '?': (c < 10)? c + '0': c - 10 + 'A'; | |
430 | } | |
431 | ||
432 | static char | |
433 | asciidigit( char c ) | |
434 | { | |
435 | return (c< 20 || c > 0x7E)? '.': c; | |
436 | } | |
437 | ||
438 | void | |
439 | dump( 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 | ||
471 | void | |
472 | printcfs( 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 | ||
487 | void | |
488 | cmpcfs( 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 | |
513 | static const char *nextLine (const char *buffer); | |
514 | static void parsePSError (char *sockBuffer, int len); | |
515 | ||
516 | ||
517 | static 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 | ||
526 | static 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 | ||
583 | void * | |
584 | readthread( 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 | ||
630 | int 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 | ||
876 | static Boolean | |
877 | encodecfstr( 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 | */ | |
885 | void 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 | ||
917 | static 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 | */ | |
1008 | static 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 | */ | |
1051 | static 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 | *-----------------------------------------------------------------------------*/ | |
1091 | static CFRange | |
1092 | DelimitSubstring( 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 | *-----------------------------------------------------------------------------*/ | |
1144 | CFStringRef | |
1145 | DeviceIDCreateValueList( 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 | ||
1181 | CompareSameString | |
1182 | ||
1183 | Desc: Return the CFCompare result for two strings, either or both of which | |
1184 | can be NULL. | |
1185 | ||
1186 | In: | |
1187 | a current value | |
1188 | b last value | |
1189 | ||
1190 | Out: | |
1191 | 0 if the strings match | |
1192 | non-zero if the strings don't match | |
1193 | ||
1194 | *-----------------------------------------------------------------------------*/ | |
1195 | static int | |
1196 | CompareSameString( 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 | /******************************************************************************/ | |
1208 | kern_return_t | |
1209 | UsbLoadClassDriver( 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 | ||
1287 | kern_return_t | |
1288 | UsbUnloadClassDriver( 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 | *-----------------------------------------------------------------------------*/ | |
1314 | void | |
1315 | UsbAddressDispose( 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 | *-----------------------------------------------------------------------------*/ | |
1373 | OSStatus | |
1374 | UsbGetPrinterAddress( 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 | ||
1501 | UsbSamePrinter | |
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 | *-----------------------------------------------------------------------------*/ | |
1511 | int | |
1512 | UsbSamePrinter( 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 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1538 | CFStringRef | |
1539 | UsbMakeFullUriAddress( 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 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1578 | CFMutableArrayRef | |
1579 | UsbGetAllPrinters( 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 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1724 | void | |
1725 | UsbReleasePrinter( 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 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1753 | void | |
1754 | UsbReleaseAllPrinters( 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 | ||
1766 | USBPrinterInfo * | |
1767 | UsbCopyPrinter( 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 | *-----------------------------------------------------------------------------*/ | |
1801 | kern_return_t | |
1802 | UsbRegistryOpen( 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 | */ | |
1873 | static 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 | */ |