]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/usb-darwin.c
Remove svn:keywords since they cause svn_load_dirs.pl to complain about every file.
[thirdparty/cups.git] / backend / usb-darwin.c
1 /*
2 * "$Id: usb-darwin.c 181 2006-06-22 20:01:18Z jlovell $"
3 *
4 * © Copyright 2005-2006 Apple Computer, Inc. All rights reserved.
5 *
6 * IMPORTANT: This Apple software is supplied to you by Apple Computer,
7 * Inc. ("Apple") in consideration of your agreement to the following
8 * terms, and your use, installation, modification or redistribution of
9 * this Apple software constitutes acceptance of these terms. If you do
10 * not agree with these terms, please do not use, install, modify or
11 * redistribute this Apple software.
12 *
13 * In consideration of your agreement to abide by the following terms, and
14 * subject to these terms, Apple grants you a personal, non-exclusive
15 * license, under Apple's copyrights in this original Apple software (the
16 * "Apple Software"), to use, reproduce, modify and redistribute the Apple
17 * Software, with or without modifications, in source and/or binary forms;
18 * provided that if you redistribute the Apple Software in its entirety and
19 * without modifications, you must retain this notice and the following
20 * text and disclaimers in all such redistributions of the Apple Software.
21 * Neither the name, trademarks, service marks or logos of Apple Computer,
22 * Inc. may be used to endorse or promote products derived from the Apple
23 * Software without specific prior written permission from Apple. Except
24 * as expressly stated in this notice, no other rights or licenses, express
25 * or implied, are granted by Apple herein, including but not limited to
26 * any patent rights that may be infringed by your derivative works or by
27 * other works in which the Apple Software may be incorporated.
28 *
29 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30 * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31 * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33 * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 *
35 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39 * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40 * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41 * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
43 */
44
45 /*
46 * USB port on Darwin backend for the Common UNIX Printing System (CUPS).
47 */
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <errno.h>
51 #include <signal.h>
52 #include <fcntl.h>
53 #include <termios.h>
54 #include <unistd.h>
55 #include <sys/sysctl.h>
56 #include <libgen.h>
57 #include <mach/mach.h>
58 #include <mach/mach_error.h>
59 #include <mach/mach_time.h>
60 #include <cups/debug.h>
61
62 #include <CoreFoundation/CoreFoundation.h>
63 #include <IOKit/usb/IOUSBLib.h>
64 #include <IOKit/IOCFPlugIn.h>
65
66 #include <pthread.h>
67
68 /*
69 * WAITEOF_DELAY is number of seconds we'll wait for responses from
70 * the printer after we've finished sending all the data
71 */
72 #define WAITEOF_DELAY 7
73 #define DEFAULT_TIMEOUT 60L
74
75 #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
76 #define kUSBLanguageEnglish 0x409
77
78 #define PRINTER_POLLING_INTERVAL 5 /* seconds */
79 #define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
80 #define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
81
82 #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
83 #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
84
85 #define kUSBClassDriverProperty CFSTR("USB Printing Class")
86
87 #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
88 #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
89
90
91 #pragma mark -
92 /*
93 * Section 5.3 USB Printing Class spec
94 */
95 #define kUSBPrintingSubclass 1
96 #define kUSBPrintingProtocolNoOpen 0
97 #define kUSBPrintingProtocolUnidirectional 1
98 #define kUSBPrintingProtocolBidirectional 2
99
100 typedef IOUSBInterfaceInterface190 **printer_interface_t;
101
102 typedef struct iodevice_request_s /**** Device request ****/
103 {
104 UInt8 requestType;
105 UInt8 request;
106 UInt16 value;
107 UInt16 index;
108 UInt16 length;
109 void *buffer;
110 } iodevice_request_t;
111
112 typedef union { /**** Centronics status byte ****/
113 char b;
114 struct {
115 unsigned reserved0:2;
116 unsigned paperError:1;
117 unsigned select:1;
118 unsigned notError:1;
119 unsigned reserved1:3;
120 } status;
121 } centronics_status_t;
122
123 typedef struct classdriver_context_s /**** Classdriver context ****/
124 {
125 IUNKNOWN_C_GUTS;
126 CFPlugInRef plugin; /* release plugin */
127 IUnknownVTbl **factory; /* Factory */
128 void *vendorReference; /* vendor class specific usage */
129 UInt32 location; /* unique location in bus topology */
130 UInt8 interfaceNumber; /* Interface number */
131 UInt16 vendorID; /* Vendor id */
132 UInt16 productID; /* Product id */
133 printer_interface_t interface; /* identify the device to IOKit */
134 UInt8 outpipe; /* mandatory bulkOut pipe */
135 UInt8 inpipe; /* optional bulkIn pipe */
136
137 /* general class requests */
138 kern_return_t (*DeviceRequest)( struct classdriver_context_s **printer, iodevice_request_t *iorequest, UInt16 timeout );
139 kern_return_t (*GetString)( struct classdriver_context_s **printer, UInt8 whichString, UInt16 language, UInt16 timeout, CFStringRef *result );
140
141 /* standard printer class requests */
142 kern_return_t (*SoftReset)( struct classdriver_context_s **printer, UInt16 timeout );
143 kern_return_t (*GetCentronicsStatus)( struct classdriver_context_s **printer, centronics_status_t *result, UInt16 timeout );
144 kern_return_t (*GetDeviceID)( struct classdriver_context_s **printer, CFStringRef *devid, UInt16 timeout );
145
146 /* standard bulk device requests */
147 kern_return_t (*ReadPipe)( struct classdriver_context_s **printer, UInt8 *buffer, UInt32 *count );
148 kern_return_t (*WritePipe)( struct classdriver_context_s **printer, UInt8 *buffer, UInt32 *count, Boolean eoj );
149
150 /* interface requests */
151 kern_return_t (*Open)( struct classdriver_context_s **printer, UInt32 location, UInt8 protocol );
152 kern_return_t (*Abort)( struct classdriver_context_s **printer );
153 kern_return_t (*Close)( struct classdriver_context_s **printer );
154
155 /* initialize and terminate */
156 kern_return_t (*Initialize)( struct classdriver_context_s **printer, struct classdriver_context_s **baseclass );
157 kern_return_t (*Terminate)( struct classdriver_context_s **printer );
158
159 } classdriver_context_t;
160
161
162 typedef Boolean (*iterator_callback_t)(void *refcon, io_service_t obj);
163
164 typedef struct iterator_reference_s { /**** Iterator reference data */
165 iterator_callback_t callback;
166 void *userdata;
167 Boolean keepRunning;
168 } iterator_reference_t;
169
170 typedef struct printer_data_s { /**** Printer context data ****/
171 io_service_t printerObj;
172 classdriver_context_t **printerDriver;
173
174 pthread_cond_t readCompleteCondition;
175 pthread_mutex_t readMutex;
176 int done;
177
178 const char *uri;
179 CFStringRef make;
180 CFStringRef model;
181 CFStringRef serial;
182
183 UInt32 location;
184 Boolean waitEOF;
185
186 } printer_data_t;
187
188
189 /*
190 * Local functions...
191 */
192
193 static Boolean list_device_callback(void *refcon, io_service_t obj);
194 static Boolean find_device_callback(void *refcon, io_service_t obj);
195 static void iterate_printers(iterator_callback_t callBack, void *userdata);
196 static void device_added(void *userdata, io_iterator_t iterator);
197 static void copy_deviceinfo(CFStringRef deviceIDString, CFStringRef *make, CFStringRef *model, CFStringRef *serial);
198 static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial);
199 static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t intf, classdriver_context_t ***driver);
200 static kern_return_t unload_classdriver(classdriver_context_t ***classDriver);
201 static kern_return_t load_printerdriver(printer_data_t *printer);
202 static kern_return_t registry_open(printer_data_t *printer);
203 static kern_return_t registry_close(printer_data_t *printer);
204 static OSStatus copy_deviceid(classdriver_context_t **printer, CFStringRef *deviceID);
205 static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation);
206 static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys);
207 static void parse_options(const char *options, char *serial, UInt32 *location, Boolean *waitEOF);
208 static void setup_cfLanguage(void);
209 static void *read_thread(void *reference);
210
211
212 #if defined(__i386__)
213 static pid_t child_pid; /* Child PID */
214 static void run_ppc_backend(int argc, char *argv[], int fd); /* Starts child backend process running as a ppc executable */
215 static void sigterm_handler(int sig); /* SIGTERM handler */
216 #endif /* __i386__ */
217
218 #ifdef PARSE_PS_ERRORS
219 static const char *next_line (const char *buffer);
220 static void parse_pserror (char *sockBuffer, int len);
221 #endif /* PARSE_PS_ERRORS */
222
223 #pragma mark -
224
225 /*
226 * 'list_devices()' - List all USB devices.
227 */
228
229 void list_devices()
230 {
231 iterate_printers(list_device_callback, NULL);
232 }
233
234
235 /*
236 * 'print_device()' - Print a file to a USB device.
237 */
238
239 int /* O - Exit status */
240 print_device(const char *uri, /* I - Device URI */
241 const char *hostname, /* I - Hostname/manufacturer */
242 const char *resource, /* I - Resource/modelname */
243 const char *options, /* I - Device options/serial number */
244 int fd, /* I - File descriptor to print */
245 int copies, /* I - Copies to print */
246 int argc, /* I - Number of command-line arguments (6 or 7) */
247 char *argv[]) /* I - Command-line arguments */
248 {
249 printer_data_t printer_data = { 0x0 }; /* Printer context */
250 char serial[1024]; /* Serial number buffer */
251 OSStatus status = noErr; /* Function results */
252 pthread_t thr; /* Read thread */
253 char buffer[2048]; /* Write buffer */
254 int thread_created = 0; /* Thread created? */
255 int countdown = INITIAL_LOG_INTERVAL; /* Logging interval */
256 pthread_cond_t *readCompleteConditionPtr = NULL; /* Read complete condition */
257 pthread_mutex_t *readMutexPtr = NULL; /* Read mutex */
258
259 setup_cfLanguage();
260 parse_options(options, serial, &printer_data.location, &printer_data.waitEOF);
261
262 if (resource[0] == '/')
263 resource++;
264
265 printer_data.uri = uri;
266 printer_data.make = CFStringCreateWithCString(NULL, hostname, kCFStringEncodingUTF8);
267 printer_data.model = CFStringCreateWithCString(NULL, resource, kCFStringEncodingUTF8);
268 printer_data.serial = CFStringCreateWithCString(NULL, serial, kCFStringEncodingUTF8);
269
270 fputs("STATE: +connecting-to-device\n", stderr);
271
272 do {
273 if (printer_data.printerObj != 0x0) {
274 IOObjectRelease(printer_data.printerObj);
275 unload_classdriver(&printer_data.printerDriver);
276 printer_data.printerObj = 0x0;
277 printer_data.printerDriver = 0x0;
278 }
279
280 fprintf(stderr, "INFO: Looking for '%s %s'\n", hostname, resource);
281 iterate_printers(find_device_callback, &printer_data);
282
283 fprintf(stderr, "INFO: Opening Connection\n");
284 status = registry_open(&printer_data);
285 #if defined(__i386__)
286 /*
287 * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
288 * In this case try to fork & exec this backend as a ppc executable so we can use them...
289 */
290 if (status == -2 /* kPMInvalidIOMContext */) {
291 run_ppc_backend(argc, argv, fd);
292 /* Never returns here */
293 }
294 #endif /* __i386__ */
295
296 if (status != noErr) {
297 sleep( PRINTER_POLLING_INTERVAL );
298 countdown -= PRINTER_POLLING_INTERVAL;
299 if ( countdown <= 0 ) {
300 fprintf(stderr, "INFO: Printer busy (status:0x%08x)\n", (int)status);
301 countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 15 seconds */
302 }
303 }
304 } while (status != noErr);
305
306 fputs("STATE: -connecting-to-device\n", stderr);
307
308 /*
309 * Now that we are "connected" to the port, ignore SIGTERM so that we
310 * can finish out any page data the driver sends (e.g. to eject the
311 * current page... Only ignore SIGTERM if we are printing data from
312 * stdin (otherwise you can't cancel raw jobs...)
313 */
314
315 if (!fd) {
316 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
317 sigset(SIGTERM, SIG_IGN);
318 #elif defined(HAVE_SIGACTION)
319 memset(&action, 0, sizeof(action));
320
321 sigemptyset(&action.sa_mask);
322 action.sa_handler = SIG_IGN;
323 sigaction(SIGTERM, &action, NULL);
324 #else
325 signal(SIGTERM, SIG_IGN);
326 #endif /* HAVE_SIGSET */
327 }
328
329 if (status == noErr) {
330 if (pthread_cond_init(&printer_data.readCompleteCondition, NULL) == 0)
331 readCompleteConditionPtr = &printer_data.readCompleteCondition;
332
333 if (pthread_mutex_init(&printer_data.readMutex, NULL) == 0)
334 readMutexPtr = &printer_data.readMutex;
335
336 if (pthread_create(&thr, NULL, read_thread, &printer_data) == 0)
337 thread_created = 1;
338
339 if (thread_created == 0)
340 fprintf(stderr, "WARNING: Couldn't create read channel\n");
341 }
342
343 /*
344 * The main thread sends the print file...
345 */
346
347 while (status == noErr && copies-- > 0) {
348 UInt32 wbytes; /* Number of bytes written */
349 ssize_t nbytes; /* Number of bytes read */
350 off_t tbytes = 0; /* Total number of bytes written */
351
352 fprintf(stderr, "INFO: Sending data\n");
353
354 if (STDIN_FILENO != fd) {
355 fputs("PAGE: 1 1", stderr);
356 lseek( fd, 0, SEEK_SET );
357 }
358
359 while (status == noErr && (nbytes = read(fd, buffer, sizeof(buffer))) > 0) {
360 char *bufptr = buffer;
361 tbytes += nbytes;
362
363 while (nbytes > 0 && status == noErr) {
364 wbytes = nbytes;
365 status = (*(printer_data.printerDriver))->WritePipe( printer_data.printerDriver, (UInt8*)bufptr, &wbytes, 0 /* nbytes > wbytes? 0: feof(fp) */ );
366 if (wbytes < 0 || noErr != status) {
367 OSStatus err = (*(printer_data.printerDriver))->Abort(printer_data.printerDriver);
368 fprintf(stderr, "ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n", status, err);
369 break;
370 }
371
372 nbytes -= wbytes;
373 bufptr += wbytes;
374 }
375
376 if (fd != 0 && status == noErr)
377 fprintf(stderr, "DEBUG: Sending print file, %qd bytes...\n", (off_t)tbytes);
378 }
379 }
380
381 if (thread_created) {
382 /* Signal the read thread that we are done... */
383 printer_data.done = 1;
384
385 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
386 * we are not signaled in that time then force the thread to exit by setting
387 * the waiteof to be false. Plese note that this relies on us using the timeout
388 * class driver.
389 */
390 struct timespec sleepUntil = { time(NULL) + WAITEOF_DELAY, 0 };
391 pthread_mutex_lock(&printer_data.readMutex);
392 if (pthread_cond_timedwait(&printer_data.readCompleteCondition, &printer_data.readMutex, (const struct timespec *)&sleepUntil) != 0)
393 printer_data.waitEOF = false;
394 pthread_mutex_unlock(&printer_data.readMutex);
395 pthread_join( thr,NULL); /* wait for the child thread to return */
396 }
397
398 /*
399 * Close the connection and input file and general clean up...
400 */
401 registry_close(&printer_data);
402
403 if (STDIN_FILENO != fd)
404 close(fd);
405
406 if (readCompleteConditionPtr != NULL)
407 pthread_cond_destroy(&printer_data.readCompleteCondition);
408
409 if (readMutexPtr != NULL)
410 pthread_mutex_destroy(&printer_data.readMutex);
411
412 if (printer_data.make != NULL)
413 CFRelease(printer_data.make);
414
415 if (printer_data.model != NULL)
416 CFRelease(printer_data.model);
417
418 if (printer_data.serial != NULL)
419 CFRelease(printer_data.serial);
420
421 if (printer_data.printerObj != 0x0)
422 IOObjectRelease(printer_data.printerObj);
423
424 return status;
425 }
426
427 #pragma mark -
428 /*
429 * 'list_device_callback()' - list_device iterator callback.
430 */
431
432 static Boolean list_device_callback(void *refcon, io_service_t obj)
433 {
434 Boolean keepRunning = (obj != 0x0);
435
436 if (keepRunning) {
437 CFStringRef deviceIDString = NULL;
438 UInt32 deviceLocation = 0;
439
440 copy_devicestring(obj, &deviceIDString, &deviceLocation);
441 if (deviceIDString != NULL) {
442 CFStringRef make = NULL, model = NULL, serial = NULL;
443 char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024], optionsstr[1024];
444 char idstr[1024];
445
446 copy_deviceinfo(deviceIDString, &make, &model, &serial);
447
448 modelstr[0] = '/';
449
450 CFStringGetCString(deviceIDString, idstr, sizeof(idstr), kCFStringEncodingUTF8);
451 CFStringGetCString(make, makestr, sizeof(makestr), kCFStringEncodingUTF8);
452 CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1, kCFStringEncodingUTF8);
453
454 /*
455 * Fix common HP 1284 bug...
456 */
457
458 if (!strcasecmp(makestr, "Hewlett-Packard"))
459 strcpy(makestr, "HP");
460
461 if (!strncasecmp(modelstr + 1, "hp ", 3))
462 _cups_strcpy(modelstr + 1, modelstr + 4);
463
464 optionsstr[0] = '\0';
465 if (serial != NULL)
466 {
467 CFStringGetCString(serial, serialstr, sizeof(serialstr), kCFStringEncodingUTF8);
468 snprintf(optionsstr, sizeof(optionsstr), "?serial=%s", serialstr);
469 }
470 else if (deviceLocation != 0)
471 {
472 snprintf(optionsstr, sizeof(optionsstr), "?location=%lx", deviceLocation);
473 }
474
475 httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
476 strncat(uristr, optionsstr, sizeof(uristr));
477
478 printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr, makestr,
479 &modelstr[1], makestr, &modelstr[1], idstr);
480
481 release_deviceinfo(&make, &model, &serial);
482 CFRelease(deviceIDString);
483 }
484 }
485
486 return keepRunning;
487 }
488
489
490 /*
491 * 'find_device_callback()' - print_device iterator callback.
492 */
493
494 static Boolean find_device_callback(void *refcon, io_service_t obj)
495 {
496 Boolean keepLooking = true;
497
498 if (obj != 0x0 && refcon != NULL) {
499 CFStringRef idString = NULL;
500 UInt32 location = -1;
501 printer_data_t *userData = (printer_data_t *)refcon;
502
503 copy_devicestring(obj, &idString, &location);
504 if (idString != NULL) {
505 CFStringRef make = NULL, model = NULL, serial = NULL;
506
507 copy_deviceinfo(idString, &make, &model, &serial);
508 if (CFStringCompare(make, userData->make, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
509 if (CFStringCompare(model, userData->model, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
510 if (userData->serial != NULL) {
511 if (serial != NULL && CFStringCompare(model, userData->model, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
512 IOObjectRetain(obj);
513 userData->printerObj = obj;
514 keepLooking = false;
515 }
516 }
517 else {
518 if (userData->printerObj != 0) {
519 IOObjectRetain(userData->printerObj);
520 }
521 userData->printerObj = obj;
522 IOObjectRetain(obj);
523
524 if (userData->location == 0 || userData->location == location) {
525 keepLooking = false;
526 }
527 }
528 }
529 }
530
531 release_deviceinfo(&make, &model, &serial);
532 CFRelease(idString);
533 }
534 }
535 else {
536 keepLooking = (refcon != NULL && ((printer_data_t *)refcon)->printerObj == 0);
537 }
538
539 return keepLooking;
540 }
541
542
543 #pragma mark -
544 /*
545 * 'iterate_printers()' - iterate over all the printers.
546 */
547
548 static void iterate_printers(iterator_callback_t callBack, void *userdata)
549 {
550 mach_port_t masterPort = 0x0;
551 kern_return_t kr = IOMasterPort (bootstrap_port, &masterPort);
552
553 if (kr == kIOReturnSuccess && masterPort != 0x0) {
554 io_iterator_t addIterator = 0x0;
555
556 iterator_reference_t reference = { callBack, userdata, true };
557 IONotificationPortRef addNotification = IONotificationPortCreate(masterPort);
558
559 int klass = kUSBPrintingClass;
560 int subklass = kUSBPrintingSubclass;
561
562 CFNumberRef usb_klass = CFNumberCreate(NULL, kCFNumberIntType, &klass);
563 CFNumberRef usb_subklass = CFNumberCreate(NULL, kCFNumberIntType, &subklass);
564 CFMutableDictionaryRef usbPrinterMatchDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
565
566 CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceClass"), usb_klass);
567 CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceSubClass"), usb_subklass);
568
569 CFRelease(usb_klass);
570 CFRelease(usb_subklass);
571
572 kr = IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification, usbPrinterMatchDictionary, &device_added, &reference, &addIterator);
573 if (addIterator != 0x0) {
574 device_added (&reference, addIterator);
575
576 if (reference.keepRunning) {
577 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode);
578 CFRunLoopRun();
579 }
580 IOObjectRelease(addIterator);
581 }
582 mach_port_deallocate(mach_task_self(), masterPort);
583 }
584 }
585
586
587 /*
588 * 'device_added()' - device added notifier.
589 */
590
591 static void device_added(void *userdata, io_iterator_t iterator)
592 {
593 iterator_reference_t *reference = userdata;
594
595 io_service_t obj;
596 while (reference->keepRunning && (obj = IOIteratorNext(iterator)) != 0x0) {
597 if (reference->callback != NULL) {
598 reference->keepRunning = reference->callback(reference->userdata, obj);
599 }
600 IOObjectRelease(obj);
601 }
602
603 /* One last call to the call back now that we are not longer have printers left to iterate...
604 */
605 if (reference->keepRunning)
606 reference->keepRunning = reference->callback(reference->userdata, 0x0);
607
608 if (!reference->keepRunning) {
609 CFRunLoopStop(CFRunLoopGetCurrent());
610 }
611 }
612
613
614 #pragma mark -
615 /*
616 * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
617 */
618
619 static void copy_deviceinfo(CFStringRef deviceIDString, CFStringRef *make, CFStringRef *model, CFStringRef *serial)
620 {
621 CFStringRef modelKeys[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL };
622 CFStringRef makeKeys[] = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL };
623 CFStringRef serialKeys[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL };
624
625 if (make != NULL)
626 *make = copy_value_for_key(deviceIDString, makeKeys);
627 if (model != NULL)
628 *model = copy_value_for_key(deviceIDString, modelKeys);
629 if (serial != NULL)
630 *serial = copy_value_for_key(deviceIDString, serialKeys);
631 }
632
633
634 /*
635 * 'release_deviceinfo()' - Release deviceinfo strings.
636 */
637
638 static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial)
639 {
640 if (make != NULL && *make != NULL) {
641 CFRelease(*make);
642 *make = NULL;
643 }
644
645 if (model != NULL && *model != NULL) {
646 CFRelease(*model);
647 *model = NULL;
648 }
649
650 if (serial != NULL && *serial != NULL) {
651 CFRelease(*serial);
652 *serial = NULL;
653 }
654 }
655
656
657 #pragma mark -
658 /*
659 * 'load_classdriver()' - Load a classdriver.
660 */
661
662 static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t intf, classdriver_context_t ***printerDriver)
663 {
664 kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
665 classdriver_context_t **driver = NULL;
666 CFStringRef bundle = (driverPath == NULL ? kUSBGenericTOPrinterClassDriver : driverPath);
667
668 if ( NULL != bundle ) {
669 CFURLRef url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
670 CFPlugInRef plugin = (url != NULL ? CFPlugInCreate(NULL, url) : NULL);
671
672 if (url != NULL)
673 CFRelease(url);
674
675 if (plugin != NULL) {
676 CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
677 if (factories != NULL && CFArrayGetCount(factories) > 0) {
678 CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
679 IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
680 if (NULL != iunknown) {
681 kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
682 if (kr == kIOReturnSuccess && driver != NULL) {
683 classdriver_context_t **genericDriver = NULL;
684 if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo) {
685 kr = load_classdriver(NULL, intf, &genericDriver);
686 }
687
688 if (kr == kIOReturnSuccess) {
689 (*driver)->interface = intf;
690 (*driver)->Initialize(driver, genericDriver);
691
692 (*driver)->plugin = plugin;
693 (*driver)->interface = intf;
694 *printerDriver = driver;
695 }
696 }
697 (*iunknown)->Release(iunknown);
698 }
699 CFRelease(factories);
700 }
701 }
702 }
703
704 #ifdef DEBUG
705 char bundlestr[1024];
706 CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
707 fprintf(stderr, "DEBUG:load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
708 #endif /* DEBUG */
709
710 return kr;
711 }
712
713
714 /*
715 * 'unload_classdriver()' - Unload a classdriver.
716 */
717
718 static kern_return_t unload_classdriver(classdriver_context_t ***classDriver)
719 {
720 if (*classDriver != NULL) {
721 (**classDriver)->Release(*classDriver);
722 *classDriver = NULL;
723 }
724
725 return kIOReturnSuccess;
726 }
727
728
729 /*
730 * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
731 */
732
733 static kern_return_t load_printerdriver(printer_data_t *printer)
734 {
735 IOCFPlugInInterface **iodev = NULL;
736 SInt32 score;
737
738 kern_return_t kr = IOCreatePlugInInterfaceForService(printer->printerObj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
739 if (kr == kIOReturnSuccess) {
740 printer_interface_t intf;
741 HRESULT res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
742 if (res == noErr) {
743 CFMutableDictionaryRef properties = NULL;
744
745 kr = IORegistryEntryCreateCFProperties(printer->printerObj, &properties, NULL, kNilOptions);
746 if (kr == kIOReturnSuccess) {
747 CFStringRef driverBundlePath = NULL;
748 if (properties != NULL) {
749 driverBundlePath = (CFStringRef) CFDictionaryGetValue(properties, kUSBClassDriverProperty);
750 }
751 kr = load_classdriver(driverBundlePath, intf, &printer->printerDriver);
752 }
753
754 if (kr != kIOReturnSuccess)
755 (*intf)->Release(intf);
756 }
757 IODestroyPlugInInterface(iodev);
758 }
759 return kr;
760 }
761
762
763 /*
764 * 'registry_open()' - Open a connection to the printer.
765 */
766
767 static kern_return_t registry_open(printer_data_t *printer)
768 {
769 kern_return_t kr = load_printerdriver(printer);
770 if (kr != kIOReturnSuccess) {
771 kr = -2;
772 }
773
774 if (printer->printerDriver != NULL) {
775 kr = (*(printer->printerDriver))->Open(printer->printerDriver, printer->location, kUSBPrintingProtocolBidirectional);
776 if (kr != kIOReturnSuccess || (*(printer->printerDriver))->interface == NULL) {
777 kr = (*(printer->printerDriver))->Open(printer->printerDriver, printer->location, kUSBPrintingProtocolUnidirectional);
778 if (kr == kIOReturnSuccess) {
779 if ((*(printer->printerDriver))->interface == NULL) {
780 (*(printer->printerDriver))->Close(printer->printerDriver);
781 kr = -1;
782 }
783 }
784 }
785 }
786
787 if (kr != kIOReturnSuccess) {
788 unload_classdriver(&printer->printerDriver);
789 }
790
791 return kr;
792 }
793
794
795 /*
796 * 'registry_close()' - Close the connection to the printer.
797 */
798
799 static kern_return_t registry_close(printer_data_t *printer)
800 {
801 if (printer->printerDriver != NULL) {
802 (*(printer->printerDriver))->Close(printer->printerDriver);
803 }
804 unload_classdriver(&printer->printerDriver);
805 return kIOReturnSuccess;
806 }
807
808
809 /*
810 * 'copy_deviceid()' - Copy the 1284 device id string.
811 */
812
813 static OSStatus copy_deviceid(classdriver_context_t **printer, CFStringRef *deviceID)
814 {
815 CFStringRef devID = NULL,
816
817 deviceMake = NULL,
818 deviceModel = NULL,
819 deviceSerial = NULL;
820
821 OSStatus err = (*printer)->GetDeviceID(printer, &devID, DEFAULT_TIMEOUT);
822
823 copy_deviceinfo(devID, &deviceMake, &deviceModel, &deviceSerial);
824
825 if (deviceMake == NULL || deviceModel == NULL || deviceSerial == NULL) {
826 IOUSBDeviceDescriptor desc;
827 iodevice_request_t request;
828
829 request.requestType = USBmakebmRequestType( kUSBIn, kUSBStandard, kUSBDevice );
830 request.request = kUSBRqGetDescriptor;
831 request.value = (kUSBDeviceDesc << 8) | 0;
832 request.index = 0;
833 request.length = sizeof(desc);
834 request.buffer = &desc;
835 err = (*printer)->DeviceRequest(printer, &request, DEFAULT_TIMEOUT);
836 if (err == kIOReturnSuccess) {
837 CFMutableStringRef newDevID = CFStringCreateMutable(NULL, 0);
838
839 if (deviceMake == NULL) {
840 CFStringRef data = NULL;
841 err = (*printer)->GetString(printer, desc.iManufacturer, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
842 if (data != NULL) {
843 CFStringAppendFormat(newDevID, NULL, CFSTR("MFG:%@;"), data);
844 CFRelease(data);
845 }
846 }
847
848 if (deviceModel == NULL) {
849 CFStringRef data = NULL;
850 err = (*printer)->GetString(printer, desc.iProduct, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
851 if (data != NULL) {
852 CFStringAppendFormat(newDevID, NULL, CFSTR("MDL:%@;"), data);
853 CFRelease(data);
854 }
855 }
856
857 if (deviceSerial == NULL && desc.iSerialNumber != 0) {
858 CFStringRef data = NULL;
859 err = (*printer)->GetString(printer, desc.iSerialNumber, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
860 if (data != NULL) {
861 CFStringAppendFormat(newDevID, NULL, CFSTR("SERN:%@;"), data);
862 CFRelease(data);
863 }
864 }
865
866 if (devID != NULL) {
867 CFStringAppend(newDevID, devID);
868 CFRelease(devID);
869 }
870
871 *deviceID = newDevID;
872 }
873 }
874 else {
875 *deviceID = devID;
876 }
877 release_deviceinfo(&deviceMake, &deviceModel, &deviceSerial);
878
879 return err;
880 }
881
882
883 /*
884 * 'copy_devicestring()' - Copy the 1284 device id string.
885 */
886
887 static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation)
888 {
889 IOCFPlugInInterface **iodev = NULL;
890 SInt32 score;
891
892 kern_return_t kr = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID,
893 kIOCFPlugInInterfaceID, &iodev, &score);
894 if (kr == kIOReturnSuccess) {
895 printer_interface_t intf;
896
897 HRESULT res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf);
898 if (res == noErr) {
899 /* ignore the result for location id... */
900 (void)(*intf)->GetLocationID(intf, deviceLocation);
901
902 CFMutableDictionaryRef properties = NULL;
903 kr = IORegistryEntryCreateCFProperties(usbInterface, &properties, NULL, kNilOptions);
904 if (kIOReturnSuccess == kr) {
905 classdriver_context_t **klassDriver = NULL;
906 CFStringRef driverBundlePath = NULL;
907
908 if (properties != NULL) {
909 driverBundlePath = (CFStringRef) CFDictionaryGetValue(properties, kUSBClassDriverProperty);
910 }
911
912 kr = load_classdriver(driverBundlePath, intf, &klassDriver);
913 if (kr != kIOReturnSuccess && driverBundlePath != NULL)
914 kr = load_classdriver(NULL, intf, &klassDriver);
915 if (kr == kIOReturnSuccess && klassDriver != NULL) {
916 kr = copy_deviceid(klassDriver, deviceID);
917 }
918 unload_classdriver(&klassDriver);
919
920 if (properties != NULL)
921 CFRelease(properties);
922 }
923
924 /* (*intf)->Release(intf); */
925 }
926 IODestroyPlugInInterface(iodev);
927 }
928 }
929
930
931 #pragma mark -
932 /*
933 * 'copy_value_for_key()' - Copy value string associated with a key.
934 */
935
936 static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys)
937 {
938 CFStringRef value = NULL;
939 CFArrayRef kvPairs = deviceID != NULL ? CFStringCreateArrayBySeparatingStrings(NULL, deviceID, CFSTR(";")) : NULL;
940 CFIndex max = kvPairs != NULL ? CFArrayGetCount(kvPairs) : 0;
941 CFIndex idx = 0;
942
943 while (idx < max && value == NULL) {
944 CFStringRef kvpair = CFArrayGetValueAtIndex(kvPairs, idx);
945 CFIndex idxx = 0;
946 while (keys[idxx] != NULL && value == NULL) {
947 CFRange range = CFStringFind(kvpair, keys[idxx], kCFCompareCaseInsensitive);
948 if (range.length != -1) {
949 if (range.location != 0) {
950 CFMutableStringRef theString = CFStringCreateMutableCopy(NULL, 0, kvpair);
951 CFStringTrimWhitespace(theString);
952 range = CFStringFind(theString, keys[idxx], kCFCompareCaseInsensitive);
953 if (range.location == 0) {
954 value = CFStringCreateWithSubstring(NULL, theString, CFRangeMake(range.length, CFStringGetLength(theString) - range.length));
955 }
956 CFRelease(theString);
957 }
958 else {
959 CFStringRef theString = CFStringCreateWithSubstring(NULL, kvpair, CFRangeMake(range.length, CFStringGetLength(kvpair) - range.length));
960 CFMutableStringRef theString2 = CFStringCreateMutableCopy(NULL, 0, theString);
961 CFRelease(theString);
962
963 CFStringTrimWhitespace(theString2);
964 value = theString2;
965 }
966 }
967 idxx++;
968 }
969 idx++;
970 }
971
972 if (kvPairs != NULL)
973 CFRelease(kvPairs);
974 return value;
975 }
976
977
978 #pragma mark -
979 /*
980 * 'parse_options()' - Parse uri options.
981 */
982
983 static void parse_options(const char *options, char *serial, UInt32 *location, Boolean *waitEOF)
984 {
985 char *serialnumber; /* ?serial=<serial> or ?location=<location> */
986 char optionName[255], /* Name of option */
987 value[255], /* Value of option */
988 *ptr; /* Pointer into name or value */
989
990 if (serial)
991 *serial = '\0';
992 if (location)
993 *location = 0;
994
995 if (!options)
996 return;
997
998 serialnumber = NULL;
999
1000 while (*options != '\0') {
1001 /* Get the name... */
1002 for (ptr = optionName; *options && *options != '=' && *options != '+'; )
1003 *ptr++ = *options++;
1004
1005 *ptr = '\0';
1006 value[0] = '\0';
1007
1008 if (*options == '=') {
1009 /* Get the value... */
1010 options ++;
1011
1012 for (ptr = value; *options && *options != '+';)
1013 *ptr++ = *options++;
1014
1015 *ptr = '\0';
1016
1017 if (*options == '+')
1018 options ++;
1019 }
1020 else if (*options == '+') {
1021 options ++;
1022 }
1023
1024 /*
1025 * Process the option...
1026 */
1027 if (strcasecmp(optionName, "waiteof") == 0) {
1028 if (strcasecmp(value, "on") == 0 ||
1029 strcasecmp(value, "yes") == 0 ||
1030 strcasecmp(value, "true") == 0) {
1031 *waitEOF = true;
1032 }
1033 else if (strcasecmp(value, "off") == 0 ||
1034 strcasecmp(value, "no") == 0 ||
1035 strcasecmp(value, "false") == 0) {
1036 *waitEOF = false;
1037 }
1038 else {
1039 fprintf(stderr, "WARNING: Boolean expected for waiteof option \"%s\"\n", value);
1040 }
1041 }
1042 else if (strcasecmp(optionName, "serial") == 0) {
1043 strcpy(serial, value);
1044 serialnumber = serial;
1045 }
1046 else if (strcasecmp(optionName, "location") == 0 && location) {
1047 *location = strtol(value, NULL, 16);
1048 }
1049 }
1050
1051 return;
1052 }
1053
1054
1055 /*!
1056 * @function setup_cfLanguage
1057 * @abstract Convert the contents of the CUPS 'LANG' environment
1058 * variable into a one element CF array of languages.
1059 *
1060 * @discussion Each submitted job comes with a natural language. CUPS passes
1061 * that language in an environment variable. We take that language
1062 * and jam it into the AppleLanguages array so that CF will use
1063 * it when reading localized resources. We need to do this before
1064 * any CF code reads and caches the languages array, so this function
1065 * should be called early in main()
1066 */
1067 static void setup_cfLanguage(void)
1068 {
1069 CFStringRef lang[1] = {NULL};
1070 CFArrayRef langArray = NULL;
1071 const char *requestedLang = NULL;
1072
1073 requestedLang = getenv("LANG");
1074 if (requestedLang != NULL) {
1075 lang[0] = CFStringCreateWithCString(kCFAllocatorDefault, requestedLang, kCFStringEncodingUTF8);
1076 langArray = CFArrayCreate(kCFAllocatorDefault, (const void **)lang, sizeof(lang) / sizeof(lang[0]), &kCFTypeArrayCallBacks);
1077
1078 CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication);
1079 DEBUG_printf((stderr, "DEBUG: usb: AppleLanguages = \"%s\"\n", requestedLang));
1080
1081 CFRelease(lang[0]);
1082 CFRelease(langArray);
1083 } else {
1084 fprintf(stderr, "DEBUG: usb: LANG environment variable missing.\n");
1085 }
1086 }
1087
1088 #pragma mark -
1089 #if defined(__i386__)
1090 /*!
1091 * @function run_ppc_backend
1092 *
1093 * @abstract Starts child backend process running as a ppc executable.
1094 *
1095 * @result Never returns; always calls exit().
1096 *
1097 * @discussion
1098 */
1099 static void run_ppc_backend(int argc, char *argv[], int fd)
1100 {
1101 int i;
1102 int exitstatus = 0;
1103 int childstatus;
1104 pid_t waitpid_status;
1105 char *my_argv[32];
1106 char *usb_ppc_status;
1107
1108 /*
1109 * If we're running as i386 and couldn't load the class driver (because they'it's
1110 * ppc-only) then try to re-exec ourselves in ppc mode to try again. If we don't have
1111 * a ppc architecture we may be running i386 again so guard against this by setting
1112 * and testing an environment variable...
1113 */
1114 usb_ppc_status = getenv("USB_PPC_STATUS");
1115
1116 if (usb_ppc_status == NULL) {
1117 /* Catch SIGTERM if we are _not_ printing data from
1118 * stdin (otherwise you can't cancel raw jobs...)
1119 */
1120
1121 if (fd != 0) {
1122 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1123 sigset(SIGTERM, sigterm_handler);
1124 #elif defined(HAVE_SIGACTION)
1125 struct sigaction action; /* Actions for POSIX signals */
1126 memset(&action, 0, sizeof(action));
1127 sigaddset(&action.sa_mask, SIGTERM);
1128 action.sa_handler = sigterm_handler;
1129 sigaction(SIGTERM, &action, NULL);
1130 #else
1131 signal(SIGTERM, sigterm_handler);
1132 #endif /* HAVE_SIGSET */
1133 }
1134
1135 if ((child_pid = fork()) == 0) {
1136 /* Child comes here. */
1137 setenv("USB_PPC_STATUS", "1", false);
1138
1139 /* Tell the kernel we want the next exec call to favor the ppc architecture... */
1140 int mib[] = { CTL_KERN, KERN_AFFINITY, 1, 1 };
1141 int namelen = 4;
1142 sysctl(mib, namelen, NULL, NULL, NULL, 0);
1143
1144 /* Set up the arguments and call exec... */
1145 for (i = 0; i < argc && i < (sizeof(my_argv)/sizeof(my_argv[0])) - 1; i++)
1146 my_argv[i] = argv[i];
1147
1148 my_argv[i] = NULL;
1149
1150 execv("/usr/libexec/cups/backend/usb", my_argv);
1151
1152 fprintf(stderr, "DEBUG: execv: %s\n", strerror(errno));
1153 exitstatus = errno;
1154 }
1155 else if (child_pid > 0) {
1156 /* Parent comes here.
1157 *
1158 * Close the fds we won't be using then wait for the child backend to exit.
1159 */
1160 close(fd);
1161 close(1);
1162
1163 fprintf(stderr, "DEBUG: Started usb(ppc) backend (PID %d)\n", (int)child_pid);
1164
1165 while ((waitpid_status = waitpid(child_pid, &childstatus, 0)) == (pid_t)-1 && errno == EINTR)
1166 usleep(1000);
1167
1168 if (WIFSIGNALED(childstatus)) {
1169 exitstatus = WTERMSIG(childstatus);
1170 fprintf(stderr, "DEBUG: usb(ppc) backend %d crashed on signal %d!\n", child_pid, exitstatus);
1171 }
1172 else {
1173 if ((exitstatus = WEXITSTATUS(childstatus)) != 0)
1174 fprintf(stderr, "DEBUG: usb(ppc) backend %d stopped with status %d!\n", child_pid, exitstatus);
1175 else
1176 fprintf(stderr, "DEBUG: PID %d exited with no errors\n", child_pid);
1177 }
1178 }
1179 else {
1180 /* fork() error */
1181 fprintf(stderr, "DEBUG: fork: %s\n", strerror(errno));
1182 exitstatus = errno;
1183 }
1184 }
1185 else {
1186 fprintf(stderr, "DEBUG: usb child running i386 again\n");
1187 exitstatus = ENOENT;
1188 }
1189
1190 exit(exitstatus);
1191 }
1192
1193 /*
1194 * 'sigterm_handler()' - SIGTERM handler.
1195 */
1196
1197 static void sigterm_handler(int sig)
1198 {
1199 /* If we started a child process pass the signal on to it...
1200 */
1201 if (child_pid)
1202 kill(child_pid, sig);
1203
1204 exit(1);
1205 }
1206
1207 #endif /* __i386__ */
1208
1209
1210 #ifdef PARSE_PS_ERRORS
1211 /*
1212 * 'next_line()' - Find the next line in a buffer.
1213 */
1214
1215 static const char *next_line (const char *buffer)
1216 {
1217 const char *cptr, *lptr = NULL;
1218
1219 for (cptr = buffer; *cptr && lptr == NULL; cptr++)
1220 if (*cptr == '\n' || *cptr == '\r')
1221 lptr = cptr;
1222 return lptr;
1223 }
1224
1225
1226 /*
1227 * 'parse_pserror()' - Scan the backchannel data for postscript errors.
1228 */
1229
1230 static void parse_pserror (char *sockBuffer, int len)
1231 {
1232 static char gErrorBuffer[1024] = "";
1233 static char *gErrorBufferPtr = gErrorBuffer;
1234 static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
1235
1236 char *pCommentBegin, *pCommentEnd, *pLineEnd;
1237 char *logLevel;
1238 char logstr[1024];
1239 int logstrlen;
1240
1241 if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
1242 gErrorBufferPtr = gErrorBuffer;
1243 if (len > sizeof(gErrorBuffer) - 1)
1244 len = sizeof(gErrorBuffer) - 1;
1245
1246 memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
1247 gErrorBufferPtr += len;
1248 *(gErrorBufferPtr + 1) = '\0';
1249
1250
1251 pLineEnd = (char *)next_line((const char *)gErrorBuffer);
1252 while (pLineEnd != NULL) {
1253 *pLineEnd++ = '\0';
1254
1255 pCommentBegin = strstr(gErrorBuffer,"%%[");
1256 pCommentEnd = strstr(gErrorBuffer, "]%%");
1257 if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL) {
1258 pCommentEnd += 3; /* Skip past "]%%" */
1259 *pCommentEnd = '\0'; /* There's always room for the nul */
1260
1261 if (strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
1262 logLevel = "DEBUG";
1263 else if (strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
1264 logLevel = "DEBUG";
1265 else
1266 logLevel = "INFO";
1267
1268 if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr)) {
1269 /* If the string was trucnated make sure it has a linefeed before the nul */
1270 logstrlen = sizeof(logstr) - 1;
1271 logstr[logstrlen - 1] = '\n';
1272 }
1273 write(STDERR_FILENO, logstr, logstrlen);
1274 }
1275
1276 /* move everything over... */
1277 strcpy(gErrorBuffer, pLineEnd);
1278 gErrorBufferPtr = gErrorBuffer;
1279 pLineEnd = (char *)next_line((const char *)gErrorBuffer);
1280 }
1281 }
1282 #endif /* PARSE_PS_ERRORS */
1283
1284
1285 /*
1286 * 'read_thread()' - A thread to read the backchannel data.
1287 */
1288
1289 static void *read_thread(void *reference)
1290 {
1291 /* post a read to the device and write results to stdout
1292 * the final pending read will be Aborted in the main thread
1293 */
1294 UInt8 readbuffer[512];
1295 UInt32 rbytes;
1296 kern_return_t readstatus;
1297 printer_data_t *userData = (printer_data_t *)reference;
1298 classdriver_context_t **classdriver = userData->printerDriver;
1299 struct mach_timebase_info timeBaseInfo;
1300 uint64_t start,
1301 delay;
1302
1303 /* Calculate what 250 milliSeconds are in mach absolute time...
1304 */
1305 mach_timebase_info(&timeBaseInfo);
1306 delay = ((uint64_t)250000000 * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer;
1307
1308 do {
1309 /* Remember when we started so we can throttle the loop after the read call...
1310 */
1311 start = mach_absolute_time();
1312
1313 rbytes = sizeof(readbuffer);
1314 readstatus = (*classdriver)->ReadPipe( classdriver, readbuffer, &rbytes );
1315 if ( kIOReturnSuccess == readstatus && rbytes > 0 ) {
1316
1317 cupsBackChannelWrite((char*)readbuffer, rbytes, 1.0);
1318
1319 /* cntrl-d is echoed by the printer.
1320 * NOTES:
1321 * Xerox Phaser 6250D doesn't echo the cntrl-d.
1322 * Xerox Phaser 6250D doesn't always send the product query.
1323 */
1324 if (userData->waitEOF && readbuffer[rbytes-1] == 0x4)
1325 break;
1326 #ifdef PARSE_PS_ERRORS
1327 parse_pserror(readbuffer, rbytes);
1328 #endif
1329 }
1330
1331 /* Make sure this loop executes no more than once every 250 miliseconds...
1332 */
1333 if ((readstatus != kIOReturnSuccess || rbytes == 0) && (userData->waitEOF || !userData->done))
1334 mach_wait_until(start + delay);
1335
1336 } while ( userData->waitEOF || !userData->done ); /* Abort from main thread tests error here */
1337
1338 /* Let the other thread (main thread) know that we have completed the read thread...
1339 */
1340 pthread_mutex_lock(&userData->readMutex);
1341 pthread_cond_signal(&userData->readCompleteCondition);
1342 pthread_mutex_unlock(&userData->readMutex);
1343
1344 return NULL;
1345 }
1346
1347
1348 /*
1349 * End of "$Id: usb-darwin.c 181 2006-06-22 20:01:18Z jlovell $".
1350 */