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