]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/usb-darwin.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / usb-darwin.c
1 /*
2 * "$Id: usb-darwin.c 6491 2007-04-30 18:21:52Z mike $"
3 *
4 * Copyright © 2005-2007 Apple 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 #include <cups/sidechannel.h>
62 #include <cups/i18n.h>
63
64 #include <CoreFoundation/CoreFoundation.h>
65 #include <IOKit/usb/IOUSBLib.h>
66 #include <IOKit/IOCFPlugIn.h>
67
68 #include <pthread.h>
69
70
71 /*
72 * WAITEOF_DELAY is number of seconds we'll wait for responses from
73 * the printer after we've finished sending all the data
74 */
75 #define WAITEOF_DELAY 7
76 #define DEFAULT_TIMEOUT 60L
77
78 #define USB_INTERFACE_KIND CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190)
79 #define kUSBLanguageEnglish 0x409
80
81 #define PRINTER_POLLING_INTERVAL 5 /* seconds */
82 #define INITIAL_LOG_INTERVAL (PRINTER_POLLING_INTERVAL)
83 #define SUBSEQUENT_LOG_INTERVAL (3*INITIAL_LOG_INTERVAL)
84
85 #define kUSBPrinterClassTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92))
86 #define kUSBPrinterClassInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92))
87
88 #define kUSBClassDriverProperty CFSTR("USB Printing Class")
89
90 #define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
91 #define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
92 #define kWriteBufferSize 2048
93
94
95 #pragma mark -
96 /*
97 * Section 5.3 USB Printing Class spec
98 */
99 #define kUSBPrintingSubclass 1
100 #define kUSBPrintingProtocolNoOpen 0
101 #define kUSBPrintingProtocolUnidirectional 1
102 #define kUSBPrintingProtocolBidirectional 2
103
104 typedef IOUSBInterfaceInterface190 **printer_interface_t;
105
106 typedef struct iodevice_request_s /**** Device request ****/
107 {
108 UInt8 requestType;
109 UInt8 request;
110 UInt16 value;
111 UInt16 index;
112 UInt16 length;
113 void *buffer;
114 } iodevice_request_t;
115
116 typedef union { /**** Centronics status byte ****/
117 char b;
118 struct {
119 unsigned reserved0:2;
120 unsigned paperError:1;
121 unsigned select:1;
122 unsigned notError:1;
123 unsigned reserved1:3;
124 } status;
125 } centronics_status_t;
126
127 typedef struct classdriver_context_s /**** Classdriver context ****/
128 {
129 IUNKNOWN_C_GUTS;
130 CFPlugInRef plugin; /* release plugin */
131 IUnknownVTbl **factory; /* Factory */
132 void *vendorReference; /* vendor class specific usage */
133 UInt32 location; /* unique location in bus topology */
134 UInt8 interfaceNumber; /* Interface number */
135 UInt16 vendorID; /* Vendor id */
136 UInt16 productID; /* Product id */
137 printer_interface_t interface; /* identify the device to IOKit */
138 UInt8 outpipe; /* mandatory bulkOut pipe */
139 UInt8 inpipe; /* optional bulkIn pipe */
140
141 /* general class requests */
142 kern_return_t (*DeviceRequest)( struct classdriver_context_s **printer, iodevice_request_t *iorequest, UInt16 timeout );
143 kern_return_t (*GetString)( struct classdriver_context_s **printer, UInt8 whichString, UInt16 language, UInt16 timeout, CFStringRef *result );
144
145 /* standard printer class requests */
146 kern_return_t (*SoftReset)( struct classdriver_context_s **printer, UInt16 timeout );
147 kern_return_t (*GetCentronicsStatus)( struct classdriver_context_s **printer, centronics_status_t *result, UInt16 timeout );
148 kern_return_t (*GetDeviceID)( struct classdriver_context_s **printer, CFStringRef *devid, UInt16 timeout );
149
150 /* standard bulk device requests */
151 kern_return_t (*ReadPipe)( struct classdriver_context_s **printer, UInt8 *buffer, UInt32 *count );
152 kern_return_t (*WritePipe)( struct classdriver_context_s **printer, UInt8 *buffer, UInt32 *count, Boolean eoj );
153
154 /* interface requests */
155 kern_return_t (*Open)( struct classdriver_context_s **printer, UInt32 location, UInt8 protocol );
156 kern_return_t (*Abort)( struct classdriver_context_s **printer );
157 kern_return_t (*Close)( struct classdriver_context_s **printer );
158
159 /* initialize and terminate */
160 kern_return_t (*Initialize)( struct classdriver_context_s **printer, struct classdriver_context_s **baseclass );
161 kern_return_t (*Terminate)( struct classdriver_context_s **printer );
162
163 } classdriver_context_t;
164
165
166 typedef Boolean (*iterator_callback_t)(void *refcon, io_service_t obj);
167
168 typedef struct iterator_reference_s { /**** Iterator reference data */
169 iterator_callback_t callback;
170 void *userdata;
171 Boolean keepRunning;
172 } iterator_reference_t;
173
174 typedef struct printer_data_s { /**** Printer context data ****/
175 io_service_t printerObj;
176 classdriver_context_t **printerDriver;
177
178 pthread_cond_t readCompleteCondition;
179 pthread_mutex_t readMutex;
180 int done;
181
182 const char *uri;
183 CFStringRef make;
184 CFStringRef model;
185 CFStringRef serial;
186
187 UInt32 location;
188 Boolean waitEOF;
189
190 CFRunLoopTimerRef statusTimer;
191
192 pthread_cond_t reqWaitCompCond;
193 pthread_mutex_t reqWaitMutex;
194 pthread_mutex_t waitCloseMutex;
195 pthread_mutex_t writeCompMutex;
196 int writeDone;
197 int reqWaitDone;
198 int reqWqitFlag;
199 int directionalFlag; /* 0=uni, 1=bidi */
200 ssize_t dataSize;
201 ssize_t dataOffset;
202 char dataBuffer[kWriteBufferSize];
203 } printer_data_t;
204
205
206 /*
207 * Local functions...
208 */
209
210 static Boolean list_device_callback(void *refcon, io_service_t obj);
211 static Boolean find_device_callback(void *refcon, io_service_t obj);
212 static void statusTimerCallback(CFRunLoopTimerRef timer, void *info);
213 static void iterate_printers(iterator_callback_t callBack, void *userdata);
214 static void device_added(void *userdata, io_iterator_t iterator);
215 static void copy_deviceinfo(CFStringRef deviceIDString, CFStringRef *make, CFStringRef *model, CFStringRef *serial);
216 static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial);
217 static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t intf, classdriver_context_t ***driver);
218 static kern_return_t unload_classdriver(classdriver_context_t ***classDriver);
219 static kern_return_t load_printerdriver(printer_data_t *printer, CFStringRef *driverBundlePath);
220 static kern_return_t registry_open(printer_data_t *printer, CFStringRef *driverBundlePath);
221 static kern_return_t registry_close(printer_data_t *printer);
222 static OSStatus copy_deviceid(classdriver_context_t **printer, CFStringRef *deviceID);
223 static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation);
224 static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys);
225 static CFStringRef cfstr_create_and_trim(const char *cstr);
226 static void parse_options(const char *options, char *serial, UInt32 *location, Boolean *waitEOF);
227 static void setup_cfLanguage(void);
228 static void *read_thread(void *reference);
229 static void *reqestWait_thread(void *reference);
230 static void usbSoftReset(printer_data_t *userData, cups_sc_status_t *status);
231 static void usbDrainOutput(printer_data_t *userData, cups_sc_status_t *status);
232 static void usbGetBidirectional(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen);
233 static void usbGetDeviceID(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen);
234 static void usbGetDevState(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen);
235
236
237 #if defined(__i386__)
238 static pid_t child_pid; /* Child PID */
239 static void run_ppc_backend(int argc, char *argv[], int fd); /* Starts child backend process running as a ppc executable */
240 static void sigterm_handler(int sig); /* SIGTERM handler */
241 #endif /* __i386__ */
242
243 #ifdef PARSE_PS_ERRORS
244 static const char *next_line (const char *buffer);
245 static void parse_pserror (char *sockBuffer, int len);
246 #endif /* PARSE_PS_ERRORS */
247
248 #pragma mark -
249
250 /*
251 * 'list_devices()' - List all USB devices.
252 */
253
254 void list_devices()
255 {
256 iterate_printers(list_device_callback, NULL);
257 }
258
259
260 /*
261 * 'print_device()' - Print a file to a USB device.
262 */
263
264 int /* O - Exit status */
265 print_device(const char *uri, /* I - Device URI */
266 const char *hostname, /* I - Hostname/manufacturer */
267 const char *resource, /* I - Resource/modelname */
268 const char *options, /* I - Device options/serial number */
269 int fd, /* I - File descriptor to print */
270 int copies, /* I - Copies to print */
271 int argc, /* I - Number of command-line arguments (6 or 7) */
272 char *argv[]) /* I - Command-line arguments */
273 {
274 printer_data_t printer_data = { 0x0 }; /* Printer context */
275 char serial[1024]; /* Serial number buffer */
276 OSStatus status = noErr; /* Function results */
277 pthread_t thr; /* Read thread */
278 char buffer[2048]; /* Write buffer */
279 int thread_created = 0; /* Thread created? */
280 int countdown = INITIAL_LOG_INTERVAL; /* Logging interval */
281 pthread_cond_t *readCompleteConditionPtr = NULL; /* Read complete condition */
282 pthread_mutex_t *readMutexPtr = NULL; /* Read mutex */
283 CFStringRef driverBundlePath; /* Class driver path */
284 int reqWait_create = 0; /* RequestWait thread created? */
285 pthread_t reqWaitThread; /* RequestWait thread */
286 pthread_cond_t *reqWaitCompCondPtr = NULL; /* RequestWait complete condition */
287 pthread_mutex_t *reqWaitMutexPtr = NULL; /* RequestWait mutex */
288 pthread_mutex_t *waitCloseMutexPtr = NULL; /* wait close mutex */
289 pthread_mutex_t *writeCompMutexPtr = NULL; /* write complete mutex */
290
291 setup_cfLanguage();
292 parse_options(options, serial, &printer_data.location, &printer_data.waitEOF);
293
294 if (resource[0] == '/')
295 resource++;
296
297 printer_data.uri = uri;
298
299 printer_data.make = cfstr_create_and_trim(hostname);
300 printer_data.model = cfstr_create_and_trim(resource);
301 printer_data.serial = cfstr_create_and_trim(serial);
302
303 fputs("STATE: +connecting-to-device\n", stderr);
304
305 do {
306 if (printer_data.printerObj != 0x0) {
307 IOObjectRelease(printer_data.printerObj);
308 unload_classdriver(&printer_data.printerDriver);
309 printer_data.printerObj = 0x0;
310 printer_data.printerDriver = 0x0;
311 }
312
313 fprintf(stderr, "DEBUG: Looking for '%s %s'\n", hostname, resource);
314 iterate_printers(find_device_callback, &printer_data);
315
316 fputs("DEBUG: Opening connection\n", stderr);
317
318 driverBundlePath = NULL;
319 status = registry_open(&printer_data, &driverBundlePath);
320 #if defined(__i386__)
321 /*
322 * If we were unable to load the class drivers for this printer it's probably because they're ppc-only.
323 * In this case try to fork & exec this backend as a ppc executable so we can use them...
324 */
325 if (status == -2 /* kPMInvalidIOMContext */) {
326 run_ppc_backend(argc, argv, fd);
327 /* Never returns here */
328 }
329 #endif /* __i386__ */
330 if (status == -2) {
331 /*
332 * If we still were unable to load the class drivers for this printer log
333 * the error and stop the queue...
334 */
335
336 if (driverBundlePath == NULL || !CFStringGetCString(driverBundlePath, buffer, sizeof(buffer), kCFStringEncodingUTF8))
337 strlcpy(buffer, "USB class driver", sizeof(buffer));
338
339 fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr);
340 fprintf(stderr, _("FATAL: Could not load %s\n"), buffer);
341
342 if (driverBundlePath)
343 CFRelease(driverBundlePath);
344
345 return CUPS_BACKEND_STOP;
346 }
347
348 if (driverBundlePath)
349 CFRelease(driverBundlePath);
350
351 if (status != noErr) {
352 sleep( PRINTER_POLLING_INTERVAL );
353 countdown -= PRINTER_POLLING_INTERVAL;
354 if ( countdown <= 0 ) {
355 fprintf(stderr, _("INFO: Printer busy (status:0x%08x)\n"), (int)status);
356 countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 15 seconds */
357 }
358 }
359 } while (status != noErr);
360
361 fputs("STATE: -connecting-to-device\n", stderr);
362
363 /*
364 * Now that we are "connected" to the port, ignore SIGTERM so that we
365 * can finish out any page data the driver sends (e.g. to eject the
366 * current page... Only ignore SIGTERM if we are printing data from
367 * stdin (otherwise you can't cancel raw jobs...)
368 */
369
370 if (!fd) {
371 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
372 sigset(SIGTERM, SIG_IGN);
373 #elif defined(HAVE_SIGACTION)
374 memset(&action, 0, sizeof(action));
375
376 sigemptyset(&action.sa_mask);
377 action.sa_handler = SIG_IGN;
378 sigaction(SIGTERM, &action, NULL);
379 #else
380 signal(SIGTERM, SIG_IGN);
381 #endif /* HAVE_SIGSET */
382 }
383
384 if (status == noErr) {
385 if (pthread_cond_init(&printer_data.readCompleteCondition, NULL) == 0)
386 readCompleteConditionPtr = &printer_data.readCompleteCondition;
387
388 if (pthread_mutex_init(&printer_data.readMutex, NULL) == 0)
389 readMutexPtr = &printer_data.readMutex;
390
391 printer_data.done = 0;
392
393 if (pthread_create(&thr, NULL, read_thread, &printer_data) == 0)
394 thread_created = 1;
395
396 if (thread_created == 0)
397 fputs(_("WARNING: Couldn't create read channel\n"), stderr);
398
399 if (pthread_cond_init(&printer_data.reqWaitCompCond, NULL) == 0)
400 reqWaitCompCondPtr = &printer_data.reqWaitCompCond;
401
402 if (pthread_mutex_init(&printer_data.reqWaitMutex, NULL) == 0)
403 reqWaitMutexPtr = &printer_data.reqWaitMutex;
404
405 printer_data.reqWaitDone = 0;
406 printer_data.reqWqitFlag = 0;
407
408 if (pthread_create(&reqWaitThread, NULL, reqestWait_thread, &printer_data) == 0)
409 reqWait_create = 1;
410
411 if (reqWait_create == 0)
412 fputs(_("WARNING: Couldn't create sidechannel thread!\n"), stderr);
413
414 if (pthread_mutex_init(&printer_data.waitCloseMutex, NULL) == 0)
415 waitCloseMutexPtr = &printer_data.waitCloseMutex;
416
417 if (pthread_mutex_init(&printer_data.writeCompMutex, NULL) == 0)
418 writeCompMutexPtr = &printer_data.writeCompMutex;
419 }
420
421 /*
422 * The main thread sends the print file...
423 */
424
425 printer_data.writeDone = 0;
426 printer_data.dataSize = 0;
427 printer_data.dataOffset = 0;
428 pthread_mutex_lock(writeCompMutexPtr);
429
430 while (status == noErr && copies-- > 0) {
431 UInt32 wbytes; /* Number of bytes written */
432 ssize_t nbytes; /* Number of bytes read */
433 off_t tbytes = 0; /* Total number of bytes written */
434
435 fputs(_("INFO: Sending data\n"), stderr);
436
437 if (STDIN_FILENO != fd) {
438 fputs("PAGE: 1 1", stderr);
439 lseek( fd, 0, SEEK_SET );
440 }
441
442 while (status == noErr && (nbytes = read(fd, buffer, sizeof(buffer))) > 0) {
443 char *bufptr = buffer;
444 tbytes += nbytes;
445
446 while (nbytes > 0 && status == noErr) {
447 if (printer_data.writeDone) {
448 printer_data.dataSize = nbytes;
449 printer_data.dataOffset = bufptr - buffer;
450 memcpy(printer_data.dataBuffer, buffer, nbytes);
451
452 status = -1;
453 break;
454 }
455
456 wbytes = nbytes;
457 status = (*(printer_data.printerDriver))->WritePipe( printer_data.printerDriver, (UInt8*)bufptr, &wbytes, 0 /* nbytes > wbytes? 0: feof(fp) */ );
458 if (wbytes < 0 || noErr != status) {
459 OSStatus err = (*(printer_data.printerDriver))->Abort(printer_data.printerDriver);
460 fprintf(stderr, _("ERROR: %ld: Unable to send print file to printer (canceled:%ld)\n"), status, err);
461 break;
462 }
463
464 nbytes -= wbytes;
465 bufptr += wbytes;
466 }
467
468 if (fd != 0 && status == noErr)
469 fprintf(stderr, "DEBUG: Sending print file, %lld bytes...\n", (off_t)tbytes);
470 }
471 }
472
473 printer_data.writeDone = 1;
474 pthread_mutex_unlock(writeCompMutexPtr);
475
476 if (thread_created) {
477 /* Signal the read thread that we are done... */
478 printer_data.done = 1;
479
480 /* Give the read thread WAITEOF_DELAY seconds to complete all the data. If
481 * we are not signaled in that time then force the thread to exit by setting
482 * the waiteof to be false. Plese note that this relies on us using the timeout
483 * class driver.
484 */
485 struct timespec sleepUntil = { time(NULL) + WAITEOF_DELAY, 0 };
486 pthread_mutex_lock(&printer_data.readMutex);
487 if (pthread_cond_timedwait(&printer_data.readCompleteCondition, &printer_data.readMutex, (const struct timespec *)&sleepUntil) != 0)
488 printer_data.waitEOF = false;
489 pthread_mutex_unlock(&printer_data.readMutex);
490 pthread_join( thr,NULL); /* wait for the child thread to return */
491 }
492
493 if (reqWait_create) {
494 /* Signal the cupsSideChannelDoRequest wait thread that we are done... */
495 printer_data.reqWaitDone = 1;
496
497 /*
498 * Give the cupsSideChannelDoRequest wait thread WAITEOF_DELAY seconds to complete
499 * all the data. If we are not signaled in that time then force the thread to exit
500 * by setting the waiteof to be false. Plese note that this relies on us using the
501 * timeout class driver.
502 */
503 struct timespec reqWaitSleepUntil = { time(NULL) + WAITEOF_DELAY, 0 };
504 pthread_mutex_lock(&printer_data.reqWaitMutex);
505
506 while (!printer_data.reqWqitFlag) {
507 if (pthread_cond_timedwait(&printer_data.reqWaitCompCond,
508 &printer_data.reqWaitMutex,
509 (const struct timespec *)&reqWaitSleepUntil) != 0) {
510 printer_data.waitEOF = false;
511 printer_data.reqWqitFlag = 1;
512 }
513 }
514 pthread_mutex_unlock(&printer_data.reqWaitMutex);
515 pthread_join(reqWaitThread,NULL); /* wait for the child thread to return */
516 }
517
518 /* interface close wait mutex(for softreset) */
519 pthread_mutex_lock(waitCloseMutexPtr);
520 pthread_mutex_unlock(waitCloseMutexPtr);
521
522 /*
523 * Close the connection and input file and general clean up...
524 */
525 registry_close(&printer_data);
526
527 if (STDIN_FILENO != fd)
528 close(fd);
529
530 if (readCompleteConditionPtr != NULL)
531 pthread_cond_destroy(&printer_data.readCompleteCondition);
532
533 if (readMutexPtr != NULL)
534 pthread_mutex_destroy(&printer_data.readMutex);
535
536 if (waitCloseMutexPtr != NULL)
537 pthread_mutex_destroy(&printer_data.waitCloseMutex);
538
539 if (writeCompMutexPtr != NULL)
540 pthread_mutex_destroy(&printer_data.writeCompMutex);
541
542 if (reqWaitCompCondPtr != NULL)
543 pthread_cond_destroy(&printer_data.reqWaitCompCond);
544
545 if (reqWaitMutexPtr != NULL)
546 pthread_mutex_destroy(&printer_data.reqWaitMutex);
547
548 if (printer_data.make != NULL)
549 CFRelease(printer_data.make);
550
551 if (printer_data.model != NULL)
552 CFRelease(printer_data.model);
553
554 if (printer_data.serial != NULL)
555 CFRelease(printer_data.serial);
556
557 if (printer_data.printerObj != 0x0)
558 IOObjectRelease(printer_data.printerObj);
559
560 return status;
561 }
562
563 #pragma mark -
564 /*
565 * 'list_device_callback()' - list_device iterator callback.
566 */
567
568 static Boolean list_device_callback(void *refcon, io_service_t obj)
569 {
570 Boolean keepRunning = (obj != 0x0);
571
572 if (keepRunning) {
573 CFStringRef deviceIDString = NULL;
574 UInt32 deviceLocation = 0;
575
576 copy_devicestring(obj, &deviceIDString, &deviceLocation);
577 if (deviceIDString != NULL) {
578 CFStringRef make = NULL, model = NULL, serial = NULL;
579 char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024];
580 char optionsstr[1024], idstr[1024];
581
582 copy_deviceinfo(deviceIDString, &make, &model, &serial);
583
584 modelstr[0] = '/';
585
586 CFStringGetCString(deviceIDString, idstr, sizeof(idstr),
587 kCFStringEncodingUTF8);
588
589 if (make)
590 CFStringGetCString(make, makestr, sizeof(makestr),
591 kCFStringEncodingUTF8);
592 else
593 strcpy(makestr, "Unknown");
594
595 if (model)
596 CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
597 kCFStringEncodingUTF8);
598 else
599 strcpy(modelstr + 1, "Printer");
600
601 optionsstr[0] = '\0';
602 if (serial != NULL)
603 {
604 CFStringGetCString(serial, serialstr, sizeof(serialstr), kCFStringEncodingUTF8);
605 snprintf(optionsstr, sizeof(optionsstr), "?serial=%s", serialstr);
606 }
607 else if (deviceLocation != 0)
608 {
609 snprintf(optionsstr, sizeof(optionsstr), "?location=%lx", deviceLocation);
610 }
611
612 httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
613 strncat(uristr, optionsstr, sizeof(uristr));
614
615 /*
616 * Fix common HP 1284 bug...
617 */
618
619 if (!strcasecmp(makestr, "Hewlett-Packard"))
620 strcpy(makestr, "HP");
621
622 if (!strncasecmp(modelstr + 1, "hp ", 3))
623 _cups_strcpy(modelstr + 1, modelstr + 4);
624
625 printf("direct %s \"%s %s\" \"%s %s USB\" \"%s\"\n", uristr, makestr,
626 &modelstr[1], makestr, &modelstr[1], idstr);
627
628 release_deviceinfo(&make, &model, &serial);
629 CFRelease(deviceIDString);
630 }
631 }
632
633 return keepRunning;
634 }
635
636
637 /*
638 * 'find_device_callback()' - print_device iterator callback.
639 */
640
641 static Boolean find_device_callback(void *refcon, io_service_t obj)
642 {
643 Boolean keepLooking = true;
644 printer_data_t *userData = (printer_data_t *)refcon;
645
646 if (obj != 0x0) {
647 CFStringRef idString = NULL;
648 UInt32 location = -1;
649
650 copy_devicestring(obj, &idString, &location);
651 if (idString != NULL) {
652 CFStringRef make = NULL, model = NULL, serial = NULL;
653
654 copy_deviceinfo(idString, &make, &model, &serial);
655 if (CFStringCompare(make, userData->make, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
656 if (CFStringCompare(model, userData->model, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
657 if (userData->serial != NULL && CFStringGetLength(userData->serial) > 0 ) {
658 if (serial != NULL && CFStringCompare(serial, userData->serial, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
659 IOObjectRetain(obj);
660 userData->printerObj = obj;
661 keepLooking = false;
662 }
663 }
664 else {
665 if (userData->printerObj != 0) {
666 IOObjectRetain(userData->printerObj);
667 }
668 userData->printerObj = obj;
669 IOObjectRetain(obj);
670
671 if (userData->location == 0 || userData->location == location) {
672 keepLooking = false;
673 }
674 }
675 }
676 }
677
678 release_deviceinfo(&make, &model, &serial);
679 CFRelease(idString);
680 }
681 }
682 else {
683 keepLooking = (userData->printerObj == 0);
684 if (obj == 0x0 && keepLooking) {
685 CFRunLoopTimerContext context = { 0, userData, NULL, NULL, NULL };
686 CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 10, 0x0, 0x0, statusTimerCallback, &context);
687 if (timer != NULL) {
688 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
689 userData->statusTimer = timer;
690 }
691 }
692 }
693
694 if (!keepLooking && userData->statusTimer != NULL) {
695 fputs("STATE: -offline-error\n", stderr);
696 fputs(_("INFO: Printer is now on-line.\n"), stderr);
697 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), userData->statusTimer, kCFRunLoopDefaultMode);
698 CFRelease(userData->statusTimer);
699 userData->statusTimer = NULL;
700 }
701
702 return keepLooking;
703 }
704
705 static void statusTimerCallback (CFRunLoopTimerRef timer, void *info)
706 {
707 fputs("STATE: +offline-error\n", stderr);
708 fputs(_("INFO: Printer is currently off-line.\n"), stderr);
709 }
710
711 #pragma mark -
712 /*
713 * 'iterate_printers()' - iterate over all the printers.
714 */
715
716 static void iterate_printers(iterator_callback_t callBack, void *userdata)
717 {
718 mach_port_t masterPort = 0x0;
719 kern_return_t kr = IOMasterPort (bootstrap_port, &masterPort);
720
721 if (kr == kIOReturnSuccess && masterPort != 0x0) {
722 io_iterator_t addIterator = 0x0;
723
724 iterator_reference_t reference = { callBack, userdata, true };
725 IONotificationPortRef addNotification = IONotificationPortCreate(masterPort);
726
727 int klass = kUSBPrintingClass;
728 int subklass = kUSBPrintingSubclass;
729
730 CFNumberRef usb_klass = CFNumberCreate(NULL, kCFNumberIntType, &klass);
731 CFNumberRef usb_subklass = CFNumberCreate(NULL, kCFNumberIntType, &subklass);
732 CFMutableDictionaryRef usbPrinterMatchDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
733
734 CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceClass"), usb_klass);
735 CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceSubClass"), usb_subklass);
736
737 CFRelease(usb_klass);
738 CFRelease(usb_subklass);
739
740 kr = IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification, usbPrinterMatchDictionary, &device_added, &reference, &addIterator);
741 if (addIterator != 0x0) {
742 device_added (&reference, addIterator);
743
744 if (reference.keepRunning) {
745 CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode);
746 CFRunLoopRun();
747 }
748 IOObjectRelease(addIterator);
749 }
750 mach_port_deallocate(mach_task_self(), masterPort);
751 }
752 }
753
754
755 /*
756 * 'device_added()' - device added notifier.
757 */
758
759 static void device_added(void *userdata, io_iterator_t iterator)
760 {
761 iterator_reference_t *reference = userdata;
762
763 io_service_t obj;
764 while (reference->keepRunning && (obj = IOIteratorNext(iterator)) != 0x0) {
765 if (reference->callback != NULL) {
766 reference->keepRunning = reference->callback(reference->userdata, obj);
767 }
768 IOObjectRelease(obj);
769 }
770
771 /* One last call to the call back now that we are not longer have printers left to iterate...
772 */
773 if (reference->keepRunning)
774 reference->keepRunning = reference->callback(reference->userdata, 0x0);
775
776 if (!reference->keepRunning) {
777 CFRunLoopStop(CFRunLoopGetCurrent());
778 }
779 }
780
781
782 #pragma mark -
783 /*
784 * 'copy_deviceinfo()' - Copy strings from the 1284 device ID.
785 */
786
787 static void copy_deviceinfo(CFStringRef deviceIDString, CFStringRef *make, CFStringRef *model, CFStringRef *serial)
788 {
789 CFStringRef modelKeys[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL };
790 CFStringRef makeKeys[] = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL };
791 CFStringRef serialKeys[] = { CFSTR("SN:"), CFSTR("SERN:"), NULL };
792
793 if (make != NULL)
794 *make = copy_value_for_key(deviceIDString, makeKeys);
795 if (model != NULL)
796 *model = copy_value_for_key(deviceIDString, modelKeys);
797 if (serial != NULL)
798 *serial = copy_value_for_key(deviceIDString, serialKeys);
799 }
800
801
802 /*
803 * 'release_deviceinfo()' - Release deviceinfo strings.
804 */
805
806 static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial)
807 {
808 if (make != NULL && *make != NULL) {
809 CFRelease(*make);
810 *make = NULL;
811 }
812
813 if (model != NULL && *model != NULL) {
814 CFRelease(*model);
815 *model = NULL;
816 }
817
818 if (serial != NULL && *serial != NULL) {
819 CFRelease(*serial);
820 *serial = NULL;
821 }
822 }
823
824
825 #pragma mark -
826 /*
827 * 'load_classdriver()' - Load a classdriver.
828 */
829
830 static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t intf, classdriver_context_t ***printerDriver)
831 {
832 kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
833 classdriver_context_t **driver = NULL;
834 CFStringRef bundle = (driverPath == NULL ? kUSBGenericTOPrinterClassDriver : driverPath);
835
836 if ( NULL != bundle ) {
837 CFURLRef url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
838 CFPlugInRef plugin = (url != NULL ? CFPlugInCreate(NULL, url) : NULL);
839
840 if (url != NULL)
841 CFRelease(url);
842
843 if (plugin != NULL) {
844 CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
845 if (factories != NULL && CFArrayGetCount(factories) > 0) {
846 CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
847 IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
848 if (NULL != iunknown) {
849 kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
850 if (kr == kIOReturnSuccess && driver != NULL) {
851 classdriver_context_t **genericDriver = NULL;
852 if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo) {
853 kr = load_classdriver(NULL, intf, &genericDriver);
854 }
855
856 if (kr == kIOReturnSuccess) {
857 (*driver)->interface = intf;
858 (*driver)->Initialize(driver, genericDriver);
859
860 (*driver)->plugin = plugin;
861 (*driver)->interface = intf;
862 *printerDriver = driver;
863 }
864 }
865 (*iunknown)->Release(iunknown);
866 }
867 CFRelease(factories);
868 }
869 }
870 }
871
872 #ifdef DEBUG
873 char bundlestr[1024];
874 CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
875 fprintf(stderr, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
876 #endif /* DEBUG */
877
878 return kr;
879 }
880
881
882 /*
883 * 'unload_classdriver()' - Unload a classdriver.
884 */
885
886 static kern_return_t unload_classdriver(classdriver_context_t ***classDriver)
887 {
888 if (*classDriver != NULL) {
889 (**classDriver)->Release(*classDriver);
890 *classDriver = NULL;
891 }
892
893 return kIOReturnSuccess;
894 }
895
896
897 /*
898 * 'load_printerdriver()' - Load a vendor's (or generic) classdriver.
899 *
900 * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
901 */
902
903 static kern_return_t load_printerdriver(printer_data_t *printer, CFStringRef *driverBundlePath)
904 {
905 IOCFPlugInInterface **iodev = NULL;
906 SInt32 score;
907 kern_return_t kr;
908 printer_interface_t intf;
909 HRESULT res;
910
911 kr = IOCreatePlugInInterfaceForService(printer->printerObj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
912 if (kr == kIOReturnSuccess)
913 {
914 if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf)) == noErr)
915 {
916 *driverBundlePath = IORegistryEntryCreateCFProperty(printer->printerObj, kUSBClassDriverProperty, NULL, kNilOptions);
917
918 kr = load_classdriver(*driverBundlePath, intf, &printer->printerDriver);
919
920 if (kr != kIOReturnSuccess)
921 (*intf)->Release(intf);
922 }
923 IODestroyPlugInInterface(iodev);
924 }
925 return kr;
926 }
927
928
929 /*
930 * 'registry_open()' - Open a connection to the printer.
931 */
932
933 static kern_return_t registry_open(printer_data_t *printer, CFStringRef *driverBundlePath)
934 {
935 printer->directionalFlag = 0;
936
937 kern_return_t kr = load_printerdriver(printer, driverBundlePath);
938 if (kr != kIOReturnSuccess) {
939 kr = -2;
940 }
941
942 if (printer->printerDriver != NULL) {
943 kr = (*(printer->printerDriver))->Open(printer->printerDriver, printer->location, kUSBPrintingProtocolBidirectional);
944 if (kr != kIOReturnSuccess || (*(printer->printerDriver))->interface == NULL) {
945 kr = (*(printer->printerDriver))->Open(printer->printerDriver, printer->location, kUSBPrintingProtocolUnidirectional);
946 if (kr == kIOReturnSuccess) {
947 if ((*(printer->printerDriver))->interface == NULL) {
948 (*(printer->printerDriver))->Close(printer->printerDriver);
949 kr = -1;
950 }
951 }
952 } else {
953 printer->directionalFlag = 1;
954 }
955 }
956
957 if (kr != kIOReturnSuccess) {
958 unload_classdriver(&printer->printerDriver);
959 }
960
961 return kr;
962 }
963
964
965 /*
966 * 'registry_close()' - Close the connection to the printer.
967 */
968
969 static kern_return_t registry_close(printer_data_t *printer)
970 {
971 if (printer->printerDriver != NULL) {
972 (*(printer->printerDriver))->Close(printer->printerDriver);
973 }
974 unload_classdriver(&printer->printerDriver);
975 return kIOReturnSuccess;
976 }
977
978
979 /*
980 * 'copy_deviceid()' - Copy the 1284 device id string.
981 */
982
983 static OSStatus copy_deviceid(classdriver_context_t **printer, CFStringRef *deviceID)
984 {
985 CFStringRef devID = NULL,
986
987 deviceMake = NULL,
988 deviceModel = NULL,
989 deviceSerial = NULL;
990
991 OSStatus err = (*printer)->GetDeviceID(printer, &devID, DEFAULT_TIMEOUT);
992
993 copy_deviceinfo(devID, &deviceMake, &deviceModel, &deviceSerial);
994
995 if (deviceMake == NULL || deviceModel == NULL || deviceSerial == NULL) {
996 IOUSBDeviceDescriptor desc;
997 iodevice_request_t request;
998
999 request.requestType = USBmakebmRequestType( kUSBIn, kUSBStandard, kUSBDevice );
1000 request.request = kUSBRqGetDescriptor;
1001 request.value = (kUSBDeviceDesc << 8) | 0;
1002 request.index = 0;
1003 request.length = sizeof(desc);
1004 request.buffer = &desc;
1005 err = (*printer)->DeviceRequest(printer, &request, DEFAULT_TIMEOUT);
1006 if (err == kIOReturnSuccess) {
1007 CFMutableStringRef newDevID = CFStringCreateMutable(NULL, 0);
1008
1009 if (deviceMake == NULL) {
1010 CFStringRef data = NULL;
1011 err = (*printer)->GetString(printer, desc.iManufacturer, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
1012 if (data != NULL) {
1013 CFStringAppendFormat(newDevID, NULL, CFSTR("MFG:%@;"), data);
1014 CFRelease(data);
1015 }
1016 }
1017
1018 if (deviceModel == NULL) {
1019 CFStringRef data = NULL;
1020 err = (*printer)->GetString(printer, desc.iProduct, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
1021 if (data != NULL) {
1022 CFStringAppendFormat(newDevID, NULL, CFSTR("MDL:%@;"), data);
1023 CFRelease(data);
1024 }
1025 }
1026
1027 if (deviceSerial == NULL && desc.iSerialNumber != 0) {
1028 CFStringRef data = NULL;
1029 err = (*printer)->GetString(printer, desc.iSerialNumber, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
1030 if (data != NULL) {
1031 CFStringAppendFormat(newDevID, NULL, CFSTR("SERN:%@;"), data);
1032 CFRelease(data);
1033 }
1034 }
1035
1036 if (devID != NULL) {
1037 CFStringAppend(newDevID, devID);
1038 CFRelease(devID);
1039 }
1040
1041 *deviceID = newDevID;
1042 }
1043 }
1044 else {
1045 *deviceID = devID;
1046 }
1047 release_deviceinfo(&deviceMake, &deviceModel, &deviceSerial);
1048
1049 return err;
1050 }
1051
1052
1053 /*
1054 * 'copy_devicestring()' - Copy the 1284 device id string.
1055 */
1056
1057 static void copy_devicestring(io_service_t usbInterface, CFStringRef *deviceID, UInt32 *deviceLocation)
1058 {
1059 IOCFPlugInInterface **iodev = NULL;
1060 SInt32 score;
1061 kern_return_t kr;
1062 printer_interface_t intf;
1063 HRESULT res;
1064 classdriver_context_t **klassDriver = NULL;
1065 CFStringRef driverBundlePath;
1066
1067 kr = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID,
1068 kIOCFPlugInInterfaceID, &iodev, &score);
1069 if (kr == kIOReturnSuccess)
1070 {
1071 if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf)) == noErr)
1072 {
1073 /* ignore the result for location id... */
1074 (void)(*intf)->GetLocationID(intf, deviceLocation);
1075
1076 driverBundlePath = IORegistryEntryCreateCFProperty( usbInterface, kUSBClassDriverProperty, NULL, kNilOptions );
1077
1078 kr = load_classdriver(driverBundlePath, intf, &klassDriver);
1079
1080 if (kr != kIOReturnSuccess && driverBundlePath != NULL)
1081 kr = load_classdriver(NULL, intf, &klassDriver);
1082
1083 if (kr == kIOReturnSuccess && klassDriver != NULL)
1084 kr = copy_deviceid(klassDriver, deviceID);
1085
1086 unload_classdriver(&klassDriver);
1087
1088 if (driverBundlePath != NULL)
1089 CFRelease(driverBundlePath);
1090
1091 /* (*intf)->Release(intf); */
1092 }
1093 IODestroyPlugInInterface(iodev);
1094 }
1095 }
1096
1097
1098 #pragma mark -
1099 /*
1100 * 'copy_value_for_key()' - Copy value string associated with a key.
1101 */
1102
1103 static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys)
1104 {
1105 CFStringRef value = NULL;
1106 CFArrayRef kvPairs = deviceID != NULL ? CFStringCreateArrayBySeparatingStrings(NULL, deviceID, CFSTR(";")) : NULL;
1107 CFIndex max = kvPairs != NULL ? CFArrayGetCount(kvPairs) : 0;
1108 CFIndex idx = 0;
1109
1110 while (idx < max && value == NULL) {
1111 CFStringRef kvpair = CFArrayGetValueAtIndex(kvPairs, idx);
1112 CFIndex idxx = 0;
1113 while (keys[idxx] != NULL && value == NULL) {
1114 CFRange range = CFStringFind(kvpair, keys[idxx], kCFCompareCaseInsensitive);
1115 if (range.length != -1) {
1116 if (range.location != 0) {
1117 CFMutableStringRef theString = CFStringCreateMutableCopy(NULL, 0, kvpair);
1118 CFStringTrimWhitespace(theString);
1119 range = CFStringFind(theString, keys[idxx], kCFCompareCaseInsensitive);
1120 if (range.location == 0) {
1121 value = CFStringCreateWithSubstring(NULL, theString, CFRangeMake(range.length, CFStringGetLength(theString) - range.length));
1122 }
1123 CFRelease(theString);
1124 }
1125 else {
1126 CFStringRef theString = CFStringCreateWithSubstring(NULL, kvpair, CFRangeMake(range.length, CFStringGetLength(kvpair) - range.length));
1127 CFMutableStringRef theString2 = CFStringCreateMutableCopy(NULL, 0, theString);
1128 CFRelease(theString);
1129
1130 CFStringTrimWhitespace(theString2);
1131 value = theString2;
1132 }
1133 }
1134 idxx++;
1135 }
1136 idx++;
1137 }
1138
1139 if (kvPairs != NULL)
1140 CFRelease(kvPairs);
1141 return value;
1142 }
1143
1144
1145 /*
1146 * 'cfstr_create_and_trim()' - Create a CFString from a c-string and
1147 * trim it's whitespace characters.
1148 */
1149
1150 CFStringRef cfstr_create_and_trim(const char *cstr)
1151 {
1152 CFStringRef cfstr;
1153 CFMutableStringRef cfmutablestr = NULL;
1154
1155 if ((cfstr = CFStringCreateWithCString(NULL, cstr, kCFStringEncodingUTF8)) != NULL)
1156 {
1157 if ((cfmutablestr = CFStringCreateMutableCopy(NULL, 1024, cfstr)) != NULL)
1158 CFStringTrimWhitespace(cfmutablestr);
1159
1160 CFRelease(cfstr);
1161 }
1162 return (CFStringRef) cfmutablestr;
1163 }
1164
1165
1166 #pragma mark -
1167 /*
1168 * 'parse_options()' - Parse uri options.
1169 */
1170
1171 static void parse_options(const char *options, char *serial, UInt32 *location, Boolean *waitEOF)
1172 {
1173 char *serialnumber; /* ?serial=<serial> or ?location=<location> */
1174 char optionName[255], /* Name of option */
1175 value[255], /* Value of option */
1176 *ptr; /* Pointer into name or value */
1177
1178 if (serial)
1179 *serial = '\0';
1180 if (location)
1181 *location = 0;
1182
1183 if (!options)
1184 return;
1185
1186 serialnumber = NULL;
1187
1188 while (*options != '\0') {
1189 /* Get the name... */
1190 for (ptr = optionName; *options && *options != '=' && *options != '+'; )
1191 *ptr++ = *options++;
1192
1193 *ptr = '\0';
1194 value[0] = '\0';
1195
1196 if (*options == '=') {
1197 /* Get the value... */
1198 options ++;
1199
1200 for (ptr = value; *options && *options != '+';)
1201 *ptr++ = *options++;
1202
1203 *ptr = '\0';
1204
1205 if (*options == '+')
1206 options ++;
1207 }
1208 else if (*options == '+') {
1209 options ++;
1210 }
1211
1212 /*
1213 * Process the option...
1214 */
1215 if (strcasecmp(optionName, "waiteof") == 0) {
1216 if (strcasecmp(value, "on") == 0 ||
1217 strcasecmp(value, "yes") == 0 ||
1218 strcasecmp(value, "true") == 0) {
1219 *waitEOF = true;
1220 }
1221 else if (strcasecmp(value, "off") == 0 ||
1222 strcasecmp(value, "no") == 0 ||
1223 strcasecmp(value, "false") == 0) {
1224 *waitEOF = false;
1225 }
1226 else {
1227 fprintf(stderr, _("WARNING: Boolean expected for waiteof option \"%s\"\n"), value);
1228 }
1229 }
1230 else if (strcasecmp(optionName, "serial") == 0) {
1231 strcpy(serial, value);
1232 serialnumber = serial;
1233 }
1234 else if (strcasecmp(optionName, "location") == 0 && location) {
1235 *location = strtol(value, NULL, 16);
1236 }
1237 }
1238
1239 return;
1240 }
1241
1242
1243 /*!
1244 * @function setup_cfLanguage
1245 * @abstract Convert the contents of the CUPS 'LANG' environment
1246 * variable into a one element CF array of languages.
1247 *
1248 * @discussion Each submitted job comes with a natural language. CUPS passes
1249 * that language in an environment variable. We take that language
1250 * and jam it into the AppleLanguages array so that CF will use
1251 * it when reading localized resources. We need to do this before
1252 * any CF code reads and caches the languages array, so this function
1253 * should be called early in main()
1254 */
1255 static void setup_cfLanguage(void)
1256 {
1257 CFStringRef lang[1] = {NULL};
1258 CFArrayRef langArray = NULL;
1259 const char *requestedLang = NULL;
1260
1261 requestedLang = getenv("LANG");
1262 if (requestedLang != NULL) {
1263 lang[0] = CFStringCreateWithCString(kCFAllocatorDefault, requestedLang, kCFStringEncodingUTF8);
1264 langArray = CFArrayCreate(kCFAllocatorDefault, (const void **)lang, sizeof(lang) / sizeof(lang[0]), &kCFTypeArrayCallBacks);
1265
1266 CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication);
1267 DEBUG_printf((stderr, "DEBUG: usb: AppleLanguages = \"%s\"\n", requestedLang));
1268
1269 CFRelease(lang[0]);
1270 CFRelease(langArray);
1271 } else {
1272 fputs("DEBUG: usb: LANG environment variable missing.\n", stderr);
1273 }
1274 }
1275
1276 #pragma mark -
1277 #if defined(__i386__)
1278 /*!
1279 * @function run_ppc_backend
1280 *
1281 * @abstract Starts child backend process running as a ppc executable.
1282 *
1283 * @result Never returns; always calls exit().
1284 *
1285 * @discussion
1286 */
1287 static void run_ppc_backend(int argc, char *argv[], int fd)
1288 {
1289 int i;
1290 int exitstatus = 0;
1291 int childstatus;
1292 pid_t waitpid_status;
1293 char *my_argv[32];
1294 char *usb_ppc_status;
1295
1296 /*
1297 * If we're running as i386 and couldn't load the class driver (because they'it's
1298 * ppc-only) then try to re-exec ourselves in ppc mode to try again. If we don't have
1299 * a ppc architecture we may be running i386 again so guard against this by setting
1300 * and testing an environment variable...
1301 */
1302 usb_ppc_status = getenv("USB_PPC_STATUS");
1303
1304 if (usb_ppc_status == NULL) {
1305 /* Catch SIGTERM if we are _not_ printing data from
1306 * stdin (otherwise you can't cancel raw jobs...)
1307 */
1308
1309 if (fd != 0) {
1310 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1311 sigset(SIGTERM, sigterm_handler);
1312 #elif defined(HAVE_SIGACTION)
1313 struct sigaction action; /* Actions for POSIX signals */
1314 memset(&action, 0, sizeof(action));
1315 sigaddset(&action.sa_mask, SIGTERM);
1316 action.sa_handler = sigterm_handler;
1317 sigaction(SIGTERM, &action, NULL);
1318 #else
1319 signal(SIGTERM, sigterm_handler);
1320 #endif /* HAVE_SIGSET */
1321 }
1322
1323 if ((child_pid = fork()) == 0) {
1324 /* Child comes here. */
1325 setenv("USB_PPC_STATUS", "1", false);
1326
1327 /* Tell the kernel we want the next exec call to favor the ppc architecture... */
1328 int mib[] = { CTL_KERN, KERN_AFFINITY, 1, 1 };
1329 int namelen = 4;
1330 sysctl(mib, namelen, NULL, NULL, NULL, 0);
1331
1332 /* Set up the arguments and call exec... */
1333 for (i = 0; i < argc && i < (sizeof(my_argv)/sizeof(my_argv[0])) - 1; i++)
1334 my_argv[i] = argv[i];
1335
1336 my_argv[i] = NULL;
1337
1338 execv("/usr/libexec/cups/backend/usb", my_argv);
1339
1340 fprintf(stderr, "DEBUG: execv: %s\n", strerror(errno));
1341 exitstatus = errno;
1342 }
1343 else if (child_pid > 0) {
1344 /* Parent comes here.
1345 *
1346 * Close the fds we won't be using then wait for the child backend to exit.
1347 */
1348 close(fd);
1349 close(1);
1350
1351 fprintf(stderr, "DEBUG: Started usb(ppc) backend (PID %d)\n", (int)child_pid);
1352
1353 while ((waitpid_status = waitpid(child_pid, &childstatus, 0)) == (pid_t)-1 && errno == EINTR)
1354 usleep(1000);
1355
1356 if (WIFSIGNALED(childstatus)) {
1357 exitstatus = WTERMSIG(childstatus);
1358 fprintf(stderr, "DEBUG: usb(ppc) backend %d crashed on signal %d!\n", child_pid, exitstatus);
1359 }
1360 else {
1361 if ((exitstatus = WEXITSTATUS(childstatus)) != 0)
1362 fprintf(stderr, "DEBUG: usb(ppc) backend %d stopped with status %d!\n", child_pid, exitstatus);
1363 else
1364 fprintf(stderr, "DEBUG: PID %d exited with no errors\n", child_pid);
1365 }
1366 }
1367 else {
1368 /* fork() error */
1369 fprintf(stderr, "DEBUG: fork: %s\n", strerror(errno));
1370 exitstatus = errno;
1371 }
1372 }
1373 else {
1374 fputs("DEBUG: usb child running i386 again\n", stderr);
1375 exitstatus = ENOENT;
1376 }
1377
1378 exit(exitstatus);
1379 }
1380
1381 /*
1382 * 'sigterm_handler()' - SIGTERM handler.
1383 */
1384
1385 static void sigterm_handler(int sig)
1386 {
1387 /* If we started a child process pass the signal on to it...
1388 */
1389 if (child_pid)
1390 kill(child_pid, sig);
1391
1392 exit(1);
1393 }
1394
1395 #endif /* __i386__ */
1396
1397
1398 #ifdef PARSE_PS_ERRORS
1399 /*
1400 * 'next_line()' - Find the next line in a buffer.
1401 */
1402
1403 static const char *next_line (const char *buffer)
1404 {
1405 const char *cptr, *lptr = NULL;
1406
1407 for (cptr = buffer; *cptr && lptr == NULL; cptr++)
1408 if (*cptr == '\n' || *cptr == '\r')
1409 lptr = cptr;
1410 return lptr;
1411 }
1412
1413
1414 /*
1415 * 'parse_pserror()' - Scan the backchannel data for postscript errors.
1416 */
1417
1418 static void parse_pserror (char *sockBuffer, int len)
1419 {
1420 static char gErrorBuffer[1024] = "";
1421 static char *gErrorBufferPtr = gErrorBuffer;
1422 static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
1423
1424 char *pCommentBegin, *pCommentEnd, *pLineEnd;
1425 char *logLevel;
1426 char logstr[1024];
1427 int logstrlen;
1428
1429 if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
1430 gErrorBufferPtr = gErrorBuffer;
1431 if (len > sizeof(gErrorBuffer) - 1)
1432 len = sizeof(gErrorBuffer) - 1;
1433
1434 memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
1435 gErrorBufferPtr += len;
1436 *(gErrorBufferPtr + 1) = '\0';
1437
1438
1439 pLineEnd = (char *)next_line((const char *)gErrorBuffer);
1440 while (pLineEnd != NULL) {
1441 *pLineEnd++ = '\0';
1442
1443 pCommentBegin = strstr(gErrorBuffer,"%%[");
1444 pCommentEnd = strstr(gErrorBuffer, "]%%");
1445 if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL) {
1446 pCommentEnd += 3; /* Skip past "]%%" */
1447 *pCommentEnd = '\0'; /* There's always room for the nul */
1448
1449 if (strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
1450 logLevel = "DEBUG";
1451 else if (strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
1452 logLevel = "DEBUG";
1453 else
1454 logLevel = "INFO";
1455
1456 if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr)) {
1457 /* If the string was trucnated make sure it has a linefeed before the nul */
1458 logstrlen = sizeof(logstr) - 1;
1459 logstr[logstrlen - 1] = '\n';
1460 }
1461 write(STDERR_FILENO, logstr, logstrlen);
1462 }
1463
1464 /* move everything over... */
1465 strcpy(gErrorBuffer, pLineEnd);
1466 gErrorBufferPtr = gErrorBuffer;
1467 pLineEnd = (char *)next_line((const char *)gErrorBuffer);
1468 }
1469 }
1470 #endif /* PARSE_PS_ERRORS */
1471
1472
1473 /*
1474 * 'read_thread()' - A thread to read the backchannel data.
1475 */
1476
1477 static void *read_thread(void *reference)
1478 {
1479 /* post a read to the device and write results to stdout
1480 * the final pending read will be Aborted in the main thread
1481 */
1482 UInt8 readbuffer[512];
1483 UInt32 rbytes;
1484 kern_return_t readstatus;
1485 printer_data_t *userData = (printer_data_t *)reference;
1486 classdriver_context_t **classdriver = userData->printerDriver;
1487 struct mach_timebase_info timeBaseInfo;
1488 uint64_t start,
1489 delay;
1490
1491 /* Calculate what 250 milliSeconds are in mach absolute time...
1492 */
1493 mach_timebase_info(&timeBaseInfo);
1494 delay = ((uint64_t)250000000 * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer;
1495
1496 do {
1497 /* Remember when we started so we can throttle the loop after the read call...
1498 */
1499 start = mach_absolute_time();
1500
1501 rbytes = sizeof(readbuffer);
1502 readstatus = (*classdriver)->ReadPipe( classdriver, readbuffer, &rbytes );
1503 if ( kIOReturnSuccess == readstatus && rbytes > 0 ) {
1504
1505 cupsBackChannelWrite((char*)readbuffer, rbytes, 1.0);
1506
1507 /* cntrl-d is echoed by the printer.
1508 * NOTES:
1509 * Xerox Phaser 6250D doesn't echo the cntrl-d.
1510 * Xerox Phaser 6250D doesn't always send the product query.
1511 */
1512 if (userData->waitEOF && readbuffer[rbytes-1] == 0x4)
1513 break;
1514 #ifdef PARSE_PS_ERRORS
1515 parse_pserror(readbuffer, rbytes);
1516 #endif
1517 }
1518
1519 /* Make sure this loop executes no more than once every 250 miliseconds...
1520 */
1521 if ((readstatus != kIOReturnSuccess || rbytes == 0) && (userData->waitEOF || !userData->done))
1522 mach_wait_until(start + delay);
1523
1524 } while ( userData->waitEOF || !userData->done ); /* Abort from main thread tests error here */
1525
1526 /* Let the other thread (main thread) know that we have completed the read thread...
1527 */
1528 pthread_mutex_lock(&userData->readMutex);
1529 pthread_cond_signal(&userData->readCompleteCondition);
1530 pthread_mutex_unlock(&userData->readMutex);
1531
1532 return NULL;
1533 }
1534
1535 /*
1536 * 'reqestWait_thread()' - A thread cupsSideChannelDoRequest wait.
1537 */
1538 static void *reqestWait_thread(void *reference) {
1539 printer_data_t *userData = (printer_data_t *)reference;
1540 int datalen;
1541 cups_sc_command_t command;
1542 cups_sc_status_t status;
1543 uint64_t start, delay;
1544 struct mach_timebase_info timeBaseInfo;
1545 char data[2048];
1546
1547 /*
1548 * Calculate what 100 milliSeconds are in mach absolute time...
1549 */
1550 mach_timebase_info(&timeBaseInfo);
1551 delay = ((uint64_t)100000000 * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer;
1552
1553 /* interface close wait mutex lock. */
1554 pthread_mutex_lock(&(userData->waitCloseMutex));
1555
1556 do {
1557 /*
1558 * Remember when we started so we can throttle the loop after the cupsSideChannelDoRequest call...
1559 */
1560 start = mach_absolute_time();
1561
1562 /* Poll for a command... */
1563 command=0;
1564 datalen = sizeof(data);
1565 bzero(data, sizeof(data));
1566
1567 if (!cupsSideChannelRead(&command, &status, data, &datalen, 0.0)) {
1568 datalen = sizeof(data);
1569
1570 switch (command) {
1571 case CUPS_SC_CMD_SOFT_RESET:
1572 /* do a soft reset */
1573 usbSoftReset(userData, &status);
1574 datalen = 0;
1575 userData->reqWaitDone = 1;
1576 break;
1577 case CUPS_SC_CMD_DRAIN_OUTPUT:
1578 /* drain all pending output */
1579 usbDrainOutput(userData, &status);
1580 datalen = 0;
1581 break;
1582 case CUPS_SC_CMD_GET_BIDI:
1583 /* return whether the connection is bidirectional */
1584 usbGetBidirectional(userData, &status, data, &datalen);
1585 break;
1586 case CUPS_SC_CMD_GET_DEVICE_ID:
1587 /* return the IEEE-1284 device ID */
1588 usbGetDeviceID(userData, &status, data, &datalen);
1589 break;
1590 case CUPS_SC_CMD_GET_STATE:
1591 /* return the device state */
1592 usbGetDevState(userData, &status, data, &datalen);
1593 break;
1594 default:
1595 status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
1596 datalen = 0;
1597 break;
1598 }
1599
1600 if (userData->writeDone) {
1601 status = CUPS_SC_STATUS_NONE;
1602 }
1603
1604 /* Send a response... */
1605 cupsSideChannelWrite(command, status, data, datalen, 1.0);
1606 }
1607
1608 /*
1609 * Make sure this loop executes no more than once every 500 miliseconds...
1610 */
1611 if ((userData->waitEOF) || (!userData->reqWaitDone)) {
1612 mach_wait_until(start + delay);
1613 }
1614 } while(!userData->reqWaitDone);
1615
1616 sleep(1);
1617 pthread_mutex_lock(&userData->reqWaitMutex);
1618 userData->reqWqitFlag = 1;
1619 pthread_cond_signal(&userData->reqWaitCompCond);
1620 pthread_mutex_unlock(&userData->reqWaitMutex);
1621
1622 /* interface close wait mutex unlock. */
1623 pthread_mutex_unlock(&(userData->waitCloseMutex));
1624
1625 return NULL;
1626 }
1627
1628 #pragma mark -
1629 /*
1630 * 'usbSoftReset'
1631 */
1632 static void usbSoftReset(printer_data_t *userData, cups_sc_status_t *status) {
1633 OSStatus err;
1634
1635 /* write stop. */
1636 userData->writeDone = 1;
1637
1638 /* Abort (print_device()-WritePipe kIOReturnAborted return) */
1639 if (userData->printerDriver != NULL)
1640 err = (*(userData->printerDriver))->Abort(userData->printerDriver);
1641
1642 /* print_device() WritePipe_Loop break wait. */
1643 pthread_mutex_lock(&(userData->writeCompMutex));
1644 pthread_mutex_unlock(&(userData->writeCompMutex));
1645
1646 /* SoftReset */
1647 if (userData->printerDriver != NULL)
1648 (*(userData->printerDriver))->SoftReset(userData->printerDriver, 0);
1649
1650 if (status != NULL)
1651 *status = CUPS_SC_STATUS_OK;
1652 }
1653
1654 /*
1655 * 'usbDrainOutput'
1656 */
1657 static void usbDrainOutput(printer_data_t *userData, cups_sc_status_t *status) {
1658 OSStatus osSts = noErr; /* Function results */
1659 OSStatus err = noErr;
1660 UInt32 wbytes; /* Number of bytes written */
1661 ssize_t nbytes; /* Number of bytes read */
1662 char *bufptr;
1663
1664 bufptr = userData->dataBuffer+userData->dataOffset;
1665 nbytes = userData->dataSize;
1666
1667 while((nbytes > 0) && (osSts == noErr)) {
1668 wbytes = nbytes;
1669 osSts = (*(userData->printerDriver))->WritePipe(userData->printerDriver, (UInt8*)bufptr, &wbytes, 0);
1670
1671 if (wbytes < 0 || noErr != osSts) {
1672 if (osSts != kIOReturnAborted) {
1673 err = (*(userData->printerDriver))->Abort(userData->printerDriver);
1674 break;
1675 }
1676 }
1677
1678 nbytes -= wbytes;
1679 bufptr += wbytes;
1680 }
1681
1682 if (status != NULL) {
1683 if ((osSts != noErr) || (err != noErr)) {
1684 *status = CUPS_SC_STATUS_IO_ERROR;
1685 } else {
1686 *status = CUPS_SC_STATUS_OK;
1687 }
1688 }
1689 }
1690
1691 /*
1692 * 'usbGetBidirectional'
1693 */
1694 static void usbGetBidirectional(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen) {
1695 *data = userData->directionalFlag;
1696 *datalen = 1;
1697
1698 if (status != NULL)
1699 *status = CUPS_SC_STATUS_OK;
1700 }
1701
1702 /*
1703 * 'usbGetDeviceID'
1704 */
1705 static void usbGetDeviceID(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen) {
1706 UInt32 deviceLocation = 0;
1707 CFStringRef deviceIDString = NULL;
1708
1709 /* GetDeviceID */
1710 copy_devicestring(userData->printerObj, &deviceIDString, &deviceLocation);
1711 CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);
1712 *datalen = strlen(data);
1713
1714 if (status != NULL) {
1715 *status = CUPS_SC_STATUS_OK;
1716 }
1717 }
1718
1719 /*
1720 * 'usbGetDevState'
1721 */
1722 static void usbGetDevState(printer_data_t *userData, cups_sc_status_t *status, char *data, int *datalen) {
1723 *data = CUPS_SC_STATE_ONLINE;
1724 *datalen = 1;
1725
1726 if (status != NULL) {
1727 *status = CUPS_SC_STATUS_OK;
1728 }
1729 }
1730
1731 /*
1732 * End of "$Id: usb-darwin.c 6491 2007-04-30 18:21:52Z mike $".
1733 */