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