]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
2 | * "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $" | |
3 | * | |
4 | * USB port on Darwin backend for the Common UNIX Printing System (CUPS). | |
5 | * | |
6 | * This file is included from "usb.c" when compiled on MacOS X or Darwin. | |
7 | * | |
8 | * Copyright 2004 Apple Computer, Inc. All rights reserved. | |
9 | * | |
10 | * IMPORTANT: This Apple software is supplied to you by Apple Computer, | |
11 | * Inc. ("Apple") in consideration of your agreement to the following | |
12 | * terms, and your use, installation, modification or redistribution of | |
13 | * this Apple software constitutes acceptance of these terms. If you do | |
14 | * not agree with these terms, please do not use, install, modify or | |
15 | * redistribute this Apple software. | |
16 | * | |
17 | * In consideration of your agreement to abide by the following terms, and | |
18 | * subject to these terms, Apple grants you a personal, non-exclusive | |
19 | * license, under Apple/s copyrights in this original Apple software (the | |
20 | * "Apple Software"), to use, reproduce, modify and redistribute the Apple | |
21 | * Software, with or without modifications, in source and/or binary forms; | |
22 | * provided that if you redistribute the Apple Software in its entirety and | |
23 | * without modifications, you must retain this notice and the following | |
24 | * text and disclaimers in all such redistributions of the Apple Software. | |
25 | * Neither the name, trademarks, service marks or logos of Apple Computer, | |
26 | * Inc. may be used to endorse or promote products derived from the Apple | |
27 | * Software without specific prior written permission from Apple. Except | |
28 | * as expressly stated in this notice, no other rights or licenses, express | |
29 | * or implied, are granted by Apple herein, including but not limited to | |
30 | * any patent rights that may be infringed by your derivative works or by | |
31 | * other works in which the Apple Software may be incorporated. | |
32 | * | |
33 | * The Apple Software is provided by Apple on an "AS IS" basis. APPLE | |
34 | * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION | |
35 | * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS | |
36 | * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND | |
37 | * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. | |
38 | * | |
39 | * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL | |
40 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
41 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
42 | * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, | |
43 | * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED | |
44 | * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), | |
45 | * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE | |
46 | * POSSIBILITY OF SUCH DAMAGE. | |
47 | * | |
48 | * Contents: | |
49 | * | |
50 | * print_device() - Send a file to the specified USB port. | |
51 | * list_devices() - List all USB devices. | |
52 | */ | |
53 | ||
54 | /* | |
55 | * Include necessary headers... | |
56 | */ | |
57 | ||
58 | #include <CoreFoundation/CoreFoundation.h> | |
59 | #include <ApplicationServices/ApplicationServices.h> | |
60 | ||
61 | #include <IOKit/usb/IOUSBLib.h> | |
62 | #include <IOKit/IOCFPlugIn.h> | |
63 | #include <mach/mach.h> | |
64 | #include <mach/mach_error.h> | |
65 | ||
66 | #include <stdio.h> | |
67 | #include <stdlib.h> | |
68 | #include <errno.h> | |
69 | #include <signal.h> | |
70 | #include <fcntl.h> | |
71 | #include <termios.h> | |
72 | #include <unistd.h> | |
73 | #include <pthread.h> /* Used for writegReadMutex */ | |
74 | ||
75 | #ifndef kPMPrinterURI | |
76 | # define kPMPrinterURI CFSTR("Printer URI") | |
77 | #endif | |
78 | ||
79 | /* | |
80 | * Panther/10.3 kIOUSBInterfaceInterfaceID190 | |
81 | * Jaguar/10.2 kIOUSBInterfaceInterfaceID182 | |
82 | */ | |
83 | ||
84 | #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190) | |
85 | #define kUSBLanguageEnglish 0x409 | |
86 | ||
87 | /* | |
88 | * Section 5.3 USB Printing Class spec | |
89 | */ | |
90 | ||
91 | #define kUSBPrintingSubclass 1 | |
92 | #define kUSBPrintingProtocolNoOpen 0 | |
93 | #define kUSBPrintingProtocolUnidirectional 1 | |
94 | #define kUSBPrintingProtocolBidirectional 2 | |
95 | ||
96 | #define kUSBPrintClassGetDeviceID 0 | |
97 | #define kUSBPrintClassGetCentronicsStatus 1 | |
98 | #define kUSBPrintClassSoftReset 2 | |
99 | ||
100 | /* | |
101 | * Apple MacOS X printer-class plugins | |
102 | */ | |
103 | ||
104 | #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92)) | |
105 | ||
106 | #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92)) | |
107 | ||
108 | #define kUSBGenericPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin") | |
109 | #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin") | |
110 | ||
111 | #define kUSBClassDriverProperty CFSTR("USB Printing Class") | |
112 | #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/ | |
113 | ||
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; | |
677 | do | |
678 | { | |
679 | /* */ | |
680 | /* given a manufacturer and product, bind to a specific printer on the bus */ | |
681 | /* */ | |
682 | usbPrinters = UsbGetAllPrinters(); | |
683 | /* */ | |
684 | /* if we have at least one element of the URI, find a printer module that matches */ | |
685 | /* */ | |
686 | if ( NULL != usbPrinters && (manufacturer || product ) ) | |
687 | { | |
688 | int i, | |
689 | numPrinters = CFArrayGetCount(usbPrinters); | |
690 | for ( i = 0; i < numPrinters; ++i ) | |
691 | { | |
692 | int match = FALSE; | |
693 | USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbPrinters, i ); | |
694 | if ( printer ) | |
695 | { | |
696 | match = printer->address.manufacturer && manufacturer? CFEqual(printer->address.manufacturer, manufacturer ): FALSE; | |
697 | if ( match ) | |
698 | { | |
699 | match = printer->address.product && product? CFEqual(printer->address.product, product ): FALSE; | |
700 | } | |
701 | if ( match && serial ) | |
702 | { | |
703 | /* Note with old queues (pre Panther) the CUPS uri may have no serial number (serial==NULL). */ | |
704 | /* In this case, we will ignore serial number (as before), and we'll match to the first */ | |
705 | /* printer that agrees with manufacturer and product. */ | |
706 | /* If the CUPS uri does include a serial number, we'll enter this clause */ | |
707 | /* which requires the printer's serial number to match the CUPS serial number. */ | |
708 | /* The net effect is that for printers with a serial number, */ | |
709 | /* new queues must match the serial number, while old queues match any printer */ | |
710 | /* that satisfies the manufacturer/product match. */ | |
711 | /* */ | |
712 | match = printer->address.serial? CFEqual(printer->address.serial, serial ): FALSE; | |
713 | } | |
714 | if ( match ) | |
715 | { | |
716 | targetPrinter = UsbCopyPrinter( printer ); | |
717 | break; /* for, compare partial address to address for each printer on usb bus */ | |
718 | } | |
719 | } | |
720 | } | |
721 | } | |
722 | UsbReleaseAllPrinters( usbPrinters ); | |
723 | if ( NULL != targetPrinter ) | |
724 | status = UsbRegistryOpen( &targetPrinter->address, &activePrinter ); | |
725 | ||
726 | if ( NULL == activePrinter ) | |
727 | { | |
728 | sleep( PRINTER_POLLING_INTERVAL ); | |
729 | countdown -= PRINTER_POLLING_INTERVAL; | |
730 | if ( !countdown ) | |
731 | { | |
732 | /* periodically, write to the log so someone knows we're waiting */ | |
733 | if (NULL == targetPrinter) | |
734 | fprintf( stderr, "WARNING: Printer not responding\n" ); | |
735 | else | |
736 | fprintf( stderr, "INFO: Printer busy\n" ); | |
737 | countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 30 minutes */ | |
738 | } | |
739 | } | |
740 | } while ( NULL == activePrinter ); | |
741 | ||
742 | classdriver = activePrinter->classdriver; | |
743 | if ( NULL == classdriver ) | |
744 | { | |
745 | perror("ERROR: Unable to open USB Printing Class port"); | |
746 | return (status); | |
747 | } | |
748 | ||
749 | /* | |
750 | * Now that we are "connected" to the port, ignore SIGTERM so that we | |
751 | * can finish out any page data the driver sends (e.g. to eject the | |
752 | * current page... Only ignore SIGTERM if we are printing data from | |
753 | * stdin (otherwise you can't cancel raw jobs...) | |
754 | */ | |
755 | ||
756 | if (fd != 0) | |
757 | { | |
758 | #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ | |
759 | sigset(SIGTERM, SIG_IGN); | |
760 | #elif defined(HAVE_SIGACTION) | |
761 | memset(&action, 0, sizeof(action)); | |
762 | ||
763 | sigemptyset(&action.sa_mask); | |
764 | action.sa_handler = SIG_IGN; | |
765 | sigaction(SIGTERM, &action, NULL); | |
766 | #else | |
767 | signal(SIGTERM, SIG_IGN); | |
768 | #endif /* HAVE_SIGSET */ | |
769 | } | |
770 | ||
771 | buffer = malloc( buffersize ); | |
772 | if ( !buffer ) { | |
773 | fprintf( stderr, "ERROR: Couldn't allocate internal buffer\n" ); | |
774 | status = -1; | |
775 | } | |
776 | else | |
777 | { | |
778 | fprintf(stderr, "INFO: Sending the print file...\n"); | |
779 | if (pthread_cond_init(&readCompleteCondition, NULL) == 0) | |
780 | { | |
781 | gReadCompleteConditionPtr = &readCompleteCondition; | |
782 | ||
783 | if (pthread_mutex_init(&readMutex, NULL) == 0) | |
784 | { | |
785 | gReadMutexPtr = &readMutex; | |
786 | ||
787 | if (pthread_create(&thr, NULL, readthread, classdriver ) > 0) | |
788 | fprintf(stderr, "WARNING: Couldn't create read channel\n"); | |
789 | else | |
790 | thread_created = 1; | |
791 | } | |
792 | } | |
793 | } | |
794 | /* | |
795 | * the main thread sends the print file... | |
796 | */ | |
797 | while (noErr == status && copies > 0) | |
798 | { | |
799 | copies --; | |
800 | if (STDIN_FILENO != fd) | |
801 | { | |
802 | fputs("PAGE: 1 1", stderr); | |
803 | lseek( fd, 0, SEEK_SET ); /* rewind */ | |
804 | } | |
805 | ||
806 | tbytes = 0; | |
807 | while (noErr == status && (nbytes = read(fd, buffer, buffersize)) > 0) | |
808 | { | |
809 | /* | |
810 | * Write the print data to the printer... | |
811 | */ | |
812 | ||
813 | tbytes += nbytes; | |
814 | bufptr = buffer; | |
815 | ||
816 | while (nbytes > 0 && noErr == status ) | |
817 | { | |
818 | wbytes = nbytes; | |
819 | status = (*classdriver)->WritePipe( classdriver, (UInt8*)bufptr, &wbytes, 0 /*nbytes > wbytes? 0: feof(fp)*/ ); | |
820 | if (wbytes < 0 || noErr != status) | |
821 | { | |
822 | OSStatus err; | |
823 | err = (*classdriver)->Abort( classdriver ); | |
824 | fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled %ld)\n", status, err ); | |
825 | break; | |
826 | } | |
827 | ||
828 | nbytes -= wbytes; | |
829 | bufptr += wbytes; | |
830 | } | |
831 | ||
832 | if (fd != 0 && noErr == status) | |
833 | fprintf(stderr, "INFO: Sending print file, %qd bytes...\n", (off_t)tbytes); | |
834 | } | |
835 | } | |
836 | done = 1; /* stop scheduling reads */ | |
837 | ||
838 | if ( thread_created ) | |
839 | { | |
840 | /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If | |
841 | * we are not signaled in that time then force the thread to exit by setting | |
842 | * the waiteof to be false. Plese note that this relies on us using the timeout | |
843 | * class driver. | |
844 | */ | |
845 | struct timespec sleepUntil = { time(NULL) + WAITEOF_DELAY, 0 }; | |
846 | pthread_mutex_lock(&readMutex); | |
847 | if (pthread_cond_timedwait(&readCompleteCondition, &readMutex, (const struct timespec *)&sleepUntil) != 0) | |
848 | gWaitEOF = false; | |
849 | pthread_mutex_unlock(&readMutex); | |
850 | pthread_join( thr,NULL); /* wait for the child thread to return */ | |
851 | } | |
852 | ||
853 | (*classdriver)->Close( classdriver ); /* forces the read to stop incase we are doing a blocking read */ | |
854 | UsbUnloadClassDriver( activePrinter ); | |
855 | /* | |
856 | * Close the socket connection and input file and return... | |
857 | */ | |
858 | free( buffer ); | |
859 | ||
860 | if (STDIN_FILENO != fd) | |
861 | close(fd); | |
862 | ||
863 | if (gReadCompleteConditionPtr != NULL) | |
864 | pthread_cond_destroy(gReadCompleteConditionPtr); | |
865 | if (gReadMutexPtr != NULL) | |
866 | pthread_mutex_destroy(gReadMutexPtr); | |
867 | ||
868 | return status == kIOReturnSuccess? 0: status; | |
869 | } | |
870 | ||
871 | static Boolean | |
872 | encodecfstr( CFStringRef cfsrc, char *dst, long len ) | |
873 | { | |
874 | return CFStringGetCString(cfsrc, dst, len, kCFStringEncodingUTF8 ); | |
875 | } | |
876 | ||
877 | /* | |
878 | * 'list_devices()' - List all USB devices. | |
879 | */ | |
880 | void list_devices(void) | |
881 | { | |
882 | char encodedManufacturer[1024]; | |
883 | char encodedProduct[1024]; | |
884 | char uri[1024]; | |
885 | CFMutableArrayRef usbBusPrinters = UsbGetAllPrinters(); | |
886 | CFIndex i, numPrinters = NULL != usbBusPrinters? CFArrayGetCount( usbBusPrinters ): 0; | |
887 | ||
888 | puts("direct usb \"Unknown\" \"USB Printer (usb)\""); | |
889 | for ( i = 0; i < numPrinters; ++i ) | |
890 | { | |
891 | USBPrinterInfo *printer = (USBPrinterInfo *) CFArrayGetValueAtIndex( usbBusPrinters, i ); | |
892 | ||
893 | if ( printer ) | |
894 | { | |
895 | CFStringRef addressRef = UsbMakeFullUriAddress( printer ); | |
896 | if ( addressRef ) | |
897 | { | |
898 | if ( CFStringGetCString(addressRef, uri, sizeof(uri), kCFStringEncodingUTF8) ) { | |
899 | ||
900 | encodecfstr( printer->address.manufacturer, encodedManufacturer, sizeof(encodedManufacturer) ); | |
901 | encodecfstr( printer->address.product, encodedProduct, sizeof(encodedProduct) ); | |
902 | printf("direct %s \"%s %s\" \"%s\"\n", uri, encodedManufacturer, encodedProduct, encodedProduct); | |
903 | } | |
904 | } | |
905 | } | |
906 | } | |
907 | UsbReleaseAllPrinters( usbBusPrinters ); | |
908 | fflush(NULL); | |
909 | } | |
910 | ||
911 | ||
912 | static void parseOptions(const char *options, char *serial) | |
913 | { | |
914 | char *serialnumber; /* ?serial=<serial> or ?location=<location> */ | |
915 | char optionName[255], /* Name of option */ | |
916 | value[255], /* Value of option */ | |
917 | *ptr; /* Pointer into name or value */ | |
918 | ||
919 | if (serial) | |
920 | *serial = '\0'; | |
921 | ||
922 | if (!options) | |
923 | return; | |
924 | ||
925 | serialnumber = NULL; | |
926 | ||
927 | while (*options != '\0') | |
928 | { | |
929 | /* | |
930 | * Get the name... | |
931 | */ | |
932 | for (ptr = optionName; *options && *options != '=' && *options != '+' && *options != '&'; ) | |
933 | *ptr++ = *options++; | |
934 | ||
935 | *ptr = '\0'; | |
936 | value[0] = '\0'; | |
937 | ||
938 | if (*options == '=') | |
939 | { | |
940 | /* | |
941 | * Get the value... | |
942 | */ | |
943 | ||
944 | options ++; | |
945 | ||
946 | for (ptr = value; *options && *options != '+' && *options != '&';) | |
947 | *ptr++ = *options++; | |
948 | ||
949 | *ptr = '\0'; | |
950 | ||
951 | if (*options == '+' || *options == '&') | |
952 | options ++; | |
953 | } | |
954 | else if (*options == '+' || *options == '&') | |
955 | { | |
956 | options ++; | |
957 | } | |
958 | ||
959 | /* | |
960 | * Process the option... | |
961 | */ | |
962 | if (strcasecmp(optionName, "waiteof") == 0) | |
963 | { | |
964 | if (strcasecmp(value, "on") == 0 || | |
965 | strcasecmp(value, "yes") == 0 || | |
966 | strcasecmp(value, "true") == 0) | |
967 | { | |
968 | gWaitEOF = true; | |
969 | } | |
970 | else if (strcasecmp(value, "off") == 0 || | |
971 | strcasecmp(value, "no") == 0 || | |
972 | strcasecmp(value, "false") == 0) | |
973 | { | |
974 | gWaitEOF = false; | |
975 | } | |
976 | else | |
977 | { | |
978 | fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value); | |
979 | } | |
980 | } | |
981 | else if (strcasecmp(optionName, "serial") == 0 || | |
982 | strcasecmp(optionName, "location") == 0 ) | |
983 | { | |
984 | strcpy(serial, value); | |
985 | serialnumber = serial; | |
986 | } | |
987 | } | |
988 | ||
989 | return; | |
990 | } | |
991 | ||
992 | ||
993 | /*! | |
994 | * @function addPercentEscapes | |
995 | * @abstract Encode a string with percent escapes | |
996 | * | |
997 | * @param src The source C string | |
998 | * @param dst Desination buffer | |
999 | * @param dstMax Size of desination buffer | |
1000 | * | |
1001 | * @result A non-zero return value for errors | |
1002 | */ | |
1003 | static int addPercentEscapes(const unsigned char* src, char* dst, int dstMax) | |
1004 | { | |
1005 | unsigned char c; | |
1006 | char *dstEnd = dst + dstMax - 1; /* -1 to leave room for the NUL */ | |
1007 | ||
1008 | while (*src) | |
1009 | { | |
1010 | c = *src++; | |
1011 | ||
1012 | if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || | |
1013 | (c >= '0' && c <= '9') || (c == '.' || c == '-' || c == '*' || c == '_')) | |
1014 | { | |
1015 | if (dst >= dstEnd) | |
1016 | return -1; | |
1017 | ||
1018 | *dst++ = c; | |
1019 | } | |
1020 | else | |
1021 | { | |
1022 | if (dst >= dstEnd - 2) | |
1023 | return -1; | |
1024 | ||
1025 | snprintf(dst, dstEnd - dst, "%%%02x", c); | |
1026 | dst += 3; | |
1027 | } | |
1028 | } | |
1029 | ||
1030 | *dst = '\0'; | |
1031 | return 0; | |
1032 | } | |
1033 | ||
1034 | ||
1035 | /*! | |
1036 | * @function removePercentEscapes | |
1037 | * @abstract Returns a string with any percent escape sequences replaced with their equivalent character | |
1038 | * | |
1039 | * @param src Source buffer | |
1040 | * @param srclen Number of bytes in source buffer | |
1041 | * @param dst Desination buffer | |
1042 | * @param dstMax Size of desination buffer | |
1043 | * | |
1044 | * @result A non-zero return value for errors | |
1045 | */ | |
1046 | static int removePercentEscapes(const char* src, unsigned char* dst, int dstMax) | |
1047 | { | |
1048 | int c; | |
1049 | const unsigned char *dstEnd = dst + dstMax; | |
1050 | ||
1051 | while (*src && dst < dstEnd) | |
1052 | { | |
1053 | c = *src++; | |
1054 | ||
1055 | if (c == '%') | |
1056 | { | |
1057 | sscanf(src, "%02x", &c); | |
1058 | src += 2; | |
1059 | } | |
1060 | *dst++ = (char)c; | |
1061 | } | |
1062 | ||
1063 | if (dst >= dstEnd) | |
1064 | return -1; | |
1065 | ||
1066 | *dst = '\0'; | |
1067 | return 0; | |
1068 | } | |
1069 | ||
1070 | /*-----------------------------------------------------------------------------* | |
1071 | ||
1072 | DelimitSubstring | |
1073 | ||
1074 | Desc: Search a string from a starting location, looking for a given | |
1075 | delimiter. Return the range from the start of the search to the | |
1076 | delimiter, or end of string (whichever is shorter). | |
1077 | ||
1078 | In: stringToSearch string which contains a substring that we search | |
1079 | delim string which marks the end of the string | |
1080 | bounds start and length of substring of stringToSearch | |
1081 | options case sensitive, anchored, etc. | |
1082 | ||
1083 | Out: Range up to the delimiter. | |
1084 | ||
1085 | *-----------------------------------------------------------------------------*/ | |
1086 | static CFRange | |
1087 | DelimitSubstring( CFStringRef stringToSearch, CFStringRef delim, CFRange bounds, CFStringCompareFlags options ) | |
1088 | { | |
1089 | CFRange where_delim, /* where the delimiter was found */ | |
1090 | value; | |
1091 | /* */ | |
1092 | /* trim leading space by changing bounds */ | |
1093 | /* */ | |
1094 | while ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, CFSTR(" "), bounds, kCFCompareAnchored, &where_delim ) ) | |
1095 | { | |
1096 | ++bounds.location; /* drop a leading ' ' */ | |
1097 | --bounds.length; | |
1098 | } | |
1099 | value = bounds; /* assume match to the end of string, may be NULL */ | |
1100 | /* */ | |
1101 | /* find the delimiter in the remaining string */ | |
1102 | /* */ | |
1103 | if ( bounds.length > 0 && CFStringFindWithOptions( stringToSearch, delim, bounds, options, &where_delim ) ) | |
1104 | { | |
1105 | /* */ | |
1106 | /* match to the delimiter */ | |
1107 | /* */ | |
1108 | value.length = where_delim.location /* delim */ - bounds.location /* start of search */; | |
1109 | } | |
1110 | DEBUG_CFString( "\tFind target", stringToSearch ); | |
1111 | DEBUG_CFString( "\tFind pattern", delim ); | |
1112 | DEBUG_ERR( (int) value.location, "\t\tFound %d\n" ); | |
1113 | DEBUG_ERR( (int) value.length, " length %d" ); | |
1114 | ||
1115 | return value; | |
1116 | } | |
1117 | ||
1118 | ||
1119 | /*-----------------------------------------------------------------------------* | |
1120 | ||
1121 | DeviceIDCreateValueList | |
1122 | ||
1123 | Desc: Create a new string for the value list of the specified key. | |
1124 | The key may be specified as two strings (an abbreviated form | |
1125 | and a standard form). NULL can be passed for either form of | |
1126 | the key. | |
1127 | ||
1128 | (Although passing NULL for both forms of the key is considered | |
1129 | bad form[!] it is handled correctly.) | |
1130 | ||
1131 | In: deviceID the device's IEEE-1284 DeviceID key-value list | |
1132 | abbrevKey the key we're interested in (NULL allowed) | |
1133 | key | |
1134 | ||
1135 | Out: CFString the value list | |
1136 | or NULL key wasn't found in deviceID | |
1137 | ||
1138 | *-----------------------------------------------------------------------------*/ | |
1139 | CFStringRef | |
1140 | DeviceIDCreateValueList( const CFStringRef deviceID, const CFStringRef abbrevKey, const CFStringRef key ) | |
1141 | { | |
1142 | CFRange found = CFRangeMake( -1,0); /* note CFStringFind sets length 0 if string not found */ | |
1143 | CFStringRef valueList = NULL; | |
1144 | ||
1145 | DEBUG_CFString( "---------DeviceIDCreateValueList DeviceID:", deviceID ); | |
1146 | DEBUG_CFString( "---------DeviceIDCreateValueList key:", key ); | |
1147 | DEBUG_CFString( "---------DeviceIDCreateValueList abbrevkey:", abbrevKey ); | |
1148 | if ( NULL != deviceID && NULL != abbrevKey ) | |
1149 | found = CFStringFind( deviceID, abbrevKey, kCFCompareCaseInsensitive ); | |
1150 | if ( NULL != deviceID && NULL != key && found.length <= 0 ) | |
1151 | found = CFStringFind( deviceID, key, kCFCompareCaseInsensitive ); | |
1152 | if ( found.length > 0 ) | |
1153 | { | |
1154 | /* the key is at found */ | |
1155 | /* the value follows the key until we reach the semi-colon, or end of string */ | |
1156 | /* */ | |
1157 | CFRange search = CFRangeMake( found.location + found.length, | |
1158 | CFStringGetLength( deviceID ) - (found.location + found.length) ); | |
1159 | /* */ | |
1160 | /* finally extract the string */ | |
1161 | /* */ | |
1162 | valueList = CFStringCreateWithSubstring ( kCFAllocatorDefault, deviceID, | |
1163 | DelimitSubstring( deviceID, kDeviceIDKeyValuePairDelimiter, search, kCFCompareCaseInsensitive ) ); | |
1164 | DEBUG_CFString( "---------DeviceIDCreateValueList:", valueList ); | |
1165 | } | |
1166 | return valueList; | |
1167 | ||
1168 | } | |
1169 | ||
1170 | ||
1171 | ||
1172 | /******************************************************************************/ | |
1173 | ||
1174 | /*-----------------------------------------------------------------------------* | |
1175 | ||
1176 | CompareSameString | |
1177 | ||
1178 | Desc: Return the CFCompare result for two strings, either or both of which | |
1179 | can be NULL. | |
1180 | ||
1181 | In: | |
1182 | a current value | |
1183 | b last value | |
1184 | ||
1185 | Out: | |
1186 | 0 if the strings match | |
1187 | non-zero if the strings don't match | |
1188 | ||
1189 | *-----------------------------------------------------------------------------*/ | |
1190 | static int | |
1191 | CompareSameString( const CFStringRef a, const CFStringRef b ) | |
1192 | { | |
1193 | if ( NULL == a && NULL == b ) | |
1194 | return 0; | |
1195 | else if ( NULL != a && NULL != b ) | |
1196 | return CFStringCompare( a, b, kCFCompareAnchored ); | |
1197 | else | |
1198 | return 1; /* one of a or b is NULL this time, but wasn't last time */ | |
1199 | } | |
1200 | ||
1201 | ||
1202 | /******************************************************************************/ | |
1203 | kern_return_t | |
1204 | UsbLoadClassDriver( USBPrinterInfo *printer, CFUUIDRef interfaceID, CFStringRef classDriverBundle ) | |
1205 | { | |
1206 | kern_return_t kr = kUSBPrinterClassDeviceNotOpen; | |
1207 | if ( NULL != classDriverBundle ) | |
1208 | printer->bundle = classDriverBundle; /* vendor-specific class override */ | |
1209 | else | |
1210 | #ifdef TIMEOUT | |
1211 | classDriverBundle = kUSBGenericTOPrinterClassDriver; /* supply the generic TIMEOUT class driver */ | |
1212 | #else | |
1213 | classDriverBundle = kUSBGenericPrinterClassDriver; /* supply the generic class driver */ | |
1214 | #endif | |
1215 | DEBUG_CFString( "UsbLoadClassDriver classDriverBundle", classDriverBundle ); | |
1216 | if ( NULL != classDriverBundle ) | |
1217 | { | |
1218 | USBPrinterClassContext **classdriver = NULL; | |
1219 | CFURLRef classDriverURL = CFURLCreateWithFileSystemPath( NULL, classDriverBundle, kCFURLPOSIXPathStyle, TRUE ); | |
1220 | CFPlugInRef plugin = NULL == classDriverURL? NULL: CFPlugInCreate( NULL, classDriverURL ); | |
1221 | if ( NULL != plugin) | |
1222 | { | |
1223 | /* See if this plug-in implements the Test type. */ | |
1224 | CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn( kUSBPrinterClassTypeID, plugin ); | |
1225 | ||
1226 | /* If there are factories for the requested type, attempt to */ | |
1227 | /* get the IUnknown interface. */ | |
1228 | DEBUG_ERR( 0, "UsbLoadClassDriver plugin %x\n" ); | |
1229 | if (NULL != factories && CFArrayGetCount(factories) > 0) | |
1230 | { | |
1231 | /* Get the factory ID for the first location in the array of IDs. */ | |
1232 | CFUUIDRef factoryID = CFArrayGetValueAtIndex( factories, 0 ); | |
1233 | /* Use the factory ID to get an IUnknown interface. */ | |
1234 | /* Here the code for the PlugIn is loaded. */ | |
1235 | IUnknownVTbl **iunknown = CFPlugInInstanceCreate( NULL, factoryID, kUSBPrinterClassTypeID ); | |
1236 | /* If this is an IUnknown interface, query for the Test interface. */ | |
1237 | DEBUG_ERR( 0, "UsbLoadClassDriver factories %x\n" ); | |
1238 | if (NULL != iunknown) | |
1239 | { | |
1240 | DEBUG_ERR( 0, "UsbLoadClassDriver CFPlugInInstanceCreate %x\n" ); | |
1241 | kr = (*iunknown)->QueryInterface( iunknown, CFUUIDGetUUIDBytes(interfaceID), (LPVOID *) &classdriver ); | |
1242 | ||
1243 | (*iunknown)->Release( iunknown ); | |
1244 | if ( S_OK == kr && NULL != classdriver ) | |
1245 | { | |
1246 | DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface %x\n" ); | |
1247 | printer->plugin = plugin; | |
1248 | kr = (*classdriver)->Initialize( classdriver, printer->classdriver ); | |
1249 | ||
1250 | kr = kIOReturnSuccess; | |
1251 | printer->classdriver = classdriver; | |
1252 | } | |
1253 | else | |
1254 | { | |
1255 | DEBUG_ERR( kr, "UsbLoadClassDriver QueryInterface FAILED %x\n" ); | |
1256 | } | |
1257 | } | |
1258 | else | |
1259 | { | |
1260 | DEBUG_ERR( kr, "UsbLoadClassDriver CFPlugInInstanceCreate FAILED %x\n" ); | |
1261 | } | |
1262 | } | |
1263 | else | |
1264 | { | |
1265 | DEBUG_ERR( kr, "UsbLoadClassDriver factories FAILED %x\n" ); | |
1266 | } | |
1267 | } | |
1268 | else | |
1269 | { | |
1270 | DEBUG_ERR( kr, "UsbLoadClassDriver plugin FAILED %x\n" ); | |
1271 | } | |
1272 | if ( kr != kIOReturnSuccess || NULL == plugin || NULL == classdriver ) | |
1273 | { | |
1274 | UsbUnloadClassDriver( printer ); | |
1275 | } | |
1276 | } | |
1277 | ||
1278 | return kr; | |
1279 | } | |
1280 | ||
1281 | ||
1282 | kern_return_t | |
1283 | UsbUnloadClassDriver( USBPrinterInfo *printer ) | |
1284 | { | |
1285 | DEBUG_ERR( kIOReturnSuccess, "UsbUnloadClassDriver %x\n" ); | |
1286 | if ( NULL != printer->classdriver ) | |
1287 | (*printer->classdriver)->Release( printer->classdriver ); | |
1288 | printer->classdriver = NULL; | |
1289 | ||
1290 | if ( NULL != printer->plugin ) | |
1291 | CFRelease( printer->plugin ); | |
1292 | printer->plugin = NULL; | |
1293 | ||
1294 | return kIOReturnSuccess; | |
1295 | } | |
1296 | ||
1297 | ||
1298 | /*-----------------------------------------------------------------------------* | |
1299 | ||
1300 | UsbAddressDispose | |
1301 | ||
1302 | Desc: deallocates anything used to create a persistent printer address | |
1303 | ||
1304 | In: address the printer address we've created | |
1305 | ||
1306 | Out: <none> | |
1307 | ||
1308 | *-----------------------------------------------------------------------------*/ | |
1309 | void | |
1310 | UsbAddressDispose( USBPrinterAddress *address ) | |
1311 | { | |
1312 | if ( address->product != NULL ) CFRelease( address->product ); | |
1313 | if ( address->manufacturer != NULL ) CFRelease( address->manufacturer ); | |
1314 | if ( address->serial != NULL ) CFRelease( address->serial ); | |
1315 | if ( address->command != NULL ) CFRelease( address->command ); | |
1316 | ||
1317 | address->product = | |
1318 | address->manufacturer = | |
1319 | address->serial = | |
1320 | address->command = NULL; | |
1321 | ||
1322 | } | |
1323 | ||
1324 | /*-----------------------------------------------------------------------------* | |
1325 | ||
1326 | UsbGetPrinterAddress | |
1327 | ||
1328 | Desc: Given a printer we're enumerating, discover it's persistent | |
1329 | reference. | |
1330 | ||
1331 | A "persistent reference" is one which enables us to identify | |
1332 | a printer regardless of where it resides on the USB topology, | |
1333 | and enumeration sequence. | |
1334 | ||
1335 | To do this, we actually construct a reference from information | |
1336 | buried inside the printer. First we look at the USB device | |
1337 | descripton: an ideally defined device will support strings for | |
1338 | manufacturer and product id, and serial number. The serial number | |
1339 | will be unique for each printer. | |
1340 | ||
1341 | Our prefered identification fetches the IEEE-1284 device id string. | |
1342 | This transparently handled IEEE-1284 compatible printers which | |
1343 | connected over a USB-parallel cable. Only if we can't get all the | |
1344 | information to uniquely identify the printer do we try the strings | |
1345 | referenced in the printer's USB device descriptor. (These strings | |
1346 | are typically absent in a USB-parallel cable.) | |
1347 | ||
1348 | If a device doesn't support serial numbers we have a problem: | |
1349 | we can't distinguish between two identical printers. Unique serial | |
1350 | numbers allow us to distinguish between two same-model, same-manufacturer | |
1351 | USB printers. | |
1352 | ||
1353 | In: | |
1354 | thePrinter iterator required for fetching device descriptor | |
1355 | devRefNum required to configure the interface | |
1356 | ||
1357 | Out: | |
1358 | address->manufacturer | |
1359 | address->product | |
1360 | address->serial | |
1361 | Any (and all) of these may be NULL if we can't retrieve | |
1362 | information for IEEE1284 DeviceID or the USB device | |
1363 | descriptor. Caller should be prepared to handle such a case. | |
1364 | address->command | |
1365 | May be updated. | |
1366 | ||
1367 | *-----------------------------------------------------------------------------*/ | |
1368 | OSStatus | |
1369 | UsbGetPrinterAddress( USBPrinterInfo *thePrinter, USBPrinterAddress *address, UInt16 timeout ) | |
1370 | { | |
1371 | ||
1372 | /* */ | |
1373 | /* start by assuming the device is not IEEE-1284 compliant */ | |
1374 | /* and that we can't read in the required strings. */ | |
1375 | /* */ | |
1376 | OSStatus err; | |
1377 | CFStringRef deviceId = NULL; | |
1378 | USBPrinterClassContext **printer = NULL == thePrinter? NULL: thePrinter->classdriver; | |
1379 | ||
1380 | address->manufacturer = | |
1381 | address->product = | |
1382 | address->compatible = | |
1383 | address->serial = | |
1384 | address->command = NULL; | |
1385 | ||
1386 | DEBUG_DUMP( "UsbGetPrinterAddress thePrinter", thePrinter, sizeof(USBPrinterInfo) ); | |
1387 | ||
1388 | err = (*printer)->GetDeviceID( printer, &deviceId, timeout ); | |
1389 | if ( noErr == err && NULL != deviceId ) | |
1390 | { | |
1391 | /* the strings embedded here are defined in the IEEE1284 spec */ | |
1392 | /* */ | |
1393 | /* use the MFG/MANUFACTURER for the manufacturer */ | |
1394 | /* and the MDL/MODEL for the product */ | |
1395 | /* there is no serial number defined in IEEE1284 */ | |
1396 | /* but it's been observed in recent HP printers */ | |
1397 | /* */ | |
1398 | address->command = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCommandAbbrev, kDeviceIDKeyCommand ); | |
1399 | ||
1400 | address->product = DeviceIDCreateValueList( deviceId, kDeviceIDKeyModelAbbrev, kDeviceIDKeyModel ); | |
1401 | address->compatible = DeviceIDCreateValueList( deviceId, kDeviceIDKeyCompatibleAbbrev, kDeviceIDKeyCompatible ); | |
1402 | ||
1403 | address->manufacturer = DeviceIDCreateValueList( deviceId, kDeviceIDKeyManufacturerAbbrev, kDeviceIDKeyManufacturer ); | |
1404 | ||
1405 | address->serial = DeviceIDCreateValueList( deviceId, kDeviceIDKeySerialAbbrev, kDeviceIDKeySerial ); | |
1406 | CFRelease( deviceId ); | |
1407 | } | |
1408 | DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->product", address->product ); | |
1409 | DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->compatible", address->compatible ); | |
1410 | DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->manufacturer", address->manufacturer ); | |
1411 | DEBUG_CFString( "UsbGetPrinterAddress DeviceID address->serial", address->serial ); | |
1412 | ||
1413 | if ( NULL == address->product || NULL == address->manufacturer || NULL == address->serial ) | |
1414 | { | |
1415 | /* */ | |
1416 | /* if the manufacturer or the product or serial number were not specified in DeviceID */ | |
1417 | /* try to construct the address using USB English string descriptors */ | |
1418 | /* */ | |
1419 | IOUSBDeviceDescriptor desc; | |
1420 | USBIODeviceRequest request; | |
1421 | ||
1422 | request.requestType = USBmakebmRequestType( kUSBIn, kUSBStandard, kUSBDevice ); | |
1423 | request.request = kUSBRqGetDescriptor; | |
1424 | request.value = (kUSBDeviceDesc << 8) | 0; | |
1425 | request.index = 0; /* not kUSBLanguageEnglish*/ | |
1426 | request.length = sizeof(desc); | |
1427 | request.buffer = &desc; | |
1428 | err = (*printer)->DeviceRequest( printer, &request, timeout ); | |
1429 | DEBUG_ERR( (kern_return_t) err, "UsbGetPrinterAddress: GetDescriptor %x" ); | |
1430 | if ( kIOReturnSuccess == err ) | |
1431 | { | |
1432 | /* once we've retrieved the device descriptor */ | |
1433 | /* try to fill in missing pieces of information */ | |
1434 | /* */ | |
1435 | /* Don't override any information already retrieved from DeviceID. */ | |
1436 | ||
1437 | if ( NULL == address->product) | |
1438 | { | |
1439 | err = (*printer)->GetString( printer, desc.iProduct, kUSBLanguageEnglish, timeout, &address->product ); | |
1440 | if ( kIOReturnSuccess != err || address->product == NULL) { | |
1441 | address->product = CFSTR("Unknown"); | |
1442 | } | |
1443 | } | |
1444 | DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->product\n", address->product ); | |
1445 | ||
1446 | if ( NULL == address->manufacturer ) | |
1447 | { | |
1448 | err = (*printer)->GetString( printer, desc.iManufacturer, kUSBLanguageEnglish, timeout, &address->manufacturer ); | |
1449 | if (kIOReturnSuccess != err || address->manufacturer == NULL) { | |
1450 | address->manufacturer = CFSTR("Unknown"); | |
1451 | } | |
1452 | } | |
1453 | DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->manufacturer\n", address->manufacturer ); | |
1454 | ||
1455 | if ( NULL == address->serial ) | |
1456 | { | |
1457 | /* if the printer doesn't have a serial number, use locationId */ | |
1458 | if ( 0 == desc.iSerialNumber ) | |
1459 | { | |
1460 | address->serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), (*printer)->location ); | |
1461 | } | |
1462 | else | |
1463 | { | |
1464 | err = (*printer)->GetString( printer, desc.iSerialNumber, kUSBLanguageEnglish, timeout, &address->serial ); | |
1465 | /* trailing NULs aren't handled correctly in URI */ | |
1466 | if ( address->serial ) | |
1467 | { | |
1468 | UniChar nulbyte = { 0 }; | |
1469 | CFStringRef trim = CFStringCreateWithCharacters(NULL, &nulbyte, 1); | |
1470 | CFMutableStringRef newserial = CFStringCreateMutableCopy(NULL, 0, address->serial); | |
1471 | ||
1472 | CFStringTrim( newserial, trim ); | |
1473 | ||
1474 | CFRelease(trim); | |
1475 | CFRelease( address->serial ); | |
1476 | ||
1477 | address->serial = newserial; | |
1478 | } | |
1479 | } | |
1480 | } | |
1481 | DEBUG_CFString( "UsbGetPrinterAddress: UsbGetString address->serial\n", address->serial ); | |
1482 | } | |
1483 | } | |
1484 | if ( NULL != address->product) | |
1485 | CFRetain(address->product); /* UsbGetString is really a UsbCopyString. */ | |
1486 | if ( NULL != address->manufacturer ) | |
1487 | CFRetain( address->manufacturer ); | |
1488 | if ( NULL != address->serial ) | |
1489 | CFRetain( address->serial ); | |
1490 | return err; | |
1491 | } | |
1492 | ||
1493 | ||
1494 | /*-----------------------------------------------------------------------------* | |
1495 | ||
1496 | UsbSamePrinter | |
1497 | ||
1498 | Desc: match two Usb printer address; return TRUE if they are the same. | |
1499 | ||
1500 | In: a the persistent address found last time | |
1501 | b the persistent address found this time | |
1502 | ||
1503 | Out: non-zero iff the addresses are the same | |
1504 | ||
1505 | *-----------------------------------------------------------------------------*/ | |
1506 | int | |
1507 | UsbSamePrinter( const USBPrinterAddress *a, const USBPrinterAddress *b ) | |
1508 | { | |
1509 | int result = 0; | |
1510 | DEBUG_CFCompareString( "UsbSamePrinter serial", a->serial, b->serial ); | |
1511 | DEBUG_CFCompareString( "UsbSamePrinter product", a->product, b->product ); | |
1512 | DEBUG_CFCompareString( "UsbSamePrinter manufacturer", a->manufacturer, b->manufacturer ); | |
1513 | ||
1514 | result = !CompareSameString( a->serial, b->serial ); | |
1515 | if ( result ) result = !CompareSameString( a->product, b->product ); | |
1516 | if ( result ) result = !CompareSameString( a->manufacturer, b->manufacturer ); | |
1517 | ||
1518 | return result; | |
1519 | } | |
1520 | ||
1521 | ||
1522 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
1523 | Method: UsbMakeFullUriAddress | |
1524 | ||
1525 | Input Parameters: | |
1526 | ||
1527 | Output Parameters: | |
1528 | ||
1529 | Description: | |
1530 | Fill in missing address information | |
1531 | ||
1532 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1533 | CFStringRef | |
1534 | UsbMakeFullUriAddress( USBPrinterInfo *printer ) | |
1535 | { | |
1536 | /* */ | |
1537 | /* fill in missing address information. */ | |
1538 | /* */ | |
1539 | CFMutableStringRef printerUri = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("usb://") ); | |
1540 | if ( NULL != printerUri ) | |
1541 | { | |
1542 | CFStringRef serial = printer->address.serial; | |
1543 | ||
1544 | CFStringAppend(printerUri, printer->address.manufacturer? CreateEncodedCFString( printer->address.manufacturer ): CFSTR("Unknown") ); | |
1545 | CFStringAppend(printerUri, CFSTR("/") ); | |
1546 | ||
1547 | CFStringAppend(printerUri, printer->address.product? CreateEncodedCFString( printer->address.product ): CFSTR("Unknown") ); | |
1548 | ||
1549 | /*Handle the common case where there is no serial number (S450?) */ | |
1550 | CFStringAppend(printerUri, serial == NULL? CFSTR("?location="): CFSTR("?serial=") ); | |
1551 | if ( serial == NULL) | |
1552 | serial = CFStringCreateWithFormat( NULL, NULL, CFSTR("%lx"), printer->location ); | |
1553 | ||
1554 | CFStringAppend(printerUri, serial? CreateEncodedCFString( serial ): CFSTR("Unknown") ); | |
1555 | } | |
1556 | ||
1557 | return printerUri; | |
1558 | } | |
1559 | ||
1560 | ||
1561 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
1562 | Method: UsbGetAllPrinters | |
1563 | ||
1564 | Input Parameters: | |
1565 | ||
1566 | Output Parameters: | |
1567 | array of all USB printers on the system | |
1568 | ||
1569 | Description: | |
1570 | Build a list of USB printers by iterating IOKit USB objects | |
1571 | ||
1572 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1573 | CFMutableArrayRef | |
1574 | UsbGetAllPrinters( void ) | |
1575 | { | |
1576 | kern_return_t kr; /* kernel errors */ | |
1577 | mach_port_t master_device_port = 0; | |
1578 | io_service_t usbInterface = 0; | |
1579 | io_iterator_t iter = 0; | |
1580 | CFMutableArrayRef printers = CFArrayCreateMutable( NULL, 0, NULL ); /* all printers */ | |
1581 | ||
1582 | do | |
1583 | { | |
1584 | ||
1585 | kr = IOMasterPort( bootstrap_port, &master_device_port ); | |
1586 | DEBUG_ERR( kr, "UsbGetAllPrinters IOMasterPort %x\n" ); | |
1587 | if(kIOReturnSuccess != kr) break; | |
1588 | ||
1589 | { | |
1590 | CFDictionaryRef usbMatch = NULL; | |
1591 | ||
1592 | /* iterate over all interfaces. */ | |
1593 | usbMatch = IOServiceMatching(kIOUSBInterfaceClassName); | |
1594 | if ( !usbMatch ) break; | |
1595 | DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceMatching %x\n" ); | |
1596 | ||
1597 | /* IOServiceGetMatchingServices() consumes the usbMatch reference so we don't need to release it. */ | |
1598 | kr = IOServiceGetMatchingServices(master_device_port, usbMatch, &iter); | |
1599 | usbMatch = NULL; | |
1600 | ||
1601 | DEBUG_ERR( kr, "UsbGetAllPrinters IOServiceGetMatchingServices %x\n" ); | |
1602 | if(kIOReturnSuccess != kr || iter == NULL) break; | |
1603 | } | |
1604 | ||
1605 | while ( NULL != (usbInterface = IOIteratorNext(iter)) ) | |
1606 | { | |
1607 | IOCFPlugInInterface **iodev; | |
1608 | USBPrinterInterface intf; | |
1609 | HRESULT res; | |
1610 | SInt32 score; | |
1611 | CFMutableDictionaryRef properties; | |
1612 | CFStringRef classDriver = NULL; | |
1613 | ||
1614 | kr = IORegistryEntryCreateCFProperties( usbInterface, &properties, kCFAllocatorDefault, kNilOptions); | |
1615 | if ( kIOReturnSuccess == kr && NULL != properties) | |
1616 | { | |
1617 | classDriver = (CFStringRef) CFDictionaryGetValue( properties, kUSBClassDriverProperty ); | |
1618 | if ( NULL != classDriver ) | |
1619 | CFRetain( classDriver ); | |
1620 | CFRelease( properties ); | |
1621 | } | |
1622 | ||
1623 | kr = IOCreatePlugInInterfaceForService( usbInterface, | |
1624 | kIOUSBInterfaceUserClientTypeID, | |
1625 | kIOCFPlugInInterfaceID, | |
1626 | &iodev, | |
1627 | &score); | |
1628 | ||
1629 | DEBUG_ERR( kr, "UsbGetAllPrinters IOCreatePlugInInterfaceForService %x\n" ); | |
1630 | if ( kIOReturnSuccess == kr ) | |
1631 | { | |
1632 | UInt8 intfClass = 0; | |
1633 | UInt8 intfSubClass = 0; | |
1634 | ||
1635 | res = (*iodev)->QueryInterface( iodev, USB_INTERFACE_KIND, (LPVOID *) &intf); | |
1636 | DEBUG_ERR( (kern_return_t) res, "UsbGetAllPrinters QueryInterface %x\n" ); | |
1637 | ||
1638 | (*iodev)->Release(iodev); | |
1639 | if ( noErr != res ) break; | |
1640 | ||
1641 | kr = (*intf)->GetInterfaceClass(intf, &intfClass); | |
1642 | DEBUG_ERR(kr, "UsbGetAllPrinters GetInterfaceClass %x\n"); | |
1643 | if ( kIOReturnSuccess == kr ) | |
1644 | kr = (*intf)->GetInterfaceSubClass(intf, &intfSubClass); | |
1645 | DEBUG_ERR(kr, "UsbGetAllPrinters GetInterfaceSubClass %x\n"); | |
1646 | ||
1647 | if ( kIOReturnSuccess == kr && | |
1648 | kUSBPrintingClass == intfClass && | |
1649 | kUSBPrintingSubclass == intfSubClass ) | |
1650 | { | |
1651 | ||
1652 | USBPrinterInfo printer, | |
1653 | *printerInfo; | |
1654 | /* | |
1655 | For each type of printer specified in the lookup spec array, find | |
1656 | all of that type of printer and add the results to the list of found | |
1657 | printers. | |
1658 | */ | |
1659 | /* create this printer's persistent address */ | |
1660 | memset( &printer, 0, sizeof(USBPrinterInfo) ); | |
1661 | kr = (*intf)->GetLocationID(intf, &printer.location); | |
1662 | DEBUG_ERR(kr, "UsbGetAllPrinters GetLocationID %x\n"); | |
1663 | if ( kIOReturnSuccess == kr ) | |
1664 | { | |
1665 | kr = UsbLoadClassDriver( &printer, kUSBPrinterClassInterfaceID, classDriver ); | |
1666 | DEBUG_ERR(kr, "UsbGetAllPrinters UsbLoadClassDriver %x\n"); | |
1667 | if ( kIOReturnSuccess == kr && printer.classdriver ) | |
1668 | { | |
1669 | (*(printer.classdriver))->interface = intf; | |
1670 | kr = UsbGetPrinterAddress( &printer, &printer.address, 60000L ); | |
1671 | { | |
1672 | /* always unload the driver */ | |
1673 | /* but don't mask last error */ | |
1674 | kern_return_t unload_err = UsbUnloadClassDriver( &printer ); | |
1675 | if ( kIOReturnSuccess == kr ) | |
1676 | kr = unload_err; | |
1677 | } | |
1678 | } | |
1679 | } | |
1680 | ||
1681 | printerInfo = UsbCopyPrinter( &printer ); | |
1682 | if ( NULL != printerInfo ) | |
1683 | CFArrayAppendValue( printers, (const void *) printerInfo ); /* keep track of it */ | |
1684 | ||
1685 | } /* if there's a printer */ | |
1686 | kr = (*intf)->Release(intf); | |
1687 | } /* if IOCreatePlugInInterfaceForService */ | |
1688 | ||
1689 | IOObjectRelease(usbInterface); | |
1690 | usbInterface = NULL; | |
1691 | ||
1692 | } /* while there's an interface */ | |
1693 | } while ( 0 ); | |
1694 | ||
1695 | if (iter) | |
1696 | { | |
1697 | IOObjectRelease(iter); | |
1698 | iter = 0; | |
1699 | } | |
1700 | ||
1701 | if (master_device_port) | |
1702 | { | |
1703 | mach_port_deallocate(mach_task_self(), master_device_port); | |
1704 | master_device_port = 0; | |
1705 | } | |
1706 | return printers; | |
1707 | ||
1708 | } /* UsbGetAllPrinters */ | |
1709 | ||
1710 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
1711 | Method: UsbReleasePrinter | |
1712 | ||
1713 | Input Parameters: | |
1714 | ||
1715 | Output Parameters: | |
1716 | ||
1717 | Description: | |
1718 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1719 | void | |
1720 | UsbReleasePrinter( USBPrinterInfo *printer ) | |
1721 | { | |
1722 | if ( printer ) | |
1723 | { | |
1724 | UsbUnloadClassDriver( printer ); | |
1725 | if ( NULL != printer->address.manufacturer ) | |
1726 | CFRelease( printer->address.manufacturer ); | |
1727 | if ( NULL != printer->address.product ) | |
1728 | CFRelease( printer->address.product ); | |
1729 | if ( NULL != printer->address.serial ) | |
1730 | CFRelease( printer->address.serial ); | |
1731 | if ( NULL != printer->address.command ) | |
1732 | CFRelease( printer->address.command ); | |
1733 | if ( NULL != printer->bundle ) | |
1734 | CFRelease( printer->bundle ); | |
1735 | free( printer ); | |
1736 | } | |
1737 | } | |
1738 | ||
1739 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
1740 | Method: UsbReleaseAllPrinters | |
1741 | ||
1742 | Input Parameters: | |
1743 | ||
1744 | Output Parameters: | |
1745 | ||
1746 | Description: | |
1747 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
1748 | void | |
1749 | UsbReleaseAllPrinters( CFMutableArrayRef printers ) | |
1750 | { | |
1751 | if ( NULL != printers ) | |
1752 | { | |
1753 | CFIndex i, | |
1754 | numPrinters = CFArrayGetCount(printers); | |
1755 | for ( i = 0; i < numPrinters; ++i ) | |
1756 | UsbReleasePrinter( (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i ) ); | |
1757 | CFRelease( printers ); | |
1758 | } | |
1759 | } | |
1760 | ||
1761 | USBPrinterInfo * | |
1762 | UsbCopyPrinter( USBPrinterInfo *aPrinter ) | |
1763 | { | |
1764 | /* */ | |
1765 | /* note this does not copy interface information, just address information */ | |
1766 | /* */ | |
1767 | USBPrinterInfo *printerInfo = (USBPrinterInfo *) calloc( 1, sizeof(USBPrinterInfo)); | |
1768 | if ( NULL != printerInfo && NULL != aPrinter ) | |
1769 | { | |
1770 | printerInfo->location = aPrinter->location; | |
1771 | if ( NULL != (printerInfo->address.manufacturer = aPrinter->address.manufacturer) ) | |
1772 | CFRetain( printerInfo->address.manufacturer ); | |
1773 | if ( NULL != (printerInfo->address.product = aPrinter->address.product) ) | |
1774 | CFRetain( printerInfo->address.product ); | |
1775 | if ( NULL != (printerInfo->address.serial = aPrinter->address.serial) ) | |
1776 | CFRetain( printerInfo->address.serial ); | |
1777 | if ( NULL != (printerInfo->address.command = aPrinter->address.command) ) | |
1778 | CFRetain( printerInfo->address.command ); | |
1779 | if ( NULL != (printerInfo->bundle = aPrinter->bundle) ) | |
1780 | CFRetain( printerInfo->bundle ); | |
1781 | } | |
1782 | ||
1783 | return printerInfo; | |
1784 | } | |
1785 | ||
1786 | /*-----------------------------------------------------------------------------* | |
1787 | ||
1788 | UsbRegistryOpen | |
1789 | ||
1790 | Desc: opens the USB printer which matches the supplied printerAddress | |
1791 | ||
1792 | In: myContext->printerAddress persistent name which identifies the printer | |
1793 | ||
1794 | Out: myContext->usbDeviceRef current IOKit address of this printer | |
1795 | *-----------------------------------------------------------------------------*/ | |
1796 | kern_return_t | |
1797 | UsbRegistryOpen( USBPrinterAddress *usbAddress, USBPrinterInfo **result ) | |
1798 | { | |
1799 | kern_return_t kr = -1; /* indeterminate failure */ | |
1800 | CFMutableArrayRef printers = UsbGetAllPrinters(); | |
1801 | CFIndex numPrinters = NULL != printers? CFArrayGetCount( printers): 0; | |
1802 | CFIndex i; | |
1803 | ||
1804 | *result = NULL; /* nothing matched */ | |
1805 | for ( i = 0; i < numPrinters; ++i ) | |
1806 | { | |
1807 | USBPrinterInfo *thisPrinter = (USBPrinterInfo *) CFArrayGetValueAtIndex( printers, i ); | |
1808 | if ( NULL != thisPrinter && UsbSamePrinter( usbAddress, &thisPrinter->address ) ) | |
1809 | { | |
1810 | *result = UsbCopyPrinter( thisPrinter ); /* retains reference */ | |
1811 | if ( NULL != *result ) | |
1812 | { | |
1813 | /* */ | |
1814 | /* if we can't find a bi-di interface, settle for a known uni-directional interface */ | |
1815 | /* */ | |
1816 | USBPrinterClassContext **printer = NULL; | |
1817 | /* */ | |
1818 | /* setup the default class driver */ | |
1819 | /* If one is specified, allow the vendor driver to override our default implementation */ | |
1820 | /* */ | |
1821 | kr = UsbLoadClassDriver( *result, kUSBPrinterClassInterfaceID, NULL ); | |
1822 | if ( kIOReturnSuccess == kr && (*result)->bundle ) | |
1823 | kr = UsbLoadClassDriver( *result, kUSBPrinterClassInterfaceID, (*result)->bundle ); | |
1824 | if ( kIOReturnSuccess == kr && NULL != (*result)->classdriver ) | |
1825 | { | |
1826 | printer = (*result)->classdriver; | |
1827 | kr = (*printer)->Open( printer, (*result)->location, kUSBPrintingProtocolBidirectional ); | |
1828 | if ( kIOReturnSuccess != kr || NULL == (*printer)->interface ) | |
1829 | kr = (*printer)->Open( printer, (*result)->location, kUSBPrintingProtocolUnidirectional ); | |
1830 | /* it's possible kIOReturnSuccess == kr && NULL == (*printer)->interface */ | |
1831 | /* in the event that we can't open either Bidirectional or Unidirectional interface */ | |
1832 | if ( kIOReturnSuccess == kr ) | |
1833 | { | |
1834 | if ( NULL == (*printer)->interface ) | |
1835 | { | |
1836 | (*printer)->Close( printer ); | |
1837 | UsbReleasePrinter( *result ); | |
1838 | *result = NULL; | |
1839 | } | |
1840 | } | |
1841 | } | |
1842 | } | |
1843 | break; | |
1844 | } | |
1845 | } | |
1846 | UsbReleaseAllPrinters( printers ); /* but, copied printer is retained */ | |
1847 | DEBUG_ERR( kr, "UsbRegistryOpen return %x\n" ); | |
1848 | ||
1849 | return kr; | |
1850 | } | |
1851 | ||
1852 | /*! | |
1853 | * @function CreateEncodedCFString | |
1854 | * | |
1855 | * @abstract Create an encoded version of the string parameter | |
1856 | * so that it can be included in a URI. | |
1857 | * | |
1858 | * @param string A CFStringRef of the string to be encoded. | |
1859 | * @result An encoded CFString. | |
1860 | * | |
1861 | * @discussion This function will change all characters in string into URL acceptable format | |
1862 | * by encoding the text using the US-ASCII coded character set. The following | |
1863 | * are invalid characters: the octets 00-1F, 7F, and 80-FF hex. Also called out | |
1864 | * are the chars "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "[", "]", "`". | |
1865 | * The reserved characters for URL syntax are also to be encoded: (so don't pass | |
1866 | * in a full URL here!) ";", "/", "?", ":", "@", "=", "%", and "&". | |
1867 | */ | |
1868 | static CFStringRef CreateEncodedCFString(CFStringRef string) | |
1869 | { | |
1870 | CFStringRef result = NULL; | |
1871 | char *bufferUTF8 = NULL; | |
1872 | char *bufferEncoded = NULL; | |
1873 | ||
1874 | if (string != NULL) | |
1875 | { | |
1876 | CFIndex bufferSizeUTF8 = (3 * CFStringGetLength(string)); | |
1877 | if ((bufferUTF8 = (char*)malloc(bufferSizeUTF8)) != NULL) | |
1878 | { | |
1879 | CFStringGetCString(string, bufferUTF8, bufferSizeUTF8, kCFStringEncodingUTF8); | |
1880 | { | |
1881 | UInt16 bufferSizeEncoded = (3 * strlen(bufferUTF8)) + 1; | |
1882 | if ((bufferEncoded = (char*)malloc(bufferSizeEncoded)) != NULL) | |
1883 | { | |
1884 | addPercentEscapes(bufferUTF8, bufferEncoded, bufferSizeEncoded); | |
1885 | result = CFStringCreateWithCString(kCFAllocatorDefault, bufferEncoded, kCFStringEncodingUTF8); | |
1886 | } | |
1887 | } | |
1888 | } | |
1889 | } | |
1890 | ||
1891 | if (bufferUTF8) free(bufferUTF8); | |
1892 | if (bufferEncoded) free(bufferEncoded); | |
1893 | ||
1894 | return result; | |
1895 | } | |
1896 | ||
1897 | /* | |
1898 | * End of "$Id: usb-darwin.c 4548 2005-06-27 02:33:50Z mike $". | |
1899 | */ |