1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * Copyright (C) 2016 Mopria Alliance, Inc. 4 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <sys/stat.h> 22 #include <fcntl.h> 23 24 #ifndef _GNU_SOURCE 25 #define _GNU_SOURCE 26 #endif 27 #ifndef __USE_UNIX98 28 #define __USE_UNIX98 29 #endif 30 31 #include <pthread.h> 32 33 #include <semaphore.h> 34 #include <printer_capabilities_types.h> 35 36 #include "ifc_print_job.h" 37 #include "wprint_debug.h" 38 #include "plugin_db.h" 39 40 #include "ifc_status_monitor.h" 41 42 #include "ippstatus_monitor.h" 43 #include "ippstatus_capabilities.h" 44 #include "ipp_print.h" 45 #include "ipphelper.h" 46 47 #include "lib_printable_area.h" 48 #include "wprint_io_plugin.h" 49 #include "../plugins/media.h" 50 51 #define TAG "lib_wprint" 52 53 /* As expected by target devices */ 54 #define USERAGENT_PREFIX "wPrintAndroid" 55 56 #define USE_PWG_OVER_PCLM 0 57 58 #if (USE_PWG_OVER_PCLM != 0) 59 #define _DEFAULT_PRINT_FORMAT PRINT_FORMAT_PWG 60 #define _DEFAULT_PCL_TYPE PCLPWG 61 #else // (USE_PWG_OVER_PCLM != 0) 62 #define _DEFAULT_PRINT_FORMAT PRINT_FORMAT_PCLM 63 #define _DEFAULT_PCL_TYPE PCLm 64 #endif // (USE_PWG_OVER_PCLM != 0) 65 66 #define _MAX_SPOOLED_JOBS 100 67 #define _MAX_MSGS (_MAX_SPOOLED_JOBS * 5) 68 69 #define _MAX_PAGES_PER_JOB 1000 70 71 #define MAX_IDLE_WAIT (5 * 60) 72 73 #define DEFAULT_RESOLUTION (300) 74 75 // When searching for a supported resolution this is the max resolution we will consider. 76 #define MAX_SUPPORTED_RESOLUTION (720) 77 78 #define MAX_DONE_WAIT (5 * 60) 79 #define MAX_START_WAIT (45) 80 81 #define IO_PORT_FILE 0 82 83 /* 84 * The following macros allow for up to 8 bits (256) for spooled job id#s and 85 * 24 bits (16 million) of a running sequence number to provide a reasonably 86 * unique job handle 87 */ 88 89 // _ENCODE_HANDLE() is only called from _get_handle() 90 #define _ENCODE_HANDLE(X) ( (((++_running_number) & 0xffffff) << 8) | ((X) & 0xff) ) 91 #define _DECODE_HANDLE(X) ((X) & 0xff) 92 93 #undef snprintf 94 #undef vsnprintf 95 96 typedef enum { 97 JOB_STATE_FREE, // queue element free 98 JOB_STATE_QUEUED, // job queued and waiting to be run 99 JOB_STATE_RUNNING, // job running (printing) 100 JOB_STATE_BLOCKED, // print job blocked due to printer stall/error 101 JOB_STATE_CANCEL_REQUEST, // print job cancelled by user, 102 JOB_STATE_CANCELLED, // print job cancelled by user, waiting to be freed 103 JOB_STATE_COMPLETED, // print job completed successfully, waiting to be freed 104 JOB_STATE_ERROR, // job could not be run due to error 105 JOB_STATE_CORRUPTED, // job could not be run due to error 106 107 NUM_JOB_STATES 108 } _job_state_t; 109 110 typedef enum { 111 TOP_MARGIN = 0, 112 LEFT_MARGIN, 113 RIGHT_MARGIN, 114 BOTTOM_MARGIN, 115 116 NUM_PAGE_MARGINS 117 } _page_margins_t; 118 119 typedef enum { 120 MSG_RUN_JOB, MSG_QUIT, 121 } wprint_msg_t; 122 123 typedef struct { 124 wprint_msg_t id; 125 wJob_t job_id; 126 } _msg_t; 127 128 /* 129 * Define an entry in the job queue 130 */ 131 typedef struct { 132 wJob_t job_handle; 133 _job_state_t job_state; 134 unsigned int blocked_reasons; 135 wprint_status_cb_t cb_fn; 136 char *printer_addr; 137 port_t port_num; 138 wprint_plugin_t *plugin; 139 ifc_print_job_t *print_ifc; 140 char *mime_type; 141 char *pathname; 142 bool is_dir; 143 bool last_page_seen; 144 int num_pages; 145 msg_q_id pageQ; 146 msg_q_id saveQ; 147 148 wprint_job_params_t job_params; 149 bool cancel_ok; 150 bool use_secure_uri; 151 152 const ifc_status_monitor_t *status_ifc; 153 char debug_path[MAX_PATHNAME_LENGTH + 1]; 154 char printer_uri[1024]; 155 int job_debug_fd; 156 int page_debug_fd; 157 } _job_queue_t; 158 159 /* 160 * An entry for queued pages 161 */ 162 typedef struct { 163 int page_num; 164 bool pdf_page; 165 bool last_page; 166 bool corrupted; 167 char filename[MAX_PATHNAME_LENGTH + 1]; 168 unsigned int top_margin; 169 unsigned int left_margin; 170 unsigned int right_margin; 171 unsigned int bottom_margin; 172 } _page_t; 173 174 /* 175 * Entry for a registered plugin 176 */ 177 typedef struct { 178 port_t port_num; 179 const wprint_io_plugin_t *io_plugin; 180 } _io_plugin_t; 181 182 static _job_queue_t _job_queue[_MAX_SPOOLED_JOBS]; 183 static msg_q_id _msgQ; 184 185 static pthread_t _job_status_tid; 186 static pthread_t _job_tid; 187 188 static pthread_mutex_t _q_lock; 189 static pthread_mutexattr_t _q_lock_attr; 190 191 static sem_t _job_end_wait_sem; 192 static sem_t _job_start_wait_sem; 193 194 static _io_plugin_t _io_plugins[2]; 195 196 char g_osName[MAX_ID_STRING_LENGTH + 1] = {0}; 197 char g_appName[MAX_ID_STRING_LENGTH + 1] = {0}; 198 char g_appVersion[MAX_ID_STRING_LENGTH + 1] = {0}; 199 200 /* 201 * Convert a pcl_t type to a human-readable string 202 */ 203 static char *getPCLTypeString(pcl_t pclenum) { 204 switch (pclenum) { 205 case PCLNONE: 206 return "PCL_NONE"; 207 case PCLm: 208 return "PCLm"; 209 case PCLJPEG: 210 return "PCL_JPEG"; 211 case PCLPWG: 212 return "PWG-Raster"; 213 default: 214 return "unkonwn PCL Type"; 215 } 216 } 217 218 /* 219 * Return a _job_queue_t item by its job_handle or NULL if not found. 220 */ 221 static _job_queue_t *_get_job_desc(wJob_t job_handle) { 222 unsigned long index; 223 if (job_handle == WPRINT_BAD_JOB_HANDLE) { 224 return NULL; 225 } 226 index = _DECODE_HANDLE(job_handle); 227 if ((index < _MAX_SPOOLED_JOBS) && (_job_queue[index].job_handle == job_handle) && 228 (_job_queue[index].job_state != JOB_STATE_FREE)) { 229 return (&_job_queue[index]); 230 } else { 231 return NULL; 232 } 233 } 234 235 /* 236 * Functions below to fill out the _debug_stream_ifc interface 237 */ 238 239 static void _stream_dbg_end_job(wJob_t job_handle) { 240 _job_queue_t *jq = _get_job_desc(job_handle); 241 if (jq && (jq->job_debug_fd >= 0)) { 242 close(jq->job_debug_fd); 243 jq->job_debug_fd = -1; 244 } 245 } 246 247 static void _stream_dbg_start_job(wJob_t job_handle, const char *ext) { 248 _stream_dbg_end_job(job_handle); 249 _job_queue_t *jq = _get_job_desc(job_handle); 250 if (jq && jq->debug_path[0]) { 251 char filename[MAX_PATHNAME_LENGTH + 1]; 252 snprintf(filename, MAX_PATHNAME_LENGTH, "%s/jobstream.%s", jq->debug_path, ext); 253 filename[MAX_PATHNAME_LENGTH] = 0; 254 jq->job_debug_fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); 255 } 256 } 257 258 static void _stream_dbg_job_data(wJob_t job_handle, const unsigned char *buff, 259 unsigned long nbytes) { 260 _job_queue_t *jq = _get_job_desc(job_handle); 261 ssize_t bytes_written; 262 if (jq && (jq->job_debug_fd >= 0)) { 263 while (nbytes > 0) { 264 bytes_written = write(jq->job_debug_fd, buff, nbytes); 265 if (bytes_written < 0) { 266 return; 267 } 268 nbytes -= bytes_written; 269 buff += bytes_written; 270 } 271 } 272 } 273 274 static void _stream_dbg_end_page(wJob_t job_handle) { 275 _job_queue_t *jq = _get_job_desc(job_handle); 276 if (jq && (jq->page_debug_fd >= 0)) { 277 close(jq->page_debug_fd); 278 jq->page_debug_fd = -1; 279 } 280 } 281 282 static void _stream_dbg_page_data(wJob_t job_handle, const unsigned char *buff, 283 unsigned long nbytes) { 284 _job_queue_t *jq = _get_job_desc(job_handle); 285 ssize_t bytes_written; 286 if (jq && (jq->page_debug_fd >= 0)) { 287 while (nbytes > 0) { 288 bytes_written = write(jq->page_debug_fd, buff, nbytes); 289 if (bytes_written < 0) { 290 return; 291 } 292 nbytes -= bytes_written; 293 buff += bytes_written; 294 } 295 } 296 } 297 298 #define PPM_IDENTIFIER "P6\n" 299 #define PPM_HEADER_LENGTH 128 300 301 static void _stream_dbg_start_page(wJob_t job_handle, int page_number, int width, int height) { 302 _stream_dbg_end_page(job_handle); 303 _job_queue_t *jq = _get_job_desc(job_handle); 304 if (jq && jq->debug_path[0]) { 305 union { 306 char filename[MAX_PATHNAME_LENGTH + 1]; 307 char ppm_header[PPM_HEADER_LENGTH + 1]; 308 } buff; 309 snprintf(buff.filename, MAX_PATHNAME_LENGTH, "%s/page%4.4d.ppm", jq->debug_path, 310 page_number); 311 buff.filename[MAX_PATHNAME_LENGTH] = 0; 312 jq->page_debug_fd = open(buff.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); 313 int length = snprintf(buff.ppm_header, sizeof(buff.ppm_header), "%s\n#%*c\n%d %d\n%d\n", 314 PPM_IDENTIFIER, 0, ' ', width, height, 255); 315 int padding = sizeof(buff.ppm_header) - length; 316 snprintf(buff.ppm_header, sizeof(buff.ppm_header), "%s\n#%*c\n%d %d\n%d\n", 317 PPM_IDENTIFIER, padding, ' ', width, height, 255); 318 _stream_dbg_page_data(job_handle, (const unsigned char *) buff.ppm_header, 319 PPM_HEADER_LENGTH); 320 } 321 } 322 323 static const ifc_wprint_debug_stream_t _debug_stream_ifc = { 324 .debug_start_job = _stream_dbg_start_job, .debug_job_data = _stream_dbg_job_data, 325 .debug_end_job = _stream_dbg_end_job, .debug_start_page = _stream_dbg_start_page, 326 .debug_page_data = _stream_dbg_page_data, .debug_end_page = _stream_dbg_end_page 327 }; 328 329 /* 330 * Return the debug stream interface corresponding to the specified job handle 331 */ 332 const ifc_wprint_debug_stream_t *getDebugStreamIfc(wJob_t handle) { 333 _job_queue_t *jq = _get_job_desc(handle); 334 if (jq) { 335 return (jq->debug_path[0] == 0) ? NULL : &_debug_stream_ifc; 336 } 337 return NULL; 338 } 339 340 const ifc_wprint_t _wprint_ifc = { 341 .msgQCreate = msgQCreate, .msgQDelete = msgQDelete, 342 .msgQSend = msgQSend, .msgQReceive = msgQReceive, .msgQNumMsgs = msgQNumMsgs, 343 .get_debug_stream_ifc = getDebugStreamIfc 344 }; 345 346 static pcl_t _default_pcl_type = _DEFAULT_PCL_TYPE; 347 348 static const ifc_print_job_t *_printer_file_connect(const ifc_wprint_t *wprint_ifc) { 349 return printer_connect(IO_PORT_FILE); 350 } 351 352 static const ifc_printer_capabilities_t *_get_caps_ifc(port_t port_num) { 353 int i; 354 for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) { 355 if (_io_plugins[i].port_num == port_num) { 356 if (_io_plugins[i].io_plugin == NULL) { 357 return NULL; 358 } 359 if (_io_plugins[i].io_plugin->getCapsIFC == NULL) { 360 return NULL; 361 } else { 362 return (_io_plugins[i].io_plugin->getCapsIFC(&_wprint_ifc)); 363 } 364 } 365 } 366 return NULL; 367 } 368 369 static const ifc_status_monitor_t *_get_status_ifc(port_t port_num) { 370 int i; 371 for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) { 372 if (_io_plugins[i].port_num == port_num) { 373 if (_io_plugins[i].io_plugin == NULL) { 374 return NULL; 375 } 376 if (_io_plugins[i].io_plugin->getStatusIFC == NULL) { 377 return NULL; 378 } else { 379 return (_io_plugins[i].io_plugin->getStatusIFC(&_wprint_ifc)); 380 } 381 } 382 } 383 return NULL; 384 } 385 386 static const ifc_print_job_t *_get_print_ifc(port_t port_num) { 387 int i; 388 for (i = 0; i < ARRAY_SIZE(_io_plugins); i++) { 389 if (_io_plugins[i].port_num == port_num) { 390 if (_io_plugins[i].io_plugin == NULL) { 391 return NULL; 392 } 393 if (_io_plugins[i].io_plugin->getPrintIFC == NULL) { 394 return NULL; 395 } else { 396 return (_io_plugins[i].io_plugin->getPrintIFC(&_wprint_ifc)); 397 } 398 } 399 } 400 return NULL; 401 } 402 403 /* 404 * Lock the semaphore for this module 405 */ 406 static void _lock(void) { 407 pthread_mutex_lock(&_q_lock); 408 } 409 410 /* 411 * Unlock the semaphore for this module 412 */ 413 static void _unlock(void) { 414 pthread_mutex_unlock(&_q_lock); 415 } 416 417 static wJob_t _get_handle(void) { 418 static unsigned long _running_number = 0; 419 wJob_t job_handle = WPRINT_BAD_JOB_HANDLE; 420 int i, index, size; 421 char *ptr; 422 423 for (i = 0; i < _MAX_SPOOLED_JOBS; i++) { 424 index = (i + _running_number) % _MAX_SPOOLED_JOBS; 425 426 if (_job_queue[index].job_state == JOB_STATE_FREE) { 427 size = MAX_MIME_LENGTH + MAX_PRINTER_ADDR_LENGTH + MAX_PATHNAME_LENGTH + 4; 428 ptr = malloc(size); 429 if (ptr) { 430 memset(&_job_queue[index], 0, sizeof(_job_queue_t)); 431 memset(ptr, 0, size); 432 433 _job_queue[index].job_debug_fd = -1; 434 _job_queue[index].page_debug_fd = -1; 435 _job_queue[index].printer_addr = ptr; 436 437 ptr += (MAX_PRINTER_ADDR_LENGTH + 1); 438 _job_queue[index].mime_type = ptr; 439 ptr += (MAX_MIME_LENGTH + 1); 440 _job_queue[index].pathname = ptr; 441 442 _job_queue[index].job_state = JOB_STATE_QUEUED; 443 _job_queue[index].job_handle = _ENCODE_HANDLE(index); 444 445 job_handle = _job_queue[index].job_handle; 446 } 447 break; 448 } 449 } 450 return job_handle; 451 } 452 453 static int _recycle_handle(wJob_t job_handle) { 454 _job_queue_t *jq = _get_job_desc(job_handle); 455 456 if (jq == NULL) { 457 return ERROR; 458 } else if ((jq->job_state == JOB_STATE_CANCELLED) || (jq->job_state == JOB_STATE_ERROR) || 459 (jq->job_state == JOB_STATE_CORRUPTED) || (jq->job_state == JOB_STATE_COMPLETED)) { 460 if (jq->print_ifc != NULL) { 461 jq->print_ifc->destroy(jq->print_ifc); 462 } 463 464 jq->print_ifc = NULL; 465 if (jq->status_ifc != NULL) { 466 jq->status_ifc->destroy(jq->status_ifc); 467 } 468 jq->status_ifc = NULL; 469 if (jq->job_params.useragent != NULL) { 470 free((void *) jq->job_params.useragent); 471 } 472 free(jq->printer_addr); 473 jq->job_state = JOB_STATE_FREE; 474 if (jq->job_debug_fd != -1) { 475 close(jq->job_debug_fd); 476 } 477 jq->job_debug_fd = -1; 478 if (jq->page_debug_fd != -1) { 479 close(jq->page_debug_fd); 480 } 481 jq->page_debug_fd = -1; 482 jq->debug_path[0] = 0; 483 484 return OK; 485 } else { 486 return ERROR; 487 } 488 } 489 490 /* 491 * Stops the job status thread if it exists 492 */ 493 static int _stop_status_thread(_job_queue_t *jq) { 494 if (!pthread_equal(_job_status_tid, pthread_self()) && (jq && jq->status_ifc)) { 495 (jq->status_ifc->stop)(jq->status_ifc); 496 _unlock(); 497 pthread_join(_job_status_tid, 0); 498 _lock(); 499 _job_status_tid = pthread_self(); 500 return OK; 501 } else { 502 return ERROR; 503 } 504 } 505 506 /* 507 * Handles a new status message from the printer. Based on the status of wprint and the printer, 508 * this function will start/end a job, send another page, or return blocking errors. 509 */ 510 static void _job_status_callback(const printer_state_dyn_t *new_status, 511 const printer_state_dyn_t *old_status, void *param) { 512 wprint_job_callback_params_t cb_param; 513 _job_queue_t *jq = (_job_queue_t *) param; 514 unsigned int i, blocked_reasons; 515 print_status_t statusnew, statusold; 516 517 statusnew = new_status->printer_status & ~PRINTER_IDLE_BIT; 518 statusold = old_status->printer_status & ~PRINTER_IDLE_BIT; 519 520 LOGD("_job_status_callback(): current printer state: %d", statusnew); 521 blocked_reasons = 0; 522 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { 523 if (new_status->printer_reasons[i] == PRINT_STATUS_MAX_STATE) { 524 break; 525 } 526 LOGD("_job_status_callback(): blocking reason %d: %d", i, new_status->printer_reasons[i]); 527 blocked_reasons |= (1 << new_status->printer_reasons[i]); 528 } 529 530 switch (statusnew) { 531 case PRINT_STATUS_UNKNOWN: 532 if ((new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) 533 || (new_status->printer_reasons[0] == PRINT_STATUS_UNKNOWN)) { 534 sem_post(&_job_start_wait_sem); 535 sem_post(&_job_end_wait_sem); 536 _lock(); 537 if ((new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) 538 && ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL))) { 539 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 540 } 541 _unlock(); 542 } 543 break; 544 545 case PRINT_STATUS_IDLE: 546 if ((statusold > PRINT_STATUS_IDLE) || (statusold == PRINT_STATUS_CANCELLED)) { 547 // Print is over but the job wasn't ended correctly 548 if (jq->is_dir && !jq->last_page_seen) { 549 wprintPage(jq->job_handle, jq->num_pages + 1, NULL, true, false, 0, 0, 0, 0); 550 } 551 sem_post(&_job_end_wait_sem); 552 } 553 break; 554 555 case PRINT_STATUS_CANCELLED: 556 sem_post(&_job_start_wait_sem); 557 if ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL)) { 558 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 559 } 560 if (statusold != PRINT_STATUS_CANCELLED) { 561 LOGI("status requested job cancel"); 562 if (new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) { 563 sem_post(&_job_start_wait_sem); 564 sem_post(&_job_end_wait_sem); 565 if ((jq->print_ifc != NULL) && (jq->print_ifc->enable_timeout != NULL)) { 566 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 567 } 568 } 569 _lock(); 570 jq->job_params.cancelled = true; 571 _unlock(); 572 } 573 if (new_status->printer_reasons[0] == PRINT_STATUS_OFFLINE) { 574 sem_post(&_job_start_wait_sem); 575 sem_post(&_job_end_wait_sem); 576 } 577 break; 578 579 case PRINT_STATUS_PRINTING: 580 sem_post(&_job_start_wait_sem); 581 _lock(); 582 if ((jq->job_state != JOB_STATE_RUNNING) || (jq->blocked_reasons != blocked_reasons)) { 583 jq->job_state = JOB_STATE_RUNNING; 584 jq->blocked_reasons = blocked_reasons; 585 if (jq->cb_fn) { 586 cb_param.state = JOB_RUNNING; 587 cb_param.blocked_reasons = jq->blocked_reasons; 588 cb_param.job_done_result = OK; 589 590 jq->cb_fn(jq->job_handle, (void *) &cb_param); 591 } 592 } 593 _unlock(); 594 break; 595 596 case PRINT_STATUS_UNABLE_TO_CONNECT: 597 sem_post(&_job_start_wait_sem); 598 _lock(); 599 _stop_status_thread(jq); 600 601 jq->blocked_reasons = blocked_reasons; 602 jq->job_params.cancelled = true; 603 jq->job_state = JOB_STATE_ERROR; 604 if (jq->cb_fn) { 605 cb_param.state = JOB_DONE; 606 cb_param.blocked_reasons = blocked_reasons; 607 cb_param.job_done_result = ERROR; 608 609 jq->cb_fn(jq->job_handle, (void *) &cb_param); 610 } 611 612 if (jq->print_ifc != NULL) { 613 jq->print_ifc->destroy(jq->print_ifc); 614 jq->print_ifc = NULL; 615 } 616 617 if (jq->status_ifc != NULL) { 618 jq->status_ifc->destroy(jq->status_ifc); 619 jq->status_ifc = NULL; 620 } 621 622 _unlock(); 623 sem_post(&_job_end_wait_sem); 624 break; 625 626 default: 627 // an error has occurred, report it back to the client 628 sem_post(&_job_start_wait_sem); 629 _lock(); 630 631 if ((jq->job_state != JOB_STATE_BLOCKED) || (jq->blocked_reasons != blocked_reasons)) { 632 jq->job_state = JOB_STATE_BLOCKED; 633 jq->blocked_reasons = blocked_reasons; 634 if (jq->cb_fn) { 635 cb_param.state = JOB_BLOCKED; 636 cb_param.blocked_reasons = blocked_reasons; 637 cb_param.job_done_result = OK; 638 639 jq->cb_fn(jq->job_handle, (void *) &cb_param); 640 } 641 } 642 _unlock(); 643 break; 644 } 645 } 646 647 static void *_job_status_thread(void *param) { 648 _job_queue_t *jq = (_job_queue_t *) param; 649 (jq->status_ifc->start)(jq->status_ifc, _job_status_callback, param); 650 return NULL; 651 } 652 653 static int _start_status_thread(_job_queue_t *jq) { 654 sigset_t allsig, oldsig; 655 int result = ERROR; 656 657 if ((jq == NULL) || (jq->status_ifc == NULL)) { 658 return result; 659 } 660 661 result = OK; 662 sigfillset(&allsig); 663 #if CHECK_PTHREAD_SIGMASK_STATUS 664 result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 665 #else // else CHECK_PTHREAD_SIGMASK_STATUS 666 pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 667 #endif // CHECK_PTHREAD_SIGMASK_STATUS 668 if (result == OK) { 669 result = pthread_create(&_job_status_tid, 0, _job_status_thread, jq); 670 if ((result == ERROR) && (_job_status_tid != pthread_self())) { 671 #if USE_PTHREAD_CANCEL 672 pthread_cancel(_job_status_tid); 673 #else // else USE_PTHREAD_CANCEL 674 pthread_kill(_job_status_tid, SIGKILL); 675 #endif // USE_PTHREAD_CANCEL 676 _job_status_tid = pthread_self(); 677 } 678 } 679 680 if (result == OK) { 681 sched_yield(); 682 #if CHECK_PTHREAD_SIGMASK_STATUS 683 result = pthread_sigmask(SIG_SETMASK, &oldsig, 0); 684 #else // else CHECK_PTHREAD_SIGMASK_STATUS 685 pthread_sigmask(SIG_SETMASK, &oldsig, 0); 686 #endif // CHECK_PTHREAD_SIGMASK_STATUS 687 } 688 return result; 689 } 690 691 /* 692 * Runs a print job. Contains logic for what to do given different printer statuses. 693 */ 694 static void *_job_thread(void *param) { 695 wprint_job_callback_params_t cb_param; 696 _msg_t msg; 697 wJob_t job_handle; 698 _job_queue_t *jq; 699 _page_t page; 700 int i; 701 status_t job_result; 702 int corrupted = 0; 703 704 while (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), WAIT_FOREVER)) { 705 if (msg.id == MSG_RUN_JOB) { 706 LOGI("_job_thread(): Received message: MSG_RUN_JOB"); 707 } else { 708 LOGI("_job_thread(): Received message: MSG_QUIT"); 709 } 710 711 if (msg.id == MSG_QUIT) { 712 break; 713 } 714 715 job_handle = msg.job_id; 716 717 // check if this is a valid job_handle that is still active 718 _lock(); 719 720 jq = _get_job_desc(job_handle); 721 722 // set state to running and invoke the plugin, there is one 723 if (jq) { 724 if (jq->job_state != JOB_STATE_QUEUED) { 725 _unlock(); 726 continue; 727 } 728 corrupted = 0; 729 job_result = OK; 730 jq->job_params.plugin_data = NULL; 731 732 // clear out the semaphore just in case 733 while (sem_trywait(&_job_start_wait_sem) == OK) { 734 } 735 while (sem_trywait(&_job_end_wait_sem) == OK) { 736 } 737 738 // initialize the status ifc 739 if (jq->status_ifc != NULL) { 740 wprint_connect_info_t connect_info; 741 connect_info.printer_addr = jq->printer_addr; 742 connect_info.uri_path = jq->printer_uri; 743 connect_info.port_num = jq->port_num; 744 if (jq->use_secure_uri) { 745 connect_info.uri_scheme = IPPS_PREFIX; 746 } else { 747 connect_info.uri_scheme = IPP_PREFIX; 748 } 749 connect_info.timeout = DEFAULT_IPP_TIMEOUT; 750 jq->status_ifc->init(jq->status_ifc, &connect_info); 751 } 752 // wait for the printer to be idle 753 if ((jq->status_ifc != NULL) && (jq->status_ifc->get_status != NULL)) { 754 int retry = 0; 755 int loop = 1; 756 printer_state_dyn_t printer_state; 757 do { 758 print_status_t status; 759 jq->status_ifc->get_status(jq->status_ifc, &printer_state); 760 status = printer_state.printer_status & ~PRINTER_IDLE_BIT; 761 762 switch (status) { 763 case PRINT_STATUS_IDLE: 764 printer_state.printer_status = PRINT_STATUS_IDLE; 765 jq->blocked_reasons = 0; 766 loop = 0; 767 break; 768 case PRINT_STATUS_UNKNOWN: 769 if (printer_state.printer_reasons[0] == PRINT_STATUS_UNKNOWN) { 770 LOGE("PRINTER STATUS UNKNOWN - Ln 747 libwprint.c"); 771 // no status available, break out and hope for the best 772 printer_state.printer_status = PRINT_STATUS_IDLE; 773 loop = 0; 774 break; 775 } 776 case PRINT_STATUS_SVC_REQUEST: 777 if ((printer_state.printer_reasons[0] == PRINT_STATUS_UNABLE_TO_CONNECT) 778 || (printer_state.printer_reasons[0] == PRINT_STATUS_OFFLINE)) { 779 LOGD("_job_thread: Received an Unable to Connect message"); 780 jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT; 781 loop = 0; 782 break; 783 } 784 default: 785 if (printer_state.printer_status & PRINTER_IDLE_BIT) { 786 LOGD("printer blocked but appears to be in an idle state. " 787 "Allowing job to proceed"); 788 printer_state.printer_status = PRINT_STATUS_IDLE; 789 loop = 0; 790 break; 791 } else if (retry >= MAX_IDLE_WAIT) { 792 jq->blocked_reasons |= BLOCKED_REASONS_PRINTER_BUSY; 793 loop = 0; 794 } else if (!jq->job_params.cancelled) { 795 int blocked_reasons = 0; 796 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { 797 if (printer_state.printer_reasons[i] == 798 PRINT_STATUS_MAX_STATE) { 799 break; 800 } 801 blocked_reasons |= (1 << printer_state.printer_reasons[i]); 802 } 803 if (blocked_reasons == 0) { 804 blocked_reasons |= BLOCKED_REASONS_PRINTER_BUSY; 805 } 806 807 if ((jq->job_state != JOB_STATE_BLOCKED) || 808 (jq->blocked_reasons != blocked_reasons)) { 809 jq->job_state = JOB_STATE_BLOCKED; 810 jq->blocked_reasons = blocked_reasons; 811 if (jq->cb_fn) { 812 cb_param.state = JOB_BLOCKED; 813 cb_param.blocked_reasons = blocked_reasons; 814 cb_param.job_done_result = OK; 815 816 jq->cb_fn(jq->job_handle, (void *) &cb_param); 817 } 818 } 819 _unlock(); 820 sleep(1); 821 _lock(); 822 retry++; 823 } 824 break; 825 } 826 if (jq->job_params.cancelled) { 827 loop = 0; 828 } 829 } while (loop); 830 831 if (jq->job_params.cancelled) { 832 job_result = CANCELLED; 833 } else { 834 job_result = (((printer_state.printer_status & ~PRINTER_IDLE_BIT) == 835 PRINT_STATUS_IDLE) ? OK : ERROR); 836 } 837 } 838 839 _job_status_tid = pthread_self(); 840 if (job_result == OK) { 841 if (jq->print_ifc) { 842 job_result = jq->print_ifc->init(jq->print_ifc, jq->printer_addr, 843 jq->port_num, jq->printer_uri, jq->use_secure_uri); 844 if (job_result == ERROR) { 845 jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT; 846 } 847 } 848 } 849 if (job_result == OK) { 850 _start_status_thread(jq); 851 } 852 853 /* call the plugin's start_job method, if no other job is running 854 use callback to notify the client */ 855 856 if ((job_result == OK) && jq->cb_fn) { 857 cb_param.state = JOB_RUNNING; 858 cb_param.blocked_reasons = 0; 859 cb_param.job_done_result = OK; 860 861 jq->cb_fn(job_handle, (void *) &cb_param); 862 } 863 864 jq->job_params.page_num = -1; 865 if (job_result == OK) { 866 if (jq->print_ifc != NULL) { 867 LOGD("_job_thread: Calling validate_job"); 868 if (jq->print_ifc->validate_job != NULL) { 869 job_result = jq->print_ifc->validate_job(jq->print_ifc, &jq->job_params); 870 } 871 872 /* PDF format plugin's start_job and end_job are to be called for each copy, 873 * inside the for-loop for num_copies. 874 */ 875 876 // Do not call start_job unless validate_job returned OK 877 if ((job_result == OK) && (jq->print_ifc->start_job != NULL) && 878 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) { 879 jq->print_ifc->start_job(jq->print_ifc, &jq->job_params); 880 } 881 } 882 883 // Do not call start_job unless validate_job returned OK 884 if (job_result == OK && jq->plugin->start_job != NULL) { 885 job_result = jq->plugin->start_job(job_handle, (void *) &_wprint_ifc, 886 (void *) jq->print_ifc, &(jq->job_params)); 887 } 888 } 889 890 if (job_result == OK) { 891 jq->job_params.page_num = 0; 892 } 893 894 // multi-page print job 895 if (jq->is_dir && (job_result == OK)) { 896 int per_copy_page_num; 897 for (i = 0; (i < jq->job_params.num_copies) && 898 ((job_result == OK) || (job_result == CORRUPT)) && 899 (!jq->job_params.cancelled); i++) { 900 if ((i > 0) && 901 jq->job_params.copies_supported && 902 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) { 903 LOGD("_job_thread multi_page: breaking out copies supported"); 904 break; 905 } 906 bool pdf_printed = false; 907 if (jq->print_ifc->start_job != NULL && 908 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) { 909 jq->print_ifc->start_job(jq->print_ifc, &jq->job_params); 910 } 911 912 per_copy_page_num = 0; 913 jq->job_state = JOB_STATE_RUNNING; 914 915 // while there is a page to print 916 _unlock(); 917 918 while (OK == msgQReceive(jq->pageQ, (char *) &page, sizeof(page), 919 WAIT_FOREVER)) { 920 _lock(); 921 922 // check for any printing problems so far 923 if (jq->print_ifc->check_status) { 924 if (jq->print_ifc->check_status(jq->print_ifc) == ERROR) { 925 job_result = ERROR; 926 break; 927 } 928 } 929 930 /* take empty filename as cue to break out of the loop 931 * but we have to do last_page processing 932 */ 933 934 // all copies are clubbed together as a single print job 935 if (page.last_page && ((i == jq->job_params.num_copies - 1) || 936 (jq->job_params.copies_supported && 937 strcmp(jq->job_params.print_format, 938 PRINT_FORMAT_PDF) == 0))) { 939 jq->job_params.last_page = page.last_page; 940 } else { 941 jq->job_params.last_page = false; 942 } 943 944 if (strlen(page.filename) > 0) { 945 per_copy_page_num++; 946 { 947 jq->job_params.page_num++; 948 } 949 if (page.pdf_page) { 950 jq->job_params.page_num = page.page_num; 951 } else { 952 jq->job_params.page_num = per_copy_page_num; 953 } 954 955 // setup page margin information 956 jq->job_params.print_top_margin += page.top_margin; 957 jq->job_params.print_left_margin += page.left_margin; 958 jq->job_params.print_right_margin += page.right_margin; 959 jq->job_params.print_bottom_margin += page.bottom_margin; 960 961 jq->job_params.copy_num = (i + 1); 962 jq->job_params.copy_page_num = page.page_num; 963 jq->job_params.page_backside = (per_copy_page_num & 0x1); 964 jq->job_params.page_corrupted = (page.corrupted ? 1 : 0); 965 jq->job_params.page_printing = true; 966 _unlock(); 967 968 if (!page.corrupted) { 969 LOGD("_job_thread(): page not corrupt, calling plugin's print_page" 970 " function for page #%d", page.page_num); 971 if (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0) { 972 job_result = jq->plugin->print_page(&(jq->job_params), 973 jq->mime_type, 974 page.filename); 975 } else if (!pdf_printed) { 976 // for PDF plugin, print_page prints entire document, 977 // so need to be called only once 978 job_result = jq->plugin->print_page(&(jq->job_params), 979 jq->mime_type, 980 page.filename); 981 pdf_printed = true; 982 } 983 } else { 984 LOGD("_job_thread(): page IS corrupt, printing blank page for " 985 "page #%d", page.page_num); 986 job_result = CORRUPT; 987 if ((jq->job_params.duplex != DUPLEX_MODE_NONE) && 988 (jq->plugin->print_blank_page != NULL)) { 989 jq->plugin->print_blank_page(job_handle, &(jq->job_params)); 990 } 991 } 992 _lock(); 993 994 jq->job_params.print_top_margin -= page.top_margin; 995 jq->job_params.print_left_margin -= page.left_margin; 996 jq->job_params.print_right_margin -= page.right_margin; 997 jq->job_params.print_bottom_margin -= page.bottom_margin; 998 jq->job_params.page_printing = false; 999 1000 // make sure we only count corrupted pages once 1001 if (page.corrupted == false) { 1002 page.corrupted = ((job_result == CORRUPT) ? true : false); 1003 corrupted += (job_result == CORRUPT); 1004 } 1005 } 1006 1007 // make sure we always print an even number of pages in duplex jobs 1008 if (page.last_page && (jq->job_params.duplex != DUPLEX_MODE_NONE) 1009 && (jq->job_params.page_backside) 1010 && (jq->plugin->print_blank_page != NULL)) { 1011 _unlock(); 1012 jq->plugin->print_blank_page(job_handle, &(jq->job_params)); 1013 _lock(); 1014 } 1015 1016 // if multiple copies are requested, save the contents of the pageQ message 1017 if (jq->saveQ && !jq->job_params.cancelled && (job_result != ERROR)) { 1018 job_result = msgQSend(jq->saveQ, (char *) &page, 1019 sizeof(page), NO_WAIT, MSG_Q_FIFO); 1020 1021 // swap pageQ and saveQ 1022 if (page.last_page && !jq->job_params.last_page) { 1023 msg_q_id tmpQ = jq->pageQ; 1024 jq->pageQ = jq->saveQ; 1025 jq->saveQ = tmpQ; 1026 1027 // defensive programming 1028 while (msgQNumMsgs(tmpQ) > 0) { 1029 msgQReceive(tmpQ, (char *) &page, sizeof(page), NO_WAIT); 1030 LOGE("pageQ inconsistencies, discarding page #%d, file %s", 1031 page.page_num, page.filename); 1032 } 1033 } 1034 } 1035 1036 if (page.last_page || jq->job_params.cancelled) { 1037 // Leave the sempahore locked 1038 break; 1039 } 1040 1041 // unlock to go back to the top of the while loop 1042 _unlock(); 1043 } // while there is another page 1044 1045 if ((strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0) && 1046 (jq->print_ifc->end_job)) { 1047 int end_job_result = jq->print_ifc->end_job(jq->print_ifc); 1048 if (job_result == OK) { 1049 if (end_job_result == ERROR) { 1050 job_result = ERROR; 1051 } else if (end_job_result == CANCELLED) { 1052 job_result = CANCELLED; 1053 } 1054 } 1055 } 1056 } // for each copy of the job 1057 } else if (job_result == OK) { 1058 // single page job 1059 for (i = 0; ((i < jq->job_params.num_copies) && (job_result == OK)); i++) { 1060 if ((i > 0) && jq->job_params.copies_supported && 1061 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) { 1062 LOGD("_job_thread single_page: breaking out copies supported"); 1063 break; 1064 } 1065 1066 // check for any printing problems so far 1067 if ((jq->print_ifc != NULL) && (jq->print_ifc->check_status)) { 1068 if (jq->print_ifc->check_status(jq->print_ifc) == ERROR) { 1069 job_result = ERROR; 1070 break; 1071 } 1072 } 1073 1074 jq->job_state = JOB_STATE_RUNNING; 1075 jq->job_params.page_num++; 1076 jq->job_params.last_page = (i == (jq->job_params.num_copies - 1)); 1077 jq->job_params.copy_num = (i + 1); 1078 jq->job_params.copy_page_num = 1; 1079 jq->job_params.page_corrupted = (job_result == CORRUPT); 1080 jq->job_params.page_printing = true; 1081 1082 _unlock(); 1083 job_result = jq->plugin->print_page(&(jq->job_params), jq->mime_type, 1084 jq->pathname); 1085 1086 if ((jq->job_params.duplex != DUPLEX_MODE_NONE) 1087 && (jq->plugin->print_blank_page != NULL)) { 1088 jq->plugin->print_blank_page(job_handle, 1089 &(jq->job_params)); 1090 } 1091 1092 _lock(); 1093 jq->job_params.page_printing = false; 1094 1095 corrupted += (job_result == CORRUPT); 1096 } // for each copy 1097 } 1098 1099 // if we started the job end it 1100 if (jq->job_params.page_num >= 0) { 1101 // if the job was cancelled without sending anything through, print a blank sheet 1102 if ((jq->job_params.page_num == 0) 1103 && (jq->plugin->print_blank_page != NULL)) { 1104 jq->plugin->print_blank_page(job_handle, &(jq->job_params)); 1105 } 1106 if (jq->plugin->end_job != NULL) { 1107 jq->plugin->end_job(&(jq->job_params)); 1108 } 1109 if ((jq->print_ifc != NULL) && (jq->print_ifc->end_job) && 1110 (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) { 1111 int end_job_result = jq->print_ifc->end_job(jq->print_ifc); 1112 if (job_result == OK) { 1113 if (end_job_result == ERROR) { 1114 job_result = ERROR; 1115 } else if (end_job_result == CANCELLED) { 1116 job_result = CANCELLED; 1117 } 1118 } 1119 } 1120 } 1121 1122 // if we started to print, wait for idle 1123 if ((jq->job_params.page_num > 0) && (jq->status_ifc != NULL)) { 1124 int retry, result; 1125 _unlock(); 1126 1127 for (retry = 0, result = ERROR; ((result == ERROR) && (retry <= MAX_START_WAIT)); 1128 retry++) { 1129 if (retry != 0) { 1130 sleep(1); 1131 } 1132 result = sem_trywait(&_job_start_wait_sem); 1133 } 1134 1135 if (result == OK) { 1136 for (retry = 0, result = ERROR; ((result == ERROR) && (retry <= MAX_DONE_WAIT)); 1137 retry++) { 1138 if (retry != 0) { 1139 _lock(); 1140 if (jq->job_params.cancelled && !jq->cancel_ok) { 1141 /* The user tried to cancel and it either didn't go through 1142 * or the printer doesn't support cancel through an OID. 1143 * Either way it's pointless to sit here waiting for idle when 1144 * may never come, so we'll bail out early 1145 */ 1146 retry = (MAX_DONE_WAIT + 1); 1147 } 1148 _unlock(); 1149 sleep(1); 1150 if (retry == MAX_DONE_WAIT) { 1151 _lock(); 1152 if (!jq->job_params.cancelled && 1153 (jq->blocked_reasons 1154 & (BLOCKED_REASON_OUT_OF_PAPER 1155 | BLOCKED_REASON_JAMMED 1156 | BLOCKED_REASON_DOOR_OPEN))) { 1157 retry = (MAX_DONE_WAIT - 1); 1158 } 1159 _unlock(); 1160 } 1161 } 1162 result = sem_trywait(&_job_end_wait_sem); 1163 } 1164 } else { 1165 LOGD("_job_thread(): the job never started"); 1166 } 1167 _lock(); 1168 } 1169 1170 // make sure page_num doesn't stay as a negative number 1171 jq->job_params.page_num = MAX(0, jq->job_params.page_num); 1172 _stop_status_thread(jq); 1173 1174 if (corrupted != 0) { 1175 job_result = CORRUPT; 1176 } 1177 1178 LOGI("job_thread(): with job_state value: %d ", jq->job_state); 1179 if ((jq->job_state == JOB_STATE_COMPLETED) || (jq->job_state == JOB_STATE_ERROR) 1180 || (jq->job_state == JOB_STATE_CANCELLED) 1181 || (jq->job_state == JOB_STATE_CORRUPTED) 1182 || (jq->job_state == JOB_STATE_FREE)) { 1183 LOGI("_job_thread(): job finished early: do not send callback again"); 1184 } else { 1185 switch (job_result) { 1186 case OK: 1187 if (!jq->job_params.cancelled) { 1188 jq->job_state = JOB_STATE_COMPLETED; 1189 jq->blocked_reasons = 0; 1190 break; 1191 } else { 1192 job_result = CANCELLED; 1193 } 1194 case CANCELLED: 1195 jq->job_state = JOB_STATE_CANCELLED; 1196 jq->blocked_reasons = BLOCKED_REASONS_CANCELLED; 1197 if (!jq->cancel_ok) { 1198 jq->blocked_reasons |= BLOCKED_REASON_PARTIAL_CANCEL; 1199 } 1200 break; 1201 case CORRUPT: 1202 LOGE("_job_thread(): %d file(s) in the job were corrupted", corrupted); 1203 jq->job_state = JOB_STATE_CORRUPTED; 1204 jq->blocked_reasons = 0; 1205 break; 1206 case ERROR: 1207 default: 1208 LOGE("_job_thread(): ERROR plugin->start_job(%ld): %s => %s", job_handle, 1209 jq->mime_type, jq->job_params.print_format); 1210 job_result = ERROR; 1211 jq->job_state = JOB_STATE_ERROR; 1212 break; 1213 } // job_result 1214 1215 // end of job callback 1216 if (jq->cb_fn) { 1217 cb_param.state = JOB_DONE; 1218 cb_param.blocked_reasons = jq->blocked_reasons; 1219 cb_param.job_done_result = job_result; 1220 1221 jq->cb_fn(job_handle, (void *) &cb_param); 1222 } 1223 1224 if (jq->print_ifc != NULL) { 1225 jq->print_ifc->destroy(jq->print_ifc); 1226 jq->print_ifc = NULL; 1227 } 1228 1229 if (jq->status_ifc != NULL) { 1230 jq->status_ifc->destroy(jq->status_ifc); 1231 jq->status_ifc = NULL; 1232 } 1233 } 1234 } else { 1235 LOGI("_job_thread(): job %ld not in queue .. maybe cancelled", job_handle); 1236 } 1237 1238 _unlock(); 1239 LOGI("_job_thread(): job finished: %ld", job_handle); 1240 } 1241 1242 sem_post(&_job_end_wait_sem); 1243 return NULL; 1244 } 1245 1246 /* 1247 * Starts the wprint background job thread 1248 */ 1249 static int _start_thread(void) { 1250 sigset_t allsig, oldsig; 1251 int result; 1252 1253 _job_tid = pthread_self(); 1254 1255 result = OK; 1256 sigfillset(&allsig); 1257 #if CHECK_PTHREAD_SIGMASK_STATUS 1258 result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 1259 #else // else CHECK_PTHREAD_SIGMASK_STATUS 1260 pthread_sigmask(SIG_SETMASK, &allsig, &oldsig); 1261 #endif // CHECK_PTHREAD_SIGMASK_STATUS 1262 if (result == OK) { 1263 result = pthread_create(&_job_tid, 0, _job_thread, NULL); 1264 if ((result == ERROR) && (_job_tid != pthread_self())) { 1265 #if USE_PTHREAD_CANCEL 1266 pthread_cancel(_job_tid); 1267 #else // else USE_PTHREAD_CANCEL 1268 pthread_kill(_job_tid, SIGKILL); 1269 #endif // USE_PTHREAD_CANCEL 1270 _job_tid = pthread_self(); 1271 } 1272 } 1273 1274 if (result == OK) { 1275 sched_yield(); 1276 #if CHECK_PTHREAD_SIGMASK_STATUS 1277 result = pthread_sigmask(SIG_SETMASK, &oldsig, 0); 1278 #else // else CHECK_PTHREAD_SIGMASK_STATUS 1279 pthread_sigmask(SIG_SETMASK, &oldsig, 0); 1280 #endif // CHECK_PTHREAD_SIGMASK_STATUS 1281 } 1282 1283 return result; 1284 } 1285 1286 /* 1287 * Waits for the job thread to reach a stopped state 1288 */ 1289 static int _stop_thread(void) { 1290 if (!pthread_equal(_job_tid, pthread_self())) { 1291 pthread_join(_job_tid, 0); 1292 _job_tid = pthread_self(); 1293 return OK; 1294 } else { 1295 return ERROR; 1296 } 1297 } 1298 1299 static const wprint_io_plugin_t _file_io_plugin = { 1300 .version = WPRINT_PLUGIN_VERSION(_INTERFACE_MINOR_VERSION), 1301 .port_num = PORT_FILE, .getCapsIFC = NULL, .getStatusIFC = NULL, 1302 .getPrintIFC = _printer_file_connect,}; 1303 1304 static const wprint_io_plugin_t _ipp_io_plugin = { 1305 .version = WPRINT_PLUGIN_VERSION(_INTERFACE_MINOR_VERSION), 1306 .port_num = PORT_IPP, .getCapsIFC = ipp_status_get_capabilities_ifc, 1307 .getStatusIFC = ipp_status_get_monitor_ifc, .getPrintIFC = ipp_get_print_ifc,}; 1308 1309 static void _setup_io_plugins() { 1310 _io_plugins[0].port_num = PORT_FILE; 1311 _io_plugins[0].io_plugin = &_file_io_plugin; 1312 1313 _io_plugins[1].port_num = PORT_IPP; 1314 _io_plugins[1].io_plugin = &_ipp_io_plugin; 1315 } 1316 1317 extern wprint_plugin_t *libwprintplugin_pcl_reg(void); 1318 1319 extern wprint_plugin_t *libwprintplugin_pdf_reg(void); 1320 1321 static void _setup_print_plugins() { 1322 plugin_reset(); 1323 plugin_add(libwprintplugin_pcl_reg()); 1324 plugin_add(libwprintplugin_pdf_reg()); 1325 } 1326 1327 bool wprintIsRunning() { 1328 return _msgQ != 0; 1329 } 1330 1331 int wprintInit(void) { 1332 int count = 0; 1333 1334 _setup_print_plugins(); 1335 _setup_io_plugins(); 1336 1337 _msgQ = msgQCreate(_MAX_MSGS, sizeof(_msg_t)); 1338 1339 if (!_msgQ) { 1340 LOGE("ERROR: cannot create msgQ"); 1341 return ERROR; 1342 } 1343 1344 sem_init(&_job_end_wait_sem, 0, 0); 1345 sem_init(&_job_start_wait_sem, 0, 0); 1346 1347 signal(SIGPIPE, SIG_IGN); // avoid broken pipe process shutdowns 1348 pthread_mutexattr_settype(&_q_lock_attr, PTHREAD_MUTEX_RECURSIVE_NP); 1349 pthread_mutex_init(&_q_lock, &_q_lock_attr); 1350 1351 if (_start_thread() != OK) { 1352 LOGE("could not start job thread"); 1353 return ERROR; 1354 } 1355 return count; 1356 } 1357 1358 static const printer_capabilities_t _default_cap = {.color = true, .borderless = true, 1359 .numSupportedMediaSizes = 0, .numSupportedMediaTrays = 0, 1360 .numSupportedMediaTypes = 0,}; 1361 1362 /* 1363 * Check if a media size is supported 1364 */ 1365 static bool is_supported(media_size_t media_size) { 1366 int i; 1367 for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { 1368 if (SupportedMediaSizes[i].media_size == media_size) return true; 1369 } 1370 return false; 1371 } 1372 1373 /* 1374 * Checks printers reported media sizes and validates that wprint supports them 1375 */ 1376 static void _validate_supported_media_sizes(printer_capabilities_t *printer_cap) { 1377 if (printer_cap == NULL) return; 1378 1379 if (printer_cap->numSupportedMediaSizes == 0) { 1380 unsigned int i = 0; 1381 printer_cap->supportedMediaSizes[i++] = ISO_A4; 1382 printer_cap->supportedMediaSizes[i++] = US_LETTER; 1383 printer_cap->supportedMediaSizes[i++] = INDEX_CARD_4X6; 1384 printer_cap->supportedMediaSizes[i++] = INDEX_CARD_5X7; 1385 printer_cap->numSupportedMediaSizes = i; 1386 } else { 1387 unsigned int read, write; 1388 for (read = write = 0; read < printer_cap->numSupportedMediaSizes; read++) { 1389 if (is_supported(printer_cap->supportedMediaSizes[read])) { 1390 printer_cap->supportedMediaSizes[write++] = 1391 printer_cap->supportedMediaSizes[read]; 1392 } 1393 } 1394 printer_cap->numSupportedMediaSizes = write; 1395 } 1396 } 1397 1398 /* 1399 * Checks printers numSupportedMediaTrays. If none, then add Auto. 1400 */ 1401 static void _validate_supported_media_trays(printer_capabilities_t *printer_cap) { 1402 if (printer_cap == NULL) return; 1403 1404 if (printer_cap->numSupportedMediaTrays == 0) { 1405 printer_cap->supportedMediaTrays[0] = TRAY_SRC_AUTO_SELECT; 1406 printer_cap->numSupportedMediaTrays = 1; 1407 } 1408 } 1409 1410 /* 1411 * Add a printer's supported input formats to the capabilities struct 1412 */ 1413 static void _collect_supported_input_formats(printer_capabilities_t *printer_caps) { 1414 unsigned long long input_formats = 0; 1415 plugin_get_passthru_input_formats(&input_formats); 1416 1417 // remove things the printer can't support 1418 if (!printer_caps->canPrintPDF) { 1419 input_formats &= ~(1 << INPUT_MIME_TYPE_PDF); 1420 } 1421 if (!printer_caps->canPrintPCLm) { 1422 input_formats &= ~(1 << INPUT_MIME_TYPE_PCLM); 1423 } 1424 if (!printer_caps->canPrintPWG) { 1425 input_formats &= ~(1 << INPUT_MIME_TYPE_PWG); 1426 } 1427 printer_caps->supportedInputMimeTypes = input_formats; 1428 } 1429 1430 /* 1431 * Check the print resolutions supported by the printer and verify that wprint supports them. 1432 * If nothing is found, the desired resolution is selected. 1433 */ 1434 static unsigned int _findCloseResolutionSupported(int desiredResolution, int maxResolution, 1435 const printer_capabilities_t *printer_cap) { 1436 int closeResolution = 0; 1437 int closeDifference = 0; 1438 unsigned int index = 0; 1439 for (index = 0; index < printer_cap->numSupportedResolutions; index++) { 1440 int resolution = printer_cap->supportedResolutions[index]; 1441 if (resolution == desiredResolution) { 1442 // An exact match wins.. stop looking. 1443 return resolution; 1444 } else { 1445 int difference = abs(desiredResolution - resolution); 1446 if ((closeResolution == 0) || (difference < closeDifference)) { 1447 if (resolution <= maxResolution) { 1448 // We found a better match now.. record it but keep looking. 1449 closeResolution = resolution; 1450 closeDifference = difference; 1451 } 1452 } 1453 } 1454 } 1455 1456 // If we get here we did not find an exact match. 1457 if (closeResolution == 0) { 1458 // We did not find anything.. just pick the desired value. 1459 closeResolution = desiredResolution; 1460 } 1461 return closeResolution; 1462 } 1463 1464 status_t wprintGetCapabilities(const wprint_connect_info_t *connect_info, 1465 printer_capabilities_t *printer_cap) { 1466 LOGD("wprintGetCapabilities: Enter"); 1467 status_t result = ERROR; 1468 int index; 1469 int port_num = connect_info->port_num; 1470 const ifc_printer_capabilities_t *caps_ifc = NULL; 1471 1472 memcpy(printer_cap, &_default_cap, sizeof(printer_capabilities_t)); 1473 1474 caps_ifc = _get_caps_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP)); 1475 LOGD("wprintGetCapabilities: after getting caps ifc: %p", caps_ifc); 1476 switch (port_num) { 1477 case PORT_FILE: 1478 printer_cap->duplex = 1; 1479 printer_cap->borderless = 1; 1480 printer_cap->canPrintPCLm = (_default_pcl_type == PCLm); 1481 printer_cap->canPrintPWG = (_default_pcl_type == PCLPWG); 1482 printer_cap->stripHeight = STRIPE_HEIGHT; 1483 result = OK; 1484 break; 1485 default: 1486 break; 1487 } 1488 1489 if (caps_ifc != NULL) { 1490 caps_ifc->init(caps_ifc, connect_info); 1491 result = caps_ifc->get_capabilities(caps_ifc, printer_cap); 1492 caps_ifc->destroy(caps_ifc); 1493 } 1494 1495 _validate_supported_media_sizes(printer_cap); 1496 _collect_supported_input_formats(printer_cap); 1497 _validate_supported_media_trays(printer_cap); 1498 1499 printer_cap->isSupported = (printer_cap->canPrintPCLm || printer_cap->canPrintPDF || 1500 printer_cap->canPrintPWG); 1501 1502 if (result == OK) { 1503 LOGD("\tmake: %s", printer_cap->make); 1504 LOGD("\thas color: %d", printer_cap->color); 1505 LOGD("\tcan duplex: %d", printer_cap->duplex); 1506 LOGD("\tcan rotate back page: %d", printer_cap->canRotateDuplexBackPage); 1507 LOGD("\tcan print borderless: %d", printer_cap->borderless); 1508 LOGD("\tcan print pdf: %d", printer_cap->canPrintPDF); 1509 LOGD("\tcan print pclm: %d", printer_cap->canPrintPCLm); 1510 LOGD("\tcan print pwg: %d", printer_cap->canPrintPWG); 1511 LOGD("\tsource application name supported: %d", printer_cap->docSourceAppName); 1512 LOGD("\tsource application version supported: %d", printer_cap->docSourceAppVersion); 1513 LOGD("\tsource os name supported: %d", printer_cap->docSourceOsName); 1514 LOGD("\tsource os version supported: %d", printer_cap->docSourceOsVersion); 1515 LOGD("\tprinter supported: %d", printer_cap->isSupported); 1516 LOGD("\tstrip height: %d", printer_cap->stripHeight); 1517 LOGD("\tinkjet: %d", printer_cap->inkjet); 1518 LOGD("\tresolutions supported:"); 1519 for (index = 0; index < printer_cap->numSupportedResolutions; index++) { 1520 LOGD("\t (%d dpi)", printer_cap->supportedResolutions[index]); 1521 } 1522 } 1523 LOGD("wprintGetCapabilities: Exit"); 1524 return result; 1525 } 1526 1527 /* 1528 * Returns a preferred print format supported by the printer 1529 */ 1530 static char *_get_print_format(const char *mime_type, const wprint_job_params_t *job_params, 1531 const printer_capabilities_t *cap) { 1532 char *print_format = NULL; 1533 1534 errno = OK; 1535 1536 if (((strcmp(mime_type, MIME_TYPE_PDF) == 0) && cap->canPrintPDF)) { 1537 // For content type=photo and a printer that supports both PCLm and PDF, 1538 // prefer PCLm over PDF. 1539 if (job_params && (strcasecmp(job_params->docCategory, "photo") == 0) && 1540 cap->canPrintPCLm) { 1541 print_format = PRINT_FORMAT_PCLM; 1542 LOGI("_get_print_format(): print_format switched from PDF to PCLm"); 1543 } else { 1544 print_format = PRINT_FORMAT_PDF; 1545 } 1546 } else if (cap->canPrintPCLm || cap->canPrintPDF) { 1547 // PCLm is a subset of PDF 1548 print_format = PRINT_FORMAT_PCLM; 1549 #if (USE_PWG_OVER_PCLM != 0) 1550 if (cap->canPrintPWG) { 1551 print_format = PRINT_FORMAT_PWG; 1552 } 1553 #endif // (USE_PWG_OVER_PCLM != 0) 1554 } else if (cap->canPrintPWG) { 1555 print_format = PRINT_FORMAT_PWG; 1556 } else { 1557 errno = EBADRQC; 1558 } 1559 1560 if (print_format != NULL) { 1561 LOGI("\t_get_print_format(): print_format: %s", print_format); 1562 } 1563 1564 return print_format; 1565 } 1566 1567 status_t wprintGetDefaultJobParams(wprint_job_params_t *job_params) { 1568 status_t result = ERROR; 1569 static const wprint_job_params_t _default_job_params = {.print_format = _DEFAULT_PRINT_FORMAT, 1570 .pcl_type = _DEFAULT_PCL_TYPE, .media_size = US_LETTER, .media_type = MEDIA_PLAIN, 1571 .duplex = DUPLEX_MODE_NONE, .dry_time = DUPLEX_DRY_TIME_NORMAL, 1572 .color_space = COLOR_SPACE_COLOR, .media_tray = TRAY_SRC_AUTO_SELECT, 1573 .pixel_units = DEFAULT_RESOLUTION, .render_flags = 0, .num_copies =1, 1574 .borderless = false, .cancelled = false, .renderInReverseOrder = false, 1575 .ipp_1_0_supported = false, .ipp_2_0_supported = false, .epcl_ipp_supported = false, 1576 .strip_height = STRIPE_HEIGHT, .docCategory = {0}, 1577 .copies_supported = false}; 1578 1579 if (job_params == NULL) return result; 1580 1581 memcpy(job_params, &_default_job_params, sizeof(_default_job_params)); 1582 1583 return OK; 1584 } 1585 1586 status_t wprintGetFinalJobParams(wprint_job_params_t *job_params, 1587 const printer_capabilities_t *printer_cap) { 1588 int i; 1589 status_t result = ERROR; 1590 float margins[NUM_PAGE_MARGINS]; 1591 1592 if (job_params == NULL) { 1593 return result; 1594 } 1595 result = OK; 1596 1597 job_params->accepts_pclm = printer_cap->canPrintPCLm; 1598 job_params->accepts_pdf = printer_cap->canPrintPDF; 1599 job_params->media_default = printer_cap->mediaDefault; 1600 1601 if (printer_cap->ePclIppVersion == 1) { 1602 job_params->epcl_ipp_supported = true; 1603 } 1604 1605 if (printer_cap->canCopy) { 1606 job_params->copies_supported = true; 1607 } 1608 1609 if (printer_cap->ippVersionMajor == 2) { 1610 job_params->ipp_1_0_supported = true; 1611 job_params->ipp_2_0_supported = true; 1612 } else if (printer_cap->ippVersionMajor == 1) { 1613 job_params->ipp_1_0_supported = true; 1614 job_params->ipp_2_0_supported = false; 1615 } 1616 1617 if (!printer_cap->color) { 1618 job_params->color_space = COLOR_SPACE_MONO; 1619 } 1620 1621 if (printer_cap->canPrintPCLm || printer_cap->canPrintPDF) { 1622 job_params->pcl_type = PCLm; 1623 #if (USE_PWG_OVER_PCLM != 0) 1624 if ( printer_cap->canPrintPWG) { 1625 job_params->pcl_type = PCLPWG; 1626 } 1627 #endif // (USE_PWG_OVER_PCLM != 0) 1628 } else if (printer_cap->canPrintPWG) { 1629 job_params->pcl_type = PCLPWG; 1630 } 1631 1632 LOGD("wprintGetFinalJobParams: Using PCL Type %s", getPCLTypeString(job_params->pcl_type)); 1633 1634 // set strip height 1635 job_params->strip_height = printer_cap->stripHeight; 1636 1637 // make sure the number of copies is valid 1638 if (job_params->num_copies <= 0) { 1639 job_params->num_copies = 1; 1640 } 1641 1642 // confirm that the media size is supported 1643 for (i = 0; i < printer_cap->numSupportedMediaSizes; i++) { 1644 if (job_params->media_size == printer_cap->supportedMediaSizes[i]) { 1645 break; 1646 } 1647 } 1648 1649 if (i >= printer_cap->numSupportedMediaSizes) { 1650 job_params->media_size = ISO_A4; 1651 job_params->media_tray = TRAY_SRC_AUTO_SELECT; 1652 } 1653 1654 // check that we support the media tray 1655 for (i = 0; i < printer_cap->numSupportedMediaTrays; i++) { 1656 if (job_params->media_tray == printer_cap->supportedMediaTrays[i]) { 1657 break; 1658 } 1659 } 1660 1661 // media tray not supported, default to automatic 1662 if (i >= printer_cap->numSupportedMediaTrays) { 1663 job_params->media_tray = TRAY_SRC_AUTO_SELECT; 1664 } 1665 1666 if (printer_cap->isMediaSizeNameSupported == true) { 1667 job_params->media_size_name = true; 1668 } else { 1669 job_params->media_size_name = false; 1670 } 1671 1672 // verify borderless setting 1673 if ((job_params->borderless == true) && !printer_cap->borderless) { 1674 job_params->borderless = false; 1675 } 1676 1677 // borderless and margins don't get along 1678 if (job_params->borderless && 1679 ((job_params->job_top_margin > 0.0f) || (job_params->job_left_margin > 0.0f) || 1680 (job_params->job_right_margin > 0.0f) 1681 || (job_params->job_bottom_margin > 0.0f))) { 1682 job_params->borderless = false; 1683 } 1684 1685 // verify duplex setting 1686 if ((job_params->duplex != DUPLEX_MODE_NONE) && !printer_cap->duplex) { 1687 job_params->duplex = DUPLEX_MODE_NONE; 1688 } 1689 1690 // borderless and duplex don't get along either 1691 if (job_params->borderless && (job_params->duplex != DUPLEX_MODE_NONE)) { 1692 job_params->duplex = DUPLEX_MODE_NONE; 1693 } 1694 1695 if ((job_params->duplex == DUPLEX_MODE_BOOK) 1696 && !printer_cap->canRotateDuplexBackPage) { 1697 job_params->render_flags |= RENDER_FLAG_ROTATE_BACK_PAGE; 1698 } 1699 1700 if (job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) { 1701 LOGD("wprintGetFinalJobParams: Duplex is on and device needs back page rotated."); 1702 } 1703 1704 if ((job_params->duplex == DUPLEX_MODE_NONE) && !printer_cap->faceDownTray) { 1705 job_params->renderInReverseOrder = true; 1706 } else { 1707 job_params->renderInReverseOrder = false; 1708 } 1709 1710 if (job_params->render_flags & RENDER_FLAG_AUTO_SCALE) { 1711 job_params->render_flags |= AUTO_SCALE_RENDER_FLAGS; 1712 } else if (job_params->render_flags & RENDER_FLAG_AUTO_FIT) { 1713 job_params->render_flags |= AUTO_FIT_RENDER_FLAGS; 1714 } 1715 1716 job_params->pixel_units = _findCloseResolutionSupported(DEFAULT_RESOLUTION, 1717 MAX_SUPPORTED_RESOLUTION, printer_cap); 1718 1719 printable_area_get_default_margins(job_params, printer_cap, &margins[TOP_MARGIN], 1720 &margins[LEFT_MARGIN], &margins[RIGHT_MARGIN], &margins[BOTTOM_MARGIN]); 1721 printable_area_get(job_params, margins[TOP_MARGIN], margins[LEFT_MARGIN], margins[RIGHT_MARGIN], 1722 margins[BOTTOM_MARGIN]); 1723 1724 job_params->accepts_app_name = printer_cap->docSourceAppName; 1725 job_params->accepts_app_version = printer_cap->docSourceAppVersion; 1726 job_params->accepts_os_name = printer_cap->docSourceOsName; 1727 job_params->accepts_os_version = printer_cap->docSourceOsVersion; 1728 1729 return result; 1730 } 1731 1732 wJob_t wprintStartJob(const char *printer_addr, port_t port_num, 1733 const wprint_job_params_t *job_params, const printer_capabilities_t *printer_cap, 1734 const char *mime_type, const char *pathname, wprint_status_cb_t cb_fn, 1735 const char *debugDir, const char *scheme) { 1736 wJob_t job_handle = WPRINT_BAD_JOB_HANDLE; 1737 _msg_t msg; 1738 struct stat stat_buf; 1739 bool is_dir = false; 1740 _job_queue_t *jq; 1741 wprint_plugin_t *plugin = NULL; 1742 char *print_format; 1743 ifc_print_job_t *print_ifc; 1744 1745 if (mime_type == NULL) { 1746 errno = EINVAL; 1747 return job_handle; 1748 } 1749 1750 print_format = _get_print_format(mime_type, job_params, printer_cap); 1751 if (print_format == NULL) return job_handle; 1752 1753 // check to see if we have an appropriate plugin 1754 if (OK == stat(pathname, &stat_buf)) { 1755 if (S_ISDIR(stat_buf.st_mode)) { 1756 is_dir = true; 1757 } else if (stat_buf.st_size == 0) { 1758 errno = EBADF; 1759 return job_handle; 1760 } 1761 } else { 1762 errno = ENOENT; 1763 return job_handle; 1764 } 1765 1766 // Make sure we have job_params 1767 if (job_params == NULL) { 1768 errno = ECOMM; 1769 return job_handle; 1770 } 1771 1772 plugin = plugin_search(mime_type, print_format); 1773 _lock(); 1774 1775 if (plugin) { 1776 job_handle = _get_handle(); 1777 if (job_handle == WPRINT_BAD_JOB_HANDLE) { 1778 errno = EAGAIN; 1779 } 1780 } else { 1781 errno = ENOSYS; 1782 LOGE("wprintStartJob(): ERROR: no plugin found for %s => %s", mime_type, print_format); 1783 } 1784 1785 if (job_handle != WPRINT_BAD_JOB_HANDLE) { 1786 print_ifc = (ifc_print_job_t *) _get_print_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP)); 1787 1788 // fill out the job queue record 1789 jq = _get_job_desc(job_handle); 1790 if (jq == NULL) { 1791 _recycle_handle(job_handle); 1792 job_handle = WPRINT_BAD_JOB_HANDLE; 1793 _unlock(); 1794 return job_handle; 1795 } 1796 1797 if (debugDir != NULL) { 1798 strncpy(jq->debug_path, debugDir, MAX_PATHNAME_LENGTH); 1799 jq->debug_path[MAX_PATHNAME_LENGTH] = 0; 1800 } 1801 1802 strncpy(jq->printer_addr, printer_addr, MAX_PRINTER_ADDR_LENGTH); 1803 strncpy(jq->mime_type, mime_type, MAX_MIME_LENGTH); 1804 strncpy(jq->pathname, pathname, MAX_PATHNAME_LENGTH); 1805 1806 jq->port_num = port_num; 1807 jq->cb_fn = cb_fn; 1808 jq->print_ifc = print_ifc; 1809 jq->cancel_ok = true; // assume cancel is ok 1810 jq->plugin = plugin; 1811 memcpy(jq->printer_uri, printer_cap->httpResource, 1812 MIN(ARRAY_SIZE(printer_cap->httpResource), ARRAY_SIZE(jq->printer_uri))); 1813 1814 jq->status_ifc = _get_status_ifc(((port_num == 0) ? PORT_FILE : PORT_IPP)); 1815 1816 memcpy((char *) &(jq->job_params), job_params, sizeof(wprint_job_params_t)); 1817 1818 jq->use_secure_uri = (strstr(scheme, IPPS_PREFIX) != NULL); 1819 1820 size_t useragent_len = strlen(USERAGENT_PREFIX) + strlen(jq->job_params.docCategory) + 1; 1821 char *useragent = (char *) malloc(useragent_len); 1822 if (useragent != NULL) { 1823 snprintf(useragent, useragent_len, USERAGENT_PREFIX "%s", jq->job_params.docCategory); 1824 jq->job_params.useragent = useragent; 1825 } 1826 1827 jq->job_params.page_num = 0; 1828 jq->job_params.print_format = print_format; 1829 if (strcmp(print_format, PRINT_FORMAT_PCLM) == 0) { 1830 if (printer_cap->canPrintPCLm || printer_cap->canPrintPDF) { 1831 jq->job_params.pcl_type = PCLm; 1832 } else { 1833 jq->job_params.pcl_type = PCLNONE; 1834 } 1835 } 1836 1837 if (strcmp(print_format, PRINT_FORMAT_PWG) == 0) { 1838 if (printer_cap->canPrintPWG) { 1839 jq->job_params.pcl_type = PCLPWG; 1840 } else { 1841 jq->job_params.pcl_type = PCLNONE; 1842 } 1843 } 1844 1845 // if the pathname is a directory, then this is a multi-page job with individual pages 1846 if (is_dir) { 1847 jq->is_dir = true; 1848 jq->num_pages = 0; 1849 1850 // create a pageQ for queuing page information 1851 jq->pageQ = msgQCreate(_MAX_PAGES_PER_JOB, sizeof(_page_t)); 1852 1853 // create a secondary page Q for subsequently saving page data for copies #2 to n 1854 if (jq->job_params.num_copies > 1) { 1855 jq->saveQ = msgQCreate(_MAX_PAGES_PER_JOB, sizeof(_page_t)); 1856 } 1857 } else { 1858 jq->num_pages = 1; 1859 } 1860 1861 // post a message with job_handle to the msgQ that is serviced by a thread 1862 msg.id = MSG_RUN_JOB; 1863 msg.job_id = job_handle; 1864 1865 if (print_ifc && plugin && plugin->print_page && 1866 (msgQSend(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT, MSG_Q_FIFO) == OK)) { 1867 errno = OK; 1868 LOGD("wprintStartJob(): print job %ld queued (%s => %s)", job_handle, 1869 mime_type, print_format); 1870 } else { 1871 if (print_ifc == NULL) { 1872 errno = EAFNOSUPPORT; 1873 } else if ((plugin == NULL) || (plugin->print_page == NULL)) { 1874 errno = ELIBACC; 1875 } else { 1876 errno = EBADMSG; 1877 } 1878 1879 LOGE("wprintStartJob(): ERROR plugin->start_job(%ld) : %s => %s", job_handle, 1880 mime_type, print_format); 1881 jq->job_state = JOB_STATE_ERROR; 1882 _recycle_handle(job_handle); 1883 job_handle = WPRINT_BAD_JOB_HANDLE; 1884 } 1885 } 1886 _unlock(); 1887 return job_handle; 1888 } 1889 1890 status_t wprintEndJob(wJob_t job_handle) { 1891 _page_t page; 1892 _job_queue_t *jq; 1893 status_t result = ERROR; 1894 1895 _lock(); 1896 jq = _get_job_desc(job_handle); 1897 1898 if (jq) { 1899 // if the job is done and is to be freed, do it 1900 if ((jq->job_state == JOB_STATE_CANCELLED) || (jq->job_state == JOB_STATE_ERROR) || 1901 (jq->job_state == JOB_STATE_CORRUPTED) || (jq->job_state == JOB_STATE_COMPLETED)) { 1902 result = OK; 1903 if (jq->pageQ) { 1904 while ((msgQNumMsgs(jq->pageQ) > 0) 1905 && (msgQReceive(jq->pageQ, (char *) &page, sizeof(page), 1906 WAIT_FOREVER) == OK)) { 1907 } 1908 result |= msgQDelete(jq->pageQ); 1909 jq->pageQ = NULL; 1910 } 1911 1912 if (jq->saveQ) { 1913 while ((msgQNumMsgs(jq->saveQ) > 0) 1914 && (msgQReceive(jq->saveQ, (char *) &page, sizeof(page), 1915 WAIT_FOREVER) == OK)) { 1916 } 1917 result |= msgQDelete(jq->saveQ); 1918 jq->saveQ = NULL; 1919 } 1920 _recycle_handle(job_handle); 1921 } else { 1922 LOGE("job %ld cannot be ended from state %d", job_handle, jq->job_state); 1923 } 1924 } else { 1925 LOGE("ERROR: wprintEndJob(%ld), job not found", job_handle); 1926 } 1927 1928 _unlock(); 1929 return result; 1930 } 1931 1932 status_t wprintPage(wJob_t job_handle, int page_num, const char *filename, bool last_page, 1933 bool pdf_page, unsigned int top_margin, unsigned int left_margin, unsigned int right_margin, 1934 unsigned int bottom_margin) { 1935 _job_queue_t *jq; 1936 _page_t page; 1937 status_t result = ERROR; 1938 struct stat stat_buf; 1939 1940 _lock(); 1941 jq = _get_job_desc(job_handle); 1942 1943 // use empty string to indicate EOJ for an empty job 1944 if (!filename) { 1945 filename = ""; 1946 last_page = true; 1947 } else if (OK == stat(filename, &stat_buf)) { 1948 if (!S_ISREG(stat_buf.st_mode) || (stat_buf.st_size == 0)) { 1949 _unlock(); 1950 return result; 1951 } 1952 } else { 1953 _unlock(); 1954 return result; 1955 } 1956 1957 // must be setup as a multi-page job, page_num must be valid, and filename must fit 1958 if (jq && jq->is_dir && !(jq->last_page_seen) && (((strlen(filename) < MAX_PATHNAME_LENGTH)) || 1959 (jq && (strcmp(filename, "") == 0) && last_page))) { 1960 memset(&page, 0, sizeof(page)); 1961 page.page_num = page_num; 1962 page.corrupted = false; 1963 page.pdf_page = pdf_page; 1964 page.last_page = last_page; 1965 page.top_margin = top_margin; 1966 page.left_margin = left_margin; 1967 page.right_margin = right_margin; 1968 page.bottom_margin = bottom_margin; 1969 1970 if ((strlen(filename) == 0) || strchr(filename, '/')) { 1971 // assume empty or complete pathname and use it as it is 1972 strncpy(page.filename, filename, MAX_PATHNAME_LENGTH); 1973 } else { 1974 // generate a complete pathname 1975 snprintf(page.filename, MAX_PATHNAME_LENGTH, "%s/%s", jq->pathname, filename); 1976 } 1977 1978 if (last_page) { 1979 jq->last_page_seen = true; 1980 } 1981 1982 result = msgQSend(jq->pageQ, (char *) &page, sizeof(page), NO_WAIT, MSG_Q_FIFO); 1983 } 1984 1985 if (result == OK) { 1986 LOGD("wprintPage(%ld, %d, %s, %d)", job_handle, page_num, filename, last_page); 1987 if (!(last_page && (strcmp(filename, "") == 0))) { 1988 jq->num_pages++; 1989 } 1990 } else { 1991 LOGE("wprintPage(%ld, %d, %s, %d)", job_handle, page_num, filename, last_page); 1992 } 1993 1994 _unlock(); 1995 return result; 1996 } 1997 1998 status_t wprintCancelJob(wJob_t job_handle) { 1999 _job_queue_t *jq; 2000 status_t result; 2001 2002 _lock(); 2003 2004 jq = _get_job_desc(job_handle); 2005 2006 if (jq) { 2007 LOGI("received cancel request"); 2008 // send a dummy page in case we're waiting on the msgQ page receive 2009 if ((jq->job_state == JOB_STATE_RUNNING) || (jq->job_state == JOB_STATE_BLOCKED)) { 2010 bool enableTimeout = true; 2011 jq->cancel_ok = true; 2012 jq->job_params.cancelled = true; 2013 wprintPage(job_handle, jq->num_pages + 1, NULL, true, false, 0, 0, 0, 0); 2014 if (jq->status_ifc) { 2015 // are we blocked waiting for the job to start 2016 if ((jq->job_state != JOB_STATE_BLOCKED) || (jq->job_params.page_num != 0)) { 2017 errno = OK; 2018 jq->cancel_ok = ((jq->status_ifc->cancel)(jq->status_ifc, 2019 jq->job_params.job_originating_user_name) == 0); 2020 if ((jq->cancel_ok == true) && (errno != OK)) { 2021 enableTimeout = false; 2022 } 2023 } 2024 } 2025 if (!jq->cancel_ok) { 2026 LOGE("CANCEL did not go through or is not supported for this device"); 2027 enableTimeout = true; 2028 } 2029 if (enableTimeout && (jq->print_ifc != NULL) && 2030 (jq->print_ifc->enable_timeout != NULL)) { 2031 jq->print_ifc->enable_timeout(jq->print_ifc, 1); 2032 } 2033 2034 errno = (jq->cancel_ok ? OK : ENOTSUP); 2035 jq->job_state = JOB_STATE_CANCEL_REQUEST; 2036 result = OK; 2037 } else if ((jq->job_state == JOB_STATE_CANCEL_REQUEST) || 2038 (jq->job_state == JOB_STATE_CANCELLED)) { 2039 result = OK; 2040 errno = (jq->cancel_ok ? OK : ENOTSUP); 2041 } else if (jq->job_state == JOB_STATE_QUEUED) { 2042 jq->job_params.cancelled = true; 2043 jq->job_state = JOB_STATE_CANCELLED; 2044 2045 if (jq->cb_fn) { 2046 wprint_job_callback_params_t cb_param; 2047 cb_param.state = JOB_DONE; 2048 cb_param.blocked_reasons = BLOCKED_REASONS_CANCELLED; 2049 cb_param.job_done_result = CANCELLED; 2050 2051 jq->cb_fn(job_handle, (void *) &cb_param); 2052 } 2053 2054 errno = OK; 2055 result = OK; 2056 } else { 2057 LOGE("job in other state"); 2058 result = ERROR; 2059 errno = EBADRQC; 2060 } 2061 } else { 2062 LOGE("could not find job"); 2063 result = ERROR; 2064 errno = EBADR; 2065 } 2066 2067 _unlock(); 2068 2069 return result; 2070 } 2071 2072 status_t wprintExit(void) { 2073 _msg_t msg; 2074 2075 if (_msgQ) { 2076 // toss the remaining messages in the msgQ 2077 while ((msgQNumMsgs(_msgQ) > 0) && 2078 (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT))) {} 2079 2080 // send a quit message 2081 msg.id = MSG_QUIT; 2082 msgQSend(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT, MSG_Q_FIFO); 2083 2084 // stop the job thread 2085 _stop_thread(); 2086 2087 // empty out the semaphore 2088 while (sem_trywait(&_job_end_wait_sem) == OK); 2089 while (sem_trywait(&_job_start_wait_sem) == OK); 2090 2091 // receive any messages just in case 2092 while ((msgQNumMsgs(_msgQ) > 0) 2093 && (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), NO_WAIT))) {} 2094 2095 // delete the msgQ 2096 msgQDelete(_msgQ); 2097 _msgQ = NULL; 2098 2099 sem_destroy(&_job_end_wait_sem); 2100 sem_destroy(&_job_start_wait_sem); 2101 pthread_mutex_destroy(&_q_lock); 2102 } 2103 2104 return OK; 2105 } 2106 2107 void wprintSetSourceInfo(const char *appName, const char *appVersion, const char *osName) { 2108 if (appName) { 2109 strncpy(g_appName, appName, (sizeof(g_appName) - 1)); 2110 } 2111 2112 if (appVersion) { 2113 strncpy(g_appVersion, appVersion, (sizeof(g_appVersion) - 1)); 2114 } 2115 2116 if (osName) { 2117 strncpy(g_osName, osName, (sizeof(g_osName) - 1)); 2118 } 2119 2120 LOGI("App Name: '%s', Version: '%s', OS: '%s'", g_appName, g_appVersion, g_osName); 2121 } 2122